name: Release on: push: branches: - release pull_request: branches: - release jobs: # ── PR check: verify the version is not already built ─────────────────────── verify-version: if: github.event_name == 'pull_request' runs-on: linux-amd64 steps: - uses: actions/checkout@v4 - name: Verify version is new run: ./ci/verify-version.sh --builds-dir /var/www/builds.skaldagent.net # ── Push/merge: build, package, and deploy the release ────────────────────── release: if: github.event_name == 'push' runs-on: linux-amd64 outputs: version: ${{ steps.extract-version.outputs.version }} env: # Deliberately NOT the nightly's target dir. No CARGO_INCREMENTAL here — # a release binary is the one people install, so it gets the fully # optimised non-incremental build — and that flag is part of cargo's # profile fingerprint. Sharing one cache between a workflow that sets it # and one that doesn't would make each run invalidate the other's # workspace crates, which is exactly the cost this whole change removes. CARGO_TARGET_DIR: /home/dguiducci/.cache/skald-ci/target-release # The persistent build tree. Separate from the nightly's for the same # reason as the target dir: this one tracks `release`, that one tracks # `main`, and a shared tree would rewrite half the files on every switch — # reintroducing precisely the mtime churn the arrangement removes. SRC: /home/dguiducci/.cache/skald-ci/src-release steps: # Deliberately not actions/checkout — see the long note in nightly.yml. # Short version: the runner deletes its workspace after every job, so a # fresh clone stamps every source file "now" and cargo, which decides # freshness by mtime, rebuilt all 20 workspace crates on every run # whatever the commit touched. A tree that survives makes `git checkout` # rewrite only the files that actually changed. - name: Sync the persistent build tree run: | set -eu # Gitea serves this repo from the same machine the runner runs on, so # the tree syncs straight off the bare repo: no network, no token. ORIGIN=/home/dguiducci/skald/gitea/data/git/repositories/dguiducci/skald-circle.git if [ ! -d "$SRC/.git" ]; then mkdir -p "$(dirname "$SRC")" git clone --no-checkout "$ORIGIN" "$SRC" fi cd "$SRC" git remote set-url origin "$ORIGIN" git fetch --prune --force origin git checkout -f --detach "$GITHUB_SHA" # Clear leftovers from the previous run (dist/ above all) so a stale # tarball can never be published as this version. git clean -ffdxq echo "[sync] $(git log --oneline -1)" - name: Extract version from Cargo.toml id: extract-version run: | cd "$SRC" VER="v$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')" echo "version=$VER" >> "$GITHUB_OUTPUT" echo "[release] Building version $VER" # Also run verify-version on push to catch any race (belt-and-suspenders) - name: Verify version is new run: | cd "$SRC" ./ci/verify-version.sh --builds-dir /var/www/builds.skaldagent.net - name: Build native (linux/amd64) run: | cd "$SRC" RUSTFLAGS="-A warnings" cargo build --release --no-default-features RUSTFLAGS="-A warnings" cargo build --release --no-default-features -p skald-setup - name: Cross-compile (linux/arm64) env: CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc AR_aarch64_unknown_linux_gnu: aarch64-linux-gnu-ar CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc run: | cd "$SRC" RUSTFLAGS="-A warnings" cargo build --release --no-default-features --target aarch64-unknown-linux-gnu RUSTFLAGS="-A warnings" cargo build --release --no-default-features -p skald-setup --target aarch64-unknown-linux-gnu - name: Package amd64 run: | cd "$SRC" ./ci/package.sh \ --version "${{ steps.extract-version.outputs.version }}" \ --os linux \ --arch amd64 \ --target-dir "$CARGO_TARGET_DIR/release" \ --output dist/ - name: Package arm64 run: | cd "$SRC" ./ci/package.sh \ --version "${{ steps.extract-version.outputs.version }}" \ --os linux \ --arch arm64 \ --target-dir "$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release" \ --output dist/ - name: Deploy to builds.skaldagent.net run: | cd "$SRC" VERSION="${{ steps.extract-version.outputs.version }}" TARGET="/var/www/builds.skaldagent.net/releases/${VERSION}" mkdir -p "$TARGET" # Publish each tarball atomically (temp name + rename) so a client can # never fetch a half-written file. for f in dist/*.tar.gz; do name="$(basename "$f")" cp "$f" "$TARGET/.$name.tmp" mv -f "$TARGET/.$name.tmp" "$TARGET/$name" done echo "[release] Deployed $VERSION:" ls -lh "$TARGET/" - name: Update latest version pointer run: | VERSION="${{ steps.extract-version.outputs.version }}" DEST=/var/www/builds.skaldagent.net/releases # Flip LATEST atomically — install.sh/update.sh read it to decide # whether to upgrade, so it must never be observed empty or partial. printf '%s\n' "$VERSION" > "$DEST/.LATEST.tmp" mv -f "$DEST/.LATEST.tmp" "$DEST/LATEST" echo "[release] Updated releases/LATEST → $VERSION" - name: Publish the release installer run: | cd "$SRC" # install.sh is served straight from the web root # (curl -fsSL https://builds.skaldagent.net/install.sh | bash), so # without this it stays whatever was copied there by hand and drifts # from the repo — a fix to the installer would reach every existing box # through update.sh but never a new one. Published here rather than on # every push so the served installer always matches a real release. ROOT=/var/www/builds.skaldagent.net cp install.sh "$ROOT/.install.sh.tmp" chmod 644 "$ROOT/.install.sh.tmp" mv -f "$ROOT/.install.sh.tmp" "$ROOT/install.sh" echo "[release] Published install.sh"