styleproof 4.4.15 → 4.4.17

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 CHANGED
@@ -7,6 +7,27 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.4.17] - 2026-07-13
11
+
12
+ ### Fixed
13
+
14
+ - **Generated cache-first CI now preserves the installed StyleProof release across
15
+ base checkouts.** Cold-cache capture installs the head's exact release into an
16
+ older base checkout and invokes the installed binary directly, preventing package
17
+ manager reconciliation from silently running the base's older capture logic.
18
+ - **Generated workflows now reuse checkout authentication for map publication.**
19
+ The redundant map-store token environment variable is omitted; the persisted
20
+ checkout credential supplies the existing least-privilege `contents: write` path.
21
+
22
+ ## [4.4.16] - 2026-07-13
23
+
24
+ ### Fixed
25
+
26
+ - **Map-store publication now retains failures that occur while preparing a retry.**
27
+ If an authenticated push fails and the next clone also fails, StyleProof reports
28
+ both errors with their attempt and phase instead of replacing the actionable push
29
+ failure with the later setup error.
30
+
10
31
  ## [4.4.15] - 2026-07-13
11
32
 
12
33
  ### Fixed
@@ -221,6 +221,7 @@ const PACKAGE_MANAGERS = {
221
221
  run: (script) => `npm run ${script}`,
222
222
  exec: (command) => `npx ${command}`,
223
223
  install: 'npm ci',
224
+ installExactStyleProof: 'npm install --no-save --package-lock=false "styleproof@$STYLEPROOF_VERSION"',
224
225
  setup: ` - uses: actions/setup-node@v4
225
226
  with:
226
227
  node-version: '20'
@@ -231,6 +232,7 @@ const PACKAGE_MANAGERS = {
231
232
  run: (script) => `npx -y yarn@1.22.22 ${script}`,
232
233
  exec: (command) => `npx -y yarn@1.22.22 ${command}`,
233
234
  install: 'npx -y yarn@1.22.22 install --frozen-lockfile --non-interactive',
235
+ installExactStyleProof: 'npx -y yarn@1.22.22 add --dev --exact "styleproof@$STYLEPROOF_VERSION"',
234
236
  setup: ` - uses: actions/setup-node@v4
235
237
  with:
236
238
  node-version: '20'
@@ -242,6 +244,7 @@ const PACKAGE_MANAGERS = {
242
244
  run: (script) => `pnpm run ${script}`,
243
245
  exec: (command) => `pnpm exec ${command}`,
244
246
  install: 'pnpm install --frozen-lockfile',
247
+ installExactStyleProof: 'pnpm add --save-dev --save-exact "styleproof@$STYLEPROOF_VERSION"',
245
248
  setup: ` - uses: actions/setup-node@v4
246
249
  with:
247
250
  node-version: '20'
@@ -254,6 +257,7 @@ const PACKAGE_MANAGERS = {
254
257
  run: (script) => `bun run ${script}`,
255
258
  exec: (command) => `bunx ${command}`,
256
259
  install: 'bun install --frozen-lockfile',
260
+ installExactStyleProof: 'bun add --dev --exact "styleproof@$STYLEPROOF_VERSION"',
257
261
  setup: ` - uses: actions/setup-node@v4
258
262
  with:
259
263
  node-version: '20'
@@ -388,8 +392,6 @@ jobs:
388
392
  # Report on open/update; the prune job below handles close.
389
393
  if: github.event.action != 'closed'
390
394
  runs-on: ubuntu-latest
391
- env:
392
- STYLEPROOF_MAP_STORE_TOKEN: \${{ github.token }}
393
395
  steps:
394
396
  - uses: actions/checkout@v4
395
397
  with:
@@ -405,9 +407,9 @@ ${PM.setup}
405
407
  MAP_ROOT="\${{ runner.temp }}/styleproof-maps"
406
408
  rm -rf "$MAP_ROOT"
407
409
  set +e
408
- ${PM.exec(`styleproof-map --restore --sha "$BASE_SHA" --dir base --base-dir "$MAP_ROOT" --spec ${specPath}`)}
410
+ PATH="$PWD/node_modules/.bin:$PATH" node node_modules/styleproof/bin/styleproof-map.mjs --restore --sha "$BASE_SHA" --dir base --base-dir "$MAP_ROOT" --spec ${specPath}
409
411
  base_code=$?
410
- ${PM.exec(`styleproof-map --restore --sha "$HEAD_SHA" --dir head --base-dir "$MAP_ROOT" --spec ${specPath}`)}
412
+ PATH="$PWD/node_modules/.bin:$PATH" node node_modules/styleproof/bin/styleproof-map.mjs --restore --sha "$HEAD_SHA" --dir head --base-dir "$MAP_ROOT" --spec ${specPath}
411
413
  head_code=$?
412
414
  set -e
413
415
  echo "base-hit=$([ "$base_code" -eq 0 ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
@@ -421,37 +423,43 @@ ${PM.setup}
421
423
  if: steps.maps.outputs.capture-needed == 'true'
422
424
  shell: bash
423
425
  run: |
426
+ set -euo pipefail
424
427
  BASE_SHA="\${{ github.event.pull_request.base.sha }}"
425
428
  HEAD_SHA="\${{ github.event.pull_request.head.sha }}"
426
429
  MAP_ROOT="\${{ runner.temp }}/styleproof-maps"
430
+ STYLEPROOF_VERSION="$(node -p "require('./node_modules/styleproof/package.json').version")"
427
431
 
428
432
  if [ "\${{ steps.maps.outputs.base-hit }}" != 'true' ]; then
429
433
  # Without a compatible base bundle, rebuild and publish the pair in
430
434
  # one pinned environment. This is the expensive cold path.
431
435
  rm -rf "$MAP_ROOT"
432
- git checkout "$BASE_SHA"
436
+ git checkout --force "$BASE_SHA"
433
437
  ${PM.install}
434
- ${PM.exec('playwright install --with-deps chromium')}
438
+ # The base may depend on an older StyleProof. Install the head's
439
+ # exact release, then invoke its binary directly so a later package
440
+ # manager command cannot silently reconcile node_modules backwards.
441
+ ${PM.installExactStyleProof}
442
+ PATH="$PWD/node_modules/.bin:$PATH" playwright install --with-deps chromium
435
443
  if [ -f "${specPath}" ]; then
436
- ${PM.exec(`styleproof-map --spec ${specPath} --dir base --base-dir "$MAP_ROOT" --keep-har --sha "$BASE_SHA" --upload`)}
444
+ PATH="$PWD/node_modules/.bin:$PATH" node node_modules/styleproof/bin/styleproof-map.mjs --spec ${specPath} --dir base --base-dir "$MAP_ROOT" --keep-har --sha "$BASE_SHA" --upload
437
445
  else
438
446
  mkdir -p "$MAP_ROOT/base"
439
447
  fi
440
448
 
441
- git checkout "$HEAD_SHA"
449
+ git checkout --force "$HEAD_SHA"
442
450
  ${PM.install}
443
- ${PM.exec('playwright install --with-deps chromium')}
451
+ PATH="$PWD/node_modules/.bin:$PATH" playwright install --with-deps chromium
444
452
  else
445
453
  # A compatible base hit proves the current head environment. Keep
446
454
  # that restored base and capture only the missing head.
447
455
  rm -rf "$MAP_ROOT/head"
448
- ${PM.exec('playwright install --with-deps chromium')}
456
+ PATH="$PWD/node_modules/.bin:$PATH" playwright install --with-deps chromium
449
457
  fi
450
458
 
451
459
  if find "$MAP_ROOT/base" -name '*.har' -print -quit | grep -q .; then
452
- STYLEPROOF_REPLAY_FROM="$MAP_ROOT/base" ${PM.exec(`styleproof-map --spec ${specPath} --dir head --base-dir "$MAP_ROOT" --sha "$HEAD_SHA" --upload`)}
460
+ PATH="$PWD/node_modules/.bin:$PATH" STYLEPROOF_REPLAY_FROM="$MAP_ROOT/base" node node_modules/styleproof/bin/styleproof-map.mjs --spec ${specPath} --dir head --base-dir "$MAP_ROOT" --sha "$HEAD_SHA" --upload
453
461
  else
454
- ${PM.exec(`styleproof-map --spec ${specPath} --dir head --base-dir "$MAP_ROOT" --sha "$HEAD_SHA" --upload`)}
462
+ PATH="$PWD/node_modules/.bin:$PATH" node node_modules/styleproof/bin/styleproof-map.mjs --spec ${specPath} --dir head --base-dir "$MAP_ROOT" --sha "$HEAD_SHA" --upload
455
463
  fi
456
464
  - uses: BenSheridanEdwards/StyleProof@v4
457
465
  with:
package/dist/map-store.js CHANGED
@@ -537,6 +537,37 @@ function pushMapStoreCommit(cwd, temporaryCheckout, remote, branch, authenticati
537
537
  ].join('\n'),
538
538
  };
539
539
  }
540
+ function removeTemporaryMapStoreCheckout(temporaryCheckout) {
541
+ if (temporaryCheckout)
542
+ fs.rmSync(temporaryCheckout, { recursive: true, force: true });
543
+ }
544
+ function errorMessage(error) {
545
+ return error instanceof Error ? error.message : String(error);
546
+ }
547
+ function publishMapStoreAttempt(options) {
548
+ let temporaryCheckout;
549
+ try {
550
+ temporaryCheckout = checkoutMapStore(options.cwd, options.remote, options.branch, options.sha);
551
+ runGit(temporaryCheckout, ['config', 'user.name', 'github-actions[bot]']);
552
+ runGit(temporaryCheckout, ['config', 'user.email', '41898282+github-actions[bot]@users.noreply.github.com']);
553
+ fs.writeFileSync(path.join(temporaryCheckout, 'README.md'), '# StyleProof maps\n\nMachine-generated reusable map bundles. Each folder is keyed by commit SHA and capture compatibility.\n');
554
+ fs.rmSync(path.join(temporaryCheckout, options.target), { recursive: true, force: true });
555
+ copyDir(options.dir, path.join(temporaryCheckout, options.target), options.includeHar);
556
+ if (!options.includeHar) {
557
+ fs.writeFileSync(path.join(temporaryCheckout, options.target, MAP_MANIFEST), JSON.stringify({ ...options.manifest, har: false }, null, 2));
558
+ }
559
+ runGit(temporaryCheckout, ['add', '-A', '--sparse', '--', 'README.md', options.target]);
560
+ runGit(temporaryCheckout, ['commit', '-q', '-m', `StyleProof map ${options.sha.slice(0, 12)} ${options.compatibilityKey}`], 1 << 20);
561
+ const push = pushMapStoreCommit(options.cwd, temporaryCheckout, options.remote, options.branch, options.authenticationArguments);
562
+ return { ok: push.status === 0, phase: 'publish', error: push.stderr.trim() || `git exited ${push.status}` };
563
+ }
564
+ catch (error) {
565
+ return { ok: false, phase: 'setup', error: errorMessage(error) };
566
+ }
567
+ finally {
568
+ removeTemporaryMapStoreCheckout(temporaryCheckout);
569
+ }
570
+ }
540
571
  export function publishMapBundle(options) {
541
572
  const cwd = options.cwd ?? process.cwd();
542
573
  const branch = options.branch ?? DEFAULT_MAP_STORE_BRANCH;
@@ -558,29 +589,26 @@ export function publishMapBundle(options) {
558
589
  ]);
559
590
  let ok = false;
560
591
  let lastError = '';
592
+ const attemptFailures = [];
561
593
  for (let attempt = 1; attempt <= 5; attempt++) {
562
- const tmp = checkoutMapStore(cwd, remote, branch, sha);
563
- try {
564
- runGit(tmp, ['config', 'user.name', 'github-actions[bot]']);
565
- runGit(tmp, ['config', 'user.email', '41898282+github-actions[bot]@users.noreply.github.com']);
566
- fs.writeFileSync(path.join(tmp, 'README.md'), '# StyleProof maps\n\nMachine-generated reusable map bundles. Each folder is keyed by commit SHA and capture compatibility.\n');
567
- fs.rmSync(path.join(tmp, target), { recursive: true, force: true });
568
- copyDir(options.dir, path.join(tmp, target), options.includeHar === true);
569
- if (!options.includeHar) {
570
- fs.writeFileSync(path.join(tmp, target, MAP_MANIFEST), JSON.stringify({ ...manifest, har: false }, null, 2));
571
- }
572
- runGit(tmp, ['add', '-A', '--sparse', '--', 'README.md', target]);
573
- runGit(tmp, ['commit', '-q', '-m', `StyleProof map ${sha.slice(0, 12)} ${compatibilityKey}`], 1 << 20);
574
- const push = pushMapStoreCommit(cwd, tmp, remote, branch, pushAuthenticationArguments);
575
- if (push.status === 0) {
576
- ok = true;
577
- break;
578
- }
579
- lastError = push.stderr.trim();
580
- }
581
- finally {
582
- fs.rmSync(tmp, { recursive: true, force: true });
594
+ const result = publishMapStoreAttempt({
595
+ cwd,
596
+ remote,
597
+ branch,
598
+ sha,
599
+ compatibilityKey,
600
+ target,
601
+ dir: options.dir,
602
+ includeHar: options.includeHar === true,
603
+ manifest,
604
+ authenticationArguments: pushAuthenticationArguments,
605
+ });
606
+ if (result.ok) {
607
+ ok = true;
608
+ break;
583
609
  }
610
+ attemptFailures.push(`attempt ${attempt} ${result.phase}:\n${result.error}`);
611
+ lastError = attemptFailures.join('\n');
584
612
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, attempt * 250);
585
613
  }
586
614
  if (!ok)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "styleproof",
3
- "version": "4.4.15",
3
+ "version": "4.4.17",
4
4
  "description": "Catch every CSS change before it ships — review PRs and certify refactors by the browser's computed styles, not pixels. Works with any styling system.",
5
5
  "keywords": [
6
6
  "playwright",