From eca06b7b311765eebcb9aac975c9de0e9c8ae72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C5=ABdolfs=20O=C5=A1i=C5=86=C5=A1?= Date: Tue, 9 Jun 2026 16:01:13 +0200 Subject: [PATCH] Check if prerequisites are met before cutting release Make sure docker and podman are up and running as they're necessary for the arch packaging and artifact building. This also shows what commands would have been run when a phase fails. --- scripts/build-artifacts | 14 +++++++++ scripts/release | 66 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/scripts/build-artifacts b/scripts/build-artifacts index 310dc4de2..0d6975f2e 100755 --- a/scripts/build-artifacts +++ b/scripts/build-artifacts @@ -71,6 +71,20 @@ step "Building release v$VERSION (${SHORT_SHA})" echo " Output: $RELEASE_DIR" mkdir -p "$RELEASE_DIR" +# ---- Preflight ---- +# +# The Linux deb/AppImage builds run in Podman; the macOS DMG does not. Fail +# fast here (before the slow native DMG build) if a Linux build is requested +# but Podman is unavailable. + +if [[ "$BUILD_DEB" == true || "$BUILD_APPIMAGE" == true ]]; then + command -v podman >/dev/null 2>&1 || { echo "Error: podman not found" >&2; exit 1; } + podman info >/dev/null 2>&1 || { + echo "Error: Podman is not running — start it (e.g. 'podman machine start')" >&2 + exit 1 + } +fi + # ---- Clean ---- if [[ "$CLEAN" == true ]]; then diff --git a/scripts/release b/scripts/release index e9bbd9387..70d280436 100755 --- a/scripts/release +++ b/scripts/release @@ -30,9 +30,10 @@ # SIGNING_KEY (default: $HOME/.ssh/apt_signing_key) # FILES_HOST (default: files.radicle.dev) -set -euo pipefail +set -Eeuo pipefail DRY_RUN=false +CURRENT_PHASE="" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" @@ -50,7 +51,21 @@ bold() { printf '\033[1m%s\033[0m\n' "$*"; } step() { echo; bold "==> $*"; } info() { echo " $*"; } warn() { echo " WARNING: $*" >&2; } -die() { echo "Error: $*" >&2; exit 1; } +die() { echo "Error: $*" >&2; trap - ERR; exit 1; } + +# Fires on any uncaught command failure inside a phase. Points at --dry-run, +# which prints the phase's full command sequence (with real values) so the +# remaining steps can be finished by hand. +on_error() { + local code=$? + trap - ERR + echo >&2 + echo "Error: phase '$CURRENT_PHASE' did not finish (exit $code)." >&2 + echo "Fix the problem above, then review the phase's commands to continue" >&2 + echo "manually from where it stopped:" >&2 + echo >&2 + echo " scripts/release --phase $CURRENT_PHASE --dry-run" >&2 +} confirm() { local prompt="$1" reply @@ -86,6 +101,43 @@ fail() { if [[ "$DRY_RUN" == true ]]; then warn "$*"; else die "$*"; fi } +# generate-srcinfo.sh needs a running Docker daemon. Check it before prep +# mutates anything, so a docker-down situation fails cleanly. Skipped under +# --dry-run, where the Docker step only prints. +docker_available() { command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; } +podman_available() { command -v podman >/dev/null 2>&1 && podman info >/dev/null 2>&1; } + +require_docker() { + if [[ "$DRY_RUN" == true ]]; then + docker_available || warn "Docker is not available (a real run needs it to regenerate .SRCINFO)" + return 0 + fi + command -v docker >/dev/null 2>&1 || die "docker not found (needed to regenerate .SRCINFO)" + docker info >/dev/null 2>&1 || die "Docker daemon is not running — start Docker and re-run" +} + +# build-artifacts enforces a running Podman itself on a real build. This mirror +# only surfaces it under --dry-run (build-artifacts isn't executed then), so the +# preview reports a down/absent Podman too. +require_podman() { + [[ "$DRY_RUN" == true ]] || return 0 + podman_available || warn "Podman is not available (a real build needs it for the Linux artifacts)" +} + +# Every phase runs on the default branch (prep branches off it; later phases run +# after the patch is merged back). Bail with a clear message if we're elsewhere, +# so the user can switch and re-run. Only a warning under --dry-run. +require_branch() { + local current + current="$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD)" + [[ "$current" == "$EXPECTED_BRANCH" ]] && return 0 + if [[ "$DRY_RUN" == true ]]; then + warn "on branch '$current', expected '$EXPECTED_BRANCH'" + else + die "on branch '$current', expected '$EXPECTED_BRANCH' — run 'git checkout $EXPECTED_BRANCH', then re-run" + fi +} + # Print the command(s) the user should run next to resume the flow. # Under --dry-run the hint carries the flag through, so previewing one phase # suggests previewing the next. @@ -107,6 +159,7 @@ USERNAME="${USERNAME:-${USER:-}}" SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519}" SIGNING_KEY="${SIGNING_KEY:-$HOME/.ssh/apt_signing_key}" FILES_HOST="${FILES_HOST:-files.radicle.dev}" +EXPECTED_BRANCH="${EXPECTED_BRANCH:-main}" # ---- Version helpers ---- @@ -126,10 +179,9 @@ arch_commit_sha() { phase_prep() { local version="$1" [[ -n "$version" ]] || die "prep requires --version X.Y.Z" + require_docker step "Preparing release v$version" - git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD | grep -qx main \ - || warn "not on main" info "Syncing and fetching" run rad sync @@ -194,6 +246,7 @@ phase_prep() { phase_build() { local version version="$(conf_version)" + require_podman step "Building release artifacts for v$version" run "$SCRIPT_DIR/build-artifacts" @@ -364,6 +417,11 @@ main() { [[ "$DRY_RUN" == true ]] && bold "DRY RUN — no commands will be executed" + require_branch + + CURRENT_PHASE="$phase" + trap on_error ERR + case "$phase" in prep) phase_prep "$version" ;; build) phase_build ;;