Build fast-ring for rc as rc, instead of copying stable's artifacts
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.
This commit is contained in:
@@ -12,12 +12,17 @@ The filesystem no longer encodes release policy. Instead:
|
||||
|
||||
- there are three channels forming a forward-only pipeline: `edge` → `rc` → `stable`
|
||||
- all packages build for `edge` unless their metadata pins `channels`
|
||||
- packages with `"release_ring": "fast"` also build directly for `stable`, and those
|
||||
artifacts are replicated into `rc` in the same release run so rc and stable stay in parity
|
||||
- packages with `"release_ring": "fast"` build natively for **all three** channels —
|
||||
each in its own image against its own base mirror, because rc's Arch snapshot may sit
|
||||
anywhere between stable's and edge's, so an artifact linked against stable's libraries
|
||||
is not necessarily correct for rc
|
||||
- a release train opens by advancing edge into rc (`bin/repo advance --from edge --to rc`)
|
||||
and ships by promoting rc into stable (`bin/repo advance --from rc --to stable`)
|
||||
- the release pair (`omarchy`, `omarchy-settings`) builds for `rc` from the standing `rc`
|
||||
branch; the dev pair (`omarchy-dev`, `omarchy-settings-dev`) is pinned to `edge`
|
||||
- the release pair (`omarchy`, `omarchy-settings`) is marked `"pinned": true`: its version
|
||||
is set per release on the standing `rc` branch, so only a build from that branch's worktree
|
||||
(`OMARCHY_RC_PINS=1`, which `omarchy-release rc` sets) may build it for rc — master's
|
||||
shipped pins can never overwrite an in-flight RC. The dev pair
|
||||
(`omarchy-dev`, `omarchy-settings-dev`) is pinned to `edge`
|
||||
- AUR sync behavior is controlled by `source`, `sync`, `aur`, patches, and hooks in `.omarchy/`
|
||||
- packages can opt out of unscoped builds with `skip_build`; explicit `--package` builds remain available
|
||||
- packages that follow a vendor release feed instead of the AUR carry an `.omarchy/upstream.sh` hook
|
||||
@@ -586,7 +591,8 @@ Fields:
|
||||
- `sync`: optional for AUR packages; defaults to `true`. Set `false` for AUR-origin packages that Omarchy maintains manually.
|
||||
- `aur`: optional AUR package name when it differs from the local package directory, usually for split packages.
|
||||
- `release_ring`: optional. `fast` means the package is built directly for stable as well as edge, with the artifacts replicated into rc for parity. Packages without a ring build in edge and reach stable through the pipeline (`bin/repo advance`).
|
||||
- `channels`: optional array pinning where the package lives (`edge`, `rc`, `stable`). With the key present the package builds in each listed channel except `stable` (stable is only fed by promotion), and `bin/repo advance` refuses to carry it anywhere it isn't a member. Without the key a package is a member of every channel and follows the default build rules above.
|
||||
- `channels`: optional array bounding where the package may be built (`edge`, `rc`, `stable`). Without the key a package is a member of every channel and follows the default build rules above; `bin/repo advance` refuses to carry a package anywhere it isn't a member.
|
||||
- `pinned`: optional boolean. A pinned package's version is set per release by `omarchy-release` on the `rc` branch, so it is never built for stable (promotion only) and is built for rc only from that branch's worktree (`OMARCHY_RC_PINS=1`). Used by `omarchy` and `omarchy-settings`.
|
||||
- `skip_build`: optional boolean; defaults to `false`. Set `true` to exclude a package from scheduled version checks and unscoped builds. The package can still be built explicitly with `bin/repo release --package <name>`.
|
||||
- `pkgrel`: optional Omarchy pkgrel suffix for a version-pinned rebuild bump. This emits `<aur pkgrel>.<suffix>` instead of replacing AUR's pkgrel. `offset` can be used only when preserving monotonic upgrades from old absolute pkgrel bumps. The metadata is removed automatically when AUR sync changes `pkgver`; the current package version is read from the checked-in PKGBUILD, so the version is not duplicated in JSON.
|
||||
- `rebuild_on`: optional array of package names this package links against closely enough that it must be rebuilt when they change, independent of its own source. Read by `bin/sync-rebuilds`.
|
||||
@@ -596,8 +602,8 @@ Fields:
|
||||
### Build Matrix
|
||||
|
||||
- **Edge unscoped builds** (`--mirror edge`): packages in `pkgbuilds/*` unless `"skip_build": true` or `channels` excludes edge
|
||||
- **Rc unscoped builds** (`--mirror rc`): packages whose `channels` include `rc` (the release pair, built from the `rc` branch worktree)
|
||||
- **Stable unscoped builds** (`--mirror stable`): packages with `"release_ring": "fast"` unless `"skip_build": true`; their artifacts replicate to rc in the same run
|
||||
- **Rc unscoped builds** (`--mirror rc`): `"release_ring": "fast"` packages, built natively in the rc image. The pinned release pair joins them only when `OMARCHY_RC_PINS=1` (the `rc` branch worktree, set by `omarchy-release rc`)
|
||||
- **Stable unscoped builds** (`--mirror stable`): packages with `"release_ring": "fast"` unless `"skip_build": true`
|
||||
- **Explicit builds** (`--package <name>`): the selected package, including packages with `"skip_build": true`, subject to mirror eligibility
|
||||
- **Channel moves** (`bin/repo advance`): copies current packages + signatures forward through edge → rc → stable, never rewriting a published filename
|
||||
|
||||
@@ -741,7 +747,7 @@ reaches the mirror in minutes rather than hours:
|
||||
|
||||
1. **check-versions** (`*:0/5`): Pulls latest from git, compares PKGBUILD versions to published versions, creates state files if builds are needed
|
||||
2. **auto-release-edge** (`*:1/5`): If a state file exists, builds all edge packages that need updates
|
||||
3. **auto-release-rc** (`*:2/5`): Builds the rc channel from the `rc` branch worktree (`/root/omarchy-pkgs-rc`), publishing into the shared channel tree. It runs from the main checkout and creates that worktree on demand, so a host with no rc branch yet is a no-op rather than a failing unit. The orchestrator also triggers this immediately over ssh when cutting an RC
|
||||
3. **auto-release-rc** (`*:2/5`): Builds fast-ring packages for rc, from the main checkout like the other two — natively in the rc image, not copied from another channel. The pinned release pair is built separately by `omarchy-release rc` in the `rc` branch worktree
|
||||
4. **auto-release-stable** (`*:3/5`): If a state file exists, builds `release_ring=fast` packages for stable and replicates them to rc
|
||||
|
||||
That cadence is only safe because of three guards:
|
||||
|
||||
@@ -202,11 +202,6 @@ package_eligible() {
|
||||
|
||||
if [[ "$FAST_RING_ONLY" == true ]]; then
|
||||
package_is_fast_ring "$pkgdir" || return 1
|
||||
elif [[ "$FROM" == "edge" && "$TO" == "rc" ]]; then
|
||||
# Fast-ring packages reach rc by replication of the STABLE build (same
|
||||
# bytes stable users get). Carrying the independently built edge artifact
|
||||
# forward would race it under the same filename.
|
||||
package_is_fast_ring "$pkgdir" && return 1
|
||||
fi
|
||||
|
||||
# The bootstrap seeds an empty rc from stable, so parity is the whole point:
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Scheduled entry point for the rc channel.
|
||||
#
|
||||
# The rc channel builds from the standing `rc` branch in its own worktree, but
|
||||
# that branch does not exist until the first RC is cut — and a host set up
|
||||
# before then has no worktree either. This runs from the MAIN checkout, which
|
||||
# always exists, and makes both conditions non-events: no branch means nothing
|
||||
# to build, and a missing worktree is created on demand rather than failing
|
||||
# the unit every five minutes.
|
||||
|
||||
set -e
|
||||
|
||||
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
||||
source "$BUILD_ROOT/helpers/message-helpers.sh"
|
||||
|
||||
RC_WORKTREE="${OMARCHY_RC_WORKTREE:-/root/omarchy-pkgs-rc}"
|
||||
STATE_DIR="${OMARCHY_STATE_DIR:-/root/.state}"
|
||||
|
||||
# Nothing queued: the common case. Exit before touching the network.
|
||||
[[ -f "$STATE_DIR/.sync-needed-rc" ]] || exit 0
|
||||
|
||||
git -C "$BUILD_ROOT" fetch --quiet origin rc 2>/dev/null || true
|
||||
|
||||
if ! git -C "$BUILD_ROOT" show-ref --verify --quiet refs/remotes/origin/rc; then
|
||||
print_info "No rc branch yet — nothing to build (the first RC cut creates it)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -d "$RC_WORKTREE" ]]; then
|
||||
print_info "Creating rc worktree at $RC_WORKTREE..."
|
||||
if git -C "$BUILD_ROOT" show-ref --verify --quiet refs/heads/rc; then
|
||||
git -C "$BUILD_ROOT" worktree add "$RC_WORKTREE" rc
|
||||
else
|
||||
git -C "$BUILD_ROOT" worktree add --track -b rc "$RC_WORKTREE" origin/rc
|
||||
fi
|
||||
fi
|
||||
|
||||
# The rc branch is rebuilt as master + pins for each cut and force-pushed, so
|
||||
# this follows with a hard reset rather than a fast-forward pull.
|
||||
git -C "$RC_WORKTREE" fetch origin rc
|
||||
git -C "$RC_WORKTREE" reset --hard origin/rc
|
||||
|
||||
exec "$RC_WORKTREE/bin/auto-release" rc
|
||||
@@ -179,6 +179,7 @@ check_mirror() {
|
||||
}
|
||||
|
||||
check_mirror edge
|
||||
check_mirror rc
|
||||
check_mirror stable
|
||||
|
||||
print_success "Version check complete!"
|
||||
|
||||
+13
-12
@@ -225,8 +225,12 @@ print_no_host_help() { # print_no_host_help <what> <script>
|
||||
echo "$2" >&2
|
||||
}
|
||||
|
||||
# Creates the rc worktree on first use (a host set up before the rc branch
|
||||
# existed has none), then syncs it and kicks the service.
|
||||
# Builds the pinned release pair for the rc channel from the rc branch's own
|
||||
# worktree, creating it on first use. OMARCHY_RC_PINS marks this as the one
|
||||
# build allowed to set those packages' rc versions; OMARCHY_REPO_ROOT points
|
||||
# the worktree at the primary checkout's channel tree so all three channels
|
||||
# stay in one place. Fast-ring packages are not built here — the scheduled rc
|
||||
# release covers those from master.
|
||||
RC_TRIGGER_SCRIPT='
|
||||
set -e
|
||||
git -C /root/omarchy-pkgs fetch origin rc
|
||||
@@ -236,24 +240,21 @@ if [ ! -d /root/omarchy-pkgs-rc ]; then
|
||||
fi
|
||||
git -C /root/omarchy-pkgs-rc fetch origin rc
|
||||
git -C /root/omarchy-pkgs-rc reset --hard origin/rc
|
||||
mkdir -p /root/.state
|
||||
touch /root/.state/.sync-needed-rc
|
||||
systemctl start --no-block omarchy-auto-release-rc.service
|
||||
cd /root/omarchy-pkgs-rc
|
||||
OMARCHY_RC_PINS=1 OMARCHY_REPO_ROOT=/root/omarchy-pkgs/pkgs.omarchy.org \
|
||||
bin/repo release --mirror rc --package omarchy omarchy-settings --skip-prod-check
|
||||
'
|
||||
|
||||
# An explicitly configured destination outranks the local-host inference: a
|
||||
# workstation that once ran a full local release carries the published-db
|
||||
# marker too, and its .repo-host must still win.
|
||||
trigger_rc_build() {
|
||||
local host
|
||||
if host=$(repo_host); then
|
||||
print_info "Triggering rc build on $host..."
|
||||
ssh "$host" "$RC_TRIGGER_SCRIPT"
|
||||
print_info "Building the RC on $host (this takes a while)..."
|
||||
ssh "$host" "source /root/.omarchy/build-credentials 2>/dev/null; $RC_TRIGGER_SCRIPT"
|
||||
elif on_repo_host; then
|
||||
print_info "Triggering rc build locally (this is the build host)..."
|
||||
print_info "Building the RC locally (this is the build host)..."
|
||||
bash -c "$RC_TRIGGER_SCRIPT"
|
||||
else
|
||||
print_no_host_help "build the rc channel" "$RC_TRIGGER_SCRIPT"
|
||||
print_no_host_help "build the release candidate" "$RC_TRIGGER_SCRIPT"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
-20
@@ -218,25 +218,5 @@ else
|
||||
notify_info "Release ran with nothing to publish: $MIRROR" "$summary"
|
||||
fi
|
||||
|
||||
# Step 7 (stable only): fast-ring packages publish to rc and stable together,
|
||||
# so rc never falls behind what stable users actually run. Skipped until the
|
||||
# rc channel has been bootstrapped.
|
||||
if [[ "$MIRROR" == "stable" ]]; then
|
||||
echo ""
|
||||
if [[ -f "$REPO_ROOT/rc/$ARCH/omarchy.db.tar.zst" ]]; then
|
||||
print_info "Step 7: Replicating fast-ring packages to rc (parity)..."
|
||||
REPLICATE_ARGS=(--from stable --to rc --fast-ring --arch "$ARCH")
|
||||
[[ -n "$SYNC_REMOTE" ]] && REPLICATE_ARGS+=(--sync-remote "$SYNC_REMOTE")
|
||||
[[ "$SKIP_PROD_CHECK" == true ]] && REPLICATE_ARGS+=(--skip-prod-check)
|
||||
"$BUILD_ROOT/bin/advance-channel" "${REPLICATE_ARGS[@]}" || {
|
||||
print_error "Fast-ring replication to rc failed"
|
||||
notify_error "Release failed: rc parity replication failed" "$RELEASE_CONTEXT"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
print_info "rc channel not bootstrapped; skipping fast-ring replication"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_success "Release workflow completed successfully!"
|
||||
|
||||
+30
-15
@@ -162,6 +162,18 @@ package_in_channel() {
|
||||
package_channels "$pkgdir" | grep -qx "$channel"
|
||||
}
|
||||
|
||||
# A pinned package's version is set per release by the orchestrator on the rc
|
||||
# branch, not by whatever the current checkout happens to say. Only a build
|
||||
# running from that branch's worktree (OMARCHY_RC_PINS=1) may build it for rc;
|
||||
# otherwise master's shipped pins would try to overwrite an in-flight RC with
|
||||
# an older version.
|
||||
package_is_pinned() {
|
||||
local pkgdir="$1" metadata
|
||||
metadata=$(metadata_file_for_dir "$pkgdir")
|
||||
[[ -f "$metadata" ]] || return 1
|
||||
[[ "$(jq -r 'if has("pinned") then .pinned else false end' "$metadata")" == "true" ]]
|
||||
}
|
||||
|
||||
package_builds_for_mirror() {
|
||||
local pkgdir="$1"
|
||||
local mirror="$2"
|
||||
@@ -169,28 +181,26 @@ package_builds_for_mirror() {
|
||||
package_has_pkgbuild "$pkgdir" || return 1
|
||||
package_has_metadata "$pkgdir" || return 1
|
||||
|
||||
# An explicit `channels` key pins where a package is BUILT: it builds in
|
||||
# each listed channel except stable, which is only ever fed by promotion.
|
||||
# Without the key, the defaults hold: everything builds for edge, and
|
||||
# fast-ring packages also build directly for stable (bin/release then
|
||||
# replicates those artifacts into rc to keep rc and stable in parity).
|
||||
if package_has_channels "$pkgdir"; then
|
||||
case "$mirror" in
|
||||
edge | rc)
|
||||
package_in_channel "$pkgdir" "$mirror"
|
||||
return
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
# An explicit `channels` key is the outer bound on where a package may be
|
||||
# built at all.
|
||||
if package_has_channels "$pkgdir" && ! package_in_channel "$pkgdir" "$mirror"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$mirror" in
|
||||
edge)
|
||||
return 0
|
||||
;;
|
||||
rc)
|
||||
# Fast-ring packages build for rc natively rather than being copied from
|
||||
# stable: the rc channel's Arch base can be sitting anywhere between
|
||||
# stable's snapshot and edge's, so an artifact linked against stable's
|
||||
# libraries is not necessarily correct for rc.
|
||||
package_is_pinned "$pkgdir" && { [[ -n "${OMARCHY_RC_PINS:-}" ]]; return; }
|
||||
package_is_fast_ring "$pkgdir"
|
||||
;;
|
||||
stable)
|
||||
package_is_pinned "$pkgdir" && return 1
|
||||
package_is_fast_ring "$pkgdir"
|
||||
;;
|
||||
*)
|
||||
@@ -412,6 +422,11 @@ validate_package_metadata() {
|
||||
*) echo "invalid release_ring for $(basename "$pkgdir"): $ring"; return 1 ;;
|
||||
esac
|
||||
|
||||
if ! jq -e 'if has("pinned") | not then true else (.pinned | type) == "boolean" end' "$metadata" >/dev/null; then
|
||||
echo "invalid pinned for $(basename "$pkgdir"): must be boolean"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! jq -e '
|
||||
if has("channels") | not then true
|
||||
else .channels | type == "array" and length > 0
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
{ "source": "local", "channels": ["edge", "rc", "stable"] }
|
||||
{
|
||||
"source": "local",
|
||||
"channels": ["edge", "rc", "stable"],
|
||||
"pinned": true
|
||||
}
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
{ "source": "local", "channels": ["edge", "rc", "stable"] }
|
||||
{
|
||||
"source": "local",
|
||||
"channels": ["edge", "rc", "stable"],
|
||||
"pinned": true
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
# Runs from the main checkout: bin/auto-release-rc creates and syncs the rc
|
||||
# branch worktree itself, so a host with no rc branch yet is a no-op instead
|
||||
# of a unit that fails every five minutes. Builds happen in the worktree but
|
||||
# publish into the primary checkout's channel tree via OMARCHY_REPO_ROOT.
|
||||
ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release-rc'
|
||||
# Runs from the main checkout, exactly like edge and stable: this builds the
|
||||
# packages the version check discovers for rc (fast-ring), natively in the rc
|
||||
# image against the rc mirror. The pinned release pair is NOT built here —
|
||||
# its version comes from the rc branch, and omarchy-release builds it in that
|
||||
# branch's worktree with OMARCHY_RC_PINS=1.
|
||||
ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release rc'
|
||||
Environment=OMARCHY_STATE_DIR=/root/.state
|
||||
Environment=OMARCHY_REPO_ROOT=/root/omarchy-pkgs/pkgs.omarchy.org
|
||||
TimeoutStartSec=7200
|
||||
|
||||
[Install]
|
||||
|
||||
Reference in New Issue
Block a user