#!/usr/bin/env bash # create-release.sh — Full release automation for Archipelago # # Bumps version in Cargo.toml and package.json, generates changelog from git log, # creates release manifest, and creates git tag. # # Usage: # ./scripts/create-release.sh 1.0.0 # Release v1.0.0 # ./scripts/create-release.sh 1.0.0 --dry-run # Preview without changes # # Releases are tarball-only. ISO builds are archived under # image-recipe/_archived/. Nodes OTA-update from releases/manifest.json. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" DRY_RUN=false VERSION="" # Parse args for arg in "$@"; do case "$arg" in --dry-run) DRY_RUN=true ;; --help|-h) echo "Usage: $0 VERSION [--dry-run]" echo "" echo "Steps performed:" echo " 1. Validate version format (SemVer)" echo " 2. Bump version in Cargo.toml and package.json" echo " 3. Build backend" echo " 4. Build frontend" echo " 5. Generate changelog from git log" echo " 6. Create release manifest" echo " 7. Commit version bump" echo " 8. Create git tag v{VERSION}" echo "" echo "Options:" echo " --dry-run Show what would be done without making changes" exit 0 ;; *) if [ -z "$VERSION" ]; then VERSION="$arg" else echo "Error: Unknown argument: $arg" exit 1 fi ;; esac done if [ -z "$VERSION" ]; then echo "Error: VERSION argument required" echo "Usage: $0 VERSION [--dry-run]" exit 1 fi # Validate SemVer format if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then echo "Error: Version '$VERSION' is not valid SemVer (expected: X.Y.Z or X.Y.Z-suffix)" exit 1 fi # Check we're on main branch BRANCH=$(git -C "$PROJECT_ROOT" branch --show-current) if [ "$BRANCH" != "main" ]; then echo "Error: Must be on 'main' branch (currently on '$BRANCH')" exit 1 fi # Check for uncommitted changes if ! git -C "$PROJECT_ROOT" diff --quiet HEAD; then echo "Error: Uncommitted changes detected. Commit or stash first." exit 1 fi # ── Pre-flight test gate ────────────────────────────────────────────── # A release must not ship if the static/frontend/backend checks fail. This # runs the release gate harness (cargo fmt/check, catalog drift, vitest, and # the focused cargo suites — incl. the receive/port-drift/secret regressions). # Skipped on --dry-run, or set SKIP_RELEASE_TESTS=1 to bypass in an emergency. # The lifecycle bats harness (tests/lifecycle/run-gate.sh) still runs separately # against live nodes — see tests/lifecycle/TESTING.md. if ! $DRY_RUN; then if [ "${SKIP_RELEASE_TESTS:-0}" = "1" ]; then echo "WARNING: SKIP_RELEASE_TESTS=1 — bypassing the pre-flight test gate" elif [ -x "$PROJECT_ROOT/tests/release/run.sh" ]; then echo "[0/7] Running release gate (tests/release/run.sh)..." if ! "$PROJECT_ROOT/tests/release/run.sh"; then echo "Error: release gate failed — aborting release. Fix the failing" echo " stage, or re-run with SKIP_RELEASE_TESTS=1 to override." exit 1 fi else echo "WARNING: tests/release/run.sh not found/executable — skipping test gate" fi fi # Check tag doesn't already exist if git -C "$PROJECT_ROOT" tag -l "v$VERSION" | grep -q "v$VERSION"; then echo "Error: Tag v$VERSION already exists" exit 1 fi # Get current version CURRENT_CARGO_VERSION=$(grep '^version' "$PROJECT_ROOT/core/archipelago/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/') CURRENT_NPM_VERSION=$(node -p "require('$PROJECT_ROOT/neode-ui/package.json').version") echo "=== Archipelago Release v${VERSION} ===" echo " Current Cargo version: ${CURRENT_CARGO_VERSION}" echo " Current npm version: ${CURRENT_NPM_VERSION}" echo " Target version: ${VERSION}" echo " Dry run: ${DRY_RUN}" echo "" if $DRY_RUN; then echo "[DRY RUN] Would perform the following:" echo " 0. Run pre-flight test gate (tests/release/run.sh) — aborts on failure" echo " 1. Update core/archipelago/Cargo.toml version to $VERSION" echo " 2. Update neode-ui/package.json version to $VERSION" echo " 3. Build backend (cargo build --release -p archipelago)" echo " 4. Build frontend (npm run build)" echo " 5. Generate changelog from git log since v${CURRENT_CARGO_VERSION}" echo " 6. Create release manifest" echo " 7. Commit: 'chore: release v${VERSION}'" echo " 8. Tag: v${VERSION}" echo "" echo "After this script, you would:" echo " - Push: git push && git push --tags" echo " - Build ISOs on server: ssh archipelago@192.168.1.228" exit 0 fi echo "[1/7] Bumping version in Cargo.toml..." # Update archipelago Cargo.toml sed -i.bak "s/^version = \".*\"/version = \"$VERSION\"/" "$PROJECT_ROOT/core/archipelago/Cargo.toml" rm -f "$PROJECT_ROOT/core/archipelago/Cargo.toml.bak" # Also update workspace Cargo.lock if it exists if [ -f "$PROJECT_ROOT/core/Cargo.lock" ]; then # Cargo will update the lock file on next build; touch the toml to trigger true fi echo "[2/7] Bumping version in package.json..." cd "$PROJECT_ROOT/neode-ui" npm version "$VERSION" --no-git-tag-version --allow-same-version 2>/dev/null || true cd "$PROJECT_ROOT" echo "[3/8] Building backend..." cd "$PROJECT_ROOT/core" cargo build --release -p archipelago cd "$PROJECT_ROOT" echo "[4/8] Building frontend..." cd "$PROJECT_ROOT/neode-ui" npm run build 2>&1 | tail -3 cd "$PROJECT_ROOT" echo "[5/8] Validating curated changelog..." CHANGELOG_FILE="$PROJECT_ROOT/CHANGELOG.md" RELEASE_DATE=$(date +%Y-%m-%d) if [ ! -f "$CHANGELOG_FILE" ] || ! grep -q "^## v${VERSION} (" "$CHANGELOG_FILE"; then echo "Error: CHANGELOG.md must already contain curated notes for v${VERSION}." echo "Add a section like:" echo "" echo "## v${VERSION} (${RELEASE_DATE})" echo "" echo "- User/operator-facing change ..." echo "- Another concrete change ..." echo "- Validation or operational note ..." exit 1 fi echo "[6/8] Creating release manifest..." mkdir -p "$PROJECT_ROOT/releases" "$SCRIPT_DIR/create-release-manifest.sh" --version "$VERSION" --date "$RELEASE_DATE" --output "$PROJECT_ROOT/releases/manifest.json" 2>&1 | grep -v "^$" cp "$PROJECT_ROOT/releases/manifest.json" "$PROJECT_ROOT/release-manifest.json" echo "[6b/8] Staging release artifacts for validation..." VERSION_DIR="$PROJECT_ROOT/releases/v${VERSION}" FRONTEND_ARCHIVE="/tmp/archipelago-frontend-${VERSION}.tar.gz" mkdir -p "$VERSION_DIR" install -m 0755 "$PROJECT_ROOT/core/target/release/archipelago" "$VERSION_DIR/archipelago" install -m 0644 "$FRONTEND_ARCHIVE" "$VERSION_DIR/archipelago-frontend-${VERSION}.tar.gz" "$SCRIPT_DIR/check-release-manifest.sh" echo "[7/8] Committing version bump..." git -C "$PROJECT_ROOT" add \ core/archipelago/Cargo.toml \ neode-ui/package.json \ neode-ui/package-lock.json \ CHANGELOG.md \ releases/manifest.json \ release-manifest.json \ 2>/dev/null || true git -C "$PROJECT_ROOT" commit -m "chore: release v${VERSION}" echo "[8/8] Creating git tag..." git -C "$PROJECT_ROOT" tag -a "v${VERSION}" -m "Release v${VERSION}" echo "" echo "=== Release v${VERSION} Ready ===" echo "" echo "Artifacts:" echo " - Version bumped in Cargo.toml and package.json" echo " - Changelog updated in CHANGELOG.md" echo " - Release manifest: releases/manifest.json" echo " - Release manifest copy: release-manifest.json" echo " - Staged artifacts: releases/v${VERSION}/" echo " - Git tag: v${VERSION}" echo "" echo "Next steps:" echo " 1. Review: git log --oneline -5" echo " 2. Publish commits, tag, artifacts, and verify download URLs:" echo " scripts/publish-release-assets.sh ${VERSION} gitea-vps2" echo " 3. Verify manifest is live on both mirrors:" echo " curl -fsS http://localhost:3000/lfg2025/archy/raw/branch/main/releases/manifest.json" echo " curl -fsS http://146.59.87.168:3000/lfg2025/archy/raw/branch/main/releases/manifest.json"