fix(rust): an update on a running server reloads the plugin #30
@@ -324,7 +324,17 @@ pub fn deploy(cli: &Cli, mode: Mode) -> Result<()> {
|
|||||||
let before = prior.as_ref().map(|p| p.bundle.clone());
|
let before = prior.as_ref().map(|p| p.bundle.clone());
|
||||||
match before {
|
match before {
|
||||||
Some(b) if b.tag == bundle.bundle => {
|
Some(b) if b.tag == bundle.bundle => {
|
||||||
|
// The same bundle can still have written something: a plugin edited by hand is
|
||||||
|
// put back, which is what `doctor` tells an operator to run `update` for.
|
||||||
|
let restored = planned.iter().filter(|p| p.plugin_action.writes()).count();
|
||||||
|
if restored == 0 && !binary_action.writes() {
|
||||||
println!("\nAlready on bundle {} — nothing moved.", bundle.bundle)
|
println!("\nAlready on bundle {} — nothing moved.", bundle.bundle)
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"\nAlready on bundle {} — put back what no longer matched it (see above).",
|
||||||
|
bundle.bundle
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(b) => {
|
Some(b) => {
|
||||||
println!(
|
println!(
|
||||||
@@ -656,7 +666,16 @@ fn deploy_instance(
|
|||||||
if plan.plugin_action.writes() {
|
if plan.plugin_action.writes() {
|
||||||
std::fs::create_dir_all(plan.server.plugins_dir())
|
std::fs::create_dir_all(plan.server.plugins_dir())
|
||||||
.with_context(|| format!("cannot create {}", plan.server.plugins_dir().display()))?;
|
.with_context(|| format!("cannot create {}", plan.server.plugins_dir().display()))?;
|
||||||
write_atomic(&plugin_path, &released.source)?;
|
// Overwritten in place, NOT `write_atomic`. Oxide and Carbon watch the plugins directory
|
||||||
|
// and reload on a CHANGE; `write_atomic` removes the old file and renames a `.tmp` over it,
|
||||||
|
// which both frameworks see as a delete — they unload the bridge — and a rename they
|
||||||
|
// ignore, so the new file is never loaded. On a running server that was an `update` that
|
||||||
|
// silently took the bridge down until the next boot (the phase 18 walk, on all three
|
||||||
|
// instances and both frameworks). A write in place is what an operator's `cp` does, and
|
||||||
|
// it reloads. A crash mid-write leaves a file that fails to compile; `doctor` reports it
|
||||||
|
// as not the deployed file and `update` writes it again.
|
||||||
|
std::fs::write(&plugin_path, &released.source)
|
||||||
|
.with_context(|| format!("cannot write {}", plugin_path.display()))?;
|
||||||
ui::ok(&format!(
|
ui::ok(&format!(
|
||||||
"plugin {} {}",
|
"plugin {} {}",
|
||||||
if plan.plugin_action == BinaryAction::Replace {
|
if plan.plugin_action == BinaryAction::Replace {
|
||||||
|
|||||||
@@ -973,8 +973,11 @@ fn remove_platform(record: &ServiceRecord) -> Removal {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
} else if let Some(user) = record.user.as_deref() {
|
} else if let Some(user) = record.user.as_deref() {
|
||||||
|
// Not "this installer did not create it": a Rust instance's service never owns the account
|
||||||
|
// even when this installer made it, because every instance shares it — the last instance's
|
||||||
|
// removal decides (`rustgame::uninstall`). Said that way, the line is true for both games.
|
||||||
out.done.push(format!(
|
out.done.push(format!(
|
||||||
"left the {user} account alone — this installer did not create it"
|
"left the {user} account alone — it is not this service's to remove"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
|
|||||||
Reference in New Issue
Block a user