styleproof 4.4.19 → 4.4.21

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,22 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.4.21] - 2026-07-14
11
+
12
+ ### Fixed
13
+
14
+ - **GitHub Actions map publication no longer sends duplicate authorization headers.**
15
+ StyleProof now resets inherited checkout headers before applying the single
16
+ effective credential to isolated map-store clone and push operations.
17
+
18
+ ## [4.4.20] - 2026-07-14
19
+
20
+ ### Fixed
21
+
22
+ - **Map-store network operations can no longer wedge CI indefinitely.** Remote
23
+ lookup, clone, and push commands now have a bounded timeout, and a stalled
24
+ isolated push falls through to the authenticated consumer checkout path.
25
+
10
26
  ## [4.4.19] - 2026-07-14
11
27
 
12
28
  ### Fixed
package/dist/map-store.js CHANGED
@@ -52,6 +52,33 @@ function gitProcessEnvironment() {
52
52
  function runGit(cwd, args, maxBuffer = 1 << 28) {
53
53
  return spawnSync('git', args, { cwd, encoding: 'utf8', maxBuffer, env: gitProcessEnvironment() });
54
54
  }
55
+ const DEFAULT_MAP_STORE_GIT_TIMEOUT_MILLISECONDS = 30_000;
56
+ function mapStoreGitTimeoutMilliseconds() {
57
+ const configuredTimeout = Number(process.env.STYLEPROOF_MAP_STORE_GIT_TIMEOUT_MS);
58
+ return Number.isFinite(configuredTimeout) && configuredTimeout > 0
59
+ ? Math.floor(configuredTimeout)
60
+ : DEFAULT_MAP_STORE_GIT_TIMEOUT_MILLISECONDS;
61
+ }
62
+ function runMapStoreNetworkGit(cwd, args, maxBuffer = 1 << 20) {
63
+ return spawnSync('git', args, {
64
+ cwd,
65
+ encoding: 'utf8',
66
+ maxBuffer,
67
+ timeout: mapStoreGitTimeoutMilliseconds(),
68
+ env: {
69
+ ...gitProcessEnvironment(),
70
+ GIT_TERMINAL_PROMPT: '0',
71
+ },
72
+ });
73
+ }
74
+ function gitFailureMessage(result, fallback) {
75
+ const standardError = typeof result.stderr === 'string' ? result.stderr.trim() : '';
76
+ if (standardError)
77
+ return standardError;
78
+ if (result.error)
79
+ return result.error.message;
80
+ return fallback;
81
+ }
55
82
  const WORKFLOW_TOKEN_CREDENTIAL_HELPER = '!f() { if [ "$1" = get ]; then printf \'%s\\n\' username=x-access-token "password=$STYLEPROOF_MAP_STORE_TOKEN"; fi; }; f';
56
83
  export function workflowTokenCredentialArguments() {
57
84
  return ['-c', 'credential.helper=', '-c', `credential.helper=${WORKFLOW_TOKEN_CREDENTIAL_HELPER}`];
@@ -67,6 +94,17 @@ function parseGitHttpExtraHeaders(configuredHeaders) {
67
94
  return [{ key: configuredHeader.slice(0, separatorIndex), value: configuredHeader.slice(separatorIndex + 1) }];
68
95
  });
69
96
  }
97
+ function resetInheritedGitHttpExtraHeaders(configuredHeaders) {
98
+ const resetHeaderKeys = new Set();
99
+ return configuredHeaders.flatMap((configuredHeader) => {
100
+ if (resetHeaderKeys.has(configuredHeader.key))
101
+ return [configuredHeader];
102
+ resetHeaderKeys.add(configuredHeader.key);
103
+ return configuredHeader.value === ''
104
+ ? [configuredHeader]
105
+ : [{ key: configuredHeader.key, value: '' }, configuredHeader];
106
+ });
107
+ }
70
108
  function effectiveGitHttpExtraHeaders(cwd) {
71
109
  const mapStoreToken = process.env.STYLEPROOF_MAP_STORE_TOKEN;
72
110
  if (mapStoreToken) {
@@ -84,7 +122,7 @@ function effectiveGitHttpExtraHeaders(cwd) {
84
122
  const configuredHeaders = runGit(cwd, ['config', '--includes', '--get-regexp', '^http\\..*\\.extraheader$'], 1 << 20);
85
123
  const effectiveHeaders = parseGitHttpExtraHeaders(configuredHeaders.stdout);
86
124
  if (effectiveHeaders.length > 0)
87
- return effectiveHeaders;
125
+ return resetInheritedGitHttpExtraHeaders(effectiveHeaders);
88
126
  const registeredIncludes = runGit(cwd, ['config', '--local', '--get-regexp', '^includeIf\\..*\\.path$'], 1 << 20);
89
127
  const includedHeaders = registeredIncludes.stdout
90
128
  .split('\n')
@@ -98,7 +136,7 @@ function effectiveGitHttpExtraHeaders(cwd) {
98
136
  return parseGitHttpExtraHeaders(includedHeaders.stdout);
99
137
  });
100
138
  if (includedHeaders.length > 0)
101
- return includedHeaders;
139
+ return resetInheritedGitHttpExtraHeaders(includedHeaders);
102
140
  return [];
103
141
  }
104
142
  function gitOutput(cwd, args) {
@@ -459,19 +497,20 @@ function checkoutMapStore(cwd, remote, branch, sparseSegment) {
459
497
  const httpExtraHeaders = effectiveGitHttpExtraHeaders(cwd);
460
498
  const authenticationArguments = httpExtraHeaders.flatMap(({ key, value }) => ['-c', `${key}=${value}`]);
461
499
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'styleproof-map-store-'));
462
- const branchExists = runGit(cwd, ['ls-remote', '--exit-code', '--heads', remote, branch], 1 << 20).status === 0;
500
+ const branchLookup = runMapStoreNetworkGit(cwd, ['ls-remote', '--exit-code', '--heads', remote, branch], 1 << 20);
501
+ if (branchLookup.status !== 0 && branchLookup.status !== 2) {
502
+ fs.rmSync(tmp, { recursive: true, force: true });
503
+ throw new MapStoreError(gitFailureMessage(branchLookup, 'could not query map store branch'));
504
+ }
505
+ const branchExists = branchLookup.status === 0;
463
506
  if (branchExists) {
464
507
  const cloneArguments = sparseSegment
465
508
  ? ['clone', '-q', '--filter=blob:none', '--no-checkout', '--depth', '1', '--single-branch', '--branch', branch]
466
509
  : ['clone', '-q', '--depth', '1', '--branch', branch];
467
- const clone = spawnSync('git', [...authenticationArguments, ...cloneArguments, remoteUrl, tmp], {
468
- encoding: 'utf8',
469
- maxBuffer: 1 << 20,
470
- env: gitProcessEnvironment(),
471
- });
510
+ const clone = runMapStoreNetworkGit(cwd, [...authenticationArguments, ...cloneArguments, remoteUrl, tmp]);
472
511
  if (clone.status !== 0) {
473
512
  fs.rmSync(tmp, { recursive: true, force: true });
474
- throw new MapStoreError(clone.stderr.trim() || 'could not clone map store branch');
513
+ throw new MapStoreError(gitFailureMessage(clone, 'could not clone map store branch'));
475
514
  }
476
515
  }
477
516
  else {
@@ -479,22 +518,22 @@ function checkoutMapStore(cwd, remote, branch, sparseSegment) {
479
518
  runGit(tmp, ['remote', 'add', 'origin', remoteUrl]);
480
519
  }
481
520
  for (const { key, value } of httpExtraHeaders)
482
- runGit(tmp, ['config', '--local', key, value]);
521
+ runGit(tmp, ['config', '--local', '--add', key, value]);
483
522
  if (sparseSegment && branchExists)
484
523
  checkoutSparseSegment(tmp, branch, sparseSegment);
485
524
  return tmp;
486
525
  }
487
526
  function pushMapStoreCommit(cwd, temporaryCheckout, remote, branch, authenticationArguments) {
488
527
  const pushFailures = [];
489
- const isolatedPush = runGit(temporaryCheckout, [...authenticationArguments, 'push', '-q', 'origin', `HEAD:${branch}`], 1 << 20);
528
+ const isolatedPush = runMapStoreNetworkGit(temporaryCheckout, [...authenticationArguments, 'push', '-q', 'origin', `HEAD:${branch}`], 1 << 20);
490
529
  if (isolatedPush.status === 0)
491
530
  return isolatedPush;
492
- pushFailures.push(`isolated map-store push: ${isolatedPush.stderr.trim() || `git exited ${isolatedPush.status}`}`);
531
+ pushFailures.push(`isolated map-store push: ${gitFailureMessage(isolatedPush, `git exited ${isolatedPush.status}`)}`);
493
532
  const mapStoreToken = process.env.STYLEPROOF_MAP_STORE_TOKEN;
494
533
  if (mapStoreToken) {
495
534
  const githubExtraHeaderKey = ['http.https:', '', 'github.com', '.extraheader'].join('/');
496
535
  runGit(temporaryCheckout, ['config', '--local', '--unset-all', githubExtraHeaderKey], 1 << 20);
497
- const credentialPush = spawnSync('git', [
536
+ const credentialPush = runMapStoreNetworkGit(temporaryCheckout, [
498
537
  '-c',
499
538
  `${githubExtraHeaderKey}=`,
500
539
  ...workflowTokenCredentialArguments(),
@@ -502,18 +541,10 @@ function pushMapStoreCommit(cwd, temporaryCheckout, remote, branch, authenticati
502
541
  '-q',
503
542
  'origin',
504
543
  `HEAD:${branch}`,
505
- ], {
506
- cwd: temporaryCheckout,
507
- encoding: 'utf8',
508
- maxBuffer: 1 << 20,
509
- env: {
510
- ...gitProcessEnvironment(),
511
- GIT_TERMINAL_PROMPT: '0',
512
- },
513
- });
544
+ ]);
514
545
  if (credentialPush.status === 0)
515
546
  return credentialPush;
516
- pushFailures.push(`workflow-token credential push: ${credentialPush.stderr.trim() || `git exited ${credentialPush.status}`}`);
547
+ pushFailures.push(`workflow-token credential push: ${gitFailureMessage(credentialPush, `git exited ${credentialPush.status}`)}`);
517
548
  }
518
549
  const mapStoreCommit = gitOutput(temporaryCheckout, ['rev-parse', 'HEAD']);
519
550
  const importCommit = runGit(cwd, ['fetch', '-q', '--no-write-fetch-head', temporaryCheckout, mapStoreCommit], 1 << 20);
@@ -526,14 +557,14 @@ function pushMapStoreCommit(cwd, temporaryCheckout, remote, branch, authenticati
526
557
  ].join('\n'),
527
558
  };
528
559
  }
529
- const consumerCheckoutPush = runGit(cwd, ['push', '-q', remote, `${mapStoreCommit}:${branch}`], 1 << 20);
560
+ const consumerCheckoutPush = runMapStoreNetworkGit(cwd, ['push', '-q', remote, `${mapStoreCommit}:${branch}`], 1 << 20);
530
561
  if (consumerCheckoutPush.status === 0)
531
562
  return consumerCheckoutPush;
532
563
  return {
533
564
  ...consumerCheckoutPush,
534
565
  stderr: [
535
566
  ...pushFailures,
536
- `consumer checkout push: ${consumerCheckoutPush.stderr.trim() || `git exited ${consumerCheckoutPush.status}`}`,
567
+ `consumer checkout push: ${gitFailureMessage(consumerCheckoutPush, `git exited ${consumerCheckoutPush.status}`)}`,
537
568
  ].join('\n'),
538
569
  };
539
570
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "styleproof",
3
- "version": "4.4.19",
3
+ "version": "4.4.21",
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",