Harden multi-architecture release pipeline
This commit is contained in:
23 files changed
+557
-271
No files matched your search
+40
-33
@@ -25,31 +25,21 @@ SKIP_PROD_CHECK=false
|
||||
FAST_RING_ONLY=false
|
||||
BOOTSTRAP=false
|
||||
PACKAGES=""
|
||||
ALL_ARCHES=false
|
||||
|
||||
# Kept for --arch all, which re-invokes this script per architecture with the
|
||||
# other arguments unchanged (minus the --arch all pair itself).
|
||||
ORIGINAL_ARGS=()
|
||||
skip_next=false
|
||||
prev=""
|
||||
arch_option=false
|
||||
for arg in "$@"; do
|
||||
if [[ "$skip_next" == true ]]; then
|
||||
skip_next=false
|
||||
prev=""
|
||||
continue
|
||||
if [[ "$arch_option" == true ]]; then
|
||||
[[ "$arg" != "all" ]] && ORIGINAL_ARGS+=("--arch" "$arg")
|
||||
arch_option=false
|
||||
elif [[ "$arg" == "--arch" ]]; then
|
||||
arch_option=true
|
||||
else
|
||||
ORIGINAL_ARGS+=("$arg")
|
||||
fi
|
||||
if [[ "$arg" == "--arch" ]]; then
|
||||
prev="--arch"
|
||||
continue
|
||||
fi
|
||||
if [[ "$prev" == "--arch" ]]; then
|
||||
prev=""
|
||||
if [[ "$arg" == "all" ]]; then
|
||||
continue
|
||||
fi
|
||||
ORIGINAL_ARGS+=("--arch" "$arg")
|
||||
continue
|
||||
fi
|
||||
ORIGINAL_ARGS+=("$arg")
|
||||
done
|
||||
|
||||
usage() {
|
||||
@@ -93,21 +83,13 @@ while [[ $# -gt 0 ]]; do
|
||||
;;
|
||||
--arch)
|
||||
if [[ "$2" == "all" ]]; then
|
||||
# A release train advances every published architecture together; a
|
||||
# channel that moved for one and not another would serve mismatched
|
||||
# versions to users on the two. Re-run this script once per
|
||||
# architecture with the same arguments, stopping at the first failure
|
||||
# so the operator sees which architecture is stuck (the advance is
|
||||
# idempotent, so re-running with --arch all resumes where it stopped).
|
||||
shift 2
|
||||
for arch in $(published_arches); do
|
||||
"$0" --arch "$arch" "${ORIGINAL_ARGS[@]}" || exit $?
|
||||
done
|
||||
exit 0
|
||||
ALL_ARCHES=true
|
||||
ARCH=all
|
||||
else
|
||||
require_valid_arch "$2"
|
||||
ARCH="$2"
|
||||
update_arch_paths
|
||||
fi
|
||||
require_valid_arch "$2"
|
||||
ARCH="$2"
|
||||
update_arch_paths
|
||||
shift 2
|
||||
;;
|
||||
--package)
|
||||
@@ -175,6 +157,31 @@ case "$FROM->$TO" in
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$ALL_ARCHES" == true ]]; then
|
||||
# Hold one lock across the whole operation so a timer cannot mutate a
|
||||
# channel between architectures. Check every source database before moving
|
||||
# the first one; a failed sync can still require an idempotent retry, but a
|
||||
# missing architecture never creates a knowingly partial advance.
|
||||
if [[ "$DRY_RUN" != true ]]; then
|
||||
acquire_release_lock || exit 1
|
||||
fi
|
||||
|
||||
ARCHES=$(published_arches)
|
||||
for arch in $ARCHES; do
|
||||
source_db="$REPO_ROOT/$FROM/$arch/omarchy.db.tar.zst"
|
||||
if [[ ! -f "$source_db" ]]; then
|
||||
print_error "Source database not found: $source_db"
|
||||
echo "Nothing has been published to the $FROM channel for $arch."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
for arch in $ARCHES; do
|
||||
"$0" --arch "$arch" "${ORIGINAL_ARGS[@]}" || exit $?
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SOURCE_DIR="$REPO_ROOT/$FROM/$ARCH"
|
||||
TARGET_DIR="$REPO_ROOT/$TO/$ARCH"
|
||||
SOURCE_DB="$SOURCE_DIR/omarchy.db.tar.zst"
|
||||
|
||||
+13
-2
@@ -131,17 +131,28 @@ release_arch() {
|
||||
|
||||
if "$BUILD_ROOT/bin/repo" release --mirror "$MIRROR" --arch "$arch" --skip-prod-check; then
|
||||
print_success "Release completed successfully for $MIRROR ($arch)"
|
||||
rm -f "$state_file" "$fail_file"
|
||||
local completed_state=("$state_file" "$fail_file")
|
||||
if [[ "$arch" == "x86_64" ]]; then
|
||||
completed_state+=("$(legacy_sync_queue_file "$MIRROR")" "$(legacy_sync_fail_file "$MIRROR")")
|
||||
fi
|
||||
if ! rm -f "${completed_state[@]}"; then
|
||||
print_error "Release succeeded, but its queue state could not be cleared"
|
||||
return 1
|
||||
fi
|
||||
print_success "State file removed: $state_file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
fail_count=$((fail_count + 1))
|
||||
cat >"$fail_file" <<EOF
|
||||
if ! cat >"$fail_file" <<EOF
|
||||
FAILURE_COUNT=$fail_count
|
||||
FAILURE_AT=$(date +%s)
|
||||
FAILURE_FINGERPRINT=$(current_fingerprint)
|
||||
EOF
|
||||
then
|
||||
print_error "Could not record failure state: $fail_file"
|
||||
return 1
|
||||
fi
|
||||
local next
|
||||
next=$(backoff_seconds "$fail_count")
|
||||
print_error "Release failed for $MIRROR ($arch) (attempt $fail_count)"
|
||||
|
||||
@@ -102,6 +102,8 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
require_valid_arch "$ARCH"
|
||||
|
||||
if [[ $DEFER_RUNTIME_DEPS != "false" && $DEFER_RUNTIME_DEPS != "true" ]]; then
|
||||
print_error "OMARCHY_DEFER_RUNTIME_DEPS must be true or false"
|
||||
exit 1
|
||||
@@ -170,8 +172,7 @@ check_docker
|
||||
HOST_ARCH=$(uname -m)
|
||||
[[ "$HOST_ARCH" == "arm64" ]] && HOST_ARCH=aarch64
|
||||
if [[ "$HOST_ARCH" != "$ARCH" ]]; then
|
||||
# shellcheck disable=SC2086
|
||||
if ! docker run --rm $(get_platform_arg "$ARCH") alpine:3.21 /bin/true >/dev/null 2>&1; then
|
||||
if ! docker run --rm "$(get_platform_arg "$ARCH")" alpine:3.21 /bin/true >/dev/null 2>&1; then
|
||||
print_info "Setting up QEMU for $ARCH emulation on this $HOST_ARCH host..."
|
||||
setup_qemu
|
||||
fi
|
||||
@@ -183,7 +184,7 @@ if [[ $KEEP_BUILD_WORKSPACE == "1" ]]; then
|
||||
print_info "Keeping existing build workspace..."
|
||||
else
|
||||
print_info "Cleaning build workspace..."
|
||||
rm -rf "$BUILD_OUTPUT_DIR"/*
|
||||
rm -rf "${BUILD_OUTPUT_DIR:?}"/*
|
||||
fi
|
||||
mkdir -p "$BUILD_OUTPUT_DIR"
|
||||
|
||||
@@ -237,7 +238,7 @@ DOCKER_ARGS=(
|
||||
# Run the builder with assembled args
|
||||
PLATFORM_ARG=$(get_platform_arg "$ARCH")
|
||||
|
||||
docker run $PLATFORM_ARG "${DOCKER_ARGS[@]}" "$IMAGE_TAG" /build/build.sh
|
||||
docker run "$PLATFORM_ARG" "${DOCKER_ARGS[@]}" "$IMAGE_TAG" /build/build.sh
|
||||
|
||||
BUILD_RESULT=$?
|
||||
|
||||
|
||||
+1
-1
@@ -180,7 +180,7 @@ check_mirror() {
|
||||
needs_build=true
|
||||
packages+=("$pkg")
|
||||
fi
|
||||
done < <(packages_for_unscoped_build "$mirror")
|
||||
done < <(packages_for_unscoped_build "$mirror" "$ARCH")
|
||||
|
||||
echo ""
|
||||
|
||||
|
||||
+7
-6
@@ -31,12 +31,13 @@ clean_packages() {
|
||||
# Skip signature files
|
||||
[[ "$pkg" == *.sig ]] && continue
|
||||
|
||||
# Extract package name (remove version and architecture)
|
||||
# Format: name-version-release-arch.pkg.tar.*
|
||||
# The version starts at the first "-<digit>" and neither pkgver nor pkgrel
|
||||
# can contain a hyphen, so the architecture is whatever sits between the
|
||||
# last hyphen and .pkg.tar — any, x86_64, aarch64, and whatever comes next.
|
||||
local pkgname=$(echo "$pkg" | sed -E 's/-[0-9]+.*-[^-]+\.pkg\.tar\..*//')
|
||||
# Format: name-version-release-arch.pkg.tar.*. Work from the right because
|
||||
# package names can themselves contain version-like pieces (qt6-5compat,
|
||||
# nvidia-580xx-utils), while pkgver and pkgrel cannot contain hyphens.
|
||||
local stem="${pkg%%.pkg.tar.*}"
|
||||
stem="${stem%-*}" # architecture
|
||||
stem="${stem%-*}" # pkgrel
|
||||
local pkgname="${stem%-*}" # pkgver
|
||||
|
||||
# Add to array
|
||||
if [[ -n "${packages[$pkgname]}" ]]; then
|
||||
|
||||
+12
-6
@@ -23,7 +23,7 @@ source "$BUILD_ROOT/helpers/paths.sh"
|
||||
source "$BUILD_ROOT/helpers/host-helpers.sh"
|
||||
|
||||
UPSTREAM_URL="${OMARCHY_UPSTREAM_URL:-https://github.com/basecamp/omarchy.git}"
|
||||
EDGE_DB_URL="${OMARCHY_EDGE_DB_URL:-https://pkgs.omarchy.org/edge/x86_64/omarchy.db.tar.zst}"
|
||||
EDGE_DB_URL="${OMARCHY_EDGE_DB_URL:-https://pkgs.omarchy.org/edge/$(reference_arch)/omarchy.db.tar.zst}"
|
||||
RELEASE_PACKAGES=(omarchy omarchy-settings)
|
||||
DEFAULT_RC_REF="quattro"
|
||||
SRCDEST_DIR="$BUILD_ROOT/.srcdest"
|
||||
@@ -327,13 +327,19 @@ regenerate_checksums() {
|
||||
|
||||
trigger_build_host() {
|
||||
local host
|
||||
queue_edge_builds() {
|
||||
mkdir -p "$STATE_DIR" || return 1
|
||||
local arch
|
||||
for arch in $(published_arches); do
|
||||
touch "$(sync_queue_file edge "$arch")" || return 1
|
||||
done
|
||||
}
|
||||
|
||||
# Explicit host configuration outranks the local-host inference (a
|
||||
# workstation that ran a full local release carries the db marker too).
|
||||
if ! resolve_repo_host "${REPO_HOST_OVERRIDE:-}" >/dev/null && on_repo_host; then
|
||||
print_info "Triggering edge build locally (this is the build host)..."
|
||||
if mkdir -p "${OMARCHY_STATE_DIR:-/root/.state}" &&
|
||||
touch "${OMARCHY_STATE_DIR:-/root/.state}/.sync-needed-edge" &&
|
||||
systemctl start --no-block omarchy-auto-release-edge.service; then
|
||||
if queue_edge_builds && systemctl start --no-block omarchy-auto-release-edge.service; then
|
||||
print_success "Edge build triggered"
|
||||
else
|
||||
print_warning "Could not start the edge release service — the 6-hourly timer will pick it up"
|
||||
@@ -343,11 +349,11 @@ trigger_build_host() {
|
||||
if ! host=$(resolve_repo_host "${REPO_HOST_OVERRIDE:-}"); then
|
||||
print_info "No repository host configured (set OMARCHY_REPO_HOST or $BUILD_ROOT/.repo-host — any ssh destination, e.g. root@<host> or an ssh-config alias)."
|
||||
print_info "The 6-hourly auto-release timer will pick this up, or trigger manually:"
|
||||
echo " ssh <build-host> 'git -C /root/omarchy-pkgs pull --ff-only && touch /root/.state/.sync-needed-edge && systemctl start omarchy-auto-release-edge.service'"
|
||||
echo " ssh <build-host> 'git -C /root/omarchy-pkgs pull --ff-only && systemctl start omarchy-check-versions.service omarchy-auto-release-edge.service'"
|
||||
return 0
|
||||
fi
|
||||
print_info "Triggering edge build on $host..."
|
||||
if ssh "$host" 'git -C /root/omarchy-pkgs pull --ff-only && mkdir -p /root/.state && touch /root/.state/.sync-needed-edge && systemctl start --no-block omarchy-auto-release-edge.service'; then
|
||||
if ssh "$host" 'git -C /root/omarchy-pkgs pull --ff-only && cd /root/omarchy-pkgs && export BUILD_ROOT=/root/omarchy-pkgs && source helpers/paths.sh && mkdir -p "$STATE_DIR" && for arch in $(published_arches); do touch "$(sync_queue_file edge "$arch")"; done && systemctl start --no-block omarchy-auto-release-edge.service'; then
|
||||
print_success "Edge build triggered on $host"
|
||||
else
|
||||
print_warning "Could not trigger $host — the 6-hourly timer will pick it up"
|
||||
|
||||
+75
-50
@@ -28,11 +28,9 @@ ISO_REPO="${OMARCHY_ISO_REPO:-omacom-io/omarchy-iso}"
|
||||
DEV_BRANCH="${OMARCHY_DEV_BRANCH:-quattro}"
|
||||
|
||||
PKGS_DB_BASE="${OMARCHY_PKGS_DB_BASE:-https://pkgs.omarchy.org}"
|
||||
# The train observes channels through the reference architecture (the first
|
||||
# published one). The pair is pinned to one version for every architecture and
|
||||
# built for all of them in one rc trigger, so one architecture's database is
|
||||
# the state of the release; per-architecture drift is a build failure the
|
||||
# scheduled pipeline reports, not something the train has to poll for.
|
||||
# The first published architecture supplies the version-ordering floor when
|
||||
# cutting pins. Readiness checks below still verify every published
|
||||
# architecture before an RC or final release can move forward.
|
||||
OBSERVED_ARCH=$(reference_arch)
|
||||
RC_DB_URL="$PKGS_DB_BASE/rc/$OBSERVED_ARCH/omarchy.db.tar.zst"
|
||||
|
||||
@@ -187,12 +185,12 @@ ensure_work_clone() {
|
||||
# Prints omarchy's published version in a channel; empty when absent, rc 2 when
|
||||
# the database cannot be read (callers must not mistake an outage for absence).
|
||||
published_version() {
|
||||
local channel="$1" tmp descs
|
||||
local channel="$1" arch="${2:-$OBSERVED_ARCH}" tmp descs
|
||||
tmp=$(mktemp) || return 2
|
||||
# A unique query string busts the CDN cache: right after a sync the plain
|
||||
# URL can keep serving the previous db for a while, which reads as "not
|
||||
# published yet" to status, the wait loop, and ship's pre-checks.
|
||||
if ! curl -sf "$PKGS_DB_BASE/$channel/$OBSERVED_ARCH/omarchy.db.tar.zst?$(date +%s%N)" -o "$tmp"; then
|
||||
if ! curl -sf "$PKGS_DB_BASE/$channel/$arch/omarchy.db.tar.zst?$(date +%s%N)" -o "$tmp"; then
|
||||
rm -f "$tmp"
|
||||
return 2
|
||||
fi
|
||||
@@ -213,6 +211,14 @@ published_version() {
|
||||
' <<<"$descs"
|
||||
}
|
||||
|
||||
all_arches_at_version() { # all_arches_at_version <channel> <pkgver>
|
||||
local channel="$1" want="$2" arch got
|
||||
for arch in $(published_arches); do
|
||||
got=$(published_version "$channel" "$arch" 2>/dev/null) || return 1
|
||||
[[ "${got%-*}" == "$want" ]] || return 1
|
||||
done
|
||||
}
|
||||
|
||||
# The rc branch of THIS repo carries the current pins. Read them without
|
||||
# touching the working tree.
|
||||
rc_branch_pin() { # prints "pkgver commit", empty when no rc branch
|
||||
@@ -297,12 +303,16 @@ host_advance() { # host_advance <from> <to> [extra args...]
|
||||
}
|
||||
|
||||
wait_for_published() { # wait_for_published <channel> <version> [timeout-seconds]
|
||||
local channel="$1" want="$2" timeout="${3:-3600}" waited=0 got
|
||||
print_info "Waiting for $want to appear in the $channel channel (up to $((timeout / 60))m)..."
|
||||
local channel="$1" want="$2" timeout="${3:-3600}" waited=0 arch got pending
|
||||
print_info "Waiting for $want in $channel for: $(published_arches | tr '\n' ' ')"
|
||||
while ((waited < timeout)); do
|
||||
got=$(published_version "$channel" 2>/dev/null) || got=""
|
||||
if [[ "${got%-*}" == "$want" ]]; then
|
||||
print_success "$channel now serves omarchy $got"
|
||||
pending=""
|
||||
for arch in $(published_arches); do
|
||||
got=$(published_version "$channel" "$arch" 2>/dev/null) || got=""
|
||||
[[ "${got%-*}" == "$want" ]] || pending+=" $arch=${got:-unreachable}"
|
||||
done
|
||||
if [[ -z "$pending" ]]; then
|
||||
print_success "$channel now serves omarchy $want on every published architecture"
|
||||
return 0
|
||||
fi
|
||||
sleep 60
|
||||
@@ -310,7 +320,7 @@ wait_for_published() { # wait_for_published <channel> <version> [timeout-seconds
|
||||
printf '.' >&2
|
||||
done
|
||||
echo "" >&2
|
||||
print_warning "Timed out waiting for $want in $channel (currently: ${got:-unknown})"
|
||||
print_warning "Timed out waiting for $want in $channel (pending:$pending)"
|
||||
print_info "The build may still be running — re-run this command to resume."
|
||||
return 1
|
||||
}
|
||||
@@ -674,15 +684,13 @@ cmd_rc() {
|
||||
if [[ -n "$pin" ]]; then
|
||||
local pin_ver="${pin%% *}" pin_commit="${pin##* }"
|
||||
if [[ "$pin_commit" == "$head" && "$pin_ver" =~ ^${version//./\\.}rc[0-9]+$ ]]; then
|
||||
local pub
|
||||
pub=$(published_version rc 2>/dev/null) || pub=""
|
||||
if [[ "${pub%-*}" == "$pin_ver" ]]; then
|
||||
print_success "$pin_ver is already cut from this head and published to rc"
|
||||
if all_arches_at_version rc "$pin_ver"; then
|
||||
print_success "$pin_ver is already cut from this head and published to rc on every architecture"
|
||||
maybe_iso "$pin_ver" rc "$iso_mode"
|
||||
return 0
|
||||
fi
|
||||
print_info "$pin_ver is pinned from this head but not published yet — re-triggering the build"
|
||||
trigger_rc_build || true
|
||||
trigger_rc_build || return 1
|
||||
[[ "$wait" == true ]] && wait_for_published rc "$pin_ver"
|
||||
maybe_iso "$pin_ver" rc "$iso_mode"
|
||||
return 0
|
||||
@@ -698,7 +706,7 @@ cmd_rc() {
|
||||
new_ver="${new_pin%% *}"
|
||||
print_success "Pinned $new_ver (rc branch pushed)"
|
||||
|
||||
trigger_rc_build || true
|
||||
trigger_rc_build || return 1
|
||||
if [[ "$wait" == true ]]; then
|
||||
wait_for_published rc "$new_ver" || return 1
|
||||
fi
|
||||
@@ -720,9 +728,7 @@ cmd_ship() {
|
||||
exit 1
|
||||
fi
|
||||
version=$(branch_to_version "$branch")
|
||||
local stable_now
|
||||
stable_now=$(published_version stable 2>/dev/null) || stable_now=""
|
||||
if [[ "${stable_now%-*}" == "$version" ]] &&
|
||||
if all_arches_at_version stable "$version" &&
|
||||
gh release view "v$version" --repo "$UPSTREAM_REPO" >/dev/null 2>&1; then
|
||||
print_success "Nothing to ship — $version is tagged, released, and live on stable"
|
||||
exit 0
|
||||
@@ -764,10 +770,8 @@ cmd_ship() {
|
||||
echo " omarchy-release rc"
|
||||
exit 1
|
||||
fi
|
||||
local pub
|
||||
pub=$(published_version rc 2>/dev/null) || pub=""
|
||||
if [[ "${pub%-*}" != "$pin_ver" ]]; then
|
||||
print_error "$pin_ver is pinned but rc serves '${pub:-nothing}' — the candidate build hasn't published"
|
||||
if ! all_arches_at_version rc "$pin_ver"; then
|
||||
print_error "$pin_ver is pinned but is not published for every architecture"
|
||||
echo "Wait for it (or re-run: omarchy-release rc), then ship."
|
||||
exit 1
|
||||
fi
|
||||
@@ -811,10 +815,9 @@ cmd_ship() {
|
||||
|
||||
# 2. Final pins into rc. Resolve the tag we just established so the final
|
||||
# PKGBUILDs record both its provenance and its exact commit.
|
||||
local rc_pub stable_pub
|
||||
rc_pub=$(published_version rc 2>/dev/null) || rc_pub=""
|
||||
if [[ "${rc_pub%-*}" == "$version" ]]; then
|
||||
print_success "2/7 Final $version already published to rc"
|
||||
local stable_pub
|
||||
if all_arches_at_version rc "$version"; then
|
||||
print_success "2/7 Final $version already published to rc on every architecture"
|
||||
else
|
||||
if [[ "$pin_ver" != "$version" ]]; then
|
||||
print_info "2/7 Pinning final $version from tag v$version..."
|
||||
@@ -822,22 +825,21 @@ cmd_ship() {
|
||||
else
|
||||
print_info "2/7 Final $version pinned — re-triggering build"
|
||||
fi
|
||||
trigger_rc_build || true
|
||||
trigger_rc_build || exit 1
|
||||
wait_for_published rc "$version" || exit 1
|
||||
fi
|
||||
|
||||
# 3. Promote rc -> stable
|
||||
stable_pub=$(published_version stable 2>/dev/null) || stable_pub=""
|
||||
if [[ "${stable_pub%-*}" == "$version" ]]; then
|
||||
print_success "3/7 Stable already serves $version"
|
||||
if all_arches_at_version stable "$version"; then
|
||||
print_success "3/7 Stable already serves $version on every architecture"
|
||||
else
|
||||
host_advance rc stable || exit 1
|
||||
stable_pub=$(published_version stable 2>/dev/null) || stable_pub=""
|
||||
if [[ "${stable_pub%-*}" != "$version" ]]; then
|
||||
print_error "Promotion ran but stable serves '${stable_pub:-nothing}' — investigate before continuing"
|
||||
if ! all_arches_at_version stable "$version"; then
|
||||
print_error "Promotion ran but stable does not serve $version on every architecture"
|
||||
exit 1
|
||||
fi
|
||||
print_success "3/7 Promoted to stable: omarchy $stable_pub"
|
||||
stable_pub=$(published_version stable 2>/dev/null) || stable_pub="$version"
|
||||
print_success "3/7 Promoted to stable: omarchy $stable_pub on every architecture"
|
||||
fi
|
||||
|
||||
# 4. Final pins onto master (keeps edge overlap publishing and the repo record)
|
||||
@@ -920,7 +922,7 @@ next_step() { # prints "<command>|<description>"
|
||||
last=$(newest_release_branch 2>/dev/null) || last=""
|
||||
if [[ -n "$last" && "$STABLE_VER" != "<unreachable>" ]]; then
|
||||
last_ver=$(branch_to_version "$last")
|
||||
if [[ "${STABLE_VER%-*}" != "$last_ver" ]] ||
|
||||
if ! all_arches_at_version stable "$last_ver" ||
|
||||
{ command -v gh >/dev/null && ! gh release view "v$last_ver" --repo "$UPSTREAM_REPO" >/dev/null 2>&1; }; then
|
||||
echo "ship|$last_ver is tagged but not fully shipped — resume ship"
|
||||
return
|
||||
@@ -936,7 +938,7 @@ next_step() { # prints "<command>|<description>"
|
||||
echo "ship|Final $TRAIN_VER is pinned — finish shipping (re-runs are safe)"
|
||||
elif [[ "$pin_commit" != "$TRAIN_HEAD" ]]; then
|
||||
echo "rc|$TRAIN has commits newer than $pin_ver — cut the next candidate"
|
||||
elif [[ "${RC_VER%-*}" != "$pin_ver" ]]; then
|
||||
elif ! all_arches_at_version rc "$pin_ver"; then
|
||||
echo "rc|$pin_ver is pinned but not published — re-run rc to re-trigger/wait"
|
||||
else
|
||||
echo "ship|$pin_ver is published to rc — test it, then ship"
|
||||
@@ -1006,20 +1008,24 @@ cmd_doctor() {
|
||||
check "makepkg available (checksums)" command -v makepkg
|
||||
check "curl available" command -v curl
|
||||
check "upstream reachable ($UPSTREAM_URL)" git ls-remote "$UPSTREAM_URL" HEAD
|
||||
local ch
|
||||
local ch arch
|
||||
for ch in edge stable; do
|
||||
if published_version "$ch" >/dev/null 2>&1; then
|
||||
print_success "$ch channel db readable"
|
||||
for arch in $(published_arches); do
|
||||
if published_version "$ch" "$arch" >/dev/null 2>&1; then
|
||||
print_success "$ch/$arch channel db readable"
|
||||
else
|
||||
print_error "$ch/$arch channel db readable"
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
done
|
||||
done
|
||||
for arch in $(published_arches); do
|
||||
if published_version rc "$arch" >/dev/null 2>&1; then
|
||||
print_success "rc/$arch channel db readable"
|
||||
else
|
||||
print_error "$ch channel db readable"
|
||||
failures=$((failures + 1))
|
||||
print_warning "rc/$arch channel db not readable — bootstrap it first: bin/repo bootstrap-rc (on the build host)"
|
||||
fi
|
||||
done
|
||||
if published_version rc >/dev/null 2>&1; then
|
||||
print_success "rc channel db readable"
|
||||
else
|
||||
print_warning "rc channel db not readable — bootstrap it first: bin/repo bootstrap-rc (on the build host)"
|
||||
fi
|
||||
local host
|
||||
if host=$(repo_host); then
|
||||
check "build host ssh ($host)" ssh -o ConnectTimeout=10 "$host" true
|
||||
@@ -1066,6 +1072,25 @@ cmd_self_test() {
|
||||
expect "version_is_patch 5.0.0" "$(version_is_patch 5.0.0 && echo yes || echo no)" "no"
|
||||
expect "previous_patch_tag 4.0.2" "$(previous_patch_tag 4.0.2)" "v4.0.1"
|
||||
expect "previous_patch_tag 4.0.10" "$(previous_patch_tag 4.0.10)" "v4.0.9"
|
||||
|
||||
# Keep release readiness fail-closed when only one architecture has reached
|
||||
# the requested version. This replaces the network reader for this process;
|
||||
# self-test exits immediately afterwards.
|
||||
published_version() {
|
||||
case "$2" in
|
||||
x86_64) echo "${TEST_X86_VERSION:-4.0.2-1}" ;;
|
||||
aarch64) echo "${TEST_ARM_VERSION:-4.0.2-1}" ;;
|
||||
esac
|
||||
}
|
||||
PUBLISHED_ARCHES=x86_64
|
||||
expect "x86-only readiness" "$(all_arches_at_version rc 4.0.2 && echo ready || echo blocked)" "ready"
|
||||
PUBLISHED_ARCHES=aarch64
|
||||
expect "ARM-only readiness" "$(all_arches_at_version rc 4.0.2 && echo ready || echo blocked)" "ready"
|
||||
PUBLISHED_ARCHES="x86_64 aarch64"
|
||||
TEST_ARM_VERSION=4.0.1-1
|
||||
expect "mixed versions block release" "$(all_arches_at_version rc 4.0.2 && echo ready || echo blocked)" "blocked"
|
||||
TEST_ARM_VERSION=4.0.2-1
|
||||
expect "both architectures ready" "$(all_arches_at_version rc 4.0.2 && echo ready || echo blocked)" "ready"
|
||||
echo ""
|
||||
if ((failures == 0)); then
|
||||
print_success "Self-test passed"
|
||||
|
||||
+9
-6
@@ -88,9 +88,12 @@ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Build/update the Docker image (always use x86_64 for removal - it's architecture independent)
|
||||
# repo-remove is mirror-independent — always use the edge x86_64 image
|
||||
build_docker_image "$BUILD_DIR" "x86_64" "edge"
|
||||
# repo-remove is architecture-independent, so use a host-native edge image.
|
||||
TOOL_ARCH=$(docker_native_arch) || {
|
||||
print_error "Unsupported host architecture: $(uname -m)"
|
||||
exit 1
|
||||
}
|
||||
build_docker_image "$BUILD_DIR" "$TOOL_ARCH" "edge"
|
||||
|
||||
acquire_release_lock || exit 1
|
||||
|
||||
@@ -99,13 +102,13 @@ print_info "Removing package..."
|
||||
# Ensure directory is writable by container user
|
||||
make_dir_writable "$REPO_DIR"
|
||||
|
||||
# Run the removal script in Docker (always use x86_64 image)
|
||||
docker run --rm --platform linux/amd64 \
|
||||
# Run the removal script in the host-native image.
|
||||
docker run --rm "$(get_platform_arg "$TOOL_ARCH")" \
|
||||
-e ARCH="$ARCH" \
|
||||
-e MIRROR="$MIRROR" \
|
||||
-v "$REPO_ROOT:/pkgs.omarchy.org" \
|
||||
-v "$BUILD_DIR:/build:ro" \
|
||||
omarchy-pkg-builder:latest-x86_64-edge /build/remove-package.sh "$PACKAGE_NAME"
|
||||
"omarchy-pkg-builder:latest-$TOOL_ARCH-edge" /build/remove-package.sh "$PACKAGE_NAME"
|
||||
|
||||
RESULT=$?
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ set -e
|
||||
|
||||
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
||||
source "$BUILD_ROOT/helpers/message-helpers.sh"
|
||||
source "$BUILD_ROOT/helpers/paths.sh"
|
||||
|
||||
CHECK_ONLY=false
|
||||
SKIP_TIMERS=false
|
||||
@@ -238,7 +239,7 @@ echo ""
|
||||
# --- release timers ----------------------------------------------------------
|
||||
|
||||
print_info "Published architectures: $(published_arches | tr '\n' ' ')"
|
||||
echo " (PUBLISHED_ARCHES in helpers/paths.sh, or OMARCHY_ARCHES in $CREDENTIALS)"
|
||||
echo " (PUBLISHED_ARCHES in helpers/paths.sh; OMARCHY_ARCHES overrides a one-off command)"
|
||||
echo ""
|
||||
|
||||
TIMERS=(omarchy-check-versions omarchy-auto-release-edge omarchy-auto-release-rc omarchy-auto-release-stable)
|
||||
|
||||
@@ -66,23 +66,28 @@ if [[ -z "$GPG_PASSPHRASE" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build/update the Docker image (always use x86_64 for signing - it's architecture independent)
|
||||
build_docker_image "$BUILD_DIR" "x86_64" "$MIRROR"
|
||||
# Signing is architecture-independent, so run its utility container natively
|
||||
# on either an x86_64 or ARM host.
|
||||
TOOL_ARCH=$(docker_native_arch) || {
|
||||
print_error "Unsupported host architecture: $(uname -m)"
|
||||
exit 1
|
||||
}
|
||||
build_docker_image "$BUILD_DIR" "$TOOL_ARCH" "$MIRROR"
|
||||
|
||||
print_info "Running package signing..."
|
||||
|
||||
# Ensure output directory is writable by container user
|
||||
make_dir_writable "$BUILD_OUTPUT_DIR"
|
||||
|
||||
# Run the signing script in Docker (always use x86_64 image)
|
||||
docker run --rm --platform linux/amd64 \
|
||||
# Run the signing script in the host-native image.
|
||||
docker run --rm "$(get_platform_arg "$TOOL_ARCH")" \
|
||||
-e ARCH="$ARCH" \
|
||||
-e MIRROR="$MIRROR" \
|
||||
-e GPG_PRIVATE_KEY="$GPG_PRIVATE_KEY" \
|
||||
-e GPG_PASSPHRASE="$GPG_PASSPHRASE" \
|
||||
-v "$BUILD_ROOT/build-output:/build-output" \
|
||||
-v "$BUILD_DIR:/build:ro" \
|
||||
omarchy-pkg-builder:latest-x86_64-$MIRROR /build/sign.sh
|
||||
"omarchy-pkg-builder:latest-$TOOL_ARCH-$MIRROR" /build/sign.sh
|
||||
|
||||
SIGN_RESULT=$?
|
||||
|
||||
|
||||
+228
-59
@@ -17,11 +17,14 @@ SELF_TEST=false
|
||||
# rebuilding for it would ship a package built against the wrong ABI.
|
||||
OFFICIAL_REPOS=" core extra multilib core-debug extra-debug "
|
||||
|
||||
# The published repository, used as the floor a bumped pkgrel has to clear.
|
||||
# Only x86_64 is published today; aarch64 has no repository to compare against.
|
||||
# The published repositories are the floor a bumped pkgrel has to clear.
|
||||
PUBLISHED_BASE_URL="${OMARCHY_PUBLISHED_BASE_URL:-https://pkgs.omarchy.org}"
|
||||
PUBLISHED_MIRRORS=(edge stable)
|
||||
PUBLISHED_ARCH=x86_64
|
||||
|
||||
# Arch Linux ARM has no dated snapshots. This is the same live repository the
|
||||
# aarch64 builder resolves; override it only when the builder mirror changes.
|
||||
ALARM_BASE_URL="${OMARCHY_ALARM_BASE_URL:-https://fl.us.mirror.archlinuxarm.org/aarch64}"
|
||||
ALARM_REPOS=(core extra alarm aur)
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
@@ -34,10 +37,13 @@ A package opts in by naming those dependencies in .omarchy/package.json:
|
||||
|
||||
{ "source": "aur", "sync": false, "rebuild_on": ["qt6-base"] }
|
||||
|
||||
The versions the current pkgrel was bumped for are recorded alongside, in
|
||||
rebuilt_against, and written by this command:
|
||||
The versions the current pkgrel was bumped for are recorded per published
|
||||
architecture in rebuilt_against, and written by this command:
|
||||
|
||||
{ "rebuild_on": ["qt6-base"], "rebuilt_against": { "qt6-base": "6.11.2-2" } }
|
||||
{ "rebuild_on": ["qt6-base"], "rebuilt_against": {
|
||||
"x86_64": { "qt6-base": "6.11.2-3" },
|
||||
"aarch64": { "qt6-base": "6.11.2-2" }
|
||||
} }
|
||||
|
||||
pkgrel is bumped unless every package named in rebuild_on is recorded and still
|
||||
matches. A name that is missing from the record counts as changed, so opting a
|
||||
@@ -60,8 +66,9 @@ Examples:
|
||||
$0 # Update every package that declares rebuild_on
|
||||
$0 quickshell-git # Update specific packages
|
||||
|
||||
Trigger versions are read from the local pacman database, so sync it first
|
||||
(pacman -Sy) or this reports whatever that database last saw.
|
||||
x86_64 trigger versions come from the local pacman database; aarch64 versions
|
||||
come from the live Arch Linux ARM repository database. Only architectures in
|
||||
PUBLISHED_ARCHES that the package supports are considered.
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -94,7 +101,7 @@ SPECIFIC_MODE=false
|
||||
# The version of a trigger package as the build container would resolve it.
|
||||
# A name pacman does not know reports nothing rather than failing, so the caller
|
||||
# gets to say which package was left alone instead of the run dying here.
|
||||
repo_version() {
|
||||
native_repo_version() {
|
||||
local package="$1"
|
||||
local info
|
||||
|
||||
@@ -108,9 +115,59 @@ repo_version() {
|
||||
' <<<"$info"
|
||||
}
|
||||
|
||||
load_alarm_repo() {
|
||||
local repo="$1" db index
|
||||
index="$TEMP_DIR/alarm-$repo.index"
|
||||
[[ -f "$index" ]] && return 0
|
||||
|
||||
db="$TEMP_DIR/alarm-$repo.db"
|
||||
if ! curl -fsSL --max-time 120 -o "$db" "$ALARM_BASE_URL/$repo/$repo.db" 2>/dev/null; then
|
||||
print_error "Could not read the aarch64 $repo repository database"
|
||||
return 1
|
||||
fi
|
||||
if ! tar -tf "$db" >/dev/null 2>&1; then
|
||||
print_error "Unreadable aarch64 $repo repository database"
|
||||
return 1
|
||||
fi
|
||||
|
||||
tar -xOf "$db" --wildcards '*/desc' 2>/dev/null | awk '
|
||||
function emit() {
|
||||
if (name != "" && version != "") {
|
||||
print name "\t" version
|
||||
if (base != "" && base != name) print base "\t" version
|
||||
}
|
||||
name=""; base=""; version=""
|
||||
}
|
||||
$0 == "%FILENAME%" { emit(); next }
|
||||
$0 == "%NAME%" { if (name != "" && version != "") emit(); getline; name=$0; next }
|
||||
$0 == "%BASE%" { getline; base=$0; next }
|
||||
$0 == "%VERSION%" { getline; version=$0; next }
|
||||
END { emit() }
|
||||
' >"$index"
|
||||
}
|
||||
|
||||
alarm_repo_version() {
|
||||
local package="$1" repo version
|
||||
for repo in "${ALARM_REPOS[@]}"; do
|
||||
load_alarm_repo "$repo" || return 1
|
||||
version=$(awk -F '\t' -v package="$package" '$1 == package { print $2; exit }' "$TEMP_DIR/alarm-$repo.index")
|
||||
if [[ -n "$version" ]]; then
|
||||
echo "$version"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
repo_version() { # repo_version <arch> <package>
|
||||
case "$1" in
|
||||
x86_64) native_repo_version "$2" ;;
|
||||
aarch64) alarm_repo_version "$2" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
declare -A PUBLISHED_VERSION=()
|
||||
PUBLISHED_LOADED=false
|
||||
PUBLISHED_AVAILABLE=true
|
||||
|
||||
remember_published() {
|
||||
local name="$1"
|
||||
@@ -131,34 +188,35 @@ load_published_versions() {
|
||||
[[ "$PUBLISHED_LOADED" == true ]] && return 0
|
||||
PUBLISHED_LOADED=true
|
||||
|
||||
local mirror db name base version
|
||||
local arch mirror db name base version
|
||||
|
||||
for mirror in "${PUBLISHED_MIRRORS[@]}"; do
|
||||
db="$TEMP_DIR/published-$mirror.db.tar.zst"
|
||||
for arch in $(published_arches); do
|
||||
for mirror in "${PUBLISHED_MIRRORS[@]}"; do
|
||||
db="$TEMP_DIR/published-$mirror-$arch.db.tar.zst"
|
||||
|
||||
if ! curl -fsSL --max-time 120 -o "$db" \
|
||||
"$PUBLISHED_BASE_URL/$mirror/$PUBLISHED_ARCH/omarchy.db.tar.zst" 2>/dev/null; then
|
||||
print_warning "Could not read the published $mirror database; bumps are not checked against it this run"
|
||||
PUBLISHED_AVAILABLE=false
|
||||
continue
|
||||
fi
|
||||
if ! curl -fsSL --max-time 120 -o "$db" \
|
||||
"$PUBLISHED_BASE_URL/$mirror/$arch/omarchy.db.tar.zst" 2>/dev/null; then
|
||||
print_warning "Could not read the published $mirror/$arch database; bumps are not checked against it this run"
|
||||
continue
|
||||
fi
|
||||
|
||||
while IFS=$'\t' read -r name base version; do
|
||||
[[ -n "$name" && -n "$version" ]] && remember_published "$name" "$version"
|
||||
[[ -n "$base" && -n "$version" ]] && remember_published "$base" "$version"
|
||||
done < <(
|
||||
tar -xOf "$db" --wildcards '*/desc' 2>/dev/null | awk '
|
||||
function emit() {
|
||||
if (name != "" && version != "") print name "\t" base "\t" version
|
||||
name=""; base=""; version=""
|
||||
}
|
||||
$0 == "%FILENAME%" { emit(); next }
|
||||
$0 == "%NAME%" { if (name != "" && version != "") emit(); getline; name=$0; next }
|
||||
$0 == "%BASE%" { getline; base=$0; next }
|
||||
$0 == "%VERSION%" { getline; version=$0; next }
|
||||
END { emit() }
|
||||
'
|
||||
)
|
||||
while IFS=$'\t' read -r name base version; do
|
||||
[[ -n "$name" && -n "$version" ]] && remember_published "$name" "$version"
|
||||
[[ -n "$base" && -n "$version" ]] && remember_published "$base" "$version"
|
||||
done < <(
|
||||
tar -xOf "$db" --wildcards '*/desc' 2>/dev/null | awk '
|
||||
function emit() {
|
||||
if (name != "" && version != "") print name "\t" base "\t" version
|
||||
name=""; base=""; version=""
|
||||
}
|
||||
$0 == "%FILENAME%" { emit(); next }
|
||||
$0 == "%NAME%" { if (name != "" && version != "") emit(); getline; name=$0; next }
|
||||
$0 == "%BASE%" { getline; base=$0; next }
|
||||
$0 == "%VERSION%" { getline; version=$0; next }
|
||||
END { emit() }
|
||||
'
|
||||
)
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
@@ -252,7 +310,17 @@ record_triggers() {
|
||||
local package_dir="$1"
|
||||
local current="$2"
|
||||
|
||||
write_metadata "$package_dir" '.rebuilt_against = $current' --argjson current "$current"
|
||||
# A flat record is the legacy x86_64 shape. Preserve records for
|
||||
# architectures outside this run, then replace the ones just rebuilt.
|
||||
write_metadata "$package_dir" '
|
||||
(.rebuilt_against // {}) as $old |
|
||||
(if ($old | length) == 0 then {}
|
||||
elif ($old | to_entries | all(.value | type == "string"))
|
||||
then {x86_64: $old}
|
||||
else $old
|
||||
end) as $by_arch |
|
||||
.rebuilt_against = ($by_arch * $current)
|
||||
' --argjson current "$current"
|
||||
}
|
||||
|
||||
# Metadata that does not parse would otherwise drop its package out of the run
|
||||
@@ -300,21 +368,41 @@ sync_package() {
|
||||
|
||||
print_info "Checking $package against ${triggers[*]}..."
|
||||
|
||||
local current="{}" trigger version
|
||||
for trigger in "${triggers[@]}"; do
|
||||
version=$(repo_version "$trigger")
|
||||
if [[ -z "$version" ]]; then
|
||||
print_error " $trigger is in no official repository; leaving $package alone"
|
||||
((++FAILED))
|
||||
return 0
|
||||
fi
|
||||
if ! current=$(jq -c --arg name "$trigger" --arg version "$version" '.[$name] = $version' <<<"$current"); then
|
||||
print_error " Could not record $trigger $version for $package"
|
||||
local current="{}" arch arch_current trigger version considered=0
|
||||
for arch in $(published_arches); do
|
||||
package_supports_arch "$package_dir" "$arch" || continue
|
||||
considered=$((considered + 1))
|
||||
arch_current="{}"
|
||||
for trigger in "${triggers[@]}"; do
|
||||
if ! version=$(repo_version "$arch" "$trigger"); then
|
||||
print_error " Could not read $arch repository versions; leaving $package alone"
|
||||
((++FAILED))
|
||||
return 0
|
||||
fi
|
||||
if [[ -z "$version" ]]; then
|
||||
print_error " $trigger is in no $arch repository; leaving $package alone"
|
||||
((++FAILED))
|
||||
return 0
|
||||
fi
|
||||
if ! arch_current=$(jq -c --arg name "$trigger" --arg version "$version" '.[$name] = $version' <<<"$arch_current"); then
|
||||
print_error " Could not record $arch/$trigger $version for $package"
|
||||
((++FAILED))
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
if ! current=$(jq -c --arg arch "$arch" --argjson versions "$arch_current" '.[$arch] = $versions' <<<"$current"); then
|
||||
print_error " Could not record $arch trigger versions for $package"
|
||||
((++FAILED))
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
if ((considered == 0)); then
|
||||
print_info " Skipping: not built for any published architecture"
|
||||
((++SKIPPED))
|
||||
return 0
|
||||
fi
|
||||
|
||||
local recorded
|
||||
if ! recorded=$(package_metadata_value "$package_dir" '.rebuilt_against' ""); then
|
||||
print_error " Could not read .omarchy/package.json for $package"
|
||||
@@ -323,13 +411,20 @@ sync_package() {
|
||||
fi
|
||||
[[ -n "$recorded" && "$recorded" != "null" ]] || recorded="{}"
|
||||
|
||||
# Before architecture-specific records existed, rebuilt_against described
|
||||
# x86_64. Read it that way without forcing a metadata-only migration.
|
||||
if jq -e 'to_entries | all(.value | type == "string")' >/dev/null <<<"$recorded"; then
|
||||
recorded=$(jq -c '{x86_64: .}' <<<"$recorded")
|
||||
fi
|
||||
|
||||
# Walk the declared triggers rather than the record, so a name the record does
|
||||
# not carry reads as changed instead of going unexamined forever.
|
||||
local moved
|
||||
if ! moved=$(jq -r --argjson recorded "$recorded" '
|
||||
to_entries
|
||||
| map(select($recorded[.key] != .value)
|
||||
| "\(.key) \($recorded[.key] // "unrecorded") -> \(.value)")
|
||||
[to_entries[] as $arch
|
||||
| $arch.value | to_entries[] as $trigger
|
||||
| select($recorded[$arch.key][$trigger.key] != $trigger.value)
|
||||
| "\($arch.key)/\($trigger.key) \($recorded[$arch.key][$trigger.key] // "unrecorded") -> \($trigger.value)"]
|
||||
| join(", ")
|
||||
' <<<"$current"); then
|
||||
print_error " Could not compare recorded trigger versions for $package"
|
||||
@@ -338,7 +433,7 @@ sync_package() {
|
||||
fi
|
||||
|
||||
if [[ -z "$moved" ]]; then
|
||||
print_info " Already rebuilt against $(jq -r 'to_entries | map("\(.key) \(.value)") | join(", ")' <<<"$current")"
|
||||
print_info " Already rebuilt against every published architecture"
|
||||
((++SKIPPED))
|
||||
return 0
|
||||
fi
|
||||
@@ -466,11 +561,11 @@ selftest_root() {
|
||||
}
|
||||
|
||||
selftest_package() {
|
||||
local root="$1" name="$2" pkgrel="$3" metadata="$4" pkgver="${5:-1.0}"
|
||||
local root="$1" name="$2" pkgrel="$3" metadata="$4" pkgver="${5:-1.0}" arches="${6:-x86_64}"
|
||||
local dir="$root/pkgbuilds/$name"
|
||||
|
||||
mkdir -p "$dir/.omarchy"
|
||||
printf 'pkgname=%s\npkgver=%s\npkgrel=%s\narch=(x86_64)\n' "$name" "$pkgver" "$pkgrel" > "$dir/PKGBUILD"
|
||||
printf 'pkgname=%s\npkgver=%s\npkgrel=%s\narch=(%s)\n' "$name" "$pkgver" "$pkgrel" "$arches" > "$dir/PKGBUILD"
|
||||
printf '%s\n' "$metadata" > "$dir/.omarchy/package.json"
|
||||
}
|
||||
|
||||
@@ -514,19 +609,51 @@ selftest_published() {
|
||||
cat > "$root/stub/curl" <<'STUB'
|
||||
#!/bin/bash
|
||||
out=""
|
||||
url=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-o) out="$2"; shift 2 ;;
|
||||
*) shift ;;
|
||||
*) url="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
db="$(dirname "$0")/omarchy.db.tar.zst"
|
||||
if [[ "$url" == */aarch64/* ]]; then
|
||||
repo="${url%/*}"
|
||||
repo="${repo##*/}"
|
||||
db="$(dirname "$0")/alarm-$repo.db"
|
||||
else
|
||||
db="$(dirname "$0")/omarchy.db.tar.zst"
|
||||
fi
|
||||
[[ -f "$db" && -n "$out" ]] || exit 22
|
||||
cp "$db" "$out"
|
||||
STUB
|
||||
chmod +x "$root/stub/curl"
|
||||
}
|
||||
|
||||
selftest_alarm() {
|
||||
local root="$1"
|
||||
shift
|
||||
local repo staging="$root/stub/alarm-db" entry name version
|
||||
|
||||
for repo in core extra; do
|
||||
rm -rf "$staging"
|
||||
mkdir -p "$staging"
|
||||
if [[ "$repo" == "core" ]]; then
|
||||
mkdir -p "$staging/unrelated-1-1"
|
||||
printf '%%FILENAME%%\nunrelated-1-1-aarch64.pkg.tar.zst\n\n%%NAME%%\nunrelated\n\n%%BASE%%\nunrelated\n\n%%VERSION%%\n1-1\n' \
|
||||
> "$staging/unrelated-1-1/desc"
|
||||
else
|
||||
for entry in "$@"; do
|
||||
name="${entry%=*}"
|
||||
version="${entry#*=}"
|
||||
mkdir -p "$staging/$name-$version"
|
||||
printf '%%FILENAME%%\n%s-%s-aarch64.pkg.tar.zst\n\n%%NAME%%\n%s\n\n%%BASE%%\n%s\n\n%%VERSION%%\n%s\n' \
|
||||
"$name" "$version" "$name" "$name" "$version" > "$staging/$name-$version/desc"
|
||||
done
|
||||
fi
|
||||
tar -czf "$root/stub/alarm-$repo.db" -C "$staging" .
|
||||
done
|
||||
}
|
||||
|
||||
cmd_self_test() {
|
||||
local failures=0
|
||||
local root
|
||||
@@ -545,7 +672,8 @@ cmd_self_test() {
|
||||
local root="$1"
|
||||
shift
|
||||
local status=0
|
||||
PATH="$root/stub:$PATH" "$root/bin/sync-rebuilds" "$@" > "$root/output" 2>&1 || status=$?
|
||||
OMARCHY_ARCHES="${SELFTEST_ARCHES:-x86_64}" \
|
||||
PATH="$root/stub:$PATH" "$root/bin/sync-rebuilds" "$@" > "$root/output" 2>&1 || status=$?
|
||||
echo "$status"
|
||||
}
|
||||
|
||||
@@ -563,7 +691,7 @@ cmd_self_test() {
|
||||
check "run succeeds" 0 "$(run_case "$root")"
|
||||
check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-partial")"
|
||||
check "unrecorded trigger now recorded" "2-2" \
|
||||
"$(jq -r '.rebuilt_against["dep-b"]' "$root/pkgbuilds/t-partial/.omarchy/package.json")"
|
||||
"$(jq -r '.rebuilt_against.x86_64["dep-b"]' "$root/pkgbuilds/t-partial/.omarchy/package.json")"
|
||||
|
||||
echo "Opting a package in buys a rebuild rather than a bare record:"
|
||||
root=$(selftest_root fresh)
|
||||
@@ -573,7 +701,7 @@ cmd_self_test() {
|
||||
check "run succeeds" 0 "$(run_case "$root")"
|
||||
check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-fresh")"
|
||||
check "trigger recorded" "1-1" \
|
||||
"$(jq -r '.rebuilt_against["dep-a"]' "$root/pkgbuilds/t-fresh/.omarchy/package.json")"
|
||||
"$(jq -r '.rebuilt_against.x86_64["dep-a"]' "$root/pkgbuilds/t-fresh/.omarchy/package.json")"
|
||||
|
||||
echo "An unchanged package is left alone:"
|
||||
root=$(selftest_root current)
|
||||
@@ -612,6 +740,47 @@ cmd_self_test() {
|
||||
check "suffix recorded for the next AUR sync" 1 \
|
||||
"$(jq -r '.pkgrel.suffix' "$root/pkgbuilds/t-aur/.omarchy/package.json")"
|
||||
|
||||
echo "A dependency is tracked independently for both published architectures:"
|
||||
root=$(selftest_root multiarch)
|
||||
selftest_package "$root" t-multi 1 '{"source":"local","rebuild_on":["dep-a"],"rebuilt_against":{"dep-a":"1-1"}}' 1.0 'x86_64 aarch64'
|
||||
selftest_pacman "$root" dep-a=1-1
|
||||
selftest_published "$root"
|
||||
selftest_alarm "$root" dep-a=2-1
|
||||
SELFTEST_ARCHES="x86_64 aarch64"
|
||||
check "run succeeds" 0 "$(run_case "$root")"
|
||||
unset SELFTEST_ARCHES
|
||||
check "pkgrel bumped once" 2 "$(pkgrel_of "$root/pkgbuilds/t-multi")"
|
||||
check "x86_64 trigger recorded" "1-1" \
|
||||
"$(jq -r '.rebuilt_against.x86_64["dep-a"]' "$root/pkgbuilds/t-multi/.omarchy/package.json")"
|
||||
check "aarch64 trigger recorded" "2-1" \
|
||||
"$(jq -r '.rebuilt_against.aarch64["dep-a"]' "$root/pkgbuilds/t-multi/.omarchy/package.json")"
|
||||
|
||||
echo "An ARM-only run records only the ARM dependency state:"
|
||||
root=$(selftest_root arm-only)
|
||||
selftest_package "$root" t-arm 1 '{"source":"local","rebuild_on":["dep-a"]}' 1.0 'x86_64 aarch64'
|
||||
selftest_pacman "$root"
|
||||
selftest_published "$root"
|
||||
selftest_alarm "$root" dep-a=2-1
|
||||
SELFTEST_ARCHES=aarch64
|
||||
check "run succeeds" 0 "$(run_case "$root")"
|
||||
unset SELFTEST_ARCHES
|
||||
check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-arm")"
|
||||
check "ARM trigger recorded" "2-1" \
|
||||
"$(jq -r '.rebuilt_against.aarch64["dep-a"]' "$root/pkgbuilds/t-arm/.omarchy/package.json")"
|
||||
check "x86_64 was not consulted" "false" \
|
||||
"$(jq -r '.rebuilt_against | has("x86_64")' "$root/pkgbuilds/t-arm/.omarchy/package.json")"
|
||||
|
||||
echo "An x86-only package ignores ARM during a dual-architecture run:"
|
||||
root=$(selftest_root x86-package)
|
||||
selftest_package "$root" t-x86 1 '{"source":"local","rebuild_on":["dep-a"],"rebuilt_against":{"dep-a":"1-1"}}'
|
||||
selftest_pacman "$root" dep-a=1-1
|
||||
selftest_published "$root"
|
||||
selftest_alarm "$root" dep-a=2-1
|
||||
SELFTEST_ARCHES="x86_64 aarch64"
|
||||
check "run succeeds" 0 "$(run_case "$root")"
|
||||
unset SELFTEST_ARCHES
|
||||
check "pkgrel untouched" 1 "$(pkgrel_of "$root/pkgbuilds/t-x86")"
|
||||
|
||||
echo ""
|
||||
if [[ "$failures" -eq 0 ]]; then
|
||||
print_success "Self-test passed"
|
||||
@@ -626,9 +795,9 @@ if [[ "$SELF_TEST" == true ]]; then
|
||||
exit $?
|
||||
fi
|
||||
|
||||
for tool in pacman vercmp jq curl; do
|
||||
for tool in pacman vercmp jq curl tar; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
print_error "$tool not found: reading trigger versions and ordering pkgrels both need pacman"
|
||||
print_error "$tool not found: rebuild trigger sync cannot run"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
+26
-26
@@ -105,32 +105,32 @@ echo ""
|
||||
paused=false
|
||||
for channel in edge rc stable; do
|
||||
for arch in $(published_arches); do
|
||||
fail_file=$(sync_fail_file "$channel" "$arch")
|
||||
[[ "$arch" == "x86_64" && ! -f "$fail_file" ]] && fail_file=$(legacy_sync_fail_file "$channel")
|
||||
[[ -f "$fail_file" ]] || continue
|
||||
if [[ "$paused" == false ]]; then
|
||||
print_error "Failing builds (backoff active)"
|
||||
paused=true
|
||||
fi
|
||||
FAILURE_COUNT=0 FAILURE_AT=0 FAILURE_FINGERPRINT=""
|
||||
# shellcheck disable=SC1090
|
||||
source "$fail_file" 2>/dev/null || true
|
||||
delay=600
|
||||
for ((i = 1; i < FAILURE_COUNT; i++)); do
|
||||
delay=$((delay * 2))
|
||||
((delay >= 21600)) && { delay=21600; break; }
|
||||
done
|
||||
retry_at=$((FAILURE_AT + delay))
|
||||
now=$(date +%s)
|
||||
if ((now < retry_at)); then
|
||||
when="retries at $(date -d "@$retry_at" '+%H:%M:%S' 2>/dev/null || echo "+$((retry_at - now))s")"
|
||||
else
|
||||
when="retries on the next tick"
|
||||
fi
|
||||
printf ' ✗ %-7s %-8s %s consecutive failure(s), %s\n' "$channel" "$arch" "$FAILURE_COUNT" "$when"
|
||||
printf ' last attempt %s on commit %s\n' \
|
||||
"$(date -d "@$FAILURE_AT" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "$FAILURE_AT")" \
|
||||
"${FAILURE_FINGERPRINT:0:12}"
|
||||
fail_file=$(sync_fail_file "$channel" "$arch")
|
||||
[[ "$arch" == "x86_64" && ! -f "$fail_file" ]] && fail_file=$(legacy_sync_fail_file "$channel")
|
||||
[[ -f "$fail_file" ]] || continue
|
||||
if [[ "$paused" == false ]]; then
|
||||
print_error "Failing builds (backoff active)"
|
||||
paused=true
|
||||
fi
|
||||
FAILURE_COUNT=0 FAILURE_AT=0 FAILURE_FINGERPRINT=""
|
||||
# shellcheck disable=SC1090
|
||||
source "$fail_file" 2>/dev/null || true
|
||||
delay=600
|
||||
for ((i = 1; i < FAILURE_COUNT; i++)); do
|
||||
delay=$((delay * 2))
|
||||
((delay >= 21600)) && { delay=21600; break; }
|
||||
done
|
||||
retry_at=$((FAILURE_AT + delay))
|
||||
now=$(date +%s)
|
||||
if ((now < retry_at)); then
|
||||
when="retries at $(date -d "@$retry_at" '+%H:%M:%S' 2>/dev/null || echo "+$((retry_at - now))s")"
|
||||
else
|
||||
when="retries on the next tick"
|
||||
fi
|
||||
printf ' ✗ %-7s %-8s %s consecutive failure(s), %s\n' "$channel" "$arch" "$FAILURE_COUNT" "$when"
|
||||
printf ' last attempt %s on commit %s\n' \
|
||||
"$(date -d "@$FAILURE_AT" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "$FAILURE_AT")" \
|
||||
"${FAILURE_FINGERPRINT:0:12}"
|
||||
done
|
||||
done
|
||||
if [[ "$paused" == true ]]; then
|
||||
|
||||
+10
-6
@@ -28,17 +28,21 @@ update_database() {
|
||||
# Make output directory writable for container
|
||||
make_dir_writable "$REPO_DIR"
|
||||
|
||||
# repo-add is architecture- and mirror-independent, so always use the edge
|
||||
# x86_64 image. This also lets bootstrap-rc build the rc database before the
|
||||
# rc channel exists remotely (an rc image can only build after it does).
|
||||
build_docker_image "$BUILD_DIR" "x86_64" "edge"
|
||||
# repo-add is architecture- and mirror-independent. Use the host-native edge
|
||||
# image, which also lets bootstrap-rc run before that channel exists remotely.
|
||||
local tool_arch
|
||||
tool_arch=$(docker_native_arch) || {
|
||||
print_error "Unsupported host architecture: $(uname -m)"
|
||||
exit 1
|
||||
}
|
||||
build_docker_image "$BUILD_DIR" "$tool_arch" "edge"
|
||||
|
||||
docker run --rm --platform linux/amd64 \
|
||||
docker run --rm "$(get_platform_arg "$tool_arch")" \
|
||||
-e ARCH="$ARCH" \
|
||||
-e MIRROR="$MIRROR" \
|
||||
-v "$REPO_ROOT:/output" \
|
||||
-v "$BUILD_DIR:/build:ro" \
|
||||
omarchy-pkg-builder:latest-x86_64-edge /build/update-repo.sh
|
||||
"omarchy-pkg-builder:latest-$tool_arch-edge" /build/update-repo.sh
|
||||
}
|
||||
|
||||
# Main execution
|
||||
|
||||
Reference in new issue
Block a user