code-foundry 1.53.1 → 1.53.3
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/.github/workflows/ci.yml +8 -8
- package/.github/workflows/eval.yml +2 -2
- package/.github/workflows/release_self-ci.yml +60 -6
- package/.github/workflows/test.yml +13 -12
- package/.github/workflows/validation-billing.yml +11 -10
- package/CHANGELOG.md +20 -15
- package/docs/CACHING.md +9 -1
- package/docs/CONFIGURATION.md +1 -1
- package/docs/qualified-publication.md +9 -6
- package/package.json +3 -1
- package/src/commands/sync.mjs +24 -9
- package/.github/workflows/qualified-foundry-publish.yml +0 -130
package/.github/workflows/ci.yml
CHANGED
|
@@ -129,8 +129,8 @@ jobs:
|
|
|
129
129
|
if-no-files-found: error
|
|
130
130
|
retention-days: 14
|
|
131
131
|
|
|
132
|
-
- name: Save
|
|
133
|
-
uses: ./.github/actions/
|
|
132
|
+
- name: Save task caches
|
|
133
|
+
uses: ./.github/actions/cache-save
|
|
134
134
|
|
|
135
135
|
lint:
|
|
136
136
|
name: Lint
|
|
@@ -193,8 +193,8 @@ jobs:
|
|
|
193
193
|
if-no-files-found: error
|
|
194
194
|
retention-days: 14
|
|
195
195
|
|
|
196
|
-
- name: Save
|
|
197
|
-
uses: ./.github/actions/
|
|
196
|
+
- name: Save task caches
|
|
197
|
+
uses: ./.github/actions/cache-save
|
|
198
198
|
|
|
199
199
|
type-check:
|
|
200
200
|
name: Type-Check
|
|
@@ -262,8 +262,8 @@ jobs:
|
|
|
262
262
|
if-no-files-found: error
|
|
263
263
|
retention-days: 14
|
|
264
264
|
|
|
265
|
-
- name: Save
|
|
266
|
-
uses: ./.github/actions/
|
|
265
|
+
- name: Save task caches
|
|
266
|
+
uses: ./.github/actions/cache-save
|
|
267
267
|
|
|
268
268
|
build:
|
|
269
269
|
name: Build
|
|
@@ -357,5 +357,5 @@ jobs:
|
|
|
357
357
|
if-no-files-found: error
|
|
358
358
|
retention-days: 14
|
|
359
359
|
|
|
360
|
-
- name: Save
|
|
361
|
-
uses: ./.github/actions/
|
|
360
|
+
- name: Save task caches
|
|
361
|
+
uses: ./.github/actions/cache-save
|
|
@@ -10,6 +10,11 @@ on:
|
|
|
10
10
|
required: false
|
|
11
11
|
type: boolean
|
|
12
12
|
default: false
|
|
13
|
+
recover-npm-tag:
|
|
14
|
+
description: Requalify and publish the exact archive for a current immutable release missing from npm.
|
|
15
|
+
required: false
|
|
16
|
+
type: string
|
|
17
|
+
default: ''
|
|
13
18
|
|
|
14
19
|
permissions:
|
|
15
20
|
contents: read
|
|
@@ -28,7 +33,7 @@ jobs:
|
|
|
28
33
|
# content they merge was already audited on its pull request, and the
|
|
29
34
|
# release merge re-qualifies the exact tree it publishes.
|
|
30
35
|
needs: [release, recovery]
|
|
31
|
-
if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && (needs.release.outputs.release_created == 'true' || needs.recovery.outputs.found == 'true')
|
|
36
|
+
if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && !(github.event_name == 'workflow_dispatch' && inputs['recover-npm-tag'] != '') && (needs.release.outputs.release_created == 'true' || needs.recovery.outputs.found == 'true')
|
|
32
37
|
uses: ./.github/workflows/consumer-qualification.yml
|
|
33
38
|
with:
|
|
34
39
|
billing-pause-bypass: ${{ github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true }}
|
|
@@ -38,7 +43,7 @@ jobs:
|
|
|
38
43
|
|
|
39
44
|
preflight:
|
|
40
45
|
name: Verify release prerequisites
|
|
41
|
-
if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true))
|
|
46
|
+
if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && !(github.event_name == 'workflow_dispatch' && inputs['recover-npm-tag'] != '')
|
|
42
47
|
runs-on: ubuntu-24.04
|
|
43
48
|
timeout-minutes: 5
|
|
44
49
|
steps:
|
|
@@ -76,7 +81,7 @@ jobs:
|
|
|
76
81
|
# it. Version pull requests keep flowing even when the candidate is broken;
|
|
77
82
|
# publication stays gated on qualification success in the stage job.
|
|
78
83
|
needs: [preflight]
|
|
79
|
-
if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true))
|
|
84
|
+
if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && !(github.event_name == 'workflow_dispatch' && inputs['recover-npm-tag'] != '')
|
|
80
85
|
uses: ./.github/workflows/release.yml
|
|
81
86
|
with:
|
|
82
87
|
billing-pause-bypass: ${{ github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true }}
|
|
@@ -97,7 +102,7 @@ jobs:
|
|
|
97
102
|
recovery:
|
|
98
103
|
name: Recover qualified draft identity
|
|
99
104
|
needs: [release]
|
|
100
|
-
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && needs.release.result == 'success'
|
|
105
|
+
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && !(github.event_name == 'workflow_dispatch' && inputs['recover-npm-tag'] != '') && needs.release.result == 'success'
|
|
101
106
|
runs-on: ubuntu-24.04
|
|
102
107
|
timeout-minutes: 5
|
|
103
108
|
permissions:
|
|
@@ -170,7 +175,7 @@ jobs:
|
|
|
170
175
|
# Qualification only runs when this push created a release or recovery
|
|
171
176
|
# found a stuck one; staging additionally requires its success so an
|
|
172
177
|
# unqualified tree can never reach publication.
|
|
173
|
-
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && github.ref == 'refs/heads/main' && needs.qualification.result == 'success' && (needs.release.outputs.release_created == 'true' || needs.recovery.outputs.found == 'true')
|
|
178
|
+
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && github.ref == 'refs/heads/main' && !(github.event_name == 'workflow_dispatch' && inputs['recover-npm-tag'] != '') && needs.qualification.result == 'success' && (needs.release.outputs.release_created == 'true' || needs.recovery.outputs.found == 'true')
|
|
174
179
|
runs-on: ubuntu-24.04
|
|
175
180
|
timeout-minutes: 15
|
|
176
181
|
permissions:
|
|
@@ -244,7 +249,7 @@ jobs:
|
|
|
244
249
|
publish:
|
|
245
250
|
name: Publish verified package
|
|
246
251
|
needs: [qualification, release, recovery, stage]
|
|
247
|
-
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && github.ref == 'refs/heads/main' && needs.stage.result == 'success' && (needs.release.outputs.release_created == 'true' || needs.recovery.outputs.found == 'true')
|
|
252
|
+
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && github.ref == 'refs/heads/main' && !(github.event_name == 'workflow_dispatch' && inputs['recover-npm-tag'] != '') && needs.stage.result == 'success' && (needs.release.outputs.release_created == 'true' || needs.recovery.outputs.found == 'true')
|
|
248
253
|
uses: ./.github/workflows/qualified-foundry-publish.yml
|
|
249
254
|
permissions:
|
|
250
255
|
actions: read
|
|
@@ -255,3 +260,52 @@ jobs:
|
|
|
255
260
|
tag: ${{ needs.release.outputs.tag_name || needs.recovery.outputs.tag }}
|
|
256
261
|
asset: ${{ needs.qualification.outputs.filename }}
|
|
257
262
|
billing-pause-bypass: ${{ github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true }}
|
|
263
|
+
|
|
264
|
+
npm_recovery_target:
|
|
265
|
+
name: Verify npm recovery target
|
|
266
|
+
if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' && inputs['recover-npm-tag'] != '' && vars.CI_BILLING_PAUSED != 'true'
|
|
267
|
+
runs-on: ubuntu-24.04
|
|
268
|
+
timeout-minutes: 5
|
|
269
|
+
permissions:
|
|
270
|
+
contents: read
|
|
271
|
+
outputs:
|
|
272
|
+
tag: ${{ steps.target.outputs.tag }}
|
|
273
|
+
asset: ${{ steps.target.outputs.asset }}
|
|
274
|
+
source-sha: ${{ steps.target.outputs.source-sha }}
|
|
275
|
+
steps:
|
|
276
|
+
- name: Checkout trusted recovery verifier
|
|
277
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
278
|
+
with:
|
|
279
|
+
persist-credentials: false
|
|
280
|
+
fetch-depth: 0
|
|
281
|
+
ref: ${{ github.sha }}
|
|
282
|
+
- name: Setup Node
|
|
283
|
+
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
284
|
+
with:
|
|
285
|
+
node-version: '24'
|
|
286
|
+
- name: Require exact immutable release and absent npm version
|
|
287
|
+
id: target
|
|
288
|
+
env:
|
|
289
|
+
GH_TOKEN: ${{ github.token }}
|
|
290
|
+
RELEASE_TAG: ${{ inputs['recover-npm-tag'] }}
|
|
291
|
+
CURRENT_SHA: ${{ github.sha }}
|
|
292
|
+
run: |
|
|
293
|
+
set -euo pipefail
|
|
294
|
+
node src/commands/release-recovery.mjs verify-target \
|
|
295
|
+
"$GITHUB_REPOSITORY" "$RELEASE_TAG" "$CURRENT_SHA" "$GITHUB_OUTPUT"
|
|
296
|
+
|
|
297
|
+
npm_recovery_publish:
|
|
298
|
+
name: Requalify and publish recovered archive
|
|
299
|
+
needs: npm_recovery_target
|
|
300
|
+
if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' && needs.npm_recovery_target.result == 'success' && vars.CI_BILLING_PAUSED != 'true'
|
|
301
|
+
uses: ./.github/workflows/qualified-foundry-publish.yml
|
|
302
|
+
permissions:
|
|
303
|
+
actions: read
|
|
304
|
+
attestations: read
|
|
305
|
+
contents: read
|
|
306
|
+
id-token: write
|
|
307
|
+
with:
|
|
308
|
+
tag: ${{ needs.npm_recovery_target.outputs.tag }}
|
|
309
|
+
asset: ${{ needs.npm_recovery_target.outputs.asset }}
|
|
310
|
+
source-sha: ${{ needs.npm_recovery_target.outputs['source-sha'] }}
|
|
311
|
+
billing-pause-bypass: false
|
|
@@ -166,8 +166,8 @@ jobs:
|
|
|
166
166
|
if-no-files-found: ignore
|
|
167
167
|
retention-days: 14
|
|
168
168
|
|
|
169
|
-
- name: Save
|
|
170
|
-
uses: ./.github/actions/
|
|
169
|
+
- name: Save task caches
|
|
170
|
+
uses: ./.github/actions/cache-save
|
|
171
171
|
|
|
172
172
|
performance:
|
|
173
173
|
name: Performance
|
|
@@ -255,8 +255,8 @@ jobs:
|
|
|
255
255
|
if-no-files-found: ignore
|
|
256
256
|
retention-days: 14
|
|
257
257
|
|
|
258
|
-
- name: Save
|
|
259
|
-
uses: ./.github/actions/
|
|
258
|
+
- name: Save task caches
|
|
259
|
+
uses: ./.github/actions/cache-save
|
|
260
260
|
|
|
261
261
|
integration:
|
|
262
262
|
name: Integration
|
|
@@ -332,8 +332,8 @@ jobs:
|
|
|
332
332
|
if-no-files-found: error
|
|
333
333
|
retention-days: 14
|
|
334
334
|
|
|
335
|
-
- name: Save
|
|
336
|
-
uses: ./.github/actions/
|
|
335
|
+
- name: Save task caches
|
|
336
|
+
uses: ./.github/actions/cache-save
|
|
337
337
|
|
|
338
338
|
integration-sharded:
|
|
339
339
|
name: Integration shard
|
|
@@ -412,8 +412,8 @@ jobs:
|
|
|
412
412
|
if-no-files-found: error
|
|
413
413
|
retention-days: 14
|
|
414
414
|
|
|
415
|
-
- name: Save
|
|
416
|
-
uses: ./.github/actions/
|
|
415
|
+
- name: Save task caches
|
|
416
|
+
uses: ./.github/actions/cache-save
|
|
417
417
|
|
|
418
418
|
e2e:
|
|
419
419
|
name: E2E
|
|
@@ -464,6 +464,7 @@ jobs:
|
|
|
464
464
|
if: steps.applicability.outputs.applicable == 'true' && steps.applicability.outputs.affected != 'false' && steps.applicability.outputs.browser == 'true'
|
|
465
465
|
uses: ./.github/actions/cache
|
|
466
466
|
with:
|
|
467
|
+
id: e2e-browsers
|
|
467
468
|
save: ${{ github.event_name == 'push' }}
|
|
468
469
|
path: |
|
|
469
470
|
~/.cache/ms-playwright
|
|
@@ -508,8 +509,8 @@ jobs:
|
|
|
508
509
|
if-no-files-found: error
|
|
509
510
|
retention-days: 14
|
|
510
511
|
|
|
511
|
-
- name: Save
|
|
512
|
-
uses: ./.github/actions/
|
|
512
|
+
- name: Save task caches
|
|
513
|
+
uses: ./.github/actions/cache-save
|
|
513
514
|
|
|
514
515
|
smoke:
|
|
515
516
|
name: Smoke
|
|
@@ -581,5 +582,5 @@ jobs:
|
|
|
581
582
|
if-no-files-found: error
|
|
582
583
|
retention-days: 14
|
|
583
584
|
|
|
584
|
-
- name: Save
|
|
585
|
-
uses: ./.github/actions/
|
|
585
|
+
- name: Save task caches
|
|
586
|
+
uses: ./.github/actions/cache-save
|
|
@@ -543,8 +543,8 @@ jobs:
|
|
|
543
543
|
if-no-files-found: error
|
|
544
544
|
retention-days: 14
|
|
545
545
|
|
|
546
|
-
- name: Save
|
|
547
|
-
uses: ./.github/actions/
|
|
546
|
+
- name: Save task caches
|
|
547
|
+
uses: ./.github/actions/cache-save
|
|
548
548
|
|
|
549
549
|
lint:
|
|
550
550
|
name: Lint
|
|
@@ -614,8 +614,8 @@ jobs:
|
|
|
614
614
|
if-no-files-found: error
|
|
615
615
|
retention-days: 14
|
|
616
616
|
|
|
617
|
-
- name: Save
|
|
618
|
-
uses: ./.github/actions/
|
|
617
|
+
- name: Save task caches
|
|
618
|
+
uses: ./.github/actions/cache-save
|
|
619
619
|
|
|
620
620
|
type-check:
|
|
621
621
|
name: Type-Check
|
|
@@ -686,8 +686,8 @@ jobs:
|
|
|
686
686
|
if-no-files-found: error
|
|
687
687
|
retention-days: 14
|
|
688
688
|
|
|
689
|
-
- name: Save
|
|
690
|
-
uses: ./.github/actions/
|
|
689
|
+
- name: Save task caches
|
|
690
|
+
uses: ./.github/actions/cache-save
|
|
691
691
|
|
|
692
692
|
unit:
|
|
693
693
|
name: Unit
|
|
@@ -782,8 +782,8 @@ jobs:
|
|
|
782
782
|
if-no-files-found: ignore
|
|
783
783
|
retention-days: 14
|
|
784
784
|
|
|
785
|
-
- name: Save
|
|
786
|
-
uses: ./.github/actions/
|
|
785
|
+
- name: Save task caches
|
|
786
|
+
uses: ./.github/actions/cache-save
|
|
787
787
|
|
|
788
788
|
e2e:
|
|
789
789
|
name: E2E
|
|
@@ -838,6 +838,7 @@ jobs:
|
|
|
838
838
|
if: steps.applicability.outputs.applicable == 'true' && steps.applicability.outputs.affected != 'false' && steps.applicability.outputs.browser == 'true'
|
|
839
839
|
uses: ./.github/actions/cache
|
|
840
840
|
with:
|
|
841
|
+
id: e2e-browsers
|
|
841
842
|
save: ${{ github.event_name == 'push' }}
|
|
842
843
|
path: |
|
|
843
844
|
~/.cache/ms-playwright
|
|
@@ -882,8 +883,8 @@ jobs:
|
|
|
882
883
|
if-no-files-found: error
|
|
883
884
|
retention-days: 14
|
|
884
885
|
|
|
885
|
-
- name: Save
|
|
886
|
-
uses: ./.github/actions/
|
|
886
|
+
- name: Save task caches
|
|
887
|
+
uses: ./.github/actions/cache-save
|
|
887
888
|
|
|
888
889
|
# Profile and dependency audit merged into one job. The split orchestrators
|
|
889
890
|
# run a profile job that feeds an audit matrix; here the language blocks run
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.53.3](https://github.com/0xPlayerOne/code-foundry/compare/v1.53.2...v1.53.3) (2026-10-30)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* save repository caches through an explicit save action ([#819](https://github.com/0xPlayerOne/code-foundry/issues/819)) ([a21f1d0](https://github.com/0xPlayerOne/code-foundry/commit/a21f1d0c153e14252bc784f301379029026121da))
|
|
9
|
+
|
|
10
|
+
## [1.53.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.53.1...v1.53.2) (2026-10-30)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* support reviewed Playwright 1.64.1 UI receipts ([#817](https://github.com/0xPlayerOne/code-foundry/issues/817)) ([814bf68](https://github.com/0xPlayerOne/code-foundry/commit/814bf68818dcf2a80253e133895cbe3e539b5b7f))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Maintenance
|
|
19
|
+
|
|
20
|
+
* add verified npm publication recovery ([#815](https://github.com/0xPlayerOne/code-foundry/issues/815)) ([5876361](https://github.com/0xPlayerOne/code-foundry/commit/58763616b3dc38d127680914eeef6151b3375b37))
|
|
21
|
+
|
|
3
22
|
## [1.53.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.53.0...v1.53.1) (2026-10-30)
|
|
4
23
|
|
|
5
24
|
|
|
@@ -131,23 +150,9 @@
|
|
|
131
150
|
|
|
132
151
|
* **release:** sync bun.lock workspace versions on generated release PRs ([#777](https://github.com/0xPlayerOne/code-foundry/issues/777)) ([b24bfae](https://github.com/0xPlayerOne/code-foundry/commit/b24bfae309ac7003fcc4cd57f79ddc418c27b039))
|
|
133
152
|
|
|
134
|
-
## [1.48.6](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.5...v1.48.6) (2026-10-23)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
### Bug Fixes
|
|
138
|
-
|
|
139
|
-
* **release:** audit generated release content for stale snapshots ([#775](https://github.com/0xPlayerOne/code-foundry/issues/775)) ([f842c27](https://github.com/0xPlayerOne/code-foundry/commit/f842c271e6b270b63a6d9b839795a849237c7a7d))
|
|
140
|
-
|
|
141
|
-
## [1.48.5](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.4...v1.48.5) (2026-10-22)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
### Bug Fixes
|
|
145
|
-
|
|
146
|
-
* **ci:** serialize billing receipt and report uploads ([#773](https://github.com/0xPlayerOne/code-foundry/issues/773)) ([6497225](https://github.com/0xPlayerOne/code-foundry/commit/6497225bf2d672380b6b5231378b424dffb8afbb))
|
|
147
|
-
|
|
148
153
|
<!-- code-foundry: packaged changelog -->
|
|
149
154
|
|
|
150
155
|
---
|
|
151
156
|
|
|
152
|
-
This packaged changelog lists the 20 most recent releases;
|
|
157
|
+
This packaged changelog lists the 20 most recent releases; 274 older releases are omitted to keep the package small.
|
|
153
158
|
See the [full changelog](https://github.com/0xPlayerOne/code-foundry/blob/main/CHANGELOG.md) for the complete history.
|
package/docs/CACHING.md
CHANGED
|
@@ -11,6 +11,13 @@ Repositories should measure before enabling large installed-tree or build
|
|
|
11
11
|
caches. Set repository variables such as `REPO_FOUNDRY_CACHE_PACKAGES` and
|
|
12
12
|
`REPO_FOUNDRY_CACHE_BUILD` only when a repeatable warm-cache benefit is proven.
|
|
13
13
|
|
|
14
|
+
These caches are restored on every event and saved on push only, so pull
|
|
15
|
+
requests and scheduled runs cannot poison them. The setup action restores them
|
|
16
|
+
and publishes a save contract; the `.github/actions/cache-save` action at the
|
|
17
|
+
end of each task job saves every published cache that did not restore an exact
|
|
18
|
+
key. Custom workflows that use `.github/actions/setup` must call it after their
|
|
19
|
+
tasks, or nothing is saved.
|
|
20
|
+
|
|
14
21
|
## Turborepo and Vercel Remote Caching
|
|
15
22
|
|
|
16
23
|
Turborepo repositories can opt into Vercel Remote Caching with:
|
|
@@ -35,7 +42,8 @@ When opted in, the setup action:
|
|
|
35
42
|
- restores `node_modules/.vite/task-cache` as its last step, after Install and
|
|
36
43
|
after every step that archives `node_modules` (Install can replace it);
|
|
37
44
|
- publishes a cache path, key, and save-intent contract for the explicit
|
|
38
|
-
`.github/actions/
|
|
45
|
+
`.github/actions/cache-save` action, which also saves the repository caches,
|
|
46
|
+
to use after repository tasks;
|
|
39
47
|
- saves it push-only, so pull requests and scheduled runs cannot poison it;
|
|
40
48
|
- keeps it out of the generic JavaScript build-state archive.
|
|
41
49
|
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -350,7 +350,7 @@ behavior:
|
|
|
350
350
|
(not a config key). It controls the local Vite+ task cache only when
|
|
351
351
|
`monorepo_tool: vite-plus` is set.
|
|
352
352
|
- Custom workflows using the setup action must add
|
|
353
|
-
`.github/actions/
|
|
353
|
+
`.github/actions/cache-save` after task execution. The generated
|
|
354
354
|
Code Foundry workflows already include this save boundary; see
|
|
355
355
|
[Caching and remote caching](CACHING.md).
|
|
356
356
|
|
|
@@ -35,13 +35,11 @@ actual caller workflow filename, `release_self-ci.yml`, in repository
|
|
|
35
35
|
environment. Both the caller and reusable publisher grant `id-token: write`.
|
|
36
36
|
For generated consumer workflows, the caller filename is `release.yml` and the
|
|
37
37
|
auth mode remains configurable with `npm_publish_auth`.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
To recover a GitHub release missing from npm, the self-only dispatch requires the
|
|
39
|
+
current stable tag, immutable release, exact archive, npm 404, fresh Node 24/26
|
|
40
|
+
qualification, and OIDC publication. Existing npm versions are never overwritten.
|
|
41
41
|
|
|
42
|
-
The publisher never rebuilds
|
|
43
|
-
already-published version conflict as proof of success. It never accepts or
|
|
44
|
-
forwards `NPM_TOKEN`.
|
|
42
|
+
The publisher never rebuilds a package or accepts `NPM_TOKEN`.
|
|
45
43
|
|
|
46
44
|
Qualification and publication are gated on `main` push or an explicitly
|
|
47
45
|
requested `workflow_dispatch`. The shared billing pause blocks normal runs. A
|
|
@@ -123,6 +121,11 @@ state still match the qualified candidate. An already-published release is never
|
|
|
123
121
|
restaged or overwritten. Inspect the retained staging and publication identity
|
|
124
122
|
receipts when recovery is needed.
|
|
125
123
|
|
|
124
|
+
To recover a published GitHub release that is missing from npm, dispatch the
|
|
125
|
+
self-only `release_self-ci.yml` workflow on `main` with `recover-npm-tag`. It
|
|
126
|
+
requires the current stable tag, immutable release, exact archive, and exact npm
|
|
127
|
+
404, then requalifies that archive on Node 24/26 before OIDC publication.
|
|
128
|
+
|
|
126
129
|
## Publication prerequisites
|
|
127
130
|
|
|
128
131
|
Enable immutable releases and verify the setting with the actual
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-foundry",
|
|
3
|
-
"version": "1.53.
|
|
3
|
+
"version": "1.53.3",
|
|
4
4
|
"description": "A fast, language-aware repository factory for agent-ready workflows, testing, security, and release automation.",
|
|
5
5
|
"homepage": "https://github.com/0xPlayerOne/code-foundry#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"src",
|
|
19
19
|
"!src/commands/qualification-receipt.mjs",
|
|
20
|
+
"!src/commands/release-recovery.mjs",
|
|
20
21
|
"!src/lib/qualification-receipt.mjs",
|
|
21
22
|
"!src/lib/release-package-contract.mjs",
|
|
22
23
|
"docs",
|
|
23
24
|
".github",
|
|
24
25
|
"!.github/actions/**",
|
|
26
|
+
"!.github/workflows/qualified-foundry-publish.yml",
|
|
25
27
|
".githooks",
|
|
26
28
|
".editorconfig",
|
|
27
29
|
".gitattributes",
|
package/src/commands/sync.mjs
CHANGED
|
@@ -1247,19 +1247,28 @@ function renderWorkflow(content, config, repository, ref, rustCodeql, file, self
|
|
|
1247
1247
|
// recovery, staging, and qualified publication. Consumer callers must
|
|
1248
1248
|
// retain the ordinary reusable Release Please path instead of inheriting
|
|
1249
1249
|
// self-only jobs whose outputs and environments do not exist for them.
|
|
1250
|
-
for (const job of [
|
|
1250
|
+
for (const job of [
|
|
1251
|
+
'qualification',
|
|
1252
|
+
'preflight',
|
|
1253
|
+
'recovery',
|
|
1254
|
+
'stage',
|
|
1255
|
+
'publish',
|
|
1256
|
+
'npm_recovery_target',
|
|
1257
|
+
'npm_recovery_publish',
|
|
1258
|
+
])
|
|
1251
1259
|
rendered = removeWorkflowBlock(rendered, job)
|
|
1260
|
+
rendered = removeWorkflowBlock(rendered, 'recover-npm-tag', ' ')
|
|
1252
1261
|
rendered = rendered.replace(/^ needs: \[preflight\]\n/m, '')
|
|
1253
1262
|
rendered = rendered.replace(/^ config-file: \.github\/release-please-foundry\.json\n/m, '')
|
|
1254
1263
|
if (config.release_defer_publication !== 'true') {
|
|
1255
1264
|
rendered = rendered.replace(/^ defer-publication: true\n/m, '')
|
|
1256
1265
|
}
|
|
1257
1266
|
rendered = rendered.replace(
|
|
1258
|
-
/^ if: github\.ref == 'refs\/heads\/main' && \(vars\.CI_BILLING_PAUSED != 'true' \|\| \(github\.event_name == 'workflow_dispatch' && inputs\['release-while-paused'\] == true\)\)\n/m,
|
|
1267
|
+
/^ if: github\.ref == 'refs\/heads\/main' && \(vars\.CI_BILLING_PAUSED != 'true' \|\| \(github\.event_name == 'workflow_dispatch' && inputs\['release-while-paused'\] == true\)\) && !\(github\.event_name == 'workflow_dispatch' && inputs\['recover-npm-tag'\] != ''\)\n/m,
|
|
1259
1268
|
" if: vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)\n"
|
|
1260
1269
|
)
|
|
1261
1270
|
rendered = rendered.replace(
|
|
1262
|
-
/^ if: github\.ref == 'refs\/heads\/main' && \(vars\.CI_BILLING_PAUSED != 'true' \|\| \(github\.event_name == 'workflow_dispatch' && inputs\['release-while-paused'\] == true\)\)\n/m,
|
|
1271
|
+
/^ if: github\.ref == 'refs\/heads\/main' && \(vars\.CI_BILLING_PAUSED != 'true' \|\| \(github\.event_name == 'workflow_dispatch' && inputs\['release-while-paused'\] == true\)\) && !\(github\.event_name == 'workflow_dispatch' && inputs\['recover-npm-tag'\] != ''\)\n/m,
|
|
1263
1272
|
" if: vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)\n"
|
|
1264
1273
|
)
|
|
1265
1274
|
// Gate ordinary pushes while allowing the release squash and manual hotfixes.
|
|
@@ -1386,19 +1395,25 @@ function renderWorkflow(content, config, repository, ref, rustCodeql, file, self
|
|
|
1386
1395
|
}
|
|
1387
1396
|
|
|
1388
1397
|
/**
|
|
1389
|
-
* Remove
|
|
1390
|
-
* This is used for workflow jobs and triggers whose feature is
|
|
1391
|
-
* configuration.
|
|
1398
|
+
* Remove an indented YAML mapping block by id while preserving the surrounding
|
|
1399
|
+
* file. This is used for workflow jobs, inputs, and triggers whose feature is
|
|
1400
|
+
* disabled by configuration.
|
|
1392
1401
|
* @param {string} content
|
|
1393
1402
|
* @param {string} blockId
|
|
1403
|
+
* @param {string} [indentation]
|
|
1394
1404
|
* @returns {string}
|
|
1395
1405
|
*/
|
|
1396
|
-
function removeWorkflowBlock(content, blockId) {
|
|
1406
|
+
function removeWorkflowBlock(content, blockId, indentation = ' ') {
|
|
1397
1407
|
const lines = content.split('\n')
|
|
1398
|
-
const start = lines.findIndex((line) => line ===
|
|
1408
|
+
const start = lines.findIndex((line) => line === `${indentation}${blockId}:`)
|
|
1399
1409
|
if (start === -1) return content
|
|
1400
1410
|
let end = start + 1
|
|
1401
|
-
while (end < lines.length
|
|
1411
|
+
while (end < lines.length) {
|
|
1412
|
+
const line = lines[end]
|
|
1413
|
+
const level = line.match(/^ */)?.[0].length ?? 0
|
|
1414
|
+
if (line.trim() && level <= indentation.length) break
|
|
1415
|
+
end += 1
|
|
1416
|
+
}
|
|
1402
1417
|
lines.splice(start, end - start)
|
|
1403
1418
|
if (content.endsWith('\n') && lines.at(-1) !== '') lines.push('')
|
|
1404
1419
|
return lines.join('\n')
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
name: Qualified Foundry Publication
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_call:
|
|
5
|
-
inputs:
|
|
6
|
-
tag:
|
|
7
|
-
description: Published immutable release tag at the caller commit.
|
|
8
|
-
required: true
|
|
9
|
-
type: string
|
|
10
|
-
asset:
|
|
11
|
-
description: Exact pre-attached Code Foundry npm tarball name.
|
|
12
|
-
required: true
|
|
13
|
-
type: string
|
|
14
|
-
billing-pause-bypass:
|
|
15
|
-
description: Allow only an explicitly dispatched release while shared CI remains paused.
|
|
16
|
-
required: false
|
|
17
|
-
type: boolean
|
|
18
|
-
default: false
|
|
19
|
-
|
|
20
|
-
permissions:
|
|
21
|
-
attestations: read
|
|
22
|
-
contents: read
|
|
23
|
-
|
|
24
|
-
jobs:
|
|
25
|
-
qualify:
|
|
26
|
-
name: Qualify Node ${{ matrix.node }}
|
|
27
|
-
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['billing-pause-bypass'] == true)) && github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
|
|
28
|
-
runs-on: ubuntu-24.04
|
|
29
|
-
timeout-minutes: 15
|
|
30
|
-
strategy:
|
|
31
|
-
fail-fast: false
|
|
32
|
-
matrix:
|
|
33
|
-
node: ['24', '26']
|
|
34
|
-
steps:
|
|
35
|
-
- name: Checkout trusted qualification runtime
|
|
36
|
-
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
37
|
-
with:
|
|
38
|
-
persist-credentials: false
|
|
39
|
-
ref: ${{ github.sha }}
|
|
40
|
-
- name: Setup Node
|
|
41
|
-
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
42
|
-
with:
|
|
43
|
-
node-version: ${{ matrix.node }}
|
|
44
|
-
- name: Install pinned Actionlint
|
|
45
|
-
run: go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.12
|
|
46
|
-
- name: Download and cryptographically verify the release archive
|
|
47
|
-
env:
|
|
48
|
-
GH_TOKEN: ${{ github.token }}
|
|
49
|
-
RELEASE_TAG: ${{ inputs.tag }}
|
|
50
|
-
RELEASE_ASSET: ${{ inputs.asset }}
|
|
51
|
-
run: |
|
|
52
|
-
set -euo pipefail
|
|
53
|
-
node src/commands/qualified-publication.mjs download \
|
|
54
|
-
"$GITHUB_REPOSITORY" "$RELEASE_TAG" "$GITHUB_SHA" "$RELEASE_ASSET" \
|
|
55
|
-
"$RUNNER_TEMP/candidate" > "$RUNNER_TEMP/download.json"
|
|
56
|
-
- name: Qualify those exact bytes
|
|
57
|
-
env:
|
|
58
|
-
RELEASE_ASSET: ${{ inputs.asset }}
|
|
59
|
-
run: |
|
|
60
|
-
set -euo pipefail
|
|
61
|
-
node src/lib/consumer-qualification.mjs \
|
|
62
|
-
"$RUNNER_TEMP/candidate/$RELEASE_ASSET" "$GITHUB_SHA" \
|
|
63
|
-
"$RUNNER_TEMP/report.json" "$(go env GOPATH)/bin/actionlint"
|
|
64
|
-
- name: Retain this attempt's qualification report
|
|
65
|
-
if: always()
|
|
66
|
-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
67
|
-
with:
|
|
68
|
-
name: qualified-${{ github.run_attempt }}-node-${{ matrix.node }}
|
|
69
|
-
path: ${{ runner.temp }}/report.json
|
|
70
|
-
if-no-files-found: error
|
|
71
|
-
retention-days: 14
|
|
72
|
-
|
|
73
|
-
publish:
|
|
74
|
-
name: Publish qualified archive
|
|
75
|
-
needs: qualify
|
|
76
|
-
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['billing-pause-bypass'] == true)) && github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
|
|
77
|
-
runs-on: ubuntu-24.04
|
|
78
|
-
timeout-minutes: 15
|
|
79
|
-
concurrency:
|
|
80
|
-
group: qualified-foundry-npm-${{ github.repository }}-${{ inputs.tag }}
|
|
81
|
-
cancel-in-progress: false
|
|
82
|
-
permissions:
|
|
83
|
-
actions: read
|
|
84
|
-
attestations: read
|
|
85
|
-
contents: read
|
|
86
|
-
id-token: write
|
|
87
|
-
env:
|
|
88
|
-
RELEASE_TAG: ${{ inputs.tag }}
|
|
89
|
-
RELEASE_ASSET: ${{ inputs.asset }}
|
|
90
|
-
steps:
|
|
91
|
-
- name: Checkout trusted publication runtime
|
|
92
|
-
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
93
|
-
with:
|
|
94
|
-
persist-credentials: false
|
|
95
|
-
ref: ${{ github.sha }}
|
|
96
|
-
- name: Setup Node for trusted publishing
|
|
97
|
-
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
98
|
-
with:
|
|
99
|
-
node-version: '24'
|
|
100
|
-
- name: Retrieve only this run attempt's evidence
|
|
101
|
-
env:
|
|
102
|
-
GH_TOKEN: ${{ github.token }}
|
|
103
|
-
run: |
|
|
104
|
-
set -euo pipefail
|
|
105
|
-
for node in 24 26; do
|
|
106
|
-
gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" \
|
|
107
|
-
--name "qualified-$GITHUB_RUN_ATTEMPT-node-$node" \
|
|
108
|
-
--dir "$RUNNER_TEMP/reports/$node"
|
|
109
|
-
done
|
|
110
|
-
node src/commands/qualified-publication.mjs download \
|
|
111
|
-
"$GITHUB_REPOSITORY" "$RELEASE_TAG" "$GITHUB_SHA" "$RELEASE_ASSET" \
|
|
112
|
-
"$RUNNER_TEMP/candidate" > "$RUNNER_TEMP/download.json"
|
|
113
|
-
- name: Reverify and publish the tested tarball without lifecycle hooks
|
|
114
|
-
env:
|
|
115
|
-
GH_TOKEN: ${{ github.token }}
|
|
116
|
-
run: |
|
|
117
|
-
set -euo pipefail
|
|
118
|
-
node src/commands/qualified-publication.mjs publish \
|
|
119
|
-
"$GITHUB_REPOSITORY" "$RELEASE_TAG" "$GITHUB_SHA" "$RELEASE_ASSET" \
|
|
120
|
-
"$RUNNER_TEMP/candidate" \
|
|
121
|
-
"$RUNNER_TEMP/reports/24/report.json" \
|
|
122
|
-
"$RUNNER_TEMP/reports/26/report.json" > "$RUNNER_TEMP/publication.json"
|
|
123
|
-
- name: Retain publication identity
|
|
124
|
-
if: always()
|
|
125
|
-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
126
|
-
with:
|
|
127
|
-
name: qualified-publication-${{ github.run_attempt }}
|
|
128
|
-
path: ${{ runner.temp }}/publication.json
|
|
129
|
-
if-no-files-found: warn
|
|
130
|
-
retention-days: 14
|