Skip to content

feat(cli): ship ftw as the native operator command - #1372

Merged
frahlg merged 3 commits into
masterfrom
fix/native-version-panel
Sep 24, 2026
Merged

frahlg merged 3 commits into
masterfrom
fix/native-version-panel

Conversation

@frahlg

@frahlg frahlg commented Sep 24, 2026

Copy link
Copy Markdown
Member

Summary

Builds on the direction in #1373. On a native install the owner runs updates on the machine, by hand or from their own timer or agent. Core supplies the steps.

  • ftw on PATH. ftw-cli ships in the release package. scripts/install.sh installs it as /usr/local/bin/ftw, owned by root. It talks only to the local Core API, asks no questions and never starts Core.
    • ftw status: version, published release, last update or rollback, previous release, health and history, plus journalctl/systemctl for the next step. Exit 1 when Core is down, still starting or not healthy.
    • ftw update [--channel beta|stable]:
      • follows a run already in progress instead of starting another
      • waits through the restart and checks the version that runs
      • gives health up to 30 s to settle before reporting it
      • already current exits 0
      • stops before a release that changes the state schema, which native cannot take yet
    • ftw rollback: binary rollback to previous, followed the same way.
    • ftw backup [--output-dir DIR]: Core's verified archive, and a copy checked by size and SHA-256 before it gets its final name.
    • ftw support [--output FILE]: the redacted zip, mode 0600, deleted if the stream was cut.
    • Every request has a timeout. Waiting is bounded by the 6 h trial deadline. Exit codes: 0 done, 1 failed, 2 usage.
  • Removed from the first version of this PR: doctor (now status), startup (its unit overwrote the installer's), the channel prompt, --port, Core's -port, and the update route in the Core binary (ftw doctor there started a setup wizard).
  • Core fixes:
    • A native restarting status stays in flight until the trial deadline instead of failing after 5 minutes.
    • The bind error asks /api/health, so it recognises a starting Core, and points to ftw status.
  • Web on native:
    • The version panel shows version and channel, the published release with a notes link, the command and the last run. It has no controls.
    • A loading panel replaces the old dialog until the first check answers.
    • Opening the panel no longer fetches snapshots, backups, component history or the driver catalog.
    • The header mark counts only Core; drivers update in Settings.
    • Setup names ftw update instead of offering Update now.
    • The old dialog stays only for a Core that is not native.
  • Docs: docs/self-update.md and docs/backup-and-restore.md describe the commands.
  • Changeset: minor, because updating needs a user step. Owner approval needed.

Tests

  • Go:
    • go test ./internal/ftwcli (also with -race -count=3), against a fake Core. It covers:
      • restart mid-run, a run already going, and a failed trial reported with the Core that runs
      • a hung Core, bounded by the follow limit
      • a state-schema stop, and a channel change only when asked
      • a Core that is not native, with self-update off or not native
      • rollback with and without previous
      • backup copy, a leftover .part, the second run and a mismatch
      • a cut support zip, and command-line errors
    • internal/selfupdate has a test for the native restart deadline, and cmd/ftw for the bind message.
    • make test passes (69 packages). The rest of make verify passes: the script suites plus go vet ./... and go build ./....
  • Web: LANG=C npm test, 654 pass. The native panel, loading panel, native open() requests, header count and setup banner are covered.
  • Packaging:
    • test_package_linux.py and test-install-native.sh pass.
    • A local arm64 package holds ftw-cli.
    • The published v0.132.1-beta.1 launcher installs that package, so older Cores can self-update to it.
  • Real native site (published releases in a slot root, restart loop in place of systemd, sim-sungrow as meter), all with this CLI:
    • ftw rollback v0.132.2-beta.1 → v0.132.1-beta.1 in 4 s
    • ftw update back in 4 s
    • ftw update again reports already current (exit 0)
    • ftw backup --output-dir with a checked copy
    • ftw support
  • Screens: branch Core and web on that site, headless Chromium: the panel in dark, light and 390 px width, and the setup banner. Still needs a human look in a browser (AGENTS.md).

Not in this PR

In the order of #1373:

  • launcher hardening: offline rollback, settling commit
  • removing the native rollback point
  • a schema-changing update path
  • deleting the old dialog and the Docker/sidecar code
  • the Pi image
  • migration

🤖 Generated with Claude Code

https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7

A native install's version panel no longer opens the old updater.
ftw update asks beta or stable, or takes --channel. The same command
backs up, checks the running Core, writes a redacted support file, and
prints a systemd snippet to paste. Starting Core on a port FTW already
holds says that it is already running.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 24, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-24T05:12:58.086648Z f2df85d PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f2df85d232

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread go/cmd/ftwupdate/main.go Outdated
Comment on lines +1 to +2
// Command ftwupdate is installed on PATH as `ftw`. It only runs updates.
// Starting Core is the long-running ftw binary, not this command.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Ship the CLI that the native UI invokes

The release path never builds or installs this command: scripts/build-core.sh still builds only cmd/ftw, cmd/ftw-backup, and cmd/ftw-launcher, while scripts/package-linux.py archives only those binaries and the installer creates no PATH entry. Consequently the advertised ftw CLI is absent from native releases; manually invoking the packaged Core binary only recognizes update, so backup, doctor, support, startup, and help also fail. Add this command to the build/package/install path and expose it as ftw.

AGENTS.md reference: AGENTS.md:L24-L25

Useful? React with 👍 / 👎.

Comment thread web/update-badge.js
_modalHTML() {
const info = this._info || {};
if (this._phase === "updating") return this._updatingModalHTML();
if (info.native) return this._nativeVersionHTML(info);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve native binary rollback in the replacement dialog

When info.native is true, this early return bypasses the existing binaryRollback action even when info.previous is available. The new CLI has no rollback command, so a native operator loses the only normal route to /api/version/binary-rollback; the existing native rollback case in web/update-dialog.test.mjs now fails as well. Keep the native rollback control reachable from this dialog.

AGENTS.md reference: AGENTS.md:L24-L25

Useful? React with 👍 / 👎.

Comment thread web/update-badge.js
_modalHTML() {
const info = this._info || {};
if (this._phase === "updating") return this._updatingModalHTML();
if (info.native) return this._nativeVersionHTML(info);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep pending driver updates actionable on native installs

For a native install with only a signed driver update pending, _pendingUpdates() still counts that driver and renders a clickable “1 component update available” badge, but this branch opens a Core-only panel that says no newer release is published and omits _componentsSectionHTML(). The user therefore receives an update notification whose dialog neither identifies nor links to the pending driver; retain the driver section or direct the badge to the Devices update flow.

AGENTS.md reference: AGENTS.md:L22-L23

Useful? React with 👍 / 👎.

Comment thread go/cmd/ftw/main.go Outdated
slog.Error("load config", "err", err)
os.Exit(1)
}
if *apiPort != 0 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply the port override before entering bootstrap

On a fresh host where the config file does not yet exist, execution calls runBootstrap and returns before reaching this override; that bootstrap server hardcodes :8080. Thus ftw -port 9090 initially serves setup on 8080 and can jump to 9090 only after setup writes the config and re-executes, contrary to the new flag's behavior. Validate and pass the override into bootstrap before the missing-config branch.

AGENTS.md reference: AGENTS.md:L24-L25

Useful? React with 👍 / 👎.

The owner runs native updates on the machine (ADR 0007 as amended in
#1373). ftw-cli ships in the release package and the installer puts it
on PATH as /usr/local/bin/ftw, owned by root. It talks only to the
local Core API, asks nothing and never starts Core:

- status: version, published release, last run, previous release,
  health and history, with the systemd commands to look next
- update [--channel]: follows a run already in progress, waits
  through the restart, reports what runs and how health settled;
  already current exits 0; stops before a state-schema change
- rollback: binary rollback to previous, followed the same way
- backup [--output-dir]: verified archive, copy checked by SHA-256
- support: the redacted zip, rejected if the stream was cut

Every request has a timeout and waiting is bounded by the trial
deadline. doctor, startup, the channel prompt, --port and Core's
-port flag are gone, and the Core binary no longer routes update.

A native "restarting" status now stays in flight until the trial
deadline instead of failing after five minutes. The bind error asks
/api/health, so a starting Core is recognised, and names ftw status.

The native web panel reports version, release, command and last run
without controls; it shows a loading panel before the first check and
fetches nothing for the old dialog. Setup on native names ftw update
instead of offering Update now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7
@frahlg frahlg changed the title feat(cli): add ftw update, backup, doctor, support, and startup feat(cli): ship ftw as the native operator command Sep 24, 2026
@frahlg
frahlg merged commit ed40b94 into master Sep 24, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant