code-foundry 1.20.2 → 1.25.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/CONTRIBUTING.md +5 -2
- package/.github/code-foundry.yml +1 -0
- package/.github/release-please-foundry.json +56 -0
- package/.github/workflows/cloudflare-delivery.yml +9 -0
- package/.github/workflows/cloudflare-deploy.yml +8 -1
- package/.github/workflows/eval.yml +116 -0
- 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/validation-no-codeql.yml +34 -6
- package/.github/workflows/validation.yml +32 -5
- package/.github/workflows/validation_audit_self-ci.yml +1 -0
- package/.github/workflows/validation_self-ci.yml +1 -0
- package/.gitignore +1 -1
- package/AGENTS.md +5 -1
- package/CHANGELOG.md +95 -0
- package/README.md +23 -17
- package/docs/CONFIGURATION.md +189 -152
- package/docs/EVALS.md +139 -0
- 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 +23 -4
- 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 +24 -15
- package/package.json +1 -1
- package/src/commands/cloudflare-delivery.mjs +6 -1
- package/src/commands/qualified-publication.mjs +103 -12
- package/src/commands/release-integrity.mjs +46 -14
- package/src/commands/sync.mjs +30 -24
- package/src/lib/eval-envelope.mjs +234 -0
- package/src/lib/merge-queue.mjs +1 -0
- package/src/lib/product-quality.mjs +220 -16
- package/src/lib/task-policy.mjs +7 -0
- package/src/lib/validation-policy.mjs +10 -6
- package/src/runtime-core.mjs +166 -8
- package/src/runtime.mjs +3 -1
- package/src/templates/gitignore +3 -1
package/.github/CONTRIBUTING.md
CHANGED
|
@@ -44,7 +44,10 @@ This repository uses the `direct` workflow. Topic pull requests target `main`.
|
|
|
44
44
|
- Keep ordinary pull requests in draft while preparing them. The generated
|
|
45
45
|
Draft Guard converts ready ordinary pull requests to draft when they are
|
|
46
46
|
opened or reopened, and runner-heavy validation starts only after an
|
|
47
|
-
explicit `ready_for_review` transition
|
|
47
|
+
explicit `ready_for_review` transition unless `draft_protection: false` is
|
|
48
|
+
configured for generated callers. That opt-out does not disable Draft Guard
|
|
49
|
+
or draft-PR automation. Cloudflare reusable callers use
|
|
50
|
+
`draft-protection: false`.
|
|
48
51
|
- Run local validation and finish review preparation before marking an ordinary
|
|
49
52
|
pull request ready. Ready pull requests stay ready when new commits arrive,
|
|
50
53
|
and validation reruns for the current head; draft updates allocate no
|
|
@@ -184,7 +187,7 @@ Keep pull requests focused and reviewable. Include screenshots or recordings for
|
|
|
184
187
|
| Push to a working branch | Draft PR workflow |
|
|
185
188
|
| Push to `main` | Release workflow plus default-branch CodeQL scan; validation ran on the merged PR |
|
|
186
189
|
|
|
187
|
-
Draft pull requests do not start validation. The lightweight Draft Guard converts ordinary pull requests opened or reopened while ready back to draft; it never checks out pull-request code and it excludes Release Please version heads, whose release workflow owns their state. Marking a pull request ready for review starts the applicable validation tier, and each new commit on a ready pull request reruns that tier for the current head. Draft updates allocate no validation runner. Converting a pull request to draft runs only the lightweight cancellation control.
|
|
190
|
+
Draft pull requests do not start validation unless `draft_protection: false` is configured. The lightweight Draft Guard converts ordinary pull requests opened or reopened while ready back to draft; it never checks out pull-request code and it excludes Release Please version heads, whose release workflow owns their state. Marking a pull request ready for review starts the applicable validation tier, and each new commit on a ready pull request reruns that tier for the current head. Draft updates allocate no validation runner while protection is enabled. Converting a pull request to draft runs only the lightweight cancellation control.
|
|
188
191
|
|
|
189
192
|
Pull-request validation keys concurrency by event and pull-request head, so a newer update cancels its superseded run. Scheduled and manual audits use a separate caller pinned to the protected default branch; this prevents caller-selected runtime code from executing with default-branch cache access. Pull-request and scheduled/manual callers use the mode-aware orchestrator, which fans out only the required jobs and concludes with the stable aggregate gate. Main pushes run the default-branch CodeQL lane separately.
|
|
190
193
|
|
package/.github/code-foundry.yml
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
|
3
|
+
"bump-minor-pre-major": true,
|
|
4
|
+
"pull-request-title-pattern": "chore(main): release ${version}",
|
|
5
|
+
"group-pull-request-title-pattern": "chore(main): release ${version}",
|
|
6
|
+
"changelog-sections": [
|
|
7
|
+
{
|
|
8
|
+
"type": "feat",
|
|
9
|
+
"section": "Features"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "fix",
|
|
13
|
+
"section": "Bug Fixes"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"type": "perf",
|
|
17
|
+
"section": "Performance"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"type": "revert",
|
|
21
|
+
"section": "Reverts"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "deps",
|
|
25
|
+
"section": "Dependencies"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "docs",
|
|
29
|
+
"section": "Documentation"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "test",
|
|
33
|
+
"section": "Tests"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"type": "ci",
|
|
37
|
+
"section": "CI"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"type": "refactor",
|
|
41
|
+
"section": "Maintenance"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "chore",
|
|
45
|
+
"section": "Maintenance"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"packages": {
|
|
49
|
+
".": {
|
|
50
|
+
"release-type": "node",
|
|
51
|
+
"include-component-in-tag": false,
|
|
52
|
+
"draft": true,
|
|
53
|
+
"force-tag-creation": true
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -23,6 +23,11 @@ on:
|
|
|
23
23
|
required: false
|
|
24
24
|
type: string
|
|
25
25
|
default: preview
|
|
26
|
+
draft-protection:
|
|
27
|
+
description: 'Skip draft pull requests by default; set false to run candidate gates for drafts.'
|
|
28
|
+
required: false
|
|
29
|
+
type: boolean
|
|
30
|
+
default: true
|
|
26
31
|
working-directory:
|
|
27
32
|
required: false
|
|
28
33
|
type: string
|
|
@@ -113,6 +118,7 @@ jobs:
|
|
|
113
118
|
candidate:
|
|
114
119
|
if: >-
|
|
115
120
|
vars.CI_BILLING_PAUSED != 'true' &&
|
|
121
|
+
(inputs['draft-protection'] != true || github.event_name != 'pull_request' || github.event.pull_request.draft == false) &&
|
|
116
122
|
github.event_name != 'pull_request_target' &&
|
|
117
123
|
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
|
|
118
124
|
runs-on: ubuntu-latest
|
|
@@ -187,6 +193,7 @@ jobs:
|
|
|
187
193
|
id: start
|
|
188
194
|
env:
|
|
189
195
|
GH_TOKEN: ${{ github.token }}
|
|
196
|
+
GITHUB_DEPLOYMENT_REF: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
190
197
|
run: node "$FOUNDRY_EXECUTOR" record-start
|
|
191
198
|
- name: Set up Bun
|
|
192
199
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
@@ -238,6 +245,7 @@ jobs:
|
|
|
238
245
|
needs: candidate
|
|
239
246
|
if: >-
|
|
240
247
|
vars.CI_BILLING_PAUSED != 'true' &&
|
|
248
|
+
(inputs['draft-protection'] != true || github.event_name != 'pull_request' || github.event.pull_request.draft == false) &&
|
|
241
249
|
inputs.mode == 'production' && needs.candidate.outputs.verified == 'true'
|
|
242
250
|
runs-on: ubuntu-latest
|
|
243
251
|
timeout-minutes: 30
|
|
@@ -291,6 +299,7 @@ jobs:
|
|
|
291
299
|
id: start
|
|
292
300
|
env:
|
|
293
301
|
GH_TOKEN: ${{ github.token }}
|
|
302
|
+
GITHUB_DEPLOYMENT_REF: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
294
303
|
run: node "$FOUNDRY_EXECUTOR" record-start
|
|
295
304
|
- name: Set up Bun for verification only
|
|
296
305
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
@@ -8,6 +8,11 @@ on:
|
|
|
8
8
|
required: false
|
|
9
9
|
type: string
|
|
10
10
|
default: preview
|
|
11
|
+
draft-protection:
|
|
12
|
+
description: 'Skip draft pull requests by default; set false to run deployments for drafts.'
|
|
13
|
+
required: false
|
|
14
|
+
type: boolean
|
|
15
|
+
default: true
|
|
11
16
|
working-directory:
|
|
12
17
|
required: false
|
|
13
18
|
type: string
|
|
@@ -68,6 +73,7 @@ jobs:
|
|
|
68
73
|
name: Cloudflare Deploy / ${{ inputs.mode }}
|
|
69
74
|
if: >-
|
|
70
75
|
vars.CI_BILLING_PAUSED != 'true' &&
|
|
76
|
+
(inputs['draft-protection'] != true || github.event_name != 'pull_request' || github.event.pull_request.draft == false) &&
|
|
71
77
|
github.event_name != 'pull_request_target' &&
|
|
72
78
|
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
|
|
73
79
|
runs-on: ${{ inputs.runner }}
|
|
@@ -98,9 +104,10 @@ jobs:
|
|
|
98
104
|
GH_TOKEN: ${{ github.token }}
|
|
99
105
|
MODE: ${{ inputs.mode }}
|
|
100
106
|
ENVIRONMENT: ${{ inputs.environment || (inputs.mode == 'production' && 'Production' || 'Preview') }}
|
|
107
|
+
GITHUB_DEPLOYMENT_REF: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
101
108
|
run: |
|
|
102
109
|
set -euo pipefail
|
|
103
|
-
body=$(jq -n --arg ref "$
|
|
110
|
+
body=$(jq -n --arg ref "$GITHUB_DEPLOYMENT_REF" --arg environment "$ENVIRONMENT" \
|
|
104
111
|
--argjson production "$([ "$MODE" = production ] && echo true || echo false)" \
|
|
105
112
|
'{ref: $ref, environment: $environment, auto_merge: false, production_environment: $production, required_contexts: []}')
|
|
106
113
|
id=$(gh api "repos/$GITHUB_REPOSITORY/deployments" --input - <<< "$body" --jq .id)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
name: Code Foundry Eval
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
runtime-repository:
|
|
7
|
+
description: Repository containing the Code Foundry runtime.
|
|
8
|
+
required: false
|
|
9
|
+
type: string
|
|
10
|
+
default: 0xPlayerOne/code-foundry
|
|
11
|
+
runtime-ref:
|
|
12
|
+
description: Code Foundry runtime tag or ref.
|
|
13
|
+
required: false
|
|
14
|
+
type: string
|
|
15
|
+
default: v1.0.5
|
|
16
|
+
runner:
|
|
17
|
+
description: >-
|
|
18
|
+
Runner used by the eval harness. Browser evals need a
|
|
19
|
+
Chrome-capable runner such as ubuntu-latest; ubuntu-slim does not
|
|
20
|
+
ship a browser.
|
|
21
|
+
required: false
|
|
22
|
+
type: string
|
|
23
|
+
default: ubuntu-latest
|
|
24
|
+
artifact-prefix:
|
|
25
|
+
description: Prefix for task receipts; use a unique value when calling this workflow more than once.
|
|
26
|
+
required: false
|
|
27
|
+
type: string
|
|
28
|
+
default: task-result
|
|
29
|
+
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
|
|
33
|
+
env:
|
|
34
|
+
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
35
|
+
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
36
|
+
REPO_FOUNDRY_PROFILE: ${{ vars.REPO_FOUNDRY_PROFILE }}
|
|
37
|
+
REPO_FOUNDRY_LANGUAGES: ${{ vars.REPO_FOUNDRY_LANGUAGES }}
|
|
38
|
+
REPO_FOUNDRY_FEATURES: ${{ vars.REPO_FOUNDRY_FEATURES }}
|
|
39
|
+
REPO_FOUNDRY_PACKAGE_MANAGER: ${{ vars.REPO_FOUNDRY_PACKAGE_MANAGER }}
|
|
40
|
+
REPO_FOUNDRY_RUNNER: ${{ vars.REPO_FOUNDRY_RUNNER }}
|
|
41
|
+
REPO_FOUNDRY_CACHE_PACKAGES: ${{ vars.REPO_FOUNDRY_CACHE_PACKAGES || 'auto' }}
|
|
42
|
+
REPO_FOUNDRY_CACHE_BUILD: ${{ vars.REPO_FOUNDRY_CACHE_BUILD || 'auto' }}
|
|
43
|
+
|
|
44
|
+
concurrency:
|
|
45
|
+
group: code-foundry-eval-${{ github.event_name }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
|
|
46
|
+
cancel-in-progress: true
|
|
47
|
+
|
|
48
|
+
jobs:
|
|
49
|
+
eval:
|
|
50
|
+
name: Eval
|
|
51
|
+
if: vars.CI_BILLING_PAUSED != 'true'
|
|
52
|
+
runs-on: ${{ inputs.runner }}
|
|
53
|
+
steps:
|
|
54
|
+
- name: Checkout
|
|
55
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
56
|
+
with:
|
|
57
|
+
persist-credentials: false
|
|
58
|
+
- name: Runtime
|
|
59
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
60
|
+
with:
|
|
61
|
+
persist-credentials: false
|
|
62
|
+
repository: ${{ inputs.runtime-repository }}
|
|
63
|
+
ref: ${{ inputs.runtime-ref }}
|
|
64
|
+
path: .github/.code-foundry
|
|
65
|
+
sparse-checkout: |
|
|
66
|
+
.github/actions
|
|
67
|
+
src/lib
|
|
68
|
+
src/runtime-core.mjs
|
|
69
|
+
src/runtime.mjs
|
|
70
|
+
- name: Install runtime
|
|
71
|
+
run: |
|
|
72
|
+
mkdir -p .github/actions
|
|
73
|
+
mv .github/.code-foundry "$RUNNER_TEMP/code-foundry"
|
|
74
|
+
cp -R "$RUNNER_TEMP/code-foundry/.github/actions/." .github/actions/
|
|
75
|
+
- name: Detect
|
|
76
|
+
id: applicability
|
|
77
|
+
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci should_run eval >> "$GITHUB_OUTPUT"
|
|
78
|
+
- name: Setup
|
|
79
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
80
|
+
uses: ./.github/actions/setup
|
|
81
|
+
with:
|
|
82
|
+
# Eval harnesses run through the repository's own package scripts and
|
|
83
|
+
# need their dependencies installed before the task executes.
|
|
84
|
+
install: 'true'
|
|
85
|
+
cache-build: 'true'
|
|
86
|
+
task: eval
|
|
87
|
+
- name: Eval
|
|
88
|
+
id: execute
|
|
89
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
90
|
+
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci eval
|
|
91
|
+
- name: Retain task receipt
|
|
92
|
+
if: >-
|
|
93
|
+
always() &&
|
|
94
|
+
(steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
|
|
95
|
+
steps.applicability.outputs.applicable == 'false') &&
|
|
96
|
+
hashFiles('.code-foundry/results/eval.json') != ''
|
|
97
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
98
|
+
with:
|
|
99
|
+
name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-eval
|
|
100
|
+
path: .code-foundry/results/eval.json
|
|
101
|
+
include-hidden-files: true
|
|
102
|
+
if-no-files-found: error
|
|
103
|
+
retention-days: 14
|
|
104
|
+
- name: Retain eval report
|
|
105
|
+
if: >-
|
|
106
|
+
always() &&
|
|
107
|
+
steps.execute.outcome == 'failure' &&
|
|
108
|
+
hashFiles('eval-results/result.json') != ''
|
|
109
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
110
|
+
with:
|
|
111
|
+
name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-eval-report
|
|
112
|
+
path: |
|
|
113
|
+
eval-results/result.json
|
|
114
|
+
eval-results/summary.json
|
|
115
|
+
if-no-files-found: error
|
|
116
|
+
retention-days: 14
|
|
@@ -14,7 +14,7 @@ permissions:
|
|
|
14
14
|
jobs:
|
|
15
15
|
detect:
|
|
16
16
|
name: OpenCode Security / Detect
|
|
17
|
-
if: vars.CI_BILLING_PAUSED != 'true' && (github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == false)
|
|
17
|
+
if: vars.CI_BILLING_PAUSED != 'true' && (github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false))
|
|
18
18
|
runs-on: ubuntu-slim
|
|
19
19
|
outputs:
|
|
20
20
|
enabled: ${{ steps.detect.outputs.enabled }}
|
|
@@ -11,11 +11,11 @@ on:
|
|
|
11
11
|
description: Exact pre-attached Code Foundry npm tarball name.
|
|
12
12
|
required: true
|
|
13
13
|
type: string
|
|
14
|
-
|
|
15
|
-
description:
|
|
14
|
+
billing-pause-bypass:
|
|
15
|
+
description: Allow only an explicitly dispatched release while shared CI remains paused.
|
|
16
16
|
required: false
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
type: boolean
|
|
18
|
+
default: false
|
|
19
19
|
secrets:
|
|
20
20
|
NPM_TOKEN:
|
|
21
21
|
required: false
|
|
@@ -27,7 +27,7 @@ permissions:
|
|
|
27
27
|
jobs:
|
|
28
28
|
qualify:
|
|
29
29
|
name: Qualify Node ${{ matrix.node }}
|
|
30
|
-
if: vars.CI_BILLING_PAUSED != 'true' && github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
|
|
30
|
+
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')
|
|
31
31
|
runs-on: ubuntu-24.04
|
|
32
32
|
timeout-minutes: 15
|
|
33
33
|
strategy:
|
|
@@ -76,10 +76,9 @@ jobs:
|
|
|
76
76
|
publish:
|
|
77
77
|
name: Publish qualified archive
|
|
78
78
|
needs: qualify
|
|
79
|
-
if: vars.CI_BILLING_PAUSED != 'true' && github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
|
|
79
|
+
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')
|
|
80
80
|
runs-on: ubuntu-24.04
|
|
81
81
|
timeout-minutes: 15
|
|
82
|
-
environment: ${{ inputs.environment }}
|
|
83
82
|
concurrency:
|
|
84
83
|
group: qualified-foundry-npm-${{ github.repository }}-${{ inputs.tag }}
|
|
85
84
|
cancel-in-progress: false
|
|
@@ -23,6 +23,29 @@ on:
|
|
|
23
23
|
required: false
|
|
24
24
|
type: boolean
|
|
25
25
|
default: false
|
|
26
|
+
config-file:
|
|
27
|
+
description: Repository-relative Release Please configuration file.
|
|
28
|
+
required: false
|
|
29
|
+
type: string
|
|
30
|
+
default: release-please-config.json
|
|
31
|
+
defer-publication:
|
|
32
|
+
description: Create a root draft release for an external qualified publisher; skip legacy publication and post-release jobs.
|
|
33
|
+
required: false
|
|
34
|
+
type: boolean
|
|
35
|
+
default: false
|
|
36
|
+
outputs:
|
|
37
|
+
release_created:
|
|
38
|
+
description: Whether Release Please created a release (possibly a draft).
|
|
39
|
+
value: ${{ jobs.release.outputs.release_created }}
|
|
40
|
+
tag_name:
|
|
41
|
+
description: Exact tag returned by Release Please.
|
|
42
|
+
value: ${{ jobs.release.outputs.tag_name }}
|
|
43
|
+
sha:
|
|
44
|
+
description: Exact source commit returned by Release Please.
|
|
45
|
+
value: ${{ jobs.release.outputs.sha }}
|
|
46
|
+
token_source:
|
|
47
|
+
description: Credential selected for release operations.
|
|
48
|
+
value: ${{ jobs.release.outputs.token_source }}
|
|
26
49
|
secrets:
|
|
27
50
|
CODE_FOUNDRY_TOKEN:
|
|
28
51
|
required: false
|
|
@@ -57,6 +80,7 @@ jobs:
|
|
|
57
80
|
outputs:
|
|
58
81
|
release_created: ${{ steps.release.outputs.release_created || 'false' }}
|
|
59
82
|
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
83
|
+
sha: ${{ steps.release.outputs.sha }}
|
|
60
84
|
npm_publish: ${{ steps.profile.outputs.npm_publish }}
|
|
61
85
|
token_source: ${{ steps.credentials.outputs.token_source }}
|
|
62
86
|
steps:
|
|
@@ -77,6 +101,9 @@ jobs:
|
|
|
77
101
|
run: mv .github/.code-foundry "$RUNNER_TEMP/code-foundry"
|
|
78
102
|
- name: Detect release profile
|
|
79
103
|
id: profile
|
|
104
|
+
env:
|
|
105
|
+
RELEASE_CONFIG_FILE: ${{ inputs.config-file }}
|
|
106
|
+
DEFER_PUBLICATION: ${{ inputs.defer-publication }}
|
|
80
107
|
run: |
|
|
81
108
|
node <<'NODE'
|
|
82
109
|
const fs = require('node:fs')
|
|
@@ -108,9 +135,25 @@ jobs:
|
|
|
108
135
|
if (releaseType === 'none' || !fs.existsSync('package.json')) npmPublish = 'false'
|
|
109
136
|
let legacyReleaseType = ''
|
|
110
137
|
if (releaseType !== 'none') {
|
|
111
|
-
const sourcePath = 'release-please-config.json'
|
|
138
|
+
const sourcePath = process.env.RELEASE_CONFIG_FILE || 'release-please-config.json'
|
|
139
|
+
if (!/^(?:[A-Za-z0-9_.-]+\/)*[A-Za-z0-9_.-]+\.json$/.test(sourcePath) || sourcePath.split('/').includes('..')) {
|
|
140
|
+
throw new Error('config-file must be a repository-relative JSON path')
|
|
141
|
+
}
|
|
112
142
|
if (!fs.existsSync(sourcePath)) throw new Error(`Missing ${sourcePath}`)
|
|
143
|
+
const path = require('node:path')
|
|
144
|
+
const relative = path.relative(fs.realpathSync('.'), fs.realpathSync(sourcePath))
|
|
145
|
+
if (relative === '..' || relative.startsWith(`..${path.sep}`) || !fs.statSync(sourcePath).isFile()) {
|
|
146
|
+
throw new Error('config-file must be a file inside the repository')
|
|
147
|
+
}
|
|
113
148
|
const releaseConfig = JSON.parse(fs.readFileSync(sourcePath, 'utf8'))
|
|
149
|
+
if (process.env.DEFER_PUBLICATION === 'true') {
|
|
150
|
+
const rootPackage = releaseConfig.packages?.['.']
|
|
151
|
+
if (!rootPackage || Object.keys(releaseConfig.packages).length !== 1 ||
|
|
152
|
+
(rootPackage.draft ?? releaseConfig.draft) !== true ||
|
|
153
|
+
(rootPackage['force-tag-creation'] ?? releaseConfig['force-tag-creation']) !== true) {
|
|
154
|
+
throw new Error('defer-publication requires one root package with draft and force-tag-creation enabled')
|
|
155
|
+
}
|
|
156
|
+
}
|
|
114
157
|
if (!releaseConfig.packages && !releaseConfig['release-type']) {
|
|
115
158
|
legacyReleaseType = releaseType
|
|
116
159
|
} else if (!fs.existsSync('.release-please-manifest.json')) {
|
|
@@ -152,7 +195,7 @@ jobs:
|
|
|
152
195
|
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5
|
|
153
196
|
with:
|
|
154
197
|
token: ${{ secrets.CODE_FOUNDRY_TOKEN }}
|
|
155
|
-
config-file:
|
|
198
|
+
config-file: ${{ inputs.config-file }}
|
|
156
199
|
release-type: ${{ steps.profile.outputs.legacy_release_type }}
|
|
157
200
|
- name: Release Please (workflow token)
|
|
158
201
|
id: release_workflow
|
|
@@ -160,7 +203,7 @@ jobs:
|
|
|
160
203
|
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5
|
|
161
204
|
with:
|
|
162
205
|
token: ${{ github.token }}
|
|
163
|
-
config-file:
|
|
206
|
+
config-file: ${{ inputs.config-file }}
|
|
164
207
|
release-type: ${{ steps.profile.outputs.legacy_release_type }}
|
|
165
208
|
- name: Normalize Release Please outputs
|
|
166
209
|
id: release
|
|
@@ -168,9 +211,11 @@ jobs:
|
|
|
168
211
|
env:
|
|
169
212
|
AUTOMATION_RELEASE_CREATED: ${{ steps.release_automation.outputs.release_created }}
|
|
170
213
|
AUTOMATION_TAG_NAME: ${{ steps.release_automation.outputs.tag_name }}
|
|
214
|
+
AUTOMATION_SHA: ${{ steps.release_automation.outputs.sha }}
|
|
171
215
|
AUTOMATION_PRS_CREATED: ${{ steps.release_automation.outputs.prs_created }}
|
|
172
216
|
WORKFLOW_RELEASE_CREATED: ${{ steps.release_workflow.outputs.release_created }}
|
|
173
217
|
WORKFLOW_TAG_NAME: ${{ steps.release_workflow.outputs.tag_name }}
|
|
218
|
+
WORKFLOW_SHA: ${{ steps.release_workflow.outputs.sha }}
|
|
174
219
|
WORKFLOW_PRS_CREATED: ${{ steps.release_workflow.outputs.prs_created }}
|
|
175
220
|
run: |
|
|
176
221
|
set -euo pipefail
|
|
@@ -182,12 +227,14 @@ jobs:
|
|
|
182
227
|
{
|
|
183
228
|
echo "release_created=$AUTOMATION_RELEASE_CREATED"
|
|
184
229
|
echo "tag_name=$AUTOMATION_TAG_NAME"
|
|
230
|
+
echo "sha=$AUTOMATION_SHA"
|
|
185
231
|
echo "prs_created=$AUTOMATION_PRS_CREATED"
|
|
186
232
|
} >> "$GITHUB_OUTPUT"
|
|
187
233
|
else
|
|
188
234
|
{
|
|
189
235
|
echo "release_created=$WORKFLOW_RELEASE_CREATED"
|
|
190
236
|
echo "tag_name=$WORKFLOW_TAG_NAME"
|
|
237
|
+
echo "sha=$WORKFLOW_SHA"
|
|
191
238
|
echo "prs_created=$WORKFLOW_PRS_CREATED"
|
|
192
239
|
} >> "$GITHUB_OUTPUT"
|
|
193
240
|
fi
|
|
@@ -325,7 +372,7 @@ jobs:
|
|
|
325
372
|
# creating a release. Reconciliation is only meaningful after a release
|
|
326
373
|
# commit/tag is actually created; otherwise the promotion's source changes
|
|
327
374
|
# are incorrectly rejected as non-metadata commits.
|
|
328
|
-
if: (vars.CI_BILLING_PAUSED != 'true' || inputs['billing-pause-bypass'] == true) && needs.release.result == 'success' && needs.release.outputs.release_created == 'true'
|
|
375
|
+
if: inputs['defer-publication'] != true && (vars.CI_BILLING_PAUSED != 'true' || inputs['billing-pause-bypass'] == true) && needs.release.result == 'success' && needs.release.outputs.release_created == 'true'
|
|
329
376
|
runs-on: ${{ inputs.runner }}
|
|
330
377
|
timeout-minutes: 15
|
|
331
378
|
permissions:
|
|
@@ -413,7 +460,7 @@ jobs:
|
|
|
413
460
|
post-release:
|
|
414
461
|
name: Post Hook
|
|
415
462
|
needs: release
|
|
416
|
-
if: (vars.CI_BILLING_PAUSED != 'true' || inputs['billing-pause-bypass'] == true) && needs.release.outputs.release_created == 'true'
|
|
463
|
+
if: inputs['defer-publication'] != true && (vars.CI_BILLING_PAUSED != 'true' || inputs['billing-pause-bypass'] == true) && needs.release.outputs.release_created == 'true'
|
|
417
464
|
runs-on: ${{ inputs.runner }}
|
|
418
465
|
timeout-minutes: 15
|
|
419
466
|
concurrency:
|
|
@@ -423,9 +470,6 @@ jobs:
|
|
|
423
470
|
actions: write
|
|
424
471
|
contents: read
|
|
425
472
|
env:
|
|
426
|
-
# The release job validates the configured automation token and exposes
|
|
427
|
-
# the selection; the post hook reuses the same selected credential so a
|
|
428
|
-
# rejected long-lived token is never used after fallback.
|
|
429
473
|
CODE_FOUNDRY_TOKEN: ${{ needs.release.outputs.token_source == 'configured' && (secrets.CODE_FOUNDRY_TOKEN) || github.token }}
|
|
430
474
|
GH_TOKEN: ${{ needs.release.outputs.token_source == 'configured' && (secrets.CODE_FOUNDRY_TOKEN) || github.token }}
|
|
431
475
|
steps:
|
|
@@ -464,7 +508,11 @@ jobs:
|
|
|
464
508
|
NODE
|
|
465
509
|
- name: Dispatch post-release hook once
|
|
466
510
|
if: steps.hook.outputs.enabled == 'true' && steps.hook.outputs.workflow != ''
|
|
467
|
-
|
|
511
|
+
env:
|
|
512
|
+
RELEASE_TAG: ${{ needs.release.outputs.tag_name }}
|
|
513
|
+
POST_RELEASE_WORKFLOW: ${{ steps.hook.outputs.workflow }}
|
|
514
|
+
POST_RELEASE_MODE: ${{ steps.hook.outputs.mode }}
|
|
515
|
+
run: node "$RUNNER_TEMP/code-foundry/src/cli.mjs" release hook --tag "$RELEASE_TAG" --workflow "$POST_RELEASE_WORKFLOW" --mode "$POST_RELEASE_MODE"
|
|
468
516
|
- name: Explain disabled post-release hook
|
|
469
517
|
if: steps.hook.outputs.enabled != 'true' || steps.hook.outputs.workflow == ''
|
|
470
518
|
run: echo 'No post-release workflow configured; artifact delivery is intentionally skipped.'
|
|
@@ -472,7 +520,7 @@ jobs:
|
|
|
472
520
|
npm:
|
|
473
521
|
name: Publish npm
|
|
474
522
|
needs: release
|
|
475
|
-
if: (vars.CI_BILLING_PAUSED != 'true' || inputs['billing-pause-bypass'] == true) && needs.release.outputs.release_created == 'true' && needs.release.outputs.npm_publish == 'true'
|
|
523
|
+
if: inputs['defer-publication'] != true && (vars.CI_BILLING_PAUSED != 'true' || inputs['billing-pause-bypass'] == true) && needs.release.outputs.release_created == 'true' && needs.release.outputs.npm_publish == 'true'
|
|
476
524
|
runs-on: ${{ inputs.runner }}
|
|
477
525
|
timeout-minutes: 15
|
|
478
526
|
permissions:
|