Skip to content

tether-707e9b2

Choose a tag to compare

@github-actions github-actions released this 30 Sep 18:06
· 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.