code-foundry 1.53.0 → 1.53.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.
@@ -289,16 +289,9 @@ jobs:
289
289
  if (!buildEnv || typeof buildEnv !== 'object' || Array.isArray(buildEnv)) {
290
290
  throw new Error('build-env must be a JSON object of string values')
291
291
  }
292
- const allowedNames = new Set([
293
- 'ADEA_PREVIEW_HYPERDRIVE_ID',
294
- 'ADEA_PREVIEW_SECRETS_STORE_ID',
295
- 'ADEA_PREVIEW_DATABASE_URL_SECRET',
296
- 'ADEA_PREVIEW_AUTH_BASE_URL_SECRET',
297
- 'ADEA_PREVIEW_AUTH_COOKIE_SECRET_NAME',
298
- 'ADEA_PREVIEW_AUTH_TRUSTED_ORIGINS_SECRET',
299
- ])
292
+ const allowedName = /^ADEA_PREVIEW_(?:HYPERDRIVE_ID|SECRETS_STORE_ID|DATABASE_URL_SECRET|AUTH_(?:BASE_URL_SECRET|COOKIE_SECRET_NAME|HYPERDRIVE_ID|TRUSTED_ORIGINS_SECRET))(?![\s\S])/
300
293
  for (const [name, value] of Object.entries(buildEnv)) {
301
- if (!allowedNames.has(name)) {
294
+ if (!allowedName.test(name)) {
302
295
  throw new Error(`Invalid build-env variable name: ${name}`)
303
296
  }
304
297
  if (typeof value !== 'string') throw new Error(`build-env value for ${name} must be a string`)
@@ -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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.53.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.53.1...v1.53.2) (2026-10-30)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * 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))
9
+
10
+
11
+ ### Maintenance
12
+
13
+ * add verified npm publication recovery ([#815](https://github.com/0xPlayerOne/code-foundry/issues/815)) ([5876361](https://github.com/0xPlayerOne/code-foundry/commit/58763616b3dc38d127680914eeef6151b3375b37))
14
+
15
+ ## [1.53.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.53.0...v1.53.1) (2026-10-30)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * allow isolated preview auth Hyperdrive build binding ([#813](https://github.com/0xPlayerOne/code-foundry/issues/813)) ([cc267fc](https://github.com/0xPlayerOne/code-foundry/commit/cc267fcb84c2828fb5c7090e0b494991ea90f249))
21
+
3
22
  ## [1.53.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.52.4...v1.53.0) (2026-10-30)
4
23
 
5
24
 
@@ -131,23 +150,9 @@
131
150
 
132
151
  * **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))
133
152
 
134
- ## [1.48.5](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.4...v1.48.5) (2026-10-22)
135
-
136
-
137
- ### Bug Fixes
138
-
139
- * **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))
140
-
141
- ## [1.48.4](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.3...v1.48.4) (2026-10-22)
142
-
143
-
144
- ### Maintenance
145
-
146
- * bun-1.4.4-cloud ([#771](https://github.com/0xPlayerOne/code-foundry/issues/771)) ([c99f0ed](https://github.com/0xPlayerOne/code-foundry/commit/c99f0ed8b66129f3b599326bc67d900719b921f4))
147
-
148
153
  <!-- code-foundry: packaged changelog -->
149
154
 
150
155
  ---
151
156
 
152
- This packaged changelog lists the 20 most recent releases; 271 older releases are omitted to keep the package small.
157
+ This packaged changelog lists the 20 most recent releases; 273 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.
@@ -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
- Existing version publication is not overwritten: retrying an already-published
39
- version fails rather than treating a registry conflict or network error as proof
40
- of identity.
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 the package, publishes a directory, or treats an
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.0",
3
+ "version": "1.53.2",
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",
@@ -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 ['qualification', 'preflight', 'recovery', 'stage', 'publish'])
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 a root-level YAML block by id while preserving the surrounding file.
1390
- * This is used for workflow jobs and triggers whose feature is disabled by
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 === ` ${blockId}:`)
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 && !/^ [A-Za-z0-9_-]+:\s*$/.test(lines[end])) end += 1
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