snapdrift 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -1
- package/README.md +3 -3
- package/actions/baseline/action.yml +1 -1
- package/actions/comment/action.yml +15 -1
- package/actions/pr-diff/action.yml +17 -3
- package/actions/resolve-baseline/action.yml +1 -1
- package/docs/contracts.md +5 -1
- package/docs/integration-guide.md +20 -4
- package/docs/workflow-templates/refresh-baseline-on-merge.yml +82 -0
- package/lib/capture-routes.mjs +2 -368
- package/lib/cli.mjs +8 -9
- package/lib/compare-results.mjs +21 -642
- package/lib/drift-summary.mjs +3 -154
- package/lib/pr-comment.mjs +2 -145
- package/lib/report.mjs +17 -246
- package/lib/snapdrift-config.mjs +20 -279
- package/lib/stage-artifacts.mjs +2 -123
- package/package.json +10 -2
- package/types/visual-diff-types.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,42 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.3.0 - 2026-05-26
|
|
4
|
+
|
|
5
|
+
### Infrastructure
|
|
6
|
+
|
|
7
|
+
- **Package extraction (Phase 1a)** — extracted `@snapdrift/manifest` (schema, validation, indexing, viewport presets) and `@snapdrift/compare-core` (pure pixel comparison, ignore-region masking) as standalone npm packages with zero runtime deps beyond `pngjs`.
|
|
8
|
+
- **Package extraction (Phase 1b)** — extracted `@snapdrift/adapter-fs` (all filesystem I/O: capture, compare, config, staging, drift report generation) and `@snapdrift/adapter-report-md` (pure markdown/HTML report generators, zero runtime deps) as standalone npm packages.
|
|
9
|
+
- **Lib/ shim refactor** — all `lib/*.mjs` modules are now thin re-export shims that delegate to packages. `lib/report.mjs` wires the default filesystem `imageReader` for HTML reports. Backward-compatible — all export surfaces preserved.
|
|
10
|
+
- **`generateDiffImage` ignore regions** — `@snapdrift/compare-core` now overlays ignored pixel regions with semi-transparent gray instead of skipping them.
|
|
11
|
+
- **Publish workflow** — `.github/workflows/publish.yml` now publishes all four `@snapdrift/*` workspace packages in dependency order before the root `snapdrift` package.
|
|
12
|
+
|
|
13
|
+
### Performance
|
|
14
|
+
|
|
15
|
+
- **Faster screenshot capture** — navigation now waits for `load` instead of `networkidle`, removing a guaranteed ≥500 ms floor per route. A 20-route run saves ~10 s in navigation waits alone.
|
|
16
|
+
- **Concurrent route capture** — routes within the same viewport are now captured in parallel (up to `SNAPDRIFT_CAPTURE_CONCURRENCY` at a time, default 5) rather than sequentially, giving a further 3–5× throughput improvement on multi-route suites.
|
|
17
|
+
- **Deterministic animation handling** — screenshots are taken with `animations: 'disabled'` so Playwright finishes/cancels CSS animations before capture, replacing reliance on the settle delay for animation timing.
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
- **`SNAPDRIFT_CAPTURE_CONCURRENCY` env var** — controls the maximum number of routes captured concurrently within a viewport context (positive integer, default `5`). Set to `1` to restore serial-per-viewport behaviour for apps with shared session or auth state.
|
|
22
|
+
|
|
23
|
+
## 0.2.1 - 2026-04-13
|
|
24
|
+
|
|
25
|
+
### Infrastructure
|
|
26
|
+
|
|
27
|
+
- **Integration test for compare → stage pipeline** — `tests/integration/capture-compare-pipeline.test.js` exercises the full `generateDriftReport → stageArtifacts` data flow with synthetic PNG fixtures (no live browser needed), catching interface mismatches between modules that unit tests alone would miss.
|
|
28
|
+
|
|
29
|
+
### Features
|
|
30
|
+
|
|
31
|
+
- **Configurable PR comment truncation limits** — `actions/pr-diff` and `actions/comment` now accept optional `max-changed-rows` (default: 20) and `max-error-rows` (default: 10) inputs, letting teams control how many rows appear in the drift and error tables before the overflow note.
|
|
32
|
+
- **Per-route navigation timeout** — routes now accept an optional `navigationTimeout` (positive integer, ms) that overrides the global 30 000 ms default for that route. Useful for slow SSR pages that need more time or fast static pages that should fail fast.
|
|
33
|
+
- **Baseline refresh workflow template** — `docs/workflow-templates/refresh-baseline-on-merge.yml` is a drop-in workflow template for consumer repos that automatically republishes the SnapDrift baseline on every push to the default branch. Supports an optional label gate to avoid republishing on every push.
|
|
34
|
+
|
|
35
|
+
### Fixes
|
|
36
|
+
|
|
37
|
+
- **Baseline refresh template trigger** — switched from `pull_request: closed` to `push` on the default branch so published artifacts are actually discoverable by the baseline resolver in `actions/pr-diff`, which hardcodes `event:'push'` when querying workflow runs.
|
|
38
|
+
- **Artifact action pins** — `actions/upload-artifact` and `actions/download-artifact` in composite action YAMLs are now pinned to v7 SHAs, matching the CI workflow (the v0.2.0 changelog noted this upgrade but only the CI workflow was updated at the time).
|
|
39
|
+
- **Stale version references** — README and integration guide examples now reference `@v0.2.1` instead of the outdated `@v0.1.0`. README "Current constraints" updated to reflect custom viewport support.
|
|
4
40
|
|
|
5
41
|
## 0.2.0 - 2026-04-08
|
|
6
42
|
|
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ You keep ownership of checkout, build, startup, readiness, and teardown. SnapDri
|
|
|
44
44
|
|
|
45
45
|
```yaml
|
|
46
46
|
- name: SnapDrift Baseline
|
|
47
|
-
uses: ranacseruet/snapdrift/actions/baseline@v0.1
|
|
47
|
+
uses: ranacseruet/snapdrift/actions/baseline@v0.2.1
|
|
48
48
|
with:
|
|
49
49
|
repo-config-path: .github/snapdrift.json
|
|
50
50
|
```
|
|
@@ -53,7 +53,7 @@ You keep ownership of checkout, build, startup, readiness, and teardown. SnapDri
|
|
|
53
53
|
|
|
54
54
|
```yaml
|
|
55
55
|
- name: SnapDrift Report
|
|
56
|
-
uses: ranacseruet/snapdrift/actions/pr-diff@v0.1
|
|
56
|
+
uses: ranacseruet/snapdrift/actions/pr-diff@v0.2.1
|
|
57
57
|
with:
|
|
58
58
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
59
59
|
repo-config-path: .github/snapdrift.json
|
|
@@ -90,7 +90,7 @@ Start with `report-only` while baselines settle. Move to `fail-on-changes` or st
|
|
|
90
90
|
|
|
91
91
|
- Ubuntu runners only
|
|
92
92
|
- Full-page capture only
|
|
93
|
-
-
|
|
93
|
+
- Viewport presets: `desktop` (1440×900) and `mobile` (390×844), or custom `{ "width": number, "height": number }`
|
|
94
94
|
- One global `diff.threshold`
|
|
95
95
|
- Dimension shifts are reported separately from pixel drift
|
|
96
96
|
|
|
@@ -134,7 +134,7 @@ runs:
|
|
|
134
134
|
EOF
|
|
135
135
|
|
|
136
136
|
- if: inputs.upload-artifact == 'true'
|
|
137
|
-
uses: actions/upload-artifact@
|
|
137
|
+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
138
138
|
with:
|
|
139
139
|
name: ${{ steps.config.outputs.artifact_name }}
|
|
140
140
|
retention-days: ${{ inputs.artifact-retention-days }}
|
|
@@ -20,6 +20,14 @@ inputs:
|
|
|
20
20
|
description: Artifact label to include in the comment.
|
|
21
21
|
required: false
|
|
22
22
|
default: ''
|
|
23
|
+
max-changed-rows:
|
|
24
|
+
description: Maximum number of changed-route rows shown in the PR comment before truncation.
|
|
25
|
+
required: false
|
|
26
|
+
default: '20'
|
|
27
|
+
max-error-rows:
|
|
28
|
+
description: Maximum number of error rows shown in the PR comment before truncation.
|
|
29
|
+
required: false
|
|
30
|
+
default: '10'
|
|
23
31
|
|
|
24
32
|
runs:
|
|
25
33
|
using: composite
|
|
@@ -34,6 +42,8 @@ runs:
|
|
|
34
42
|
VISUAL_DIFF_RUN_URL: ${{ inputs.run-url }}
|
|
35
43
|
VISUAL_DIFF_ARTIFACT_NAME: ${{ inputs.artifact-name }}
|
|
36
44
|
INPUT_PR_NUMBER: ${{ inputs.pr-number }}
|
|
45
|
+
MAX_CHANGED_ROWS: ${{ inputs.max-changed-rows }}
|
|
46
|
+
MAX_ERROR_ROWS: ${{ inputs.max-error-rows }}
|
|
37
47
|
with:
|
|
38
48
|
github-token: ${{ inputs.github-token }}
|
|
39
49
|
script: |
|
|
@@ -59,9 +69,13 @@ runs:
|
|
|
59
69
|
const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
|
|
60
70
|
const runUrl = process.env.VISUAL_DIFF_RUN_URL || `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
|
61
71
|
|
|
72
|
+
const maxChangedRows = Number(process.env.MAX_CHANGED_ROWS) || 20;
|
|
73
|
+
const maxErrorRows = Number(process.env.MAX_ERROR_ROWS) || 10;
|
|
62
74
|
const body = buildReportCommentBody(summary, {
|
|
63
75
|
artifactName: process.env.VISUAL_DIFF_ARTIFACT_NAME || '',
|
|
64
|
-
runUrl
|
|
76
|
+
runUrl,
|
|
77
|
+
maxChangedRows,
|
|
78
|
+
maxErrorRows
|
|
65
79
|
});
|
|
66
80
|
|
|
67
81
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
@@ -45,6 +45,14 @@ inputs:
|
|
|
45
45
|
description: Whether to publish the SnapDrift PR report.
|
|
46
46
|
required: false
|
|
47
47
|
default: 'true'
|
|
48
|
+
max-changed-rows:
|
|
49
|
+
description: Maximum number of changed-route rows shown in the PR comment before truncation.
|
|
50
|
+
required: false
|
|
51
|
+
default: '20'
|
|
52
|
+
max-error-rows:
|
|
53
|
+
description: Maximum number of error rows shown in the PR comment before truncation.
|
|
54
|
+
required: false
|
|
55
|
+
default: '10'
|
|
48
56
|
|
|
49
57
|
outputs:
|
|
50
58
|
should-run:
|
|
@@ -254,7 +262,7 @@ runs:
|
|
|
254
262
|
}
|
|
255
263
|
|
|
256
264
|
- if: steps.scope.outputs.should_run == 'true' && steps.baseline.outputs.found == 'true'
|
|
257
|
-
uses: actions/download-artifact@
|
|
265
|
+
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
|
258
266
|
with:
|
|
259
267
|
github-token: ${{ inputs.github-token }}
|
|
260
268
|
repository: ${{ inputs.baseline-repository || github.repository }}
|
|
@@ -467,7 +475,7 @@ runs:
|
|
|
467
475
|
await fs.appendFile(process.env.GITHUB_OUTPUT, `bundle_dir=${result.bundleDir}\n`);
|
|
468
476
|
EOF
|
|
469
477
|
|
|
470
|
-
- uses: actions/upload-artifact@
|
|
478
|
+
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
471
479
|
with:
|
|
472
480
|
name: ${{ steps.context.outputs.artifact_name }}
|
|
473
481
|
retention-days: ${{ inputs.artifact-retention-days }}
|
|
@@ -490,6 +498,8 @@ runs:
|
|
|
490
498
|
SUMMARY_PATH: ${{ steps.compare.outputs.summary_path || steps.skipped_baseline.outputs.summary_path || steps.skipped_scope.outputs.summary_path }}
|
|
491
499
|
ARTIFACT_NAME: ${{ steps.context.outputs.artifact_name }}
|
|
492
500
|
RUN_URL: ${{ steps.context.outputs.run_url }}
|
|
501
|
+
MAX_CHANGED_ROWS: ${{ inputs.max-changed-rows }}
|
|
502
|
+
MAX_ERROR_ROWS: ${{ inputs.max-error-rows }}
|
|
493
503
|
with:
|
|
494
504
|
github-token: ${{ inputs.github-token }}
|
|
495
505
|
script: |
|
|
@@ -525,9 +535,13 @@ runs:
|
|
|
525
535
|
const { buildReportCommentBody } = await import(pathToFileURL(modulePath));
|
|
526
536
|
|
|
527
537
|
const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
|
|
538
|
+
const maxChangedRows = Number(process.env.MAX_CHANGED_ROWS) || 20;
|
|
539
|
+
const maxErrorRows = Number(process.env.MAX_ERROR_ROWS) || 10;
|
|
528
540
|
const body = buildReportCommentBody(summary, {
|
|
529
541
|
artifactName: process.env.ARTIFACT_NAME || '',
|
|
530
|
-
runUrl: process.env.RUN_URL || ''
|
|
542
|
+
runUrl: process.env.RUN_URL || '',
|
|
543
|
+
maxChangedRows,
|
|
544
|
+
maxErrorRows
|
|
531
545
|
});
|
|
532
546
|
|
|
533
547
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
@@ -135,7 +135,7 @@ runs:
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
- if: steps.resolve.outputs.found == 'true'
|
|
138
|
-
uses: actions/download-artifact@
|
|
138
|
+
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
|
139
139
|
with:
|
|
140
140
|
github-token: ${{ inputs.github-token }}
|
|
141
141
|
repository: ${{ inputs.repository || github.repository }}
|
package/docs/contracts.md
CHANGED
|
@@ -26,6 +26,7 @@ SnapDrift reads runtime behavior from `.github/snapdrift.json` by default.
|
|
|
26
26
|
| `path` | `string` | URL path appended to `baseUrl` |
|
|
27
27
|
| `viewport` | `string` or `object` | Preset name (`"desktop"`, `"mobile"`) or a custom object `{ "width": number, "height": number }` |
|
|
28
28
|
| `changePaths` | `string[]` | Optional prefixes used for changed-file scoping |
|
|
29
|
+
| `navigationTimeout` | `number` | Optional per-route navigation timeout in ms (overrides the 30 000 ms global default) |
|
|
29
30
|
|
|
30
31
|
### Optional fields
|
|
31
32
|
|
|
@@ -179,9 +180,11 @@ Additional missing-baseline fields: `baselineAvailable`, `currentResultsPath`.
|
|
|
179
180
|
|
|
180
181
|
| Setting | Value |
|
|
181
182
|
|:--------|:------|
|
|
182
|
-
| Navigation wait | `
|
|
183
|
+
| Navigation wait | `load` |
|
|
183
184
|
| Navigation timeout | 30000ms |
|
|
184
185
|
| Settle delay | 300ms |
|
|
186
|
+
| Screenshot animations | `disabled` (Playwright finishes/cancels CSS animations before capture) |
|
|
187
|
+
| Capture concurrency | 5 routes per viewport (overridable via `SNAPDRIFT_CAPTURE_CONCURRENCY`)|
|
|
185
188
|
|
|
186
189
|
## Local CLI directory layout
|
|
187
190
|
|
|
@@ -231,3 +234,4 @@ These are for custom orchestration only. Wrapper actions set them automatically.
|
|
|
231
234
|
| `SNAPDRIFT_BASELINE_ARTIFACT_NAME` | `compare-results.mjs` | Baseline artifact label to embed in the report |
|
|
232
235
|
| `SNAPDRIFT_BASELINE_SOURCE_SHA` | `compare-results.mjs` | Baseline source SHA to embed in the report |
|
|
233
236
|
| `SNAPDRIFT_ENFORCE_OUTCOME` | `compare-results.mjs` | Set to `0` to disable enforcement in direct CLI usage |
|
|
237
|
+
| `SNAPDRIFT_CAPTURE_CONCURRENCY` | `capture-routes.mjs` | Max concurrent route captures per viewport context (positive integer, default `5`). Set to `1` to restore serial behaviour for apps with shared session/auth state. |
|
|
@@ -46,7 +46,7 @@ SnapDrift owns route selection, capture, comparison, skipped-report generation,
|
|
|
46
46
|
|
|
47
47
|
```yaml
|
|
48
48
|
- name: SnapDrift Baseline
|
|
49
|
-
uses: ranacseruet/snapdrift/actions/baseline@v0.1
|
|
49
|
+
uses: ranacseruet/snapdrift/actions/baseline@v0.2.1
|
|
50
50
|
with:
|
|
51
51
|
repo-config-path: .github/snapdrift.json
|
|
52
52
|
artifact-retention-days: '30'
|
|
@@ -70,7 +70,7 @@ Then add SnapDrift after the app is running:
|
|
|
70
70
|
|
|
71
71
|
```yaml
|
|
72
72
|
- name: SnapDrift Report
|
|
73
|
-
uses: ranacseruet/snapdrift/actions/pr-diff@v0.1
|
|
73
|
+
uses: ranacseruet/snapdrift/actions/pr-diff@v0.2.1
|
|
74
74
|
with:
|
|
75
75
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
76
76
|
repo-config-path: .github/snapdrift.json
|
|
@@ -111,7 +111,7 @@ jobs:
|
|
|
111
111
|
done
|
|
112
112
|
|
|
113
113
|
- name: SnapDrift Report
|
|
114
|
-
uses: ranacseruet/snapdrift/actions/pr-diff@v0.1
|
|
114
|
+
uses: ranacseruet/snapdrift/actions/pr-diff@v0.2.1
|
|
115
115
|
with:
|
|
116
116
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
117
117
|
repo-config-path: .github/snapdrift.json
|
|
@@ -150,7 +150,7 @@ jobs:
|
|
|
150
150
|
done
|
|
151
151
|
|
|
152
152
|
- name: SnapDrift Report
|
|
153
|
-
uses: ranacseruet/snapdrift/actions/pr-diff@v0.1
|
|
153
|
+
uses: ranacseruet/snapdrift/actions/pr-diff@v0.2.1
|
|
154
154
|
with:
|
|
155
155
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
156
156
|
repo-config-path: .github/snapdrift.json
|
|
@@ -178,6 +178,8 @@ jobs:
|
|
|
178
178
|
| `baseline-workflow-id` | Override the baseline workflow id |
|
|
179
179
|
| `baseline-branch` | Override the baseline branch |
|
|
180
180
|
| `comment-on-pr` | Set to `false` to suppress the PR report |
|
|
181
|
+
| `max-changed-rows` | Max changed-route rows shown before truncation (default: `20`) |
|
|
182
|
+
| `max-error-rows` | Max error rows shown before truncation (default: `10`) |
|
|
181
183
|
|
|
182
184
|
## Low-level actions
|
|
183
185
|
|
|
@@ -207,6 +209,20 @@ The CLI reads the same `.github/snapdrift.json` config used by the Actions workf
|
|
|
207
209
|
|
|
208
210
|
See the [Local CLI guide](local-cli.md) for full command reference, flags, directory layout, and examples.
|
|
209
211
|
|
|
212
|
+
## Refresh the baseline automatically
|
|
213
|
+
|
|
214
|
+
After an intentional layout change or dimension shift merges, the baseline must be republished before SnapDrift can compare like-for-like frames again. Without automation, this is a manual step.
|
|
215
|
+
|
|
216
|
+
Use the provided workflow template to refresh the baseline automatically on every push to your default branch (i.e. every merge):
|
|
217
|
+
|
|
218
|
+
**`docs/workflow-templates/refresh-baseline-on-merge.yml`**
|
|
219
|
+
|
|
220
|
+
Drop a copy into your repo at `.github/workflows/snapdrift-refresh-baseline.yml`, then fill in the `TODO` blocks with your app's build and start steps — the same steps you use in your PR workflow. The template uses a `push` trigger on the default branch so published artifacts are discoverable by the baseline resolver in `actions/pr-diff`.
|
|
221
|
+
|
|
222
|
+
### Label-gated refreshes
|
|
223
|
+
|
|
224
|
+
If you don't want to republish the baseline on every push, the template includes a commented `if` condition that gates on a label (e.g. `snapdrift:refresh-baseline`). A preceding job can check the most recent merged PR for the label and set an output to control whether the baseline refresh runs.
|
|
225
|
+
|
|
210
226
|
## Troubleshooting
|
|
211
227
|
|
|
212
228
|
**"No non-expired SnapDrift baseline artifact was found"**
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# SnapDrift — Baseline Refresh on Merge
|
|
2
|
+
#
|
|
3
|
+
# Drop this file into your consumer repo at:
|
|
4
|
+
# .github/workflows/snapdrift-refresh-baseline.yml
|
|
5
|
+
#
|
|
6
|
+
# What it does:
|
|
7
|
+
# Automatically re-publishes the SnapDrift baseline on every push to your
|
|
8
|
+
# default branch. Since merging a PR produces a push event, this keeps the
|
|
9
|
+
# baseline in sync after intentional layout changes, dimension shifts, or
|
|
10
|
+
# any other UI refactor lands.
|
|
11
|
+
#
|
|
12
|
+
# Prerequisites:
|
|
13
|
+
# - A working `.github/snapdrift.json` in your repo
|
|
14
|
+
# - The same build + start steps used in your PR workflow
|
|
15
|
+
#
|
|
16
|
+
# Uses a push trigger so published artifacts are discoverable by the baseline
|
|
17
|
+
# resolver in actions/pr-diff (which filters on event: 'push'). A merge into
|
|
18
|
+
# the default branch produces a push event, so this fires exactly when needed.
|
|
19
|
+
#
|
|
20
|
+
# Optional — refresh only on a specific label:
|
|
21
|
+
# If you don't want to refresh on every push, you can gate on a label applied
|
|
22
|
+
# before merging. Use the pull_request: labeled event in a separate workflow to
|
|
23
|
+
# set an output, or simply restrict this workflow to specific paths.
|
|
24
|
+
# See the commented block below for a label-based gate using a preceding job.
|
|
25
|
+
|
|
26
|
+
name: SnapDrift Refresh Baseline
|
|
27
|
+
|
|
28
|
+
on:
|
|
29
|
+
push:
|
|
30
|
+
branches: [main]
|
|
31
|
+
|
|
32
|
+
permissions:
|
|
33
|
+
contents: read
|
|
34
|
+
actions: write # required to upload the baseline artifact
|
|
35
|
+
|
|
36
|
+
jobs:
|
|
37
|
+
refresh-baseline:
|
|
38
|
+
# Optional: gate on a label to avoid refreshing on every push.
|
|
39
|
+
# Requires a preceding job that checks for the label on the most recent
|
|
40
|
+
# merged PR and sets an output. Replace "snapdrift:refresh-baseline" with
|
|
41
|
+
# your label name.
|
|
42
|
+
# if: needs.check-label.outputs.refresh == 'true'
|
|
43
|
+
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
|
|
49
|
+
# ── TODO: add your app's build + dependency steps here ──────────────────
|
|
50
|
+
# Example for a Node app:
|
|
51
|
+
#
|
|
52
|
+
# - uses: actions/setup-node@v4
|
|
53
|
+
# with:
|
|
54
|
+
# node-version: '22'
|
|
55
|
+
# cache: npm
|
|
56
|
+
# - run: npm ci
|
|
57
|
+
# - run: npm run build
|
|
58
|
+
# ────────────────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
# ── TODO: start your app and wait for it to be ready ────────────────────
|
|
61
|
+
# SnapDrift needs a running server to capture screenshots.
|
|
62
|
+
# Replace the URL and port to match your app's baseUrl from snapdrift.json.
|
|
63
|
+
#
|
|
64
|
+
# - name: Start app
|
|
65
|
+
# run: |
|
|
66
|
+
# npm start &
|
|
67
|
+
# for i in $(seq 1 45); do
|
|
68
|
+
# curl -sf http://127.0.0.1:8080 && break || sleep 1
|
|
69
|
+
# done
|
|
70
|
+
# ────────────────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
- name: Publish SnapDrift baseline
|
|
73
|
+
uses: ranacseruet/snapdrift/actions/baseline@v0.2.1
|
|
74
|
+
with:
|
|
75
|
+
repo-config-path: .github/snapdrift.json
|
|
76
|
+
artifact-retention-days: '30'
|
|
77
|
+
|
|
78
|
+
# ── TODO: shut down your app if needed ───────────────────────────────────
|
|
79
|
+
# - name: Stop app
|
|
80
|
+
# if: always()
|
|
81
|
+
# run: pkill -f "npm start" || true
|
|
82
|
+
# ────────────────────────────────────────────────────────────────────────
|