update-snapshots.yml
Regenerate testthat snapshots for an R package: run the test suite, accept the output the code now produces (testthat::snapshot_accept()), re-run the tests, then commit and push the changed files under tests/testthat/_snaps/. Optionally sets up Julia first, for packages whose tests call a bundled Julia project.
Inputs
| Input | Type | Default | Description |
|---|---|---|---|
ref |
string | '' |
Branch or SHA to check out. Defaults to the event ref when empty; pass a branch name for workflow_dispatch callers or github.head_ref for pull_request callers. |
pr-mode |
boolean | false |
Use r-lib/actions/pr-fetch and pr-push instead of direct checkout and push. Set true for PR-comment (issue_comment) triggers. |
julia |
boolean | false |
Set up the Julia runtime before running tests. |
julia-version |
string | '1' |
Julia version to install. Only used when julia is true. |
julia-project |
string | 'inst/julia' |
Path to the Julia project to instantiate. Only used when julia is true. |
extra-packages |
string | 'any::testthat' |
Extra R packages forwarded to r-lib/actions/setup-r-dependencies. |
needs |
string | 'check' |
needs label forwarded to r-lib/actions/setup-r-dependencies. |
apt-packages |
string | (standard R system-deps list) | Space-separated apt packages to install before R setup. The default covers the system libraries a typical R package’s dependencies need to build from source (Cairo, fonts, graphics, gettext, OpenMP). Pass your own list to override, or an empty string to skip the install step. |
commit-message |
string | 'Update testthat snapshots [auto]' |
Commit message for the snapshot update commit. |
committer-name |
string | 'github-actions[bot]' |
Git committer name. |
committer-email |
string | '41898282+github-actions[bot]@users.noreply.github.com' |
Git committer email. |
Permissions
Grant contents: write so the job can push the snapshot commit back to the branch.
When are the updated snapshots checked?
The workflow itself checks only consistency, never correctness. Its test-and-accept step:
- runs the test suite once, so that failing snapshot expectations write their pending new snapshot files (a
tryCatchkeeps a hard R error, e.g. a test file that errors outright, from killing the job before the accept step; plain expectation failures don’t stop this run anyway, sincedevtools::test()defaults tostop_on_failure = FALSE); - calls
testthat::snapshot_accept(), which accepts whatever the new output is, unconditionally; - runs
devtools::test(stop_on_failure = TRUE)a second time, failing the job, and skipping the commit-and-push steps, if the suite still fails.
That second test run proves the suite passes against the freshly accepted snapshots. It cannot judge whether the new output is right: code that produces wrong-but-stable output passes it.
The semantic check, “do the updated snapshots make sense?”, is left to pull request review. When the workflow runs against a PR’s head branch (pr-mode: true from an issue_comment trigger, a workflow_dispatch with ref set to the PR’s branch, or a pull_request-triggered caller passing ref: github.head_ref), the snapshot commit lands on the PR, and the reviewer inspects the tests/testthat/_snaps/ diff before merge. That review is the only point where snapshot content is judged; nothing re-checks it after merge.
Two consequences worth planning around:
- Dispatching against a plain branch skips review entirely. The example caller stub defaults to
main, where the snapshot commit lands directly, with no reviewable diff in between. Prefer running against a PR branch when the snapshot changes deserve eyes. - The snapshot commit does not re-trigger CI normally. The push uses the workflow’s own
GITHUB_TOKEN, and events triggered byGITHUB_TOKENdo not create new workflow runs (the same behavior theWORKFLOW_TOKENnote on Permissions describes), with one exception that applies here: on a pull request, the resultingsynchronizeevent creates the runs in an approval-required state, and a user with write access starts them from the “Approve workflows to run” prompt in the PR’s merge box. For a direct push to a plain branch there is no such prompt; re-trigger checks yourself if you need them, e.g. by pushing an empty commit.
Example
# Copy to .github/workflows/update-snapshots.yml in your repo.
name: Update snapshots
on:
workflow_dispatch:
inputs:
branch:
description: Branch to update snapshots on
required: true
default: main
jobs:
update-snapshots:
uses: Morrison-Lab/gha/.github/workflows/update-snapshots.yml@v2
permissions:
contents: write
with:
ref: ${{ inputs.branch }}
# julia: true
# julia-version: '1'
# julia-project: inst/julia
# apt-packages: '' # default installs the standard R system-deps list; set '' to skip, or pass your own
# extra-packages: any::testthat
# needs: check
# commit-message: 'Update testthat snapshots [auto]'
# committer-name: github-actions[bot]
# committer-email: 41898282+github-actions[bot]@users.noreply.github.comSee examples/update-snapshots.yml for the full caller stub.