Build prebuilt releases, installable with one command - #74
Merged
Merged
Conversation
A version tag now publishes a GitHub release with a prebuilt app for Linux x86_64
and one for Apple Silicon, plus an installer:
curl -fsSL https://colonizer.dev/install.sh | sh
Nothing on the machine needs Rust or Node.js to install it.
.github/workflows/release.yml
- linux-binaries: builds colonizer-agentd and rtk for both colony architectures,
and the Linux harness, as static musl binaries. They are built inside
rust:1-alpine with `docker run`, because GitHub's JavaScript actions don't run
in Alpine containers. A static harness starts on any glibc; one built on the
Ubuntu 24.04 runner would not start on Debian 12 or Ubuntu 22.04.
- bundle: runs scripts/install.sh --bundle on ubuntu-24.04 and macos-15, then a
smoke test. The app starts from the unpacked archive, finds its assets there,
reports agentd, the web UI and the claude-code module, and serves the UI.
- release: runs for tags only. The tag must match crates/colonizer's version. It
writes SHA256SUMS and publishes the archives with install.sh.
A release contains no Anthropic code. The Claude Agent SDK is "all rights reserved",
and its optional platform packages are Claude Code itself (224 MB).
- The bundle installs the module with --omit=optional. The runner already passes
pathToClaudeCodeExecutable, so those packages were never used.
- scripts/record-fetch-at-install.mjs takes the SDK out and writes its lockfile
tarball URL and sha256, after checking the tarball against package-lock.json's
integrity.
- scripts/install-release.sh fetches the SDK from the npm registry against that
record. On a Mac it also fetches the linux-arm64 Claude Code build from
Anthropic's stable channel, checked against the manifest. macOS's own plutil
reads the manifest, so no Node.js is needed. The Workflow's Package step fails
if the SDK is found in a bundle.
scripts/install-release.sh (published as install.sh)
- Refuses platforms it can't serve, including Linux without read-write /dev/kvm.
- Checks the archive against SHA256SUMS.
- Swaps ~/.local/share/colonizer/app in whole, and links ~/.local/bin/colonizer.
- Running it again updates in place. It reuses a Claude Code binary that still
matches the manifest, and never touches settings or colonies.
- Accepts COLONIZER_VERSION, COLONIZER_APP and --pull-image.
- Everything runs inside main(), so a truncated download runs nothing.
scripts/install.sh --bundle skips the KVM check and the Claude Code fetch, and
uses binaries from $COLONIZER_PREBUILT instead of building them.
build-agentd.sh and build-rtk.sh accept COLONIZER_BUILD_HERE=1, to build inside
an existing rust:1-alpine instead of a microVM. rtk's source is then unpacked
outside the repository, because cargo refused to build it as a stray member of
the harness workspace (the local test below caught that).
Tested on an Apple Silicon Mac:
- In a rust:1-alpine microVM with COLONIZER_BUILD_HERE=1: agentd, rtk and the
harness built as static aarch64 musl binaries. The static harness started in a
Debian (node:24-bookworm) microVM and served /api/status.
- `install.sh --bundle` with those guest binaries: a 129 MB app, a 44 MB archive,
and no Agent SDK inside.
- `curl … | sh` against the archive served locally, into an empty HOME, took 41 s.
It fetched the SDK (sha256 checked) and Claude Code 2.1.267 (manifest checked).
- The installed app reported its assets in the new location: agentd, web,
claude-code, the guest Claude binary, and msb 0.6.18 from the relocated vendor
directory. It served the UI.
- Running the installer again reused Claude Code, left no app.new or app.old,
and kept the data directories.
- The installed colonizer-agentd, rtk and claude-guest each ran `--version`
inside node:24-bookworm.
- The runner's 58 tests passed against an SDK restored from the npm tarball
without optional packages.
Not tested yet:
- The workflow itself; this pull request is its first run.
- The linux-x86_64 bundle end to end.
- Launching a colony from an installed release.
- colonizer.dev/install.sh, which the website adds once a release exists. Until
then, install from the release URL directly.
ParallelEntrepreneur
force-pushed
the
main
branch
from
September 17, 2026 07:56
4ae6f05 to
8bad6ae
Compare
ParallelEntrepreneur
force-pushed
the
feat/prebuilt-releases
branch
from
September 17, 2026 07:56
5f6b82a to
476d198
Compare
This was referenced Sep 17, 2026
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A version tag now publishes a GitHub release with a prebuilt app for Linux x86_64
and one for Apple Silicon, plus an installer:
Nothing on the machine needs Rust or Node.js to install it.
.github/workflows/release.yml
and the Linux harness, as static musl binaries. They are built inside
rust:1-alpine with
docker run, because GitHub's JavaScript actions don't runin Alpine containers. A static harness starts on any glibc; one built on the
Ubuntu 24.04 runner would not start on Debian 12 or Ubuntu 22.04.
smoke test. The app starts from the unpacked archive, finds its assets there,
reports agentd, the web UI and the claude-code module, and serves the UI.
writes SHA256SUMS and publishes the archives with install.sh.
A release contains no Anthropic code. The Claude Agent SDK is "all rights reserved",
and its optional platform packages are Claude Code itself (224 MB).
pathToClaudeCodeExecutable, so those packages were never used.
tarball URL and sha256, after checking the tarball against package-lock.json's
integrity.
record. On a Mac it also fetches the linux-arm64 Claude Code build from
Anthropic's stable channel, checked against the manifest. macOS's own plutil
reads the manifest, so no Node.js is needed. The Workflow's Package step fails
if the SDK is found in a bundle.
scripts/install-release.sh (published as install.sh)
matches the manifest, and never touches settings or colonies.
scripts/install.sh --bundle skips the KVM check and the Claude Code fetch, and
uses binaries from $COLONIZER_PREBUILT instead of building them.
build-agentd.sh and build-rtk.sh accept COLONIZER_BUILD_HERE=1, to build inside
an existing rust:1-alpine instead of a microVM. rtk's source is then unpacked
outside the repository, because cargo refused to build it as a stray member of
the harness workspace (the local test below caught that).
Tested on an Apple Silicon Mac:
harness built as static aarch64 musl binaries. The static harness started in a
Debian (node:24-bookworm) microVM and served /api/status.
install.sh --bundlewith those guest binaries: a 129 MB app, a 44 MB archive,and no Agent SDK inside.
curl … | shagainst the archive served locally, into an empty HOME, took 41 s.It fetched the SDK (sha256 checked) and Claude Code 2.1.267 (manifest checked).
claude-code, the guest Claude binary, and msb 0.6.18 from the relocated vendor
directory. It served the UI.
and kept the data directories.
--versioninside node:24-bookworm.
without optional packages.
Not tested yet:
then, install from the release URL directly.