Skip to content

Say which Colonizer this is, and when a newer one is out - #110

Merged
ParallelEntrepreneur merged 1 commit into
mainfrom
feat/version-and-update-check
Sep 18, 2026
Merged

ParallelEntrepreneur merged 1 commit into
mainfrom
feat/version-and-update-check

Conversation

@ParallelEntrepreneur

Copy link
Copy Markdown
Collaborator

Part of #45 — the check, and the version it compares against. Applying an update is not in this PR; see the end.

The issue's premise has moved

#45 opens with "There are no versions to compare." That stopped being true: v0.1.0v0.1.4 are tagged and Cargo.toml tracks them. What was still missing is the next sentence — a build records nothing about where it came from, so "is there an update?" had no answer.

What this adds

build.rs Stamps git describe --tags --always --dirty, the commit, and a build time
GET /api/version {version, commit, dirty, built_at, release}
GET/PUT /api/update The check: on by default, switch in Settings, COLONIZER_UPDATE_CHECK=0 from the environment
Settings → Updates Installed version, commit, "built from a modified tree", and release notes when newer

Degrades rather than fails. A source package with no .git still compiles and reports the crate version with no commit.

built_at honours SOURCE_DATE_EPOCH. An unconditional clock reading would give every rebuild a different binary — precisely what the attestation work in #89/#105 needs not to happen.

The comparison rule

A build after a tag is measured against the tag it contains, as the issue asks: v0.1.4-12-gabc1234 is told about v0.1.5 and not about v0.1.4. A version that cannot be placed is never told it is behind — silence beats crying wolf. Drafts and prereleases are ignored.

Eight tests cover it, including v0.9.0v0.10.0, which string ordering gets wrong.

"Off means no request"

That acceptance criterion is structural, not incidental: the gate is in the loop before anything touches the network. Switching off also forgets the last answer, so no banner lingers for a check that is no longer running.

Verified

Build stamping, on this machine:

DESCRIBE="1367191-dirty"  COMMIT="1367191de3…"  BUILT_AT="1700000000"
resolved: version "v0.1.4", dirty true, release Some("v0.1.4"), built_at 2023-11-14T22:13:20Z

— an untagged shallow clone falling back to the crate version, dirty caught, and SOURCE_DATE_EPOCH honoured.

In a browser against the mock: the section reads "v0.1.4 available", the banner shows the notes, and switching the check off replaces it with "the Mothership makes no request to GitHub about releases". The mock also cleared its cached release on switch-off only after I fixed it — it previously kept showing a banner the real backend would have dropped.

cargo test -p colonizer-harness    115 passed, 0 failed   (8 new)
cargo check                        0 warnings
npx tsc --noEmit                   clean

Not exercised: a real request to the GitHub releases API, and the endpoints served by the real binary.

Acceptance in #45

  • The UI shows the installed version, and "update available" with release notes
  • A fresh install checks by default
  • With checking off, no request to GitHub for it
  • Updating without losing colonies — not this PR

Why applying is left out

It needs the versioned app directory and current symlink from #90/#104 to land first, so an old release stays readable while running colonies still mount plugins out of it (sessions.rs, -v …:ro). Building that on top of today's rm -rf "$app" install would be the unsafe version of exactly what #45 asks for.

Part of #45. The check and the version it compares; applying an update is not
here.

#45 opens with "there are no versions to compare", which has since stopped being
true: v0.1.0 to v0.1.4 are tagged and Cargo.toml tracks them. What was still
missing is that a build records nothing about where it came from, so "is there
an update?" had no answer.

build.rs stamps `git describe --tags --always --dirty`, the commit, and a build
time. Everything degrades rather than fails: a source package with no .git
compiles and reports the crate version with no commit. The timestamp honours
SOURCE_DATE_EPOCH, because an unconditional clock reading would give every
rebuild a different binary, which is what #89/#105's attestation needs not to
happen.

GET /api/version returns that. GET/PUT /api/update carries the check: on by
default, a switch in Settings, and COLONIZER_UPDATE_CHECK=0 to keep it off from
the environment. It asks GitHub a minute after start and every six hours, only
while it is on — switched off it makes no request and forgets the last answer,
so no banner lingers for a check that is not running. Drafts and prereleases are
ignored. COLONIZER_RELEASES_URL points it elsewhere for a fork or a test.

A build after a tag is measured against the tag it contains, as the issue asks:
v0.1.4-12-gabc1234 is told about v0.1.5 and not about v0.1.4. A version that
cannot be placed is never told it is behind — better silence than crying wolf.

Settings gains an Updates section: the installed version, its commit, whether it
was built from a modified tree, and the release notes when there is a newer one.
It says plainly that updating means re-running the installer for now.

Checked in a browser against the mock: the section reads "v0.1.4 available",
the banner shows the notes, and switching the check off replaces it with "the
Mothership makes no request to GitHub about releases". The mock now clears its
cached release on switch-off too, which it did not before, so it no longer shows
a banner the real backend would have dropped.

Not done here, deliberately: applying the update. It wants the versioned app
directory from #90/#104 first, so an old release stays readable while running
colonies still mount plugins out of it.
@ParallelEntrepreneur
ParallelEntrepreneur merged commit 5dcf7fa into main Sep 18, 2026
@ParallelEntrepreneur
ParallelEntrepreneur deleted the feat/version-and-update-check branch September 18, 2026 03:28
ParallelEntrepreneur added a commit that referenced this pull request Sep 18, 2026
)

* Update in place, without taking plugins away from running colonies

Closes #45. #110 did the check; this applies it.

It runs scripts/install-release.sh from inside the app rather than
reimplementing the download, the checksum and the swap. That installer already
unpacks beside the running app and moves the symlink with one rename, so a
failure part-way leaves the running version exactly as it was — which is the
"a build that fails leaves the running version untouched" line, for free.

The proposal said to fetch the tag into a clone the mothership owns and build
it. Prebuilt releases landed since (#74), so there is a verified archive to
install and no cargo, npm or node needed on the machine. install.sh now ships
the installer into dist/ so an installed mothership has a local copy, instead of
downloading a script to execute.

The part that would have lost work: colonies mount vendored plugins straight out
of the app directory the mothership started from, because resolve_assets
canonicalises the symlink away — and the installer deletes the slot it replaces.
Applying an update under a running colony would take its plugins with it. So the
installer learned COLONIZER_KEEP_PREVIOUS=1, each session records the directory
its mounts resolved through, and a kept directory is swept at the next start
once no live colony still names it. A person running the installer by hand keeps
today's behaviour.

Publishing colonies hold the update, and are named in the refusal: the microVM
is already gone and the host is committing and pushing, so interrupting it
leaves the colony failed with its pull request unopened. A colony that is only
working does not hold it — it is detached, and recover reconnects it, which is
the whole reason this can be done at all.

The restart is an exec of the app symlink's binary, after the live map is told
this mothership is going and the mesh children are killed so the new process can
take their ports.

Checked by driving the real installer on this machine with fake releases: with
KEEP_PREVIOUS the previous slot survives an update and its plugin file is still
readable, and without it the slot is removed exactly as before. The UI was
driven against the mock: Update to v0.1.4 goes to Installing, then Restarting
with "colonies keep their microVMs and reconnect" and a line per colony.

Not exercised: a real release installed by a running mothership, and the exec
itself. Both need two real releases and an installed app.

* Say how far behind the running build is, and which commit it is

The pane showed a version and a date. It now also says how old the build is
relative to the release being offered — "Released 9/18/2026, 12 days after the
build you are running" — because a version number alone does not tell you
whether you are a day or a season behind.

The commit is shown beside the build time, and a development build says which
release it came after.

The gap is computed from the build time and the release date, so it is honest
about the build rather than about the tag: someone running a build from a
checkout sees how far that checkout has fallen behind.

---------

Co-authored-by: Nick <40026523+Nick-CHI@users.noreply.github.com>
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