From dfd28a0aa625f1446517ccf6d93f03fdcc27db3c Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 25 Sep 2026 09:40:08 -0400 Subject: [PATCH] fix(ci): the base refresh's lanes test main, the branch it publishes (#4430) A refresh publishes `:latest` from `main`, but the six lanes kept the default checkout, which is the cron's triggering commit on dev. The gate therefore tested dev's code and passed main's. The new LANE_REF is `main` on a refresh and empty otherwise. Empty keeps the checkout default, so push and PR runs are unchanged, including a PR's merge ref, which the default reaches by ref rather than by SHA. Every lane still runs on every trigger: no condition is added and no lane can skip, so the `needs:` gate on each publishing job is unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR --- .forgejo/workflows/build.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 1640059..2dab36a 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -189,6 +189,15 @@ concurrency: env: IS_REFRESH: ${{ (github.event_name == 'schedule' || format('{0}', github.event.inputs.refresh) == 'true') && 'true' || 'false' }} BUILD_REF: ${{ (github.event_name == 'schedule' || format('{0}', github.event.inputs.refresh) == 'true') && 'main' || github.sha }} +# What the six LANES check out. Empty — the checkout default, the triggering +# commit (or a PR's merge ref) — on every trigger but the refresh, where it is +# `main`: the refresh publishes main, so the gate has to test main (#4430). It +# is not BUILD_REF itself because a pull_request run's `github.sha` is a merge +# commit the default checkout reaches through its ref, not by sha. Each job +# still resolves `main` when it starts, so a merge to main during the ~5 min of +# a Sunday-06:00 refresh could put the lanes and the build one commit apart; +# the build jobs' own guards assert the branch, not the commit. + LANE_REF: ${{ (github.event_name == 'schedule' || format('{0}', github.event.inputs.refresh) == 'true') && 'main' || '' }} # Requires repo secret RELEASE_TOKEN — a Forgejo PAT with scopes: # - write:package, read:package (for docker push to git.fabledsword.com) @@ -229,6 +238,8 @@ jobs: image: git.fabledsword.com/bvandeusen/ci-python:3.14 steps: - uses: actions/checkout@v4 + with: + ref: ${{ env.LANE_REF }} - name: Ruff lint # agent/ included so the GPU-agent is linted before its image is built # (build.yml only `docker build`s it — this is where it gets checked). @@ -275,6 +286,7 @@ jobs: steps: - uses: actions/checkout@v4 with: + ref: ${{ env.LANE_REF }} # The derivation needs real history: a depth-1 clone sees one commit # and produces a wrong, too-low value RATHER THAN FAILING. Checking # that here is half the point of the lane. @@ -330,6 +342,7 @@ jobs: steps: - uses: actions/checkout@v4 with: + ref: ${{ env.LANE_REF }} # Full history for tests/test_artifact_identity.py, which derives # each artifact's revision to check the identity scheme. On a # depth-1 clone that derivation either fails or returns the tip sha @@ -376,6 +389,8 @@ jobs: working-directory: frontend steps: - uses: actions/checkout@v4 + with: + ref: ${{ env.LANE_REF }} # No package-lock.json is tracked yet (we don't run npm locally per # feedback-no-local-runs). Using `npm install` instead of `npm ci`. # If we want strict lockfile-based reproducibility later, commit a @@ -405,6 +420,8 @@ jobs: image: node:24-bookworm-slim steps: - uses: actions/checkout@v4 + with: + ref: ${{ env.LANE_REF }} # Not --no-save: vitest and web-ext are both real devDependencies now, # and the suite needs vitest resolvable from node_modules. - name: Install dev dependencies @@ -507,6 +524,8 @@ jobs: --health-retries 10 steps: - uses: actions/checkout@v4 + with: + ref: ${{ env.LANE_REF }} - name: Integration suite (resolve service IPs, migrate, test) run: | set -eux