Let sc replace itself with the newest build on its own channel #18
No reviewers
Labels
No labels
⏳in-progress
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Grey.ooo/someones.computer_agent!18
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/self-update"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
No UI surface — this is a CLI command, so there is nothing to screenshot.
What changed
sc -U— also--update, alsosc update— reads the platform's manifest forthe channel it came from, downloads the archive for its own os/arch, verifies it
against the published sha256, and swaps itself out.
The channel needs no flag and no config file, because the build already
answers it.
.goreleaser.yaml's snapshotversion_templatestamps-snapshot-into every untagged build and never into a tagged one, so a version containing it
follows
devand anything else followsstable. The alternative was a fourth-Xldflag carrying the channel, which both publish paths would have to setcorrectly and which is empty on every binary already in the wild — a fallback to
guess at anyway.
version == "dev")ErrNotWritablenaming the path and printing theinstall.shone-liner. NeversudoNothing unverified reaches the filesystem: the archive is held in memory
until its sha256 matches. The replacement stages the new binary in the install
directory, renames the old one aside, renames the new one in, and puts the old
one back if that second rename fails — so every failure path leaves a working
scwhere it was. That is also the shape that works on Windows, where therunning image is locked but its directory entry can still be moved; a plain
rename onto the target would be fine on Unix and refused there.
-Uis on the root command rather than persistent, sosc deploy -Uis an errorrather than two requests in one line with no obvious order.
Depends on
GET /install/{channel}.jsonon the platform —Someones.Computer#371.
This cannot work until that merges. The catalogue's other surfaces are a web
page, a shell script and a stream of bytes that never names its own version.
That endpoint deliberately does not fall back from an unpublished
stabletodev, and neither does this: a stable binary handed a dev snapshot is a silentdowngrade onto an unreleased build. There is a test for exactly that.
Verified
go vet ./...,go test ./...and the CI cross-compile matrix(linux/darwin/windows × amd64/arm64) all green locally. 14 new tests drive the
whole path — resolve, fetch, verify, replace — against an
httptestserver and atemp file, including the mismatched-checksum refusal, the unwritable directory,
the Windows zip, and the stable-is-not-offered-dev property.
What's left
Nothing here, but this stays draft until
Someones.Computer#371
merges — until then the endpoint it reads does not exist in production, so the
command is correct but unusable.
One thing is deliberately undecided and recorded on the issue rather than guessed
at here: whether
-Ushould support Windows at all. The replacement code iswritten to work there and archives for it are published, but no one has run it on
Windows.
Part of
Someones.Computer#237.
sc -U (also --update, also `sc update`) reads the platform's manifest for the channel it came from, downloads the archive for its own os/arch, verifies it against the published sha256 and swaps itself out. The channel needs no flag and no config file, because the build already answers it. .goreleaser.yaml's snapshot version_template stamps -snapshot- into every untagged build and never into a tagged one, so a version containing it followed dev and anything else followed stable. The alternative was a fourth -X ldflag carrying the channel, which both publish paths would have to set correctly and which is empty on every binary already in the wild — a fallback to guess at anyway. An unreleased local build reports version "dev", belongs to neither channel, and is refused. That binary is somebody's own go build, possibly of uncommitted work, with no version to compare against and nothing to go back to. Nothing unverified reaches the filesystem: the archive is held in memory until its sha256 matches, and a mismatch is a hard failure with no retry, because the two things it can mean are corruption and substitution and retrying is the wrong answer to both. The replacement stages the new binary in the install directory, renames the old one aside, renames the new one in, and puts the old one back if that second rename fails — so every failure path leaves a working sc where it was. That is also the shape that works on Windows, where the running image is locked but its directory entry can still be moved; a plain rename onto the target would be fine on Unix and refused there. An install directory this user cannot write is an ErrNotWritable naming the path and printing the install.sh one-liner. sc does not escalate, for the same reason install.sh does not: it picks ~/.local/bin over sudo, and a self-updater that quietly acquired root would be a much larger promotion of privilege than the original install ever asked for. -U is on the root command rather than persistent, so `sc deploy -U` is an error rather than two requests in one line with no obvious order. Depends on GET /install/{channel}.json, added platform-side in the someones. computer repo: the catalogue's other surfaces are a web page, a shell script and a stream of bytes that never names its own version. That endpoint deliberately does not fall back from an unpublished stable to dev, and neither does this — a stable binary handed a dev snapshot is a silent downgrade onto an unreleased build. Part of Someones.Computer#237.WIP: Let sc replace itself with the newest build on its own channelto Let sc replace itself with the newest build on its own channel