fix(rust): the handoff says what the run did, not that a link exists (D156) #29

Merged
whitlocktech merged 1 commits from fix/rust-handoff-wording into edge 2026-09-26 06:27:32 +00:00
3 changed files with 42 additions and 9 deletions

View File

@@ -134,13 +134,14 @@ Gateway website.
Usage: runicgateway-installer <COMMAND> [OPTIONS] Usage: runicgateway-installer <COMMAND> [OPTIONS]
Commands: Commands:
install Deploy the plugin overlay, install the uo-link sidecar and its install Deploy the game-side plugin (the ServUO overlay, or the Rust
service, record what was deployed, and print the values the plugin), install the sidecar and its service, record what was
website needs. deployed, and print the values the website needs.
doctor Diagnose an existing deployment end to end. doctor Diagnose an existing deployment end to end.
update Re-resolve the bundle and move both components to it. update Re-resolve the bundle and move both components to it.
uninstall Remove what the installer exclusively owns. Never edits the 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: Options:
--game <servuo|rust> Which game's shard side. Default servuo. --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), .unwrap_or(false),
)?; )?;
record.instances.insert(plan.id.clone(), instance); 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. // 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 { match mode {
Mode::Install => { Mode::Install => {
let host = crate::install::resolve_host(cli); let host = crate::install::resolve_host(cli);
for (id, doc, registered) in &handoffs { for (id, doc, registered, running) in &handoffs {
println!( 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. /// 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( pub fn handoff(
server_id: &str, server_id: &str,
doc: &ConfigDoc, doc: &ConfigDoc,
host: &str, host: &str,
site_url: Option<&str>, site_url: Option<&str>,
registered: bool, registered: bool,
running: bool,
) -> String { ) -> String {
let port = port_of(&doc.web.bind) let port = port_of(&doc.web.bind)
.map(|p| p.to_string()) .map(|p| p.to_string())
@@ -167,7 +172,7 @@ pub fn handoff(
.unwrap_or_else(|| "https://<your-site>".to_string()); .unwrap_or_else(|| "https://<your-site>".to_string());
let loopback = doc.web.bind.starts_with("127.") || doc.web.bind.starts_with("[::1]"); let loopback = doc.web.bind.starts_with("127.") || doc.web.bind.starts_with("[::1]");
format!( 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 \ One manual step remains — add it to the website:\n\n \
Server id {server_id}\n \ Server id {server_id}\n \
Sidecar URL http://{host}:{port}\n \ Sidecar URL http://{host}:{port}\n \
@@ -178,6 +183,13 @@ pub fn handoff(
token = doc.web.auth_token, token = doc.web.auth_token,
config = doc.config_path, config = doc.config_path,
protocol = doc.protocol, 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 { loopback_note = if loopback {
format!( format!(
"\nThe sidecar listens on {} only. If the website runs on another machine, put a \ "\nThe sidecar listens on {} only. If the website runs on another machine, put a \
@@ -229,7 +241,7 @@ mod tests {
) )
.unwrap(); .unwrap();
assert_eq!(port_of(&doc.web.bind), Some(8091)); 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 [ for needle in [
"alpha", "alpha",
"http://rust.example:8091", "http://rust.example:8091",
@@ -240,6 +252,19 @@ mod tests {
assert!(text.contains(needle), "{needle} missing from:\n{text}"); assert!(text.contains(needle), "{needle} missing from:\n{text}");
} }
assert!(text.contains("TLS proxy"), "{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] #[test]