The rc channel's Arch base can sit anywhere between stable's snapshot and
edge's, so a package built against stable's libraries is not necessarily
correct for rc. Copying stable's fast-ring artifacts into rc therefore shipped
possibly-mislinked packages to RC testers. Fast-ring packages now build
natively for all three channels, each in its own image against its own base
mirror, and the stable release's replication step is gone.
That required separating 'may be built here' from 'whose version wins'. The
release pair is now marked "pinned": its version is set per release on the rc
branch, so it builds for rc only from that branch's worktree
(OMARCHY_RC_PINS=1, set by omarchy-release rc) — master's shipped pins can
never overwrite an in-flight RC, even though check-versions now discovers rc
work like it does for edge and stable.
The rc service fetched and reset /root/omarchy-pkgs-rc in ExecStartPre, but
that worktree does not exist until the first RC creates the rc branch — so on
a freshly set up host the unit failed every five minutes, forever, and showed
up as a failed unit in the timer report.
It now runs bin/auto-release-rc from the main checkout, which always exists:
nothing queued exits silently, no rc branch is a clean no-op, and a missing
worktree is created on demand before handing off to the worktree's own
auto-release.
A push reaching the mirror should take minutes, not up to six hours. All four
units now fire every 5 minutes, staggered a minute apart. Three guards make
that cadence safe:
- Scheduled runs take the release lock NON-BLOCKING (try_release_lock) and
skip the tick when a build is running. Blocking would stack one stalled
process per tick behind a long build and stampede when it finished. Manual
commands still wait, as an operator expects.
- check-versions takes the lock too, and now owns its git pull (--pull, passed
by the unit) instead of an ExecStartPre: at this cadence an unlocked pull
would swap PKGBUILDs out from under a running build.
- A failed release records .build-failed-<channel> and backs off
exponentially (10m, 20m, 40m … capped at 6h) rather than rebuilding the same
broken tree every 5 minutes. Any new commit clears the backoff, since a push
is the most likely fix.
Idle ticks exit without output so the journal keeps showing the runs that
matter, and bin/repo timers reports backoff state — a paused channel is
otherwise indistinguishable from an idle one.
A release train has three human moments, each one command: start (release
branch on basecamp/omarchy + notes staging PR + edge→rc advance for
minor/major), rc (pin both PKGBUILDs to the branch head as X.Y.ZrcN on the
pkgs rc branch, trigger the rc channel build, wait for publish, optional RC
ISO), and ship (final pins → promote rc→stable → tag → pins to master → GitHub
release from the staging PR body → final ISO → website bump, each step
skip-if-done so a crashed run resumes).
Bare omarchy-release is the shepherd: it derives the train state from observed
reality (remote branches, rc-branch pins, published channel dbs, tags — no
state files) and offers the correct next step. Versions are inferred from
branch names (v4-0-2 ⇒ 4.0.2rcN ⇒ v4.0.2). ship refuses to promote a commit
no RC was cut from. pick is a multi-select over merged quattro PRs,
cherry-picking merge commits. doctor pre-flights every credential and
connection. self-test wired into CI.
bin/omarchy-pkgs stays as the pin engine, driven with its db URL pointed at
the rc channel and pins committed to the standing rc branch (rebuilt as
master + pins per cut and force-pushed; the server rc worktree follows with
reset --hard).
The rc service builds from the rc branch worktree (/root/omarchy-pkgs-rc,
created by bin/setup) but publishes into the primary checkout's channel tree
via OMARCHY_REPO_ROOT. The timer is a retry backstop: rc builds are normally
triggered immediately over SSH by the release orchestrator.