-
Notifications
You must be signed in to change notification settings - Fork 59
250 lines (226 loc) · 10.1 KB
/
Copy pathcode-storybook.yml
File metadata and controls
250 lines (226 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
name: Storybook visual regression
# Captures a screenshot of every story (dark + light) and submits them to the
# PostHog Visual Review product, which diffs against the signed baseline
# manifest committed at apps/code/snapshots.yml. Visual changes are reviewed
# and approved at https://us.posthog.com/project/2/visual_review; on approval
# the VR backend commits the updated manifest back to the PR branch. PNGs are
# never committed to this repo.
#
# Until the one-time setup is done (VR repo registration + VR_API_TOKEN secret
# + committed snapshots.yml), the vr step silently no-ops: the job still runs
# the capture, which catches stories that crash or never settle.
on:
pull_request:
push:
branches: [main]
concurrency:
group: code-storybook-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
changes:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
code: ${{ steps.filter.outputs.code }}
baseline_only_push: ${{ steps.baseline_push.outputs.result }}
steps:
- name: Detect relevant changes
id: filter
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
with:
predicate-quantifier: every
filters: |
# Anything that can change a rendered story. The baseline manifest
# is excluded, but note paths-filter diffs the whole PR against
# its base, so once a PR has code changes this filter stays true
# for every later push — the baseline_push step below is what
# skips the VR backend's own approval commits.
code:
- "{apps/code/**,packages/**,pnpm-lock.yaml,.github/workflows/code-storybook.yml}"
- "!apps/code/snapshots.yml"
- "!**/*.md"
# Visual Review commits the approved baseline back to the PR branch as
# a bot; re-running the suite for that push is pure waste (only the
# manifest changed). Compare the push delta (before...after), not the
# PR diff, so it only matches the bot's own baseline commit.
- name: Detect Visual Review baseline-only push
id: baseline_push
if: github.event_name == 'pull_request' && github.event.action == 'synchronize'
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
ACTOR: ${{ github.actor }}
BEFORE: ${{ github.event.before }}
AFTER: ${{ github.event.after }}
run: |
set -euo pipefail
result=false
case "$ACTOR" in
*'[bot]' | posthog-bot) is_bot=true ;;
*) is_bot=false ;;
esac
zero=0000000000000000000000000000000000000000
if [ "$is_bot" = true ] && [ -n "$BEFORE" ] && [ "$BEFORE" != "$zero" ] && [ -n "$AFTER" ]; then
# compare uses merge-base(before, after), which for an appended
# commit is exactly the push delta; a force-push/rebase widens it
# and won't match.
files=$(gh api "repos/${REPO}/compare/${BEFORE}...${AFTER}" --jq '.files[].filename' || true)
if [ -n "$files" ] && ! grep -qv '^apps/code/snapshots\.yml$' <<<"$files"; then
result=true
fi
fi
echo "result=$result" >>"$GITHUB_OUTPUT"
echo "Visual Review baseline-only push: $result"
visual-regression:
needs: changes
# Fail closed: if change detection failed (or this is a main push, where
# `changes` is skipped), run instead of skipping — unless the push was
# just the VR bot committing an approved baseline.
if: >-
${{ !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.code == 'true') &&
needs.changes.outputs.baseline_only_push != 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22
cache: "pnpm"
- name: Cache Playwright browsers
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: playwright-chromium-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
playwright-chromium-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install Playwright Chromium
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: apps/code
run: pnpm exec playwright install --with-deps chromium
- name: Install Playwright system dependencies
# The cache restores browser binaries but not the OS packages they need.
if: steps.playwright-cache.outputs.cache-hit == 'true'
working-directory: apps/code
run: pnpm exec playwright install-deps chromium
- name: Build workspace packages
# Bare `wait` returns the exit status of only the last job passed to
# it, silently swallowing failures from the others. `wait_all` waits
# on each PID individually and checks its own status so any failed
# build fails the step.
run: |
wait_all() {
status=0
for pid in "$@"; do
wait "$pid" || status=$?
done
[ "$status" -eq 0 ]
}
pnpm --filter @posthog/electron-trpc build &
pid1=$!
(pnpm --filter @posthog/shared build && pnpm --filter @posthog/platform build) &
pid2=$!
wait_all "$pid1" "$pid2"
# @posthog/agent imports the dist of @posthog/git and
# @posthog/harness, so both must finish before the last group starts.
pnpm --filter @posthog/git build &
pid3=$!
pnpm --filter @posthog/harness build &
pid4=$!
wait_all "$pid3" "$pid4"
pnpm --filter @posthog/enricher build &
pid5=$!
pnpm --filter @posthog/agent build &
pid6=$!
wait_all "$pid5" "$pid6"
- name: Build Storybook
working-directory: apps/code
run: pnpm build-storybook
- name: Serve Storybook
working-directory: apps/code
run: |
pnpm exec http-server storybook-static --port 6006 --silent &
pnpm exec wait-on http://127.0.0.1:6006/iframe.html --timeout 30000
- name: Capture story screenshots
working-directory: apps/code
run: |
rm -rf .storybook/__snapshots__
pnpm test:visual:ci:update
# The vr CLI isn't published anywhere; build it from posthog/posthog's
# master, the same way that repo's own CI does.
- name: Checkout Visual Review CLI
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: PostHog/posthog
path: vr-cli
persist-credentials: false
sparse-checkout: |
products/visual_review/cli
products/visual_review/frontend/generated/api.schemas.ts
sparse-checkout-cone-mode: false
- name: Install Visual Review CLI
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push'
run: cd vr-cli/products/visual_review/cli && npm ci && npm run build && npm link
- name: Submit snapshots to Visual Review
# Fork PRs can't read the token; their captures still ran above, so a
# broken story fails the job either way.
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push'
# On main pushes the run is tracking-only ("observe"), but vr submit
# still exits 1 when it detects changes; keep main green regardless.
continue-on-error: ${{ github.event_name == 'push' }}
env:
VR_TOKEN: ${{ secrets.VR_API_TOKEN }}
VR_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }}
VR_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
VR_PR: ${{ github.event.pull_request.number }}
VR_PURPOSE: ${{ github.event_name == 'push' && 'observe' || 'review' }}
# Lets the VR web UI re-trigger this job via the Actions API.
JOB_CHECK_RUN_ID: ${{ job.check_run_id }}
run: |
if [ -z "$VR_TOKEN" ]; then
echo "::notice::VR_API_TOKEN secret not set - skipping Visual Review submission"
exit 0
fi
vr submit \
--type storybook \
--dir apps/code/.storybook/__snapshots__/ \
--baseline apps/code/snapshots.yml \
--branch "$VR_BRANCH" \
--commit "$VR_COMMIT" \
${VR_PR:+--pr "$VR_PR"} \
--purpose "$VR_PURPOSE" \
--token "$VR_TOKEN"
- name: Upload failure screenshots
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0.0
with:
name: storybook-visual-failures
path: |
apps/code/.storybook/__snapshots__/__diff_output__/
apps/code/.storybook/__snapshots__/__failures__/
if-no-files-found: ignore
retention-days: 7
- name: Upload test results to Trunk
# Run even when tests fail so flaky/failed results are still reported,
# but never let an upload problem fail the job.
if: ${{ !cancelled() }}
continue-on-error: true
uses: trunk-io/analytics-uploader@385f1ccdf345b4532dc4b6c665dd432b702b8e28 # v2.1.2
with:
junit-paths: "apps/code/junit.xml"
org-slug: posthog-inc
token: ${{ secrets.TRUNK_API_TOKEN }}