code-foundry 1.20.1 → 1.22.1
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/CONTRIBUTING.md +5 -2
- package/.github/code-foundry.yml +1 -0
- package/.github/release-please-foundry.json +56 -0
- package/.github/workflows/ci.yml +61 -0
- package/.github/workflows/cloudflare-delivery.yml +9 -0
- package/.github/workflows/cloudflare-deploy.yml +8 -1
- package/.github/workflows/opencode-security_self-ci.yml +1 -1
- package/.github/workflows/qualified-foundry-publish.yml +6 -7
- package/.github/workflows/release.yml +58 -10
- package/.github/workflows/release_self-ci.yml +211 -8
- package/.github/workflows/test.yml +75 -0
- package/.github/workflows/validation-no-codeql.yml +7 -0
- package/.github/workflows/validation.yml +7 -0
- package/.gitignore +1 -1
- package/AGENTS.md +4 -1
- package/CHANGELOG.md +60 -0
- package/README.md +20 -17
- package/docs/CONFIGURATION.md +182 -152
- package/docs/EXTENSIONS.md +28 -7
- package/docs/INITIALIZATION.md +13 -9
- package/docs/PERFORMANCE.md +67 -59
- package/docs/PUBLISHING.md +39 -14
- package/docs/README.md +37 -22
- package/docs/RELEASES.md +18 -9
- package/docs/WORKFLOWS.md +11 -0
- package/docs/agent-validation.md +6 -5
- package/docs/cloudflare-delivery.md +20 -8
- package/docs/consumer-qualification.md +11 -9
- package/docs/fleet-release-eligibility.md +14 -15
- package/docs/fleet-rollouts.md +3 -3
- package/docs/merge-queues.md +21 -21
- package/docs/product-quality.md +9 -10
- package/docs/qualified-publication.md +166 -93
- package/docs/release-integrity.md +7 -6
- package/docs/required-capabilities.md +42 -15
- package/package.json +1 -1
- package/src/commands/cloudflare-delivery.mjs +6 -1
- package/src/commands/qualified-publication.mjs +23 -10
- package/src/commands/release-integrity.mjs +9 -0
- package/src/commands/sync.mjs +29 -24
- package/src/lib/product-quality.mjs +220 -16
- package/src/runtime-core.mjs +12 -8
- package/src/runtime.mjs +1 -1
- package/src/templates/gitignore +1 -1
|
@@ -12,32 +12,235 @@ on:
|
|
|
12
12
|
default: false
|
|
13
13
|
|
|
14
14
|
permissions:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
# One owner for draft staging and npm publication. Never cancel an active release.
|
|
18
|
+
concurrency:
|
|
19
|
+
group: code-foundry-qualified-release-${{ github.repository }}-${{ github.ref }}
|
|
20
|
+
cancel-in-progress: false
|
|
20
21
|
|
|
21
22
|
jobs:
|
|
22
23
|
qualification:
|
|
23
24
|
name: Qualify consumers
|
|
24
|
-
if: vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)
|
|
25
|
+
if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true))
|
|
25
26
|
uses: ./.github/workflows/consumer-qualification.yml
|
|
26
27
|
with:
|
|
27
28
|
billing-pause-bypass: ${{ github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true }}
|
|
28
29
|
permissions:
|
|
29
30
|
actions: read
|
|
30
31
|
contents: read
|
|
32
|
+
|
|
33
|
+
preflight:
|
|
34
|
+
name: Verify release prerequisites
|
|
35
|
+
if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true))
|
|
36
|
+
runs-on: ubuntu-24.04
|
|
37
|
+
timeout-minutes: 5
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout trusted verifier
|
|
40
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
41
|
+
with:
|
|
42
|
+
persist-credentials: false
|
|
43
|
+
ref: ${{ github.sha }}
|
|
44
|
+
- name: Setup Node
|
|
45
|
+
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
46
|
+
with:
|
|
47
|
+
node-version: '24'
|
|
48
|
+
- name: Require activation and verify immutability without changing it
|
|
49
|
+
env:
|
|
50
|
+
REQUIRE_IMMUTABLE_RELEASES: ${{ vars.REQUIRE_IMMUTABLE_RELEASES }}
|
|
51
|
+
CODE_FOUNDRY_TOKEN: ${{ secrets.CODE_FOUNDRY_TOKEN }}
|
|
52
|
+
GH_TOKEN: ${{ github.token }}
|
|
53
|
+
run: |
|
|
54
|
+
set -euo pipefail
|
|
55
|
+
test "$REQUIRE_IMMUTABLE_RELEASES" = true || { echo 'Enable and review immutable release prerequisites before this cutover.' >&2; exit 1; }
|
|
56
|
+
gh release verify --help >/dev/null
|
|
57
|
+
gh release verify-asset --help >/dev/null
|
|
58
|
+
# Match the reusable producer's credential selection. A rejected
|
|
59
|
+
# configured token must not make the later staging job use it.
|
|
60
|
+
if [ -n "$CODE_FOUNDRY_TOKEN" ] && GH_TOKEN="$CODE_FOUNDRY_TOKEN" gh api "repos/${GITHUB_REPOSITORY}" --jq '.full_name' >/dev/null 2>&1; then
|
|
61
|
+
GH_TOKEN="$CODE_FOUNDRY_TOKEN" node src/commands/release-integrity.mjs settings --repo "$GITHUB_REPOSITORY"
|
|
62
|
+
else
|
|
63
|
+
node src/commands/release-integrity.mjs settings --repo "$GITHUB_REPOSITORY"
|
|
64
|
+
fi
|
|
65
|
+
|
|
31
66
|
release:
|
|
32
67
|
name: Release
|
|
33
|
-
needs: qualification
|
|
34
|
-
if: vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)
|
|
68
|
+
needs: [qualification, preflight]
|
|
69
|
+
if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true))
|
|
35
70
|
uses: ./.github/workflows/release.yml
|
|
36
71
|
with:
|
|
37
72
|
billing-pause-bypass: ${{ github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true }}
|
|
38
73
|
runner: ubuntu-slim
|
|
39
74
|
runtime-repository: 0xPlayerOne/code-foundry
|
|
40
75
|
runtime-ref: ${{ github.sha }}
|
|
76
|
+
config-file: .github/release-please-foundry.json
|
|
77
|
+
defer-publication: true
|
|
78
|
+
permissions:
|
|
79
|
+
actions: write
|
|
80
|
+
contents: write
|
|
81
|
+
issues: write
|
|
82
|
+
pull-requests: write
|
|
83
|
+
# Required by the reusable legacy workflow's declared npm job, which is disabled here.
|
|
84
|
+
id-token: write
|
|
41
85
|
secrets:
|
|
42
86
|
CODE_FOUNDRY_TOKEN: ${{ secrets.CODE_FOUNDRY_TOKEN }}
|
|
87
|
+
|
|
88
|
+
recovery:
|
|
89
|
+
name: Recover qualified draft identity
|
|
90
|
+
needs: [qualification, release]
|
|
91
|
+
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && needs.qualification.result == 'success' && needs.release.result == 'success'
|
|
92
|
+
runs-on: ubuntu-24.04
|
|
93
|
+
timeout-minutes: 5
|
|
94
|
+
permissions:
|
|
95
|
+
contents: read
|
|
96
|
+
outputs:
|
|
97
|
+
found: ${{ steps.recover.outputs.found }}
|
|
98
|
+
tag: ${{ steps.recover.outputs.tag }}
|
|
99
|
+
sha: ${{ steps.recover.outputs.sha }}
|
|
100
|
+
env:
|
|
101
|
+
RELEASE_CREATED: ${{ needs.release.outputs.release_created }}
|
|
102
|
+
SOURCE_SHA: ${{ needs.qualification.outputs.source-sha }}
|
|
103
|
+
steps:
|
|
104
|
+
- name: Checkout trusted recovery runtime
|
|
105
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
106
|
+
with:
|
|
107
|
+
persist-credentials: false
|
|
108
|
+
ref: ${{ github.sha }}
|
|
109
|
+
- name: Setup Node
|
|
110
|
+
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
111
|
+
with:
|
|
112
|
+
node-version: '24'
|
|
113
|
+
- name: Locate an exact existing draft
|
|
114
|
+
id: recover
|
|
115
|
+
env:
|
|
116
|
+
GH_TOKEN: ${{ github.token }}
|
|
117
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
118
|
+
run: |
|
|
119
|
+
set -euo pipefail
|
|
120
|
+
node --input-type=module <<'NODE'
|
|
121
|
+
import { appendFileSync, readFileSync } from "node:fs"
|
|
122
|
+
import { resolveTagCommit, runGh } from "./src/commands/release-integrity.mjs"
|
|
123
|
+
const output = process.env.GITHUB_OUTPUT
|
|
124
|
+
const write = (found, tag = "", sha = "") => appendFileSync(output, `found=${found}\ntag=${tag}\nsha=${sha}\n`)
|
|
125
|
+
if (process.env.RELEASE_CREATED === "true") {
|
|
126
|
+
write("false")
|
|
127
|
+
process.exit(0)
|
|
128
|
+
}
|
|
129
|
+
const version = JSON.parse(readFileSync("package.json", "utf8")).version
|
|
130
|
+
const tag = `v${version}`
|
|
131
|
+
// GitHub's get-by-tag endpoint does not return draft releases. Use
|
|
132
|
+
// the authenticated release list and inspect every page instead.
|
|
133
|
+
const endpoint = `repos/${process.env.GITHUB_REPOSITORY}/releases?per_page=100`
|
|
134
|
+
const response = runGh(["api", "--paginate", "--slurp", endpoint])
|
|
135
|
+
if (response.status !== 0) throw new Error("Could not inspect the expected draft release; recovery remains blocked")
|
|
136
|
+
let pages
|
|
137
|
+
try {
|
|
138
|
+
pages = JSON.parse(response.stdout)
|
|
139
|
+
} catch {
|
|
140
|
+
throw new Error("GitHub returned an invalid release response; recovery remains blocked")
|
|
141
|
+
}
|
|
142
|
+
if (!Array.isArray(pages) || pages.some((page) => !Array.isArray(page))) {
|
|
143
|
+
throw new Error("GitHub returned an invalid release list; recovery remains blocked")
|
|
144
|
+
}
|
|
145
|
+
const release = pages.flat().find((entry) => entry?.tag_name === tag)
|
|
146
|
+
if (!release || release.draft !== true) {
|
|
147
|
+
write("false")
|
|
148
|
+
process.exit(0)
|
|
149
|
+
}
|
|
150
|
+
if (release.tag_name !== tag) throw new Error("Existing draft tag does not match the expected package version")
|
|
151
|
+
const sourceSha = resolveTagCommit(process.env.GITHUB_REPOSITORY, tag, runGh)
|
|
152
|
+
if (sourceSha.toLowerCase() !== process.env.SOURCE_SHA.toLowerCase()) throw new Error("Existing draft tag does not match the qualified source SHA")
|
|
153
|
+
write("true", tag, sourceSha)
|
|
154
|
+
NODE
|
|
155
|
+
|
|
156
|
+
stage:
|
|
157
|
+
name: Stage qualified release
|
|
158
|
+
needs: [qualification, release, recovery]
|
|
159
|
+
if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && github.ref == 'refs/heads/main' && (needs.release.outputs.release_created == 'true' || needs.recovery.outputs.found == 'true')
|
|
160
|
+
runs-on: ubuntu-24.04
|
|
161
|
+
timeout-minutes: 15
|
|
162
|
+
permissions:
|
|
163
|
+
actions: read
|
|
164
|
+
attestations: read
|
|
165
|
+
contents: write
|
|
166
|
+
env:
|
|
167
|
+
RELEASE_TAG: ${{ needs.release.outputs.tag_name || needs.recovery.outputs.tag }}
|
|
168
|
+
RELEASE_SHA: ${{ needs.release.outputs.sha || needs.recovery.outputs.sha }}
|
|
169
|
+
SOURCE_SHA: ${{ needs.qualification.outputs.source-sha }}
|
|
170
|
+
PACKAGE: ${{ needs.qualification.outputs.filename }}
|
|
171
|
+
CANDIDATE_SHA256: ${{ needs.qualification.outputs.sha256 }}
|
|
172
|
+
CANDIDATE_ARTIFACT: ${{ needs.qualification.outputs.candidate-artifact }}
|
|
173
|
+
steps:
|
|
174
|
+
- name: Checkout trusted staging runtime
|
|
175
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
176
|
+
with:
|
|
177
|
+
persist-credentials: false
|
|
178
|
+
ref: ${{ github.sha }}
|
|
179
|
+
- name: Setup Node
|
|
180
|
+
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
181
|
+
with:
|
|
182
|
+
node-version: '24'
|
|
183
|
+
- name: Require the release to identify this qualified candidate
|
|
184
|
+
run: |
|
|
185
|
+
node --input-type=module <<'NODE'
|
|
186
|
+
import assert from 'node:assert/strict'
|
|
187
|
+
import { readFileSync } from 'node:fs'
|
|
188
|
+
const env = process.env
|
|
189
|
+
const version = JSON.parse(readFileSync('package.json', 'utf8')).version
|
|
190
|
+
assert.match(env.SOURCE_SHA, /^[a-f0-9]{40}$/)
|
|
191
|
+
assert.equal(env.SOURCE_SHA, env.GITHUB_SHA)
|
|
192
|
+
assert.equal(env.RELEASE_SHA, env.SOURCE_SHA)
|
|
193
|
+
assert.equal(env.RELEASE_TAG, `v${version}`)
|
|
194
|
+
assert.equal(env.PACKAGE, `code-foundry-${version}.tgz`)
|
|
195
|
+
assert.match(env.CANDIDATE_SHA256, /^[a-f0-9]{64}$/)
|
|
196
|
+
assert.equal(env.CANDIDATE_ARTIFACT, `qualification-candidate-${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}`)
|
|
197
|
+
NODE
|
|
198
|
+
- name: Retrieve only this attempt's qualified bytes and reports
|
|
199
|
+
env:
|
|
200
|
+
GH_TOKEN: ${{ github.token }}
|
|
201
|
+
run: |
|
|
202
|
+
set -euo pipefail
|
|
203
|
+
gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" \
|
|
204
|
+
--name "$CANDIDATE_ARTIFACT" --dir "$RUNNER_TEMP/candidate"
|
|
205
|
+
test "$(sha256sum "$RUNNER_TEMP/candidate/$PACKAGE" | cut -d' ' -f1)" = "$CANDIDATE_SHA256"
|
|
206
|
+
for node in 20 22 24; do
|
|
207
|
+
gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" \
|
|
208
|
+
--name "consumer-qualification-$GITHUB_RUN_ATTEMPT-node-$node" \
|
|
209
|
+
--dir "$RUNNER_TEMP/reports/$node"
|
|
210
|
+
done
|
|
211
|
+
- name: Stage identical assets then publish and verify the immutable release
|
|
212
|
+
env:
|
|
213
|
+
GH_TOKEN: ${{ needs.release.outputs.token_source == 'configured' && secrets.CODE_FOUNDRY_TOKEN || github.token }}
|
|
214
|
+
run: |
|
|
215
|
+
set -euo pipefail
|
|
216
|
+
node src/commands/qualified-publication.mjs stage \
|
|
217
|
+
"$GITHUB_REPOSITORY" "$RELEASE_TAG" "$SOURCE_SHA" "$PACKAGE" \
|
|
218
|
+
"$RUNNER_TEMP/candidate" \
|
|
219
|
+
"$RUNNER_TEMP/reports/20/report.json" \
|
|
220
|
+
"$RUNNER_TEMP/reports/22/report.json" \
|
|
221
|
+
"$RUNNER_TEMP/reports/24/report.json" > "$RUNNER_TEMP/staging.json"
|
|
222
|
+
- name: Retain staging identity
|
|
223
|
+
if: always()
|
|
224
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
225
|
+
with:
|
|
226
|
+
name: release-staging-${{ github.run_id }}-${{ github.run_attempt }}
|
|
227
|
+
path: ${{ runner.temp }}/staging.json
|
|
228
|
+
if-no-files-found: warn
|
|
229
|
+
retention-days: 14
|
|
230
|
+
|
|
231
|
+
publish:
|
|
232
|
+
name: Publish verified package
|
|
233
|
+
needs: [qualification, release, recovery, stage]
|
|
234
|
+
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')
|
|
235
|
+
uses: ./.github/workflows/qualified-foundry-publish.yml
|
|
236
|
+
permissions:
|
|
237
|
+
actions: read
|
|
238
|
+
attestations: read
|
|
239
|
+
contents: read
|
|
240
|
+
id-token: write
|
|
241
|
+
with:
|
|
242
|
+
tag: ${{ needs.release.outputs.tag_name || needs.recovery.outputs.tag }}
|
|
243
|
+
asset: ${{ needs.qualification.outputs.filename }}
|
|
244
|
+
billing-pause-bypass: ${{ github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true }}
|
|
245
|
+
secrets:
|
|
43
246
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -33,6 +33,11 @@ on:
|
|
|
33
33
|
required: false
|
|
34
34
|
type: boolean
|
|
35
35
|
default: false
|
|
36
|
+
artifact-prefix:
|
|
37
|
+
description: Prefix for task receipts; use a unique value when calling this workflow more than once.
|
|
38
|
+
required: false
|
|
39
|
+
type: string
|
|
40
|
+
default: task-result
|
|
36
41
|
secrets:
|
|
37
42
|
TURBO_TOKEN:
|
|
38
43
|
required: false
|
|
@@ -104,8 +109,22 @@ jobs:
|
|
|
104
109
|
task-python: ${{ steps.applicability.outputs.python }}
|
|
105
110
|
task-rust: ${{ steps.applicability.outputs.rust }}
|
|
106
111
|
- name: Unit
|
|
112
|
+
id: execute
|
|
107
113
|
if: steps.applicability.outputs.applicable == 'true'
|
|
108
114
|
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci unit
|
|
115
|
+
- name: Retain task receipt
|
|
116
|
+
if: >-
|
|
117
|
+
always() &&
|
|
118
|
+
(steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
|
|
119
|
+
steps.applicability.outputs.applicable == 'false') &&
|
|
120
|
+
hashFiles('.code-foundry/results/unit.json') != ''
|
|
121
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
122
|
+
with:
|
|
123
|
+
name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-unit
|
|
124
|
+
path: .code-foundry/results/unit.json
|
|
125
|
+
include-hidden-files: true
|
|
126
|
+
if-no-files-found: error
|
|
127
|
+
retention-days: 14
|
|
109
128
|
- name: Upload coverage
|
|
110
129
|
if: >-
|
|
111
130
|
always() &&
|
|
@@ -171,8 +190,22 @@ jobs:
|
|
|
171
190
|
task-python: ${{ steps.applicability.outputs.python }}
|
|
172
191
|
task-rust: ${{ steps.applicability.outputs.rust }}
|
|
173
192
|
- name: Performance
|
|
193
|
+
id: execute
|
|
174
194
|
if: steps.applicability.outputs.applicable == 'true'
|
|
175
195
|
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci performance
|
|
196
|
+
- name: Retain task receipt
|
|
197
|
+
if: >-
|
|
198
|
+
always() &&
|
|
199
|
+
(steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
|
|
200
|
+
steps.applicability.outputs.applicable == 'false') &&
|
|
201
|
+
hashFiles('.code-foundry/results/performance.json') != ''
|
|
202
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
203
|
+
with:
|
|
204
|
+
name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-performance
|
|
205
|
+
path: .code-foundry/results/performance.json
|
|
206
|
+
include-hidden-files: true
|
|
207
|
+
if-no-files-found: error
|
|
208
|
+
retention-days: 14
|
|
176
209
|
- name: Upload performance results
|
|
177
210
|
if: >-
|
|
178
211
|
always() &&
|
|
@@ -236,8 +269,22 @@ jobs:
|
|
|
236
269
|
task-python: ${{ steps.applicability.outputs.python }}
|
|
237
270
|
task-rust: ${{ steps.applicability.outputs.rust }}
|
|
238
271
|
- name: Integration
|
|
272
|
+
id: execute
|
|
239
273
|
if: steps.applicability.outputs.applicable == 'true'
|
|
240
274
|
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci integration
|
|
275
|
+
- name: Retain task receipt
|
|
276
|
+
if: >-
|
|
277
|
+
always() &&
|
|
278
|
+
(steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
|
|
279
|
+
steps.applicability.outputs.applicable == 'false') &&
|
|
280
|
+
hashFiles('.code-foundry/results/integration.json') != ''
|
|
281
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
282
|
+
with:
|
|
283
|
+
name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-integration
|
|
284
|
+
path: .code-foundry/results/integration.json
|
|
285
|
+
include-hidden-files: true
|
|
286
|
+
if-no-files-found: error
|
|
287
|
+
retention-days: 14
|
|
241
288
|
|
|
242
289
|
e2e:
|
|
243
290
|
name: E2E
|
|
@@ -298,8 +345,22 @@ jobs:
|
|
|
298
345
|
task-python: ${{ steps.applicability.outputs.python }}
|
|
299
346
|
task-rust: ${{ steps.applicability.outputs.rust }}
|
|
300
347
|
- name: E2E
|
|
348
|
+
id: execute
|
|
301
349
|
if: steps.applicability.outputs.applicable == 'true'
|
|
302
350
|
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci e2e
|
|
351
|
+
- name: Retain task receipt
|
|
352
|
+
if: >-
|
|
353
|
+
always() &&
|
|
354
|
+
(steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
|
|
355
|
+
steps.applicability.outputs.applicable == 'false') &&
|
|
356
|
+
hashFiles('.code-foundry/results/e2e.json') != ''
|
|
357
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
358
|
+
with:
|
|
359
|
+
name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-e2e
|
|
360
|
+
path: .code-foundry/results/e2e.json
|
|
361
|
+
include-hidden-files: true
|
|
362
|
+
if-no-files-found: error
|
|
363
|
+
retention-days: 14
|
|
303
364
|
|
|
304
365
|
smoke:
|
|
305
366
|
name: Smoke
|
|
@@ -348,5 +409,19 @@ jobs:
|
|
|
348
409
|
task-python: ${{ steps.applicability.outputs.python }}
|
|
349
410
|
task-rust: ${{ steps.applicability.outputs.rust }}
|
|
350
411
|
- name: Smoke
|
|
412
|
+
id: execute
|
|
351
413
|
if: steps.applicability.outputs.applicable == 'true'
|
|
352
414
|
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci smoke
|
|
415
|
+
- name: Retain task receipt
|
|
416
|
+
if: >-
|
|
417
|
+
always() &&
|
|
418
|
+
(steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
|
|
419
|
+
steps.applicability.outputs.applicable == 'false') &&
|
|
420
|
+
hashFiles('.code-foundry/results/smoke.json') != ''
|
|
421
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
422
|
+
with:
|
|
423
|
+
name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-smoke
|
|
424
|
+
path: .code-foundry/results/smoke.json
|
|
425
|
+
include-hidden-files: true
|
|
426
|
+
if-no-files-found: error
|
|
427
|
+
retention-days: 14
|
|
@@ -62,6 +62,11 @@ on:
|
|
|
62
62
|
required: false
|
|
63
63
|
type: number
|
|
64
64
|
default: 1
|
|
65
|
+
artifact-prefix:
|
|
66
|
+
description: Prefix for task receipts; use a unique value when calling this workflow more than once.
|
|
67
|
+
required: false
|
|
68
|
+
type: string
|
|
69
|
+
default: task-result
|
|
65
70
|
secrets:
|
|
66
71
|
TURBO_TOKEN:
|
|
67
72
|
required: false
|
|
@@ -86,6 +91,7 @@ jobs:
|
|
|
86
91
|
runtime-repository: ${{ inputs.runtime-repository }}
|
|
87
92
|
runtime-ref: ${{ inputs.runtime-ref }}
|
|
88
93
|
runner: ${{ inputs.ci-runner }}
|
|
94
|
+
artifact-prefix: ${{ inputs.artifact-prefix }}
|
|
89
95
|
secrets:
|
|
90
96
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
91
97
|
NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
|
|
@@ -101,6 +107,7 @@ jobs:
|
|
|
101
107
|
unit-runner: ${{ inputs.unit-runner }}
|
|
102
108
|
performance-runner: ${{ inputs.performance-runner }}
|
|
103
109
|
unit-only: ${{ inputs.mode == 'fast' }}
|
|
110
|
+
artifact-prefix: ${{ inputs.artifact-prefix }}
|
|
104
111
|
secrets:
|
|
105
112
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
106
113
|
|
|
@@ -62,6 +62,11 @@ on:
|
|
|
62
62
|
required: false
|
|
63
63
|
type: number
|
|
64
64
|
default: 1
|
|
65
|
+
artifact-prefix:
|
|
66
|
+
description: Prefix for task receipts; use a unique value when calling this workflow more than once.
|
|
67
|
+
required: false
|
|
68
|
+
type: string
|
|
69
|
+
default: task-result
|
|
65
70
|
secrets:
|
|
66
71
|
TURBO_TOKEN:
|
|
67
72
|
required: false
|
|
@@ -86,6 +91,7 @@ jobs:
|
|
|
86
91
|
runtime-repository: ${{ inputs.runtime-repository }}
|
|
87
92
|
runtime-ref: ${{ inputs.runtime-ref }}
|
|
88
93
|
runner: ${{ inputs.ci-runner }}
|
|
94
|
+
artifact-prefix: ${{ inputs.artifact-prefix }}
|
|
89
95
|
secrets:
|
|
90
96
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
91
97
|
NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
|
|
@@ -101,6 +107,7 @@ jobs:
|
|
|
101
107
|
unit-runner: ${{ inputs.unit-runner }}
|
|
102
108
|
performance-runner: ${{ inputs.performance-runner }}
|
|
103
109
|
unit-only: ${{ inputs.mode == 'fast' }}
|
|
110
|
+
artifact-prefix: ${{ inputs.artifact-prefix }}
|
|
104
111
|
secrets:
|
|
105
112
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
106
113
|
|
package/.gitignore
CHANGED
package/AGENTS.md
CHANGED
|
@@ -106,7 +106,10 @@ This repository uses the `direct` workflow. Topic pull requests target `main`.
|
|
|
106
106
|
- Keep ordinary pull requests in draft while preparing them. The generated
|
|
107
107
|
Draft Guard converts ready ordinary pull requests to draft when they are
|
|
108
108
|
opened or reopened, and runner-heavy validation starts only after an
|
|
109
|
-
explicit `ready_for_review` transition
|
|
109
|
+
explicit `ready_for_review` transition unless `draft_protection: false` is
|
|
110
|
+
configured for generated callers. That opt-out does not disable Draft Guard
|
|
111
|
+
or draft-PR automation. Cloudflare reusable callers use
|
|
112
|
+
`draft-protection: false`.
|
|
110
113
|
- Run local validation and finish review preparation before marking an ordinary
|
|
111
114
|
pull request ready. Ready pull requests stay ready when new commits arrive,
|
|
112
115
|
and validation reruns for the current head; draft updates allocate no
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.22.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.22.0...v1.22.1) (2026-09-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **release:** use shell-safe recovery script ([#574](https://github.com/0xPlayerOne/code-foundry/issues/574)) ([264036f](https://github.com/0xPlayerOne/code-foundry/commit/264036f34afa4d7bc405285918395f2ce075bde3))
|
|
9
|
+
|
|
10
|
+
## [1.22.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.21.3...v1.22.0) (2026-09-09)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **workflows:** protect draft pull requests by default ([#572](https://github.com/0xPlayerOne/code-foundry/issues/572)) ([6fa4ce1](https://github.com/0xPlayerOne/code-foundry/commit/6fa4ce1b2dde5eddff361b819722912fed7a9815))
|
|
16
|
+
|
|
17
|
+
## [1.21.3](https://github.com/0xPlayerOne/code-foundry/compare/v1.21.2...v1.21.3) (2026-09-09)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* **release:** unblock draft publication and PR deployment links ([#570](https://github.com/0xPlayerOne/code-foundry/issues/570)) ([35ab90f](https://github.com/0xPlayerOne/code-foundry/commit/35ab90f185483b0cd6a9c622b9793e46c90dd60c))
|
|
23
|
+
|
|
24
|
+
## [1.21.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.21.1...v1.21.2) (2026-09-09)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Documentation
|
|
28
|
+
|
|
29
|
+
* refresh documentation for current workflows ([#568](https://github.com/0xPlayerOne/code-foundry/issues/568)) ([d0028e0](https://github.com/0xPlayerOne/code-foundry/commit/d0028e096371e1019c466f72f8744bdf832e067c))
|
|
30
|
+
|
|
31
|
+
## [1.21.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.21.0...v1.21.1) (2026-09-09)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
* release-hook-and-performance-safety ([#565](https://github.com/0xPlayerOne/code-foundry/issues/565)) ([0d410c6](https://github.com/0xPlayerOne/code-foundry/commit/0d410c68abfbc0d9fdd03b7aa81676489e459c19))
|
|
37
|
+
* **release:** automate self publication after CI ([91b2655](https://github.com/0xPlayerOne/code-foundry/commit/91b265585aa03e742e5bfc8a839b2794bf234491))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### Performance
|
|
41
|
+
|
|
42
|
+
* **ci:** batch native Rust test targets by category ([#564](https://github.com/0xPlayerOne/code-foundry/issues/564)) ([6def882](https://github.com/0xPlayerOne/code-foundry/commit/6def8826e90b23d021a4f4587fa843dbb37df0e7))
|
|
43
|
+
|
|
44
|
+
## [1.21.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.20.2...v1.21.0) (2026-09-09)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### Features
|
|
48
|
+
|
|
49
|
+
* **release:** wire qualified draft staging and immutable publication ([#558](https://github.com/0xPlayerOne/code-foundry/issues/558)) ([19aea32](https://github.com/0xPlayerOne/code-foundry/commit/19aea32f50c747cdc5642d1752a9f02008c157f6))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
### Bug Fixes
|
|
53
|
+
|
|
54
|
+
* **quality:** harden HTML filtering ([#562](https://github.com/0xPlayerOne/code-foundry/issues/562)) ([ea45590](https://github.com/0xPlayerOne/code-foundry/commit/ea45590db24dc05ef12db57e80c7405fa217514f))
|
|
55
|
+
|
|
56
|
+
## [1.20.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.20.1...v1.20.2) (2026-09-09)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
### Bug Fixes
|
|
60
|
+
|
|
61
|
+
* **ci:** retain scoped task receipts across success and failure ([#557](https://github.com/0xPlayerOne/code-foundry/issues/557)) ([ee0fe45](https://github.com/0xPlayerOne/code-foundry/commit/ee0fe45e821621e3193831d70a533077b7bcf8a9))
|
|
62
|
+
|
|
3
63
|
## [1.20.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.20.0...v1.20.1) (2026-09-09)
|
|
4
64
|
|
|
5
65
|
|
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ preserved and can coexist with the standard baseline.
|
|
|
58
58
|
The generated configuration is the one place to control the baseline. See
|
|
59
59
|
[Configuration reference](docs/CONFIGURATION.md) for the visual configuration
|
|
60
60
|
guide and [Initialization and synchronization](docs/INITIALIZATION.md) for
|
|
61
|
-
the
|
|
61
|
+
the init, sync, and doctor workflow.
|
|
62
62
|
|
|
63
63
|
New repositories default to GPL-3.0-or-later. Existing projects preserve an
|
|
64
64
|
authored license unless a replacement is explicitly selected. Our maintained
|
|
@@ -82,9 +82,11 @@ PRs: it converts them to drafts without checking out PR code. It verifies the
|
|
|
82
82
|
event head and update timestamp before changing state, and excludes Release
|
|
83
83
|
Please version PRs whose release workflow owns readiness. Pull-request
|
|
84
84
|
validation runs on the ready-for-review transition and on new commits while a
|
|
85
|
-
PR remains ready; draft updates allocate no validation runner
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
PR remains ready; draft updates allocate no validation runner by default. Set
|
|
86
|
+
`draft_protection: false` in generated consumer configuration, or pass
|
|
87
|
+
`draft-protection: false` to a Cloudflare reusable workflow, to run those gates
|
|
88
|
+
for drafts. Converting a PR to draft runs only the lightweight cancellation
|
|
89
|
+
control. Scheduled and manually dispatched audits are unaffected.
|
|
88
90
|
|
|
89
91
|
Jobs are language-aware and skip irrelevant setup inside the applicable
|
|
90
92
|
aggregate checks. TypeScript uses Oxlint, Oxfmt, and Bun's native
|
|
@@ -125,9 +127,16 @@ workflow runs.
|
|
|
125
127
|
In the default `direct` flow, changes reach `main` through feature pull
|
|
126
128
|
requests and Release Please opens a versioned release PR against `main`. In
|
|
127
129
|
the `staging-release` flow, a promotion PR promotes `staging` into `main`
|
|
128
|
-
first.
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
first. Generated consumer callers create a GitHub release after the version PR
|
|
131
|
+
is merged; npm publication is opt-in through `npm_publish: true` and supports
|
|
132
|
+
npm trusted publishing or an `NPM_TOKEN` fallback.
|
|
133
|
+
|
|
134
|
+
Code Foundry's own release caller is stricter: it qualifies the package across
|
|
135
|
+
Node 20, 22, and 24, stages the exact qualified archive, publishes the
|
|
136
|
+
immutable GitHub Release, and publishes that archive through the verified
|
|
137
|
+
publisher. See
|
|
138
|
+
[Consumer qualification](docs/consumer-qualification.md) and [Qualified
|
|
139
|
+
publication](docs/qualified-publication.md).
|
|
131
140
|
|
|
132
141
|
Read [Release management](docs/RELEASES.md),
|
|
133
142
|
[Release integrity and build provenance](docs/release-integrity.md), and
|
|
@@ -137,16 +146,10 @@ copied into other repositories.
|
|
|
137
146
|
|
|
138
147
|
## Documentation and extensions
|
|
139
148
|
|
|
140
|
-
The `docs/` directory contains generalized operational guides.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
- [Documentation index](docs/README.md)
|
|
145
|
-
- [Initialization and synchronization](docs/INITIALIZATION.md)
|
|
146
|
-
- [Workflow and CI conventions](docs/WORKFLOWS.md)
|
|
147
|
-
- [Release management](docs/RELEASES.md)
|
|
148
|
-
- [Publishing packages](docs/PUBLISHING.md)
|
|
149
|
-
- [Caching and remote caching](docs/CACHING.md)
|
|
149
|
+
The `docs/` directory contains generalized operational guides. Start with the
|
|
150
|
+
[documentation index](docs/README.md), which groups every guide by task. Add
|
|
151
|
+
repository-specific documentation there as well; synchronization preserves
|
|
152
|
+
files in `docs/`.
|
|
150
153
|
|
|
151
154
|
## License
|
|
152
155
|
|