fix(rust): the handoff says what the run did, not that a link exists (D156)
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 58s

`install --game rust` ended every instance with "Rust server "alpha" is
connected to its sidecar" — printed unconditionally, before the plugin had
dialled anything, on a stopped server where it had not even loaded, and
beside "No service was registered, so nothing is listening yet". The
phase 18 walk read it as a claim and then found beta's plugin connected to
the wrong sidecar.

It now reads "Rust server "alpha" is set up." followed by either "The
plugin loads now; `doctor --game rust --server-id alpha` confirms it
connected." or "The plugin connects when the server next starts.".

The top-level --help no longer describes `install` as the uo-link sidecar
and overlay for both games, and says what `uninstall` removes for Rust.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-09-26 01:21:32 -05:00
parent 0fad3e3b2d
commit 3b2881eb9c
3 changed files with 42 additions and 9 deletions

View File

@@ -134,13 +134,14 @@ Gateway website.
Usage: runicgateway-installer <COMMAND> [OPTIONS]
Commands:
install Deploy the plugin overlay, install the uo-link sidecar and its
service, record what was deployed, and print the values the
website needs.
install Deploy the game-side plugin (the ServUO overlay, or the Rust
plugin), install the sidecar and its service, record what was
deployed, and print the values the website needs.
doctor Diagnose an existing deployment end to end.
update Re-resolve the bundle and move both components to it.
uninstall Remove what the installer exclusively owns. Never edits the
ServUO tree — it prints what to remove there.
ServUO tree — it prints what to remove there. For Rust it
removes only its own plugin file and keeps the plugin config.
Options:
--game <servuo|rust> Which game's shard side. Default servuo.

View File

@@ -234,7 +234,7 @@ pub fn deploy(cli: &Cli, mode: Mode) -> Result<()> {
.unwrap_or(false),
)?;
record.instances.insert(plan.id.clone(), instance);
handoffs.push((plan.id.clone(), doc, registered));
handoffs.push((plan.id.clone(), doc, registered, plan.running));
}
// Instances this run did not touch still run the binary it just replaced.
@@ -306,10 +306,17 @@ pub fn deploy(cli: &Cli, mode: Mode) -> Result<()> {
match mode {
Mode::Install => {
let host = crate::install::resolve_host(cli);
for (id, doc, registered) in &handoffs {
for (id, doc, registered, running) in &handoffs {
println!(
"{}",
sidecar::handoff(id, doc, &host, cli.site_url.as_deref(), *registered)
sidecar::handoff(
id,
doc,
&host,
cli.site_url.as_deref(),
*registered,
*running
)
);
}
}

View File

@@ -152,12 +152,17 @@ pub fn choose_port(start: u16, held: &[u16], keep: Option<u16>) -> Result<u16> {
}
/// The end-of-run block for one instance: what `/admin/rust/servers` asks for.
///
/// It says what the run did, not what it hopes: nothing here has seen the plugin connect — on a
/// stopped server it has not even loaded — so the first line never claims a link. `doctor` is the
/// command that checks one (D156).
pub fn handoff(
server_id: &str,
doc: &ConfigDoc,
host: &str,
site_url: Option<&str>,
registered: bool,
running: bool,
) -> String {
let port = port_of(&doc.web.bind)
.map(|p| p.to_string())
@@ -167,7 +172,7 @@ pub fn handoff(
.unwrap_or_else(|| "https://<your-site>".to_string());
let loopback = doc.web.bind.starts_with("127.") || doc.web.bind.starts_with("[::1]");
format!(
"\nRust server {server_id:?} is connected to its sidecar.\n\n\
"\nRust server {server_id:?} is set up. {when}\n\n\
One manual step remains — add it to the website:\n\n \
Server id {server_id}\n \
Sidecar URL http://{host}:{port}\n \
@@ -178,6 +183,13 @@ pub fn handoff(
token = doc.web.auth_token,
config = doc.config_path,
protocol = doc.protocol,
when = if running {
format!(
"The plugin loads now; `doctor --game rust --server-id {server_id}` confirms it connected."
)
} else {
"The plugin connects when the server next starts.".to_string()
},
loopback_note = if loopback {
format!(
"\nThe sidecar listens on {} only. If the website runs on another machine, put a \
@@ -229,7 +241,7 @@ mod tests {
)
.unwrap();
assert_eq!(port_of(&doc.web.bind), Some(8091));
let text = handoff("alpha", &doc, "rust.example", None, true);
let text = handoff("alpha", &doc, "rust.example", None, true, false);
for needle in [
"alpha",
"http://rust.example:8091",
@@ -240,6 +252,19 @@ mod tests {
assert!(text.contains(needle), "{needle} missing from:\n{text}");
}
assert!(text.contains("TLS proxy"), "{text}");
// D156: never a claim about a link nothing has seen.
assert!(!text.contains("is connected"), "{text}");
assert!(
text.contains("The plugin connects when the server next starts."),
"{text}"
);
let running = handoff("alpha", &doc, "rust.example", None, true, true);
assert!(!running.contains("is connected"), "{running}");
assert!(
running.contains("doctor --game rust --server-id alpha"),
"{running}"
);
}
#[test]