@andwhy/e2e-pipeline 0.14.0 → 0.14.2

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
@@ -120,9 +120,11 @@ const run = async () => {
120
120
  }
121
121
  const createdWorkflow = result.created.some((f) => f.includes(`.github${path.sep}workflows`));
122
122
  if (createdWorkflow) {
123
+ const heading = result.replacedWorkflows.length > 0
124
+ ? "\nGitHub workflows replaced with callers of the pipeline's reusable workflows. They\nuse the repository secrets the old ones used"
125
+ : "\nGitHub workflows created. They need repository secrets before they run";
123
126
  console.log([
124
- "\nGitHub workflows created. They need repository secrets before they run",
125
- "(values in 1Password):",
127
+ `${heading} (values in 1Password):`,
126
128
  " CHECKLY_API_KEY, CHECKLY_ACCOUNT_ID — deploy monitors, auto-heal",
127
129
  " VERCEL_AUTOMATION_BYPASS_SECRET — PR tests through Vercel",
128
130
  " deployment protection",
@@ -4,6 +4,8 @@ interface CopyResult {
4
4
  regenerated: string[];
5
5
  /** Pre-plugin copies of package files deleted from the project (--force). */
6
6
  removed: string[];
7
+ /** Workflows --update-workflows replaced (a subset of created). */
8
+ replacedWorkflows: string[];
7
9
  /** Things the scaffold cannot fix itself and the user must act on. */
8
10
  warnings: string[];
9
11
  }
@@ -1,3 +1,4 @@
1
+ import { execSync } from "node:child_process";
1
2
  import path from "node:path";
2
3
  import fs from "fs-extra";
3
4
  // The agents, the skill and the always-on rules ship as the `e2e-pipeline`
@@ -130,6 +131,8 @@ const writeWorkflows = async (githubSrc, projectRoot, updateWorkflows, result) =
130
131
  await fs.ensureDir(path.dirname(destPath));
131
132
  await fs.copy(srcPath, destPath);
132
133
  result.created.push(destPath);
134
+ if (exists)
135
+ result.replacedWorkflows.push(destPath);
133
136
  }
134
137
  if (stale.length > 0) {
135
138
  result.warnings.push(`Workflows from before 0.14.0: ${stale.join(", ")}. They still work, but changes to them ` +
@@ -254,12 +257,55 @@ export const findSpecsOutsideE2e = async (projectRoot) => {
254
257
  await walk(testsRoot);
255
258
  return [...found].sort();
256
259
  };
260
+ // A repo that ignores `.claude/*` (a common pattern from before the plugin,
261
+ // keeping settings.local.json and session files out of git) silently drops
262
+ // the one file the plugin needs: nobody else gets the plugin entry, and
263
+ // nothing in the diff shows it is missing. Git decides: `check-ignore`
264
+ // applies every rule the repo has, so a negation is appended only when the
265
+ // file really is ignored, at the end of .gitignore where it wins over the
266
+ // rule above it. Outside a git repo there is nothing to do.
267
+ const SETTINGS_REL = ".claude/settings.json";
268
+ const isGitIgnored = (projectRoot, rel) => {
269
+ try {
270
+ execSync(`git check-ignore -q -- ${rel}`, {
271
+ cwd: projectRoot,
272
+ stdio: "ignore",
273
+ });
274
+ return true;
275
+ }
276
+ catch {
277
+ // Exit 1: not ignored. Exit 128: not a git repository. Either way, the
278
+ // file is not being dropped by an ignore rule.
279
+ return false;
280
+ }
281
+ };
282
+ const unignoreClaudeSettings = async (projectRoot, result) => {
283
+ if (!isGitIgnored(projectRoot, SETTINGS_REL))
284
+ return;
285
+ const gitignorePath = path.join(projectRoot, ".gitignore");
286
+ const existing = (await fs.pathExists(gitignorePath))
287
+ ? await fs.readFile(gitignorePath, "utf-8")
288
+ : "";
289
+ const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
290
+ await fs.appendFile(gitignorePath, `${separator}\n# The e2e-pipeline plugin entry is committed (settings.local.json stays ignored)\n!${SETTINGS_REL}\n`);
291
+ if (!result.created.includes(gitignorePath))
292
+ result.created.push(gitignorePath);
293
+ if (isGitIgnored(projectRoot, SETTINGS_REL)) {
294
+ result.warnings.push(`.gitignore still ignores ${SETTINGS_REL} after appending \`!${SETTINGS_REL}\` ` +
295
+ `(a rule ignores the .claude directory itself, which no negation can undo). ` +
296
+ `Change that rule to \`.claude/*\` so the plugin entry can be committed.`);
297
+ return;
298
+ }
299
+ result.warnings.push(`.gitignore ignored ${SETTINGS_REL}, which carries the ${PLUGIN_ID} plugin entry every ` +
300
+ `collaborator needs. Appended \`!${SETTINGS_REL}\`; commit both files.`);
301
+ };
257
302
  export const scaffold = async (templatesRoot, projectRoot, { force = false, fileFormat = "ts", resetConfig = false, updateWorkflows = false, } = {}) => {
258
303
  const result = {
259
304
  created: [],
260
305
  skipped: [],
261
306
  regenerated: [],
262
307
  removed: [],
308
+ replacedWorkflows: [],
263
309
  warnings: [],
264
310
  };
265
311
  const configExt = fileFormat === "mjs" ? "mjs" : "ts";
@@ -308,7 +354,9 @@ export const scaffold = async (templatesRoot, projectRoot, { force = false, file
308
354
  const srcPath = path.join(sharedSrc, src);
309
355
  const destPath = path.join(projectRoot, dest);
310
356
  const exists = await fs.pathExists(destPath);
311
- if (exists && resetConfig && src.endsWith(`.config.template.${configExt}`)) {
357
+ if (exists &&
358
+ resetConfig &&
359
+ src.endsWith(`.config.template.${configExt}`)) {
312
360
  // checkly.config.new.ts, not checkly.config.ts.new — keeping the real
313
361
  // extension means prettier can format it and the editor highlights it,
314
362
  // so the diff against your config shows content rather than style.
@@ -332,5 +380,6 @@ export const scaffold = async (templatesRoot, projectRoot, { force = false, file
332
380
  // walks the user through adding them.
333
381
  await writeWorkflows(path.join(templatesRoot, "github"), projectRoot, updateWorkflows, result);
334
382
  await appendGitignore(projectRoot, result);
383
+ await unignoreClaudeSettings(projectRoot, result);
335
384
  return result;
336
385
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andwhy/e2e-pipeline",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
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",
@@ -23,7 +23,8 @@
23
23
  "build": "tsc",
24
24
  "build:plugin": "node scripts/build-plugin.mjs",
25
25
  "check:plugin": "node scripts/build-plugin.mjs --check",
26
- "prepublishOnly": "npm run build && npm run check:plugin"
26
+ "prepublishOnly": "npm run build && npm run check:plugin",
27
+ "skill:zip": "node scripts/zip-skill.mjs"
27
28
  },
28
29
  "dependencies": {
29
30
  "fs-extra": "^11.3.0",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "e2e-pipeline",
3
3
  "displayName": "&why e2e pipeline",
4
- "version": "0.14.0",
4
+ "version": "0.14.2",
5
5
  "description": "Agent-assisted Playwright e2e tests: planner, generator and healer agents plus the playwright-test-pipeline skill that drives Plan → Generate → Heal, Checkly preview validation and deployment.",
6
6
  "author": {
7
7
  "name": "&why",
@@ -6,6 +6,8 @@ description: Guides the full workflow for creating and maintaining Playwright
6
6
  deployment. Use when the user wants to create, extend, or fix e2e/Playwright
7
7
  tests, asks to "test" a page/feature/flow, or asks how the test pipeline
8
8
  works.
9
+ metadata:
10
+ version: "0.14.2"
9
11
  ---
10
12
 
11
13
  # Playwright Test Pipeline
@@ -500,7 +502,7 @@ Set up once per project:
500
502
  6. **One Slack Workflow Builder rule** routes this project's messages from
501
503
  the shared e2e channel to the project channel. All notifications post to
502
504
  the e2e channel tagged `[e2e:<repo-name>]`; the rule matches the **full
503
- bracketed tag** — never a short name like `gbk`, which collides with
505
+ bracketed tag** — never a short name like `shop`, which collides with
504
506
  other text — and sends the message to the project channel. The sent
505
507
  message's template is just the message-text variable (plus the
506
508
  link-to-original variable when available, so threads happen on the