Run Meticulous tests
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
A GitHub Action that performs visual regression testing by comparing screenshots between base and head commits in pull requests.
Recommendation: Pick the first approach that fits your app:
1. upload-assets if your app can be served as a folder of static assets (HTML/JS/CSS).
2. upload-container if your app is server-rendered (Next.js, Nuxt, etc.) — build a Docker image and let Meticulous host it.
3. cloud-compute only if neither of the above work — it serves the app from CI and connects via a secure tunnel, which is more brittle.檢視完整 README ↗A GitHub Action that performs visual regression testing by comparing screenshots between base and head commits in pull requests.
Full setup guide and documentation →
| Action | Use Case |
|---|---|
upload-assets | Upload built static assets for Meticulous to test (recommended for static sites) |
upload-container | Upload a built Docker image for Meticulous to host and test (recommended for server-rendered apps, e.g. Next.js) |
ensure-base | First step before upload: dispatch a missing base build so it runs in parallel with the PR build |
cloud-compute | Test a locally-served app via a secure tunnel (last resort) |
report-diffs-action | Run tests in GitHub Actions runner (legacy) |
Recommendation: Pick the first approach that fits your app:
upload-assetsif your app can be served as a folder of static assets (HTML/JS/CSS).upload-containerif your app is server-rendered (Next.js, Nuxt, etc.) — build a Docker image and let Meticulous host it.cloud-computeonly if neither of the above work — it serves the app from CI and connects via a secure tunnel, which is more brittle.
- uses: alwaysmeticulous/report-diffs-action/upload-assets@v1
with:
api-token: ${{ secrets.METICULOUS_API_TOKEN }}
app-directory: "dist" # Your build output directory
Build a Docker image of your app and upload it. Meticulous runs the container in its own infrastructure for the duration of the test run, so no server needs to stay alive in CI.
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
tags: my-app:${{ github.sha }}
push: false
- uses: alwaysmeticulous/report-diffs-action/upload-container@v1
with:
api-token: ${{ secrets.METICULOUS_API_TOKEN }}
image-tag: my-app:${{ github.sha }}
# Optional: set if your container does not respect the PORT env var
container-port: 3000
# Optional: extra env vars passed to the container at run time
container-env: |
NODE_ENV=production
The container must:
linux/amd64PORT env var (or set container-port to the hard-coded port)2xx to the health-check endpoint (defaults to GET /, override with container-health-check-endpoint)Only use this if your app can't be uploaded as static assets or as a container image.
- name: Serve app
run: |
npm run build && npm run serve &
sleep 5
- uses: alwaysmeticulous/report-diffs-action/cloud-compute@v1
with:
api-token: ${{ secrets.METICULOUS_API_TOKEN }}
app-url: "http://localhost:3000/"
All inputs are documented in the action.yml files:
upload-assets/action.yamlupload-container/action.ymlensure-base/action.ymlcloud-compute/action.ymlaction.yml (legacy)on:
push:
branches: [main]
pull_request: {}
workflow_dispatch:
inputs:
meticulous-commit-sha:
description: Commit Meticulous has asked this run to build. Defaults to the branch head.
required: false
permissions:
actions: write
contents: read
issues: write
pull-requests: write
statuses: read
env:
# Prefer the dispatched commit when set; otherwise the PR head. On pull_request
# github.sha is the merge commit, not the PR head SHA that Meticulous looks up.
METICULOUS_COMMIT_SHA: ${{ github.event.inputs['meticulous-commit-sha'] || (github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha) }}
jobs:
test:
steps:
# Same workflow file as the upload step — ensure-base dispatches *this*
# workflow on the base branch. Run it before checkout/build so the base
# can start while this job continues.
- uses: alwaysmeticulous/report-diffs-action/ensure-base@v1
with:
api-token: ${{ secrets.METICULOUS_API_TOKEN }}
# Same ref as checkout, so we pre-warm the base the upload step will ask for.
ref: ${{ env.METICULOUS_COMMIT_SHA }}
- uses: actions/checkout@v4
with:
ref: ${{ env.METICULOUS_COMMIT_SHA }}
ensure-base needs no checkout. It works out the commit the upload step will compare
against — by default the first parent of GitHub's temporary merge commit, read over the API
rather than from a checkout — and, if that commit has no test run yet, dispatches this
workflow and returns immediately, recording the dispatched run ID and that commit as the
base-workflow-run-id and base-commit-sha outputs, and as
METICULOUS_BASE_WORKFLOW_RUN_ID / METICULOUS_BASE_WORKFLOW_COMMIT_SHA for later steps in
the same job. The upload step waits on that run — a pinned workflow_dispatch cannot be found
by commit SHA, because its head_sha is the dispatched ref's tip rather than
meticulous-commit-sha.
The example above checks out the PR head via METICULOUS_COMMIT_SHA, matching the
setup guide. Pass that same value as
ensure-base's ref so it pre-warms the merge base of that commit and the base branch —
what the upload step will resolve after seeing HEAD !== GITHUB_SHA. Omit ref only if
checkout uses github.sha. If ref does not match the later checkout, the upload step
still resolves the base for itself and refuses the recorded run when it isn't building
that commit, so the base is built afresh rather than compared wrongly.
Env vars don't cross jobs, so pass both of ensure-base's outputs through. Without them the
upload job builds the base a second time.
jobs:
ensure-base:
runs-on: ubuntu-latest
outputs:
base-workflow-run-id: ${{ steps.ensure-base.outputs.base-workflow-run-id }}
base-commit-sha: ${{ steps.ensure-base.outputs.base-commit-sha }}
steps:
- uses: alwaysmeticulous/report-diffs-action/ensure-base@v1
id: ensure-base
with:
api-token: ${{ secrets.METICULOUS_API_TOKEN }}
test:
needs: ensure-base
runs-on: ubuntu-latest
steps:
# ... checkout and build ...
- uses: alwaysmeticulous/report-diffs-action/upload-assets@v1
with:
api-token: ${{ secrets.METICULOUS_API_TOKEN }}
app-directory: ./dist
base-workflow-run-id: ${{ needs.ensure-base.outputs.base-workflow-run-id }}
base-commit-sha: ${{ needs.ensure-base.outputs.base-commit-sha }}
base-commit-sha is what makes the run ID safe to use: the upload job resolves the base for
itself and waits on that run only when the two agree, dispatching its own build when they
don't. Passing the run ID without it makes the upload step wait unchecked and emit a warning,
since there is then no way to tell whether the run is building the right commit.
meticulous-commit-sha lets Meticulous ask this workflow to build a specific commit when a
PR's base hasn't been tested yet. Without it a dispatched run can only build whatever the base
branch points at now, so the comparison is skipped when that branch has moved on, and always
for stacked PRs, whose base commit no branch points at. Workflows that don't declare the input
keep their existing behaviour.
main into releases/v1 → Create PRv1.x.y targeting releases/v1@v1 tag auto-updates after build completes