fix(update): say that setup cannot move felis-api to a newer release
`felis update` offers `sudo felis setup` for every planner-backed target and closed with a trailer calling setup idempotent. That is true for velocity -- install_velocity re-resolves the newest build of the pinned minor on each run -- and misleading for felis-api, which both --panel and --plugins resolve to. setup hands bootstrap the binary it is itself running and takes the bootstrap_from_tui arm, which skips the release lookup. The run rebuilds the image and rolls the deployment off that SAME binary: it reports success and leaves the version exactly where it was. An operator following this guidance to apply a felis-api update would watch it appear to work and then see the same version reported again. The report now says so, scoped to runs that actually offered a felis-api target, and points at the bootstrap installer -- the path that resolves and downloads a release. felis update stays report-only, so no command changed; only the claim about what the offered one accomplishes. Tested three ways, because the scoping is the whole point: --panel carries the caveat, --velocity keeps the ordinary trailer without it, and --mc, which offers no command at all, gets neither.
This commit is contained in:
2 files changed
+45
No files matched your search
@@ -219,6 +219,7 @@ func renderApplyGuidance(res updater.Result, selected map[string]bool, force boo
|
||||
|
||||
var b strings.Builder
|
||||
var offeredCommand bool
|
||||
var offeredFelisAPI bool
|
||||
for _, t := range updateTargets {
|
||||
if !selected[t.selector] {
|
||||
continue
|
||||
@@ -247,11 +248,22 @@ func renderApplyGuidance(res updater.Result, selected map[string]bool, force boo
|
||||
}
|
||||
fmt.Fprintf(&b, " run: %s\n", t.command)
|
||||
offeredCommand = true
|
||||
offeredFelisAPI = offeredFelisAPI || t.component == "felis-api"
|
||||
}
|
||||
// Only explain the command when one was actually offered; a --mc-only run has
|
||||
// nothing to run and the trailer would be a non-sequitur.
|
||||
if offeredCommand {
|
||||
b.WriteString("\nfelis setup is idempotent and re-runs the installer that owns these components;\nit does not reinstall what is already current. Restart game servers afterwards.\n")
|
||||
}
|
||||
// Scoped to felis-api because it is the only component setup cannot move forward.
|
||||
// velocity is fine: install_velocity re-resolves the newest build of the pinned minor
|
||||
// on every run. But setup hands deploy/bootstrap.sh the binary it is itself running
|
||||
// (FELIS_BOOTSTRAP_BINARY), and that arm skips the release lookup entirely, so it
|
||||
// rebuilds the image and rolls the deployment from the SAME binary -- a run that looks
|
||||
// like a successful update and leaves the version unchanged. Only the installer, which
|
||||
// resolves and downloads a release, actually moves felis-api.
|
||||
if offeredFelisAPI {
|
||||
b.WriteString("\nfelis-api (panel, plugins) is the exception: setup re-images it from the felis binary\nalready on this host, so it cannot install a NEWER felis-api. To move to a newer\nrelease, re-run the bootstrap installer (see README).\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
@@ -132,3 +132,36 @@ func TestUpdateTargetsMatchTopology(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `sudo felis setup` is the right answer for velocity and the wrong one for felis-api,
|
||||
// so the caveat has to be scoped rather than appended to every run. setup hands
|
||||
// bootstrap the binary it is already running, and that arm skips the release lookup:
|
||||
// the run rebuilds the image and rolls the deployment off the SAME binary, which looks
|
||||
// like a successful update and changes nothing. install_velocity, by contrast, really
|
||||
// does re-resolve the newest build on every run.
|
||||
func TestApplyGuidanceScopesTheFelisAPICaveat(t *testing.T) {
|
||||
const caveat = "cannot install a NEWER felis-api"
|
||||
|
||||
api := renderApplyGuidance(
|
||||
planResult([]updates.Action{{Component: "felis-api", Kind: updates.ActionNotify, LatestKnown: true}}),
|
||||
map[string]bool{"panel": true}, false)
|
||||
if !strings.Contains(api, caveat) {
|
||||
t.Fatalf("--panel resolves to felis-api and must carry the caveat:\n%s", api)
|
||||
}
|
||||
|
||||
vel := renderApplyGuidance(
|
||||
planResult([]updates.Action{{Component: "velocity", Kind: updates.ActionNotify, LatestKnown: true}}),
|
||||
map[string]bool{"velocity": true}, false)
|
||||
if strings.Contains(vel, caveat) {
|
||||
t.Fatalf("velocity IS fixed by setup; the caveat would misdirect the operator:\n%s", vel)
|
||||
}
|
||||
if !strings.Contains(vel, "felis setup is idempotent") {
|
||||
t.Fatalf("velocity still wants the ordinary trailer:\n%s", vel)
|
||||
}
|
||||
|
||||
// --mc offers no command at all, so neither trailer belongs.
|
||||
mc := renderApplyGuidance(planResult(nil), map[string]bool{"mc": true}, true)
|
||||
if strings.Contains(mc, caveat) {
|
||||
t.Fatalf("--mc offers no command; the caveat is a non-sequitur:\n%s", mc)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user