@andwhy/e2e-pipeline 0.12.2 → 0.13.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/dist/bin/init.js CHANGED
@@ -117,8 +117,10 @@ const run = async () => {
117
117
  " deployment protection",
118
118
  " SLACK_WEBHOOK_URL — auto-heal notifications",
119
119
  "(ANTHROPIC_API_KEY is an org-level secret inherited by all repos —",
120
- "nothing to add.) The auto-heal workflow stays inert until a Checkly",
121
- "webhook alert channel dispatches to it — the skill walks through that.",
120
+ "nothing to add; if the heal agent fails to authenticate, a repo",
121
+ "secret of the same name overrides it.) The auto-heal workflow stays",
122
+ "inert until a Checkly webhook alert channel dispatches to it — the",
123
+ "skill walks through that.",
122
124
  ].join("\n"));
123
125
  }
124
126
  console.log("Next steps:");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andwhy/e2e-pipeline",
3
- "version": "0.12.2",
3
+ "version": "0.13.0",
4
4
  "description": "Scaffolds the full e2e testing pipeline: agent-assisted Playwright tests, CI runs against Vercel previews, and Checkly monitors that follow production deploys",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -92,8 +92,13 @@ The scaffold writes templates, not a working setup. Walk these with the user:
92
92
  value from 1Password ("e2e Slack webhook"). Same value in every repo;
93
93
  do not walk the user through creating a Slack app or webhook — routing
94
94
  to the project channel happens via a Slack rule in Phase 5.
95
- - `ANTHROPIC_API_KEY` needs **no action**: it is an organization-level
96
- secret inherited by every repo. Do not tell the user to add it.
95
+ - `ANTHROPIC_API_KEY` usually needs **no action**: it is an
96
+ organization-level secret inherited by every repo — do not walk the
97
+ user through adding it up front. But it can fail to reach a repo
98
+ (org-secret scoping, or a stale stored value): if a heal run's agent
99
+ step fails to authenticate, have the user add a repository-level
100
+ `ANTHROPIC_API_KEY` — a repo secret with the same name overrides the
101
+ organization one.
97
102
  5. **Existing tests under `tests/`** (unit, a11y — the scaffold lists them as
98
103
  "outside tests/e2e"): leave `testDir` at `./tests/e2e` and give that suite
99
104
  its own Playwright project with its own `testDir` if it should run under
@@ -181,7 +186,9 @@ overwritten):
181
186
  `CHECKLY_API_KEY` and `CHECKLY_ACCOUNT_ID` for the deploy and auto-heal,
182
187
  `VERCEL_AUTOMATION_BYPASS_SECRET` for the PR run through deployment
183
188
  protection, `SLACK_WEBHOOK_URL` for auto-heal notifications
184
- (`ANTHROPIC_API_KEY` is inherited from the organization — nothing to add).
189
+ (`ANTHROPIC_API_KEY` is inherited from the organization — nothing to add
190
+ unless a heal run fails to authenticate; then a repo-level secret of the
191
+ same name overrides it, see Phase 0).
185
192
  If the scaffold just created the workflows, say so explicitly and list the
186
193
  secrets — a merge without them fails CI and looks like a pipeline bug.
187
194
 
@@ -201,7 +208,9 @@ Set up once per project:
201
208
 
202
209
  1. Confirm the `SLACK_WEBHOOK_URL` repository secret from Phase 0 exists —
203
210
  the workflow escalates nothing without it. (`ANTHROPIC_API_KEY` comes
204
- from the organization automatically.)
211
+ from the organization automatically — but if the dry run in step 6 ends
212
+ agent-failed with an authentication error, override it with a repo-level
213
+ secret of the same name, per Phase 0.)
205
214
  2. The shared `GH_DISPATCH_TOKEN` secret exists in Checkly and covers this
206
215
  repo. This is **one-time organization setup, not yours to perform**: the
207
216
  token (`checkly-heal-dispatch`, a fine-grained PAT) is created once and
@@ -11,7 +11,9 @@ name: Checkly auto-heal
11
11
  # commit, PR, and Slack are workflow steps.
12
12
  # Needs repo secrets CHECKLY_API_KEY, CHECKLY_ACCOUNT_ID, SLACK_WEBHOOK_URL
13
13
  # (the org-shared e2e-channel webhook; values in 1Password); ANTHROPIC_API_KEY
14
- # is inherited from the organization. Slack messages carry an [e2e:<repo>]
14
+ # is inherited from the organization (a repo secret of the same name
15
+ # overrides it if the org value fails to authenticate). Slack messages
16
+ # carry an [e2e:<repo>]
15
17
  # tag so Slack Workflow Builder rules route them from the shared channel to
16
18
  # project channels. Setup docs: the playwright-test-pipeline skill walks
17
19
  # through the Checkly webhook channel and the Slack rule.
@@ -310,28 +312,48 @@ jobs:
310
312
  run: |
311
313
  case "${MODE}:${CLASSIFICATION}" in
312
314
  repeat:*)
313
- text=":rotating_light: *Checkly: \`$CHECK_NAME\` is failing again* — a heal PR for this check merged within the last $REPEAT_WINDOW_DAYS days, so no second automatic fix was attempted. This needs a developer." ;;
315
+ title=":rotating_light: *\`$CHECK_NAME\` is failing again*"
316
+ body="A heal PR for this check merged within the last $REPEAT_WINDOW_DAYS days, so no second automatic fix was attempted. This needs a developer." ;;
314
317
  *:healed)
315
- text=":wrench: *Checkly: \`$CHECK_NAME\` failed — fix ready for review.* $SUMMARY\nPR: $PR_URL" ;;
318
+ title=":wrench: *\`$CHECK_NAME\` failed — fix ready for review*"
319
+ body="$SUMMARY" ;;
316
320
  *:flaky)
317
- text=":wrench: *Checkly: \`$CHECK_NAME\` was flaky — test hardened, fix ready for review.* $SUMMARY\nPR: $PR_URL" ;;
321
+ title=":wrench: *\`$CHECK_NAME\` was flaky — test hardened, fix ready for review*"
322
+ body="$SUMMARY" ;;
318
323
  *:regression)
319
- text=":rotating_light: *Checkly: \`$CHECK_NAME\` failed — production regression.* The test is correct; the site is broken. $SUMMARY" ;;
324
+ title=":rotating_light: *\`$CHECK_NAME\` failed — production regression*"
325
+ body="The test is correct; the site is broken. $SUMMARY" ;;
320
326
  *:needs-app-change)
321
- text=":warning: *Checkly: \`$CHECK_NAME\` failed — fix needs an app code change.* $SUMMARY" ;;
327
+ title=":warning: *\`$CHECK_NAME\` failed — fix needs an app code change*"
328
+ body="$SUMMARY" ;;
322
329
  *:not-reproducible)
323
330
  # Transient blip, nothing to act on — summary only, no ping.
324
331
  echo "Not reproducible — no Slack notification." >> "$GITHUB_STEP_SUMMARY"
325
332
  exit 0 ;;
326
333
  *)
327
- text=":rotating_light: *Checkly: \`$CHECK_NAME\` failed and the auto-heal run did not finish cleanly.* ${SUMMARY:-Check the run logs.} This needs a developer." ;;
334
+ title=":rotating_light: *\`$CHECK_NAME\` failed and the auto-heal run did not finish cleanly*"
335
+ body="${SUMMARY:-Check the run logs.} This needs a developer." ;;
328
336
  esac
329
- # Routing tag: Slack Workflow Builder rules match the full bracketed
330
- # tag ([e2e:<repo>]) to fan messages out from the shared e2e channel
331
- # to project channels without substring collisions.
332
- text="[e2e:${GITHUB_REPOSITORY##*/}] $text"
333
- [ "$APP_CHANGE" = "true" ] && text="$text\nIncludes app-side test hooks — the PR's preview run validates them."
334
- [ -n "$RESULT_LINK" ] && text="$text\nCheckly result: $RESULT_LINK"
337
+
338
+ # Repeat mode skips the agent, so there is no outcome classification;
339
+ # label it as what it is rather than defaulting to agent-failed.
340
+ label="${CLASSIFICATION:-agent-failed}"
341
+ [ "$MODE" = "repeat" ] && label="repeat-offender"
342
+
343
+ # Failing test titles, best effort from the Checkly result's embedded
344
+ # Playwright report — the recursive search tolerates schema changes,
345
+ # and an empty result simply omits the field.
346
+ failed_tests=""
347
+ if [ -f .checkly-heal/result.json ]; then
348
+ failed_tests=$(jq -r '[.. | objects | select((.status? == "failed") or (.outcome? == "unexpected")) | .title? // empty] | unique | .[0:3] | join(", ")' .checkly-heal/result.json 2>/dev/null || true)
349
+ fi
350
+
351
+ # Plain-text fallback: what notifications, search, and the Slack
352
+ # routing rule see — the Workflow Builder rule matches the [e2e:<repo>]
353
+ # tag here and forwards this text, so it stays a complete message.
354
+ repo="${GITHUB_REPOSITORY##*/}"
355
+ text="[e2e:$repo] $title — $body"
356
+ [ -n "$PR_URL" ] && text="$text\nPR: $PR_URL"
335
357
  text="$text\nRun: $RUN_URL"
336
358
 
337
359
  if [ -z "$SLACK_WEBHOOK_URL" ]; then
@@ -339,7 +361,51 @@ jobs:
339
361
  printf '%b\n' "$text" >> "$GITHUB_STEP_SUMMARY"
340
362
  exit 0
341
363
  fi
342
- jq -n --arg text "$(printf '%b' "$text")" '{text: $text}' \
364
+
365
+ # Block Kit body: title, classification/repo fields, failing tests
366
+ # when known, the diagnosis, and link buttons assembled per outcome —
367
+ # a button never renders without its target.
368
+ jq -n \
369
+ --arg text "$(printf '%b' "$text")" \
370
+ --arg title "$title" \
371
+ --arg body "$body" \
372
+ --arg classification "$label" \
373
+ --arg repo "$repo" \
374
+ --arg failed "$failed_tests" \
375
+ --arg app_change "$APP_CHANGE" \
376
+ --arg pr "$PR_URL" \
377
+ --arg run "$RUN_URL" \
378
+ --arg result "$RESULT_LINK" \
379
+ --arg rerun "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/workflows/checkly-heal.yml" \
380
+ '
381
+ def btn($label; $url): {type: "button", text: {type: "plain_text", text: $label}, url: $url};
382
+ {
383
+ text: $text,
384
+ blocks: (
385
+ [
386
+ {type: "section", text: {type: "mrkdwn", text: $title}},
387
+ {type: "section", fields: ([
388
+ {type: "mrkdwn", text: ("*Classification*\n" + $classification)},
389
+ {type: "mrkdwn", text: ("*Repository*\n" + $repo)}
390
+ ] + (if $failed != "" then
391
+ [{type: "mrkdwn", text: ("*Failed test*\n" + $failed)}]
392
+ else [] end))}
393
+ ]
394
+ + (if $body != "" then
395
+ [{type: "section", text: {type: "mrkdwn", text: $body}}]
396
+ else [] end)
397
+ + (if $app_change == "true" then
398
+ [{type: "context", elements: [{type: "mrkdwn", text: "Includes app-side test hooks — the PR'\''s preview run validates them."}]}]
399
+ else [] end)
400
+ + [{type: "actions", elements: (
401
+ (if $pr != "" then [btn("Review PR"; $pr)] else [] end)
402
+ + [btn("View run"; $run)]
403
+ + (if $result != "" then [btn("Checkly result"; $result)] else [] end)
404
+ + (if $classification == "agent-failed" then [btn("Re-run heal"; $rerun)] else [] end)
405
+ )}]
406
+ + [{type: "context", elements: [{type: "mrkdwn", text: ("[e2e:" + $repo + "]")}]}]
407
+ )
408
+ }' \
343
409
  | curl -sf --max-time 15 -X POST -H "Content-Type: application/json" -d @- "$SLACK_WEBHOOK_URL" \
344
410
  || echo "Slack notification failed — see summary." >> "$GITHUB_STEP_SUMMARY"
345
411