Skip to content

ci: resolve fixtures pin from the PR branch in update-snapshots - #424

Merged
mmcky merged 2 commits into
mainfrom
ci/snapshot-bot-reads-pr-fixtures-pin
Aug 6, 2026
Merged

ci: resolve fixtures pin from the PR branch in update-snapshots#424
mmcky merged 2 commits into
mainfrom
ci/snapshot-bot-reads-pr-fixtures-pin

Conversation

@mmcky

@mmcky mmcky commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

The bug

update-snapshots.yml is triggered by issue_comment, and GitHub always runs the default branch's copy of a workflow for that event. So the FIXTURES_SHA in its top-level env: was always main's pin — never the pin of the PR under test.

This produces a silent, self-defeating failure. A PR that bumps the fixtures pin — which is exactly what a PR adding a theme option has to do, since the fixtures site must set that option to exercise it — has its snapshots regenerated against the old fixtures. Those come out byte-identical to the existing baselines, so nothing gets committed, while the PR's own visual job (which does use the PR's pin, because pull_request events run the PR's workflow file) keeps failing. Re-running /update-snapshots can never fix it.

It happened on #404. Run 27820556433 logged FIXTURES_SHA: d8ffc17c… (main's pin) while the PR branch pins 32763e43…, ended with No snapshot changes to commit — and still posted "✅ All visual snapshots have been regenerated and committed to this PR." Nothing was committed; that PR has been stuck at 92 failing visual tests since 19 June.

The fix

Both jobs now resolve the pin from the checked-out branch's ci.yml after the PR checkout, falling back to the workflow-level value if it can't be found. This adds no trust surface — the workflow already checks out and executes PR-branch code (pip install ., then Playwright).

Verified the extraction against the real files: main's ci.yml resolves to d8ffc17c…, #404's resolves to 32763e43…, and a file with no match falls back to the workflow default.

The summary comments also now report what actually happened — committed vs. unchanged, and which fixtures commit was built against — instead of unconditionally claiming success. The committed branch additionally notes that a GITHUB_TOKEN commit does not itself trigger workflows, so an empty commit is needed to re-run CI against the new baselines.

Why not just bump the pin on main instead

That was the first idea, and it doesn't work. The fixtures commit adds html_theme_options.announcement, an option main's theme doesn't support yet; Sphinx warns on unknown theme options and the fixtures build runs jb build . --warningiserror, so bumping the pin standalone would break main's CI. The pin and the feature have to land together — which is precisely why the bot needs to read the PR's pin.

Note

Unrelated to this change, but found while in the file: update-snapshots.yml has no author-association gate. Any user who can comment on a PR can trigger a job that checks out PR-branch code and runs it with a contents: write token. Worth a follow-up.

🤖 Generated with Claude Code

On `issue_comment` events GitHub always runs the workflow definition from the
default branch, so `FIXTURES_SHA` in the top-level `env:` was main's pin, not
the pin of the PR under test.

The failure mode this produces is silent and self-defeating. A PR that bumps
the fixtures pin — which is exactly what a PR adding a theme option must do,
since the fixtures site has to set that option to exercise it — gets its
snapshots regenerated against the OLD fixtures. Those come out identical to the
existing baselines, so nothing is committed, while the PR's own visual job
(which does use the PR's pin) keeps failing. Re-running the command can never
help. Observed on #404, where the bot reported success and changed nothing.

Both jobs now read the pin out of the checked-out branch's ci.yml, falling back
to the workflow-level value if it can't be found. The summary comments report
what actually happened — committed vs. unchanged, and which fixtures commit was
built against — rather than unconditionally claiming success, and the committed
case notes that a GITHUB_TOKEN commit does not itself trigger CI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 6, 2026 03:35
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

@github-actions
github-actions Bot temporarily deployed to pull request August 6, 2026 03:36 Inactive
@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@0ecd926). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #424   +/-   ##
=======================================
  Coverage        ?   47.75%           
=======================================
  Files           ?        2           
  Lines           ?      423           
  Branches        ?        0           
=======================================
  Hits            ?      202           
  Misses          ?      221           
  Partials        ?        0           
Flag Coverage Δ
pytests 47.75% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a CI correctness bug in the /update-snapshots GitHub Actions workflow: because issue_comment workflows always run from the default branch, the workflow-level FIXTURES_SHA fallback was silently using main’s fixtures pin instead of the PR branch’s pin, making snapshot regeneration ineffective for PRs that bump fixtures.

Changes:

  • Re-resolve FIXTURES_SHA / FIXTURES_REPO after checking out the PR branch by extracting the values from the PR branch’s .github/workflows/ci.yml, with fallback to workflow env.
  • Improve PR summary comments to reflect whether anything was actually committed (and include fixtures provenance for /update-snapshots).
  • Document the behavior change in CHANGELOG.md.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
CHANGELOG.md Documents the CI fix for /update-snapshots fixtures pin resolution and improved reporting.
.github/workflows/update-snapshots.yml Resolves fixtures pin from the checked-out PR branch and improves snapshot commit/reporting behavior.
Suppressed comments (2)

.github/workflows/update-snapshots.yml:142

  • Same security concern for the /update-snapshots job: it’s runnable by any commenter and then executes PR-branch code with write permissions. Add an author_association gate (or equivalent) to the job-level if: so only trusted users can trigger it.
            const committed = process.env.COMMITTED === 'true';
            const body = committed
              ? `✅ Added ${process.env.CHANGED} missing visual snapshot(s) and committed them to this PR.`
              : 'ℹ️ No missing visual snapshots — nothing was committed. Every test already has a baseline, so this PR is unchanged.';
            await github.rest.issues.createComment({

.github/workflows/update-snapshots.yml:255

  • Same wc -l whitespace issue here: CHANGED will likely have leading spaces, which then shows up in the commit message and PR summary. Trim whitespace when computing the count.
            CHANGED=$(git diff --staged --name-only | wc -l)
            git commit -m "UPDATE: Regenerate all visual snapshots ($CHANGED files)"

Comment on lines +7 to +11
# below are only a FALLBACK: on `issue_comment` events GitHub always runs the
# copy of this workflow that is on the default branch, so these would pin the
# fixtures main uses rather than the ones the PR under test uses. Each job
# re-reads the pin out of the checked-out PR branch's ci.yml instead — see the
# "Resolve fixtures pin" step.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and fixed in e641151 — both jobs now require OWNER, MEMBER, or COLLABORATOR. This is pre-existing rather than introduced here (I'd noted it in the PR description as a follow-up), but you're right that it's worth closing while the file is open. The exposure is real: the jobs check out the PR branch and pip install . runs that branch's build backend, all under a contents: write token.

Comment thread .github/workflows/update-snapshots.yml Outdated
Comment on lines +133 to +141
env:
COMMITTED: ${{ steps.commit.outputs.committed }}
CHANGED: ${{ steps.commit.outputs.changed }}
with:
script: |
const committed = process.env.COMMITTED === 'true';
const body = committed
? `✅ Added ${process.env.CHANGED} missing visual snapshot(s) and committed them to this PR.`
: 'ℹ️ No missing visual snapshots — nothing was committed. Every test already has a baseline, so this PR is unchanged.';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in e641151 by adding the pin to the /update-new-snapshots comment rather than weakening the wording, since knowing which fixtures commit produced a baseline is useful in both jobs. The CHANGELOG entry now says "both summary comments" explicitly.

Comment thread .github/workflows/update-snapshots.yml Outdated
echo "No snapshot changes to commit"
echo "committed=false" >> "$GITHUB_OUTPUT"
else
CHANGED=$(git diff --staged --name-only | wc -l)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in e641151 (| tr -d "[:space:]"), though the premise needs one correction: GNU coreutils wc -l does not pad when reading from a pipe, and these jobs run on ubuntu-latest, so the value was already clean in practice. BSD wc does pad — I checked locally and got [ 3] — so the trim is worth having for robustness, and the count is interpolated into both a commit message and a PR comment where a stray space would show. Same fix applied at both sites.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

🎭 Visual Regression Test Results

passed  103 passed
skipped  3 skipped

Details

stats  106 tests across 1 suite
duration  3 minutes, 59 seconds
commit  e641151

Skipped tests

desktop-chrome › theme.spec.ts › Visual Regression Tests › prob-matrix - full page screenshot
mobile-chrome › theme.spec.ts › Visual Regression Tests › prob-matrix - full page screenshot
mobile-chrome › theme.spec.ts › Theme Features › f-string interpolation styling

Address Copilot review on #424.

Add an author_association gate (OWNER/MEMBER/COLLABORATOR) to both jobs. They
check out the PR branch and execute its code — `pip install .` runs the PR's
build backend — with a `contents: write` token, and had no gate at all, so any
user able to comment on a PR could trigger that. Pre-existing, but worth closing
while the file is open.

Report the resolved fixtures pin in the /update-new-snapshots summary too. Only
the /update-snapshots job did, which made the CHANGELOG's claim that the summary
comments name the fixtures commit true of just one of the two.

Trim `wc -l` when computing the changed-file count. GNU coreutils does not pad
for piped stdin so this never bit on ubuntu-latest, but BSD wc does, and the
value is interpolated into a commit message and a PR comment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pull request August 6, 2026 03:44 Inactive
@mmcky
mmcky merged commit b3b0477 into main Aug 6, 2026
12 checks passed
@mmcky
mmcky deleted the ci/snapshot-bot-reads-pr-fixtures-pin branch August 6, 2026 03:49
mmcky added a commit that referenced this pull request Aug 6, 2026
…425)

* ci: verify regenerated snapshots and let the bot's commit trigger CI

Addresses #358.

Two problems, one symptom — regenerated baselines reaching a PR with nothing
having checked them and no CI run to notice.

Verify before committing. The workflow wrote new baselines and pushed them with
nothing establishing they were reproducible. Both jobs now re-run the suite
without --update-snapshots against the same built site. Passing shows the render
is deterministic; failing means the images just written already don't reproduce,
which is non-deterministic rendering rather than a stale baseline, and is worth
knowing before the commit lands rather than after it reaches main.

Make the push able to trigger CI. Commits pushed with GITHUB_TOKEN never raise
push/pull_request events, so the baselines landed while the PR's visual check
still showed its previous failure and the author had to push an empty commit by
hand. Both checkouts now use `secrets.SNAPSHOT_BOT_TOKEN || secrets.GITHUB_TOKEN`,
so setting that secret makes CI re-run automatically while leaving the workflow
working unchanged when it is absent. The summary comments report which path was
taken and what, if anything, the author still needs to do.

Note that #358's first stated cause is stale: the commit messages no longer
carry `[skip ci]`. The GITHUB_TOKEN event-suppression rule is the whole of it.

Docs cover the new secret, the verification step, and correct two points that
#424 changed — ci.yml is now the source of truth for the fixtures pin, and the
pin is the one thing read from the PR branch rather than the default branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci: stop the verify run wiping the snapshot diff artifact

Address Copilot review on #425, plus two defects found reviewing the result.

Report the verify outcome in every case, not only when something was committed.
The verify step has no `if:`, so it always runs; suppressing its result when
nothing was committed hid the most useful signal of all — for /update-new-snapshots,
a failure with nothing committed means the PR has real visual regressions.

Give the verify run its own output directory. Playwright clears its output dir
as the first task of every run, so the verify run was deleting the regeneration
run's images out of test-results/ — precisely the content the snapshot-update-diff
artifact exists to carry. The artifact would have uploaded nothing while the
comment still linked to it, with only a buried upload-artifact warning as signal.
Verified by repro: a sentinel file in test-results/ does not survive a plain
`playwright test`, and does survive `playwright test --output=test-results-verify`.
The upload now covers both directories, so a verify failure stays diagnosable.

Stop asserting a cause the workflow cannot know. /update-new-snapshots runs
--update-snapshots=missing, which creates only absent baselines and leaves
existing-but-differing ones failing, while the verify run covers the whole suite.
So `committed && !verified` there does not imply the new snapshots are at fault —
it is reachable through the repo's own documented fixtures-bump procedure, where
a bump seeds one new baseline correctly and perturbs unrelated existing pages.
That job's message now gives both possibilities; the regenerate-all job, where
every baseline was rewritten, still states the specific cause because there it
is the only one available.

Docs and CHANGELOG follow, and no longer imply the verification gates the commit.
It reports; the commit happens either way, which is deliberate — the images stay
available to inspect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants