#!/bin/bash # Run the complete release workflow: build, sign, promote, clean, sync set -e BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") source "$BUILD_ROOT/helpers/message-helpers.sh" source "$BUILD_ROOT/helpers/paths.sh" source "$BUILD_ROOT/helpers/lock-helpers.sh" source "$BUILD_ROOT/helpers/basecamp-notifier.sh" SYNC_REMOTE="" SKIP_PROD_CHECK=false DRY_RUN=false print_header "Complete Release Workflow" echo "" print_info "This will run the complete release workflow:" echo " 1. Build packages" echo " 2. Sign packages" echo " 3. Promote to production" echo " 4. Clean old versions" echo " 5. Update repository database" echo " 6. Sync to remote" echo "" # Parse arguments BUILD_ARGS=() while [[ $# -gt 0 ]]; do case $1 in --arch) ARCH="$2" BUILD_ARGS+=("--arch" "$2") update_arch_paths shift 2 ;; --mirror) MIRROR="$2" BUILD_ARGS+=("--mirror" "$2") update_arch_paths shift 2 ;; --package) BUILD_ARGS+=("--package" "$2") shift 2 ;; --sync-remote) SYNC_REMOTE="$2" shift 2 ;; --skip-prod-check) SKIP_PROD_CHECK=true shift ;; --dry-run) DRY_RUN=true BUILD_ARGS+=("--dry-run") shift ;; -h | --help) echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --arch Target architecture (x86_64 or aarch64, default: x86_64)" echo " --mirror Mirror to use (edge, rc, or stable, default: edge)" echo " --package Build only the specified package" echo " --sync-remote Rclone remote for sync (default: pkgs.omarchy.org:omarchy-pkgs)" echo " --skip-prod-check Skip production environment check during sync --dry-run Show build plan only; do not sign/promote/clean/update/sync" echo " -h, --help Show this help message" echo "" echo "This script runs the complete workflow:" echo " build → sign → promote → clean → sync" exit 0 ;; *) print_error "Unknown option: $1" exit 1 ;; esac done echo "" print_info "Target architecture: $ARCH" print_info "Mirror: $MIRROR" print_info "Build workspace: $BUILD_OUTPUT_DIR" # One channel mutation at a time: a timer firing mid-run or a second operator # waits here instead of interleaving a partial publish. if [[ "$DRY_RUN" != true ]]; then acquire_release_lock || exit 1 fi # --- reporting --------------------------------------------------------------- RELEASE_STARTED_AT=$(date +%s) RELEASE_COMMIT=$(git -C "$BUILD_ROOT" rev-parse --short HEAD 2>/dev/null || echo "unknown") RELEASE_COMMIT_SUBJECT=$(git -C "$BUILD_ROOT" log -1 --pretty=%s 2>/dev/null || echo "") RELEASE_CONTEXT="Channel: $MIRROR · Arch: $ARCH · $(hostname -s 2>/dev/null || echo host)" if [[ "$RELEASE_COMMIT" != "unknown" ]]; then RELEASE_CONTEXT+="
Commit: $RELEASE_COMMIT $(basecamp_html_escape <<<"$RELEASE_COMMIT_SUBJECT")" fi # Captured after the build step: promote MOVES these files out of build-output, # so the list has to be taken while they are still there. BUILT_FILES="" if [[ "$DRY_RUN" != true ]]; then notify_start "Release started: $MIRROR" "$RELEASE_CONTEXT" fi # Step 1: Build echo "" if [[ "$DRY_RUN" == true ]]; then print_info "Step 1/6: Planning build..." else print_info "Step 1/6: Building packages..." fi "$BUILD_ROOT/bin/build" "${BUILD_ARGS[@]}" || { print_error "Build failed" notify_error "Release failed: Build step failed" "$RELEASE_CONTEXT" exit 1 } if [[ "$DRY_RUN" == true ]]; then echo "" print_success "Release dry run complete (build plan only)." exit 0 fi BUILT_FILES=$(built_package_files "$BUILD_OUTPUT_DIR") BUILT_COUNT=$(grep -c '' <<<"$BUILT_FILES") [[ -z "$BUILT_FILES" ]] && BUILT_COUNT=0 print_info "Built $BUILT_COUNT package(s) this run" # Step 2: Sign echo "" print_info "Step 2/6: Signing packages..." "$BUILD_ROOT/bin/sign" --arch "$ARCH" --mirror "$MIRROR" || { print_error "Signing failed" notify_error "Release failed: Signing step failed" "$RELEASE_CONTEXT" exit 1 } # Step 3: Promote echo "" print_info "Step 3/6: Promoting to production..." "$BUILD_ROOT/bin/promote-build" --arch "$ARCH" --mirror "$MIRROR" || { print_error "Promotion failed" notify_error "Release failed: Promotion step failed" "$RELEASE_CONTEXT" exit 1 } # Step 4: Clean echo "" print_info "Step 4/6: Cleaning old versions..." "$BUILD_ROOT/bin/clean-repo" --arch "$ARCH" --mirror "$MIRROR" || { print_error "Cleaning failed" notify_error "Release failed: Clean step failed" "$RELEASE_CONTEXT" exit 1 } # Step 5: Update DB echo "" print_info "Step 5/6: Updating repository database..." "$BUILD_ROOT/bin/update-repo" --arch "$ARCH" --mirror "$MIRROR" || { print_error "Database update failed" notify_error "Release failed: Database update step failed" "$RELEASE_CONTEXT" exit 1 } # Step 6: Sync echo "" print_info "Step 6/6: Syncing to remote..." SYNC_ARGS=("--mirror" "$MIRROR" "--arch" "$ARCH") if [[ -n "$SYNC_REMOTE" ]]; then SYNC_ARGS+=("--remote" "$SYNC_REMOTE") fi if [[ "$SKIP_PROD_CHECK" == true ]]; then SYNC_ARGS+=("--skip-prod-check") fi "$BUILD_ROOT/bin/sync-repo" "${SYNC_ARGS[@]}" || { print_error "Sync failed" notify_error "Release failed: Sync step failed" "$RELEASE_CONTEXT" exit 1 } # 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!" duration=$(format_duration $(($(date +%s) - RELEASE_STARTED_AT))) summary="$RELEASE_CONTEXT
Duration: $duration" if ((BUILT_COUNT > 0)); then summary+="

$BUILT_COUNT package(s) published:" summary+="$(format_package_list_html "$BUILT_FILES")" summary+="

Live at https://pkgs.omarchy.org/$MIRROR/$ARCH/" notify_success "Release published: $MIRROR" "$summary" else # Nothing to build usually means a queued run raced another that already did # the work. Worth saying, but not worth dressing up as a publish. summary+="
No packages needed building — the channel was already current." notify_info "Release complete (no changes): $MIRROR" "$summary" fi