ci: build from a persistent tree instead of the runner workspace
Nightly Build / build (push) Successful in 9m28s

The previous commit tried to fix the mtime invalidation with
`git restore-mtime`. It does not work on this box, in the worst way: the
packaged version (2022.12) drives `git whatchanged`, which git 2.53
refuses to run without --i-still-use-this — and the tool swallows that
failure and exits 0 having updated nothing. Verified on the runner: "0
commits evaluated, 675 files missing, 0 files updated", while the job
happily went on to rebuild everything.

Fix the cause instead of the symptom. Both building workflows now sync a
tree that survives between runs and build there. `git checkout` only
rewrites files whose content actually changed, so mtimes are correct as a
consequence rather than as a reconstruction — and no external tool is
involved. Gitea serves this repo from the same machine the runner runs
on, so the sync reads the bare repo directly: no network, no token.

Two properties this buys that restore-mtime did not:

- The absolute source path is pinned. The runner derives its workspace
  path from the job definition, so editing a workflow moved it and
  invalidated every workspace crate by itself — the previous commit paid
  that cost without knowing it.
- It cannot fail towards staleness. Checking out an older commit stamps
  those files newer, which costs an extra rebuild; restore-mtime moved
  mtimes backwards, which could have let cargo reuse artifacts built from
  newer code.

Each workflow gets its own tree, for the same reason they already have
their own CARGO_TARGET_DIR: they track different branches, and one shared
tree would rewrite half the files on every switch.
This commit is contained in:
Daniele
2026-08-10 18:27:21 +01:00
parent e0d75a8dc8
commit 905fc54775
2 changed files with 91 additions and 42 deletions
+39 -18
View File
@@ -36,35 +36,55 @@ jobs:
# 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:
- uses: actions/checkout@v4
with:
# Full history: `git restore-mtime` below dates each file from the
# last commit that touched it.
fetch-depth: 0
# See the long note in nightly.yml: the runner wipes the job workspace
# after every run, so without this every source file is stamped "now" on
# checkout and all 20 workspace crates rebuild from scratch regardless of
# what the commit changed. This is what lets CARGO_TARGET_DIR cache our
# own crates and not just the ~700 third-party deps.
- name: Restore source mtimes from git history
run: git restore-mtime
# 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: ./ci/verify-version.sh --builds-dir /var/www/builds.skaldagent.net
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
@@ -74,12 +94,13 @@ jobs:
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 "${GITHUB_WORKSPACE:-.}"
cd "$SRC"
./ci/package.sh \
--version "${{ steps.extract-version.outputs.version }}" \
--os linux \
@@ -89,7 +110,7 @@ jobs:
- name: Package arm64
run: |
cd "${GITHUB_WORKSPACE:-.}"
cd "$SRC"
./ci/package.sh \
--version "${{ steps.extract-version.outputs.version }}" \
--os linux \
@@ -99,7 +120,7 @@ jobs:
- name: Deploy to builds.skaldagent.net
run: |
cd "${GITHUB_WORKSPACE:-.}"
cd "$SRC"
VERSION="${{ steps.extract-version.outputs.version }}"
TARGET="/var/www/builds.skaldagent.net/releases/${VERSION}"
mkdir -p "$TARGET"
@@ -125,7 +146,7 @@ jobs:
- name: Publish the release installer
run: |
cd "${GITHUB_WORKSPACE:-.}"
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