Repository navigation
tether-707e9b2
·
73 commits
to main
since this release
feat: self-update — startup release probe, banner row, `tether update` An install is a throwaway tarball run from ~/.local/bin, so updating it meant rebuilding or re-downloading by hand. Releases are already published per push to main under tag tether-<sha>, so the newest build is one small text file away. Identity is a git short sha, which has no ordering, so any cached sha other than the running build reads as "update available" — a locally-built newer commit therefore suggests a downgrade, the ceiling marked ponytail: in update.lua. The probe never runs in front of the user: startup reads ~/.tether/update.json, decides from disk inside the 24h TTL, and otherwise hands the VERSION fetch to tether.fetch_bg with an update.json.pending marker, reusing metadata.lua's detached-probe discipline (empty marker younger than 120s = child still running, older = dead, drop and reschedule; FETCH_FAILED cools down by stamping checked_at). The banner is one role="system" row seeded after the splash in ui._startup_rows, so /clear and /new stay splash-only and it shows at most once per session. The copy lives in update.lua because copy.lua's TCOPY guard rejects the literal release-name token it would have to quote. update_check=false in the config skips probe and banner; --print skips both too. CI now publishes two more assets next to the tarball: the bare binary and a VERSION file. A tar.gz would need a gzip+tar decoder on the Lua side, and both URLs use the releases/latest/download web host rather than the REST API, which is capped at 60 unauthenticated requests per hour per IP. `tether update` is trimmed before the flag parser, like the extension verbs: http_get VERSION, http_get the bare binary, reject an empty body, a body without the ELF magic or one under the size floor, then write a temp file in the running executable's own directory, fchmod 0755, os.rename over the target. Rename is required, not tidiness — this process IS the file being replaced, so an in-place write would fail with ETXTBSY while a rename only re-links the dirent and leaves the live inode alone. Refusals (no exepath, dev build, unwritable directory, failed rename) exit non-zero with the path and the sudo / make install PREFIX=$HOME/.local way forward. T293–T312 in tests/update_tests.lua (config coercion and bootstrap, strict version parse, cache round-trip and truncated file, probe lifecycle against a stubbed tether table, notice rules, seed/width/mono, verb dispatch before flag parsing, full swap on a fake exe tree plus rejected blobs), and a pty run of the built binary against a local stub release server: banner renders once under the wordmark, empty-cache start returns in 0.12s and the marker folds into update.json for the next launch, disabled and unreachable-source starts stay silent and fast.