返回 DeepSeek-Reasonix
release-npm.yml
根目录 / .github / workflows / release-npm.yml
1 name: Release npm
2
3 # npm line. Stable releases are called by release-stable.yml after its single
4 # GitHub environment approval. Direct prerelease tag publication is disabled;
5 # manual workflow_dispatch recovers only an approved Stable version from an
6 # existing tag. Historical canary and next package identities remain readable,
7 # but normal publication advances only the official release.
8 on:
9 workflow_dispatch:
10 inputs:
11 channel:
12 description: "Standalone npm recovery channel"
13 required: true
14 default: stable
15 type: choice
16 options:
17 - stable
18 base_version:
19 description: "Stable version to recover exactly"
20 required: true
21 type: string
22 tag:
23 description: "stable: existing npm tag to publish (for example npm-v1.18.0)"
24 required: false
25 type: string
26 workflow_call:
27 inputs:
28 channel:
29 description: "Publish channel selected by the approved release orchestrator"
30 required: true
31 type: string
32 base_version:
33 description: "Version to publish"
34 required: true
35 type: string
36 tag:
37 description: "Existing npm tag to check out for stable publication"
38 required: false
39 default: ""
40 type: string
41 approved_cli_tag:
42 description: "Stable CLI tag recorded by the approved orchestrator"
43 required: true
44 type: string
45 approved_sha:
46 description: "Immutable commit recorded by the approved orchestrator"
47 required: true
48 type: string
49 orchestrated:
50 description: "True only when called by an approved release orchestrator"
51 required: false
52 default: false
53 type: boolean
54 orchestrator:
55 description: "Trusted release orchestrator (legacy Preview calls remain readable)"
56 required: false
57 default: stable
58 type: string
59 preview_number:
60 description: "Legacy Preview ordinal for old workflow-call compatibility"
61 required: false
62 default: ""
63 type: string
64 candidate_artifact_name:
65 description: "Same-run artifact containing a verified sealed release candidate"
66 required: false
67 default: ""
68 type: string
69 candidate_verified:
70 description: "Protected Stable preflight verified candidate provenance and bytes"
71 required: false
72 default: false
73 type: boolean
74
75 permissions:
76 contents: read
77
78 concurrency:
79 # Serialize every publisher for one npm dist-tag. Historical canary calls
80 # remain ordered even though normal publication now uses Stable only.
81 group: release-npm-${{ inputs.channel || 'next' }}
82 cancel-in-progress: false
83
84 jobs:
85 orchestration-guard:
86 name: verify approved orchestrator
87 if: ${{ inputs.orchestrated }}
88 runs-on: ubuntu-latest
89 permissions:
90 contents: read
91 steps:
92 - uses: actions/checkout@v7
93 with:
94 fetch-depth: 0
95 ref: ${{ github.sha }}
96 - name: Verify caller and approved release ref
97 env:
98 ACTUAL_CALLER_WORKFLOW_REF: ${{ github.workflow_ref }}
99 EXPECTED_CALLER_WORKFLOW_REF: ${{ format('{0}/.github/workflows/release-{1}.yml@{2}', github.repository, inputs.orchestrator, github.ref) }}
100 CALLER_EVENT_NAME: ${{ github.event_name }}
101 CALLER_REF: ${{ github.ref }}
102 CALLER_REF_PROTECTED: ${{ github.ref_protected }}
103 CALLER_WORKFLOW_SHA: ${{ github.workflow_sha }}
104 CALLER_SHA: ${{ github.sha }}
105 APPROVED_CLI_TAG: ${{ inputs.approved_cli_tag }}
106 APPROVED_SHA: ${{ inputs.approved_sha }}
107 APPROVED_CHANNEL: ${{ inputs.orchestrator == 'promote' && 'stable' || inputs.orchestrator }}
108 RELEASE_TAG: ${{ inputs.approved_cli_tag }}
109 VERIFY_RELEASE_CHECKOUT: false
110 run: |
111 bash scripts/verify-release-authorization.sh
112 bash scripts/verify-release-tag.sh
113 if [ -n "${{ inputs.candidate_artifact_name }}" ] && [ "${{ inputs.candidate_verified }}" != "true" ]; then
114 echo "::error::prepared npm packages require verified candidate provenance"
115 exit 1
116 fi
117
118 release-gate:
119 name: approve standalone npm release
120 if: ${{ !inputs.orchestrated }}
121 runs-on: ubuntu-latest
122 environment: release
123 steps:
124 - env:
125 RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
126 run: echo "Approved standalone npm release $RELEASE_TAG"
127
128 cache-guard:
129 name: cache hit guard
130 needs: [orchestration-guard, release-gate]
131 if: ${{ always() && !cancelled() && ((inputs.orchestrated && needs.orchestration-guard.result == 'success') || (!inputs.orchestrated && needs.release-gate.result == 'success')) }}
132 runs-on: ubuntu-latest
133 steps:
134 - uses: actions/checkout@v7
135 with:
136 ref: ${{ inputs.approved_sha || inputs.tag || github.ref }}
137 - uses: actions/setup-go@v7
138 if: ${{ !inputs.candidate_verified }}
139 with:
140 go-version-file: go.mod
141 cache: true
142 - if: ${{ !inputs.candidate_verified }}
143 run: ./scripts/cache-guard.sh
144 - name: Verify embedded documentation identity
145 if: ${{ !inputs.candidate_verified }}
146 env:
147 DOCS_BUILD_VERSION: v${{ inputs.base_version }}
148 DOCS_SOURCE_REVISION: ${{ inputs.approved_sha }}
149 run: |
150 if [ ! -f scripts/verify-embedded-docs.sh ]; then
151 echo "Legacy candidate predates the embedded docs contract; skipping."
152 exit 0
153 fi
154 revision="$DOCS_SOURCE_REVISION"
155 if [ -z "$revision" ]; then revision="$(git rev-parse HEAD)"; fi
156 bash scripts/verify-embedded-docs.sh "$DOCS_BUILD_VERSION" "$revision"
157
158 npm:
159 name: publish npm packages
160 needs: cache-guard
161 if: ${{ always() && !cancelled() && needs.cache-guard.result == 'success' }}
162 runs-on: ubuntu-latest
163 # id-token lets npm publish --provenance attach a Sigstore attestation; the
164 # registry token still authenticates the publish itself.
165 permissions:
166 contents: read
167 id-token: write
168 # Orchestrated releases have already passed their GitHub environment
169 # approval. Direct prereleases and manual Stable recovery pass release-gate.
170 steps:
171 - uses: actions/checkout@v7
172 with:
173 ref: ${{ inputs.approved_sha || inputs.tag || github.ref }}
174 - name: Load approved npm publication control plane
175 env:
176 RECOVERY_CONTROL_SHA: ${{ github.workflow_sha }}
177 run: |
178 set -euo pipefail
179 git fetch --no-tags --depth=1 origin "$RECOVERY_CONTROL_SHA"
180 git restore --source="$RECOVERY_CONTROL_SHA" -- \
181 npm/publish.mjs \
182 npm/publish-candidate.mjs \
183 scripts/finalize-npm-official-release.mjs
184 - uses: actions/setup-go@v7
185 if: ${{ inputs.candidate_artifact_name == '' }}
186 with:
187 go-version-file: go.mod
188 cache: true
189 - uses: actions/setup-node@v7
190 with:
191 node-version: '22'
192 registry-url: 'https://registry.npmjs.org'
193 - name: Download sealed release candidate
194 if: ${{ inputs.candidate_artifact_name != '' }}
195 uses: actions/download-artifact@v8
196 with:
197 name: ${{ inputs.candidate_artifact_name }}
198 path: ${{ runner.temp }}/release-candidate
199 # Stable publication uses the exact npm-v* tag. build.mjs strips the
200 # leading `npm-`/`v`; historical Preview inputs remain only for old
201 # workflow-call compatibility and are not reachable from a public entry.
202 - name: Resolve version
203 id: ver
204 env:
205 EVENT_NAME: ${{ github.event_name }}
206 IN_ORCHESTRATED: ${{ inputs.orchestrated }}
207 IN_CHANNEL: ${{ inputs.channel }}
208 IN_BASE_VERSION: ${{ inputs.base_version }}
209 IN_TAG: ${{ inputs.tag }}
210 REF_NAME: ${{ github.ref_name }}
211 RUN_NUMBER: ${{ github.run_number }}
212 IN_PREVIEW_NUMBER: ${{ inputs.preview_number }}
213 run: bash scripts/resolve-npm-release.sh
214 - name: Revalidate approved release ref
215 if: ${{ inputs.orchestrated }}
216 env:
217 RELEASE_TAG: ${{ inputs.approved_cli_tag }}
218 APPROVED_SHA: ${{ inputs.approved_sha }}
219 run: bash scripts/verify-release-tag.sh
220 - name: Publish or recover immutable npm packages
221 if: ${{ inputs.candidate_artifact_name == '' }}
222 env:
223 VERSION_ARG: ${{ steps.ver.outputs.arg }}
224 NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
225 run: node npm/build.mjs "$VERSION_ARG" --publish
226 - name: Publish or recover prepared npm packages
227 if: ${{ inputs.candidate_artifact_name != '' }}
228 env:
229 VERSION: ${{ inputs.base_version }}
230 APPROVED_SHA: ${{ inputs.approved_sha }}
231 NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
232 run: node npm/publish-candidate.mjs "$RUNNER_TEMP/release-candidate/npm" "$VERSION" "$APPROVED_SHA"
233 - name: Align legacy aliases with the official release
234 if: ${{ inputs.channel == 'stable' }}
235 env:
236 VERSION: ${{ inputs.base_version }}
237 APPROVED_SHA: ${{ inputs.approved_sha }}
238 NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
239 run: |
240 set -euo pipefail
241 candidate="$APPROVED_SHA"
242 if [ -z "$candidate" ]; then candidate="$(git rev-parse HEAD)"; fi
243 EXPECTED_SHA="$candidate" node scripts/finalize-npm-official-release.mjs "$VERSION"
244
244 lines YAML