feat(rust): install the helpers a plugin release ships beside the bridge (D182)
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 1m37s

Rust-Plugins now releases a ZoneManager helper, RunicGatewayZones.cs, beside
RunicGateway.cs (docs/modules/rust/PLAN_FIXES.md D181, D182), and the org lead
decided it is installed by default. The tarball reader took only the bridge,
so the helper would have been downloaded and dropped.

- plugin::read_tarball reads every other .cs the release's manifest lists in
  `files`, and refuses the release when one is missing, does not match its own
  sha256, or is not a plain `<Name>.cs` (the name is written into a plugins
  directory, so nothing in a tarball may reach outside it). A .cs the manifest
  does not list is ignored, so a release older than helpers has none.
- install/update place each helper before the bridge, in place like the
  bridge (Oxide and Carbon reload on a change, not on a rename), put back one
  that was deleted or edited, and remove one a later release stops shipping.
- The record gains per-instance `helpers` (path + sha256). It is absent from
  every record written before this and reads back as none.
- doctor warns — never fails — on a missing or edited helper and says what
  the bridge loses without it; uninstall removes helpers before the bridge.

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 21:24:33 -05:00
parent 3a3676a22d
commit 9d9ee3344b
5 changed files with 384 additions and 14 deletions

View File

@@ -59,6 +59,9 @@ pub fn run(cli: &Cli) -> Result<i32> {
println!(" · remove the service {}", svc.name);
}
println!(" · remove {}", i.plugin_path);
for helper in i.helpers.values() {
println!(" · remove {}", helper.path);
}
println!(
" · keep {} (the website's; it names this server)",
i.plugin_config
@@ -106,6 +109,11 @@ pub fn run(cli: &Cli) -> Result<i32> {
done.extend(removal.done);
problems.extend(removal.problems);
}
// The helpers first: without the bridge they have nothing to do, and a helper left behind
// would keep patching ZoneManager for a bridge that is gone.
for helper in instance.helpers.values() {
remove_file(Path::new(&helper.path), &mut done, &mut problems);
}
remove_file(Path::new(&instance.plugin_path), &mut done, &mut problems);
if cli.purge {
remove_file(Path::new(&instance.config_path), &mut done, &mut problems);