返回 CodeWhale
release-artifacts.yml
根目录 / .github / workflows / release-artifacts.yml
1 name: Release artifacts
2
3 on:
4 workflow_call:
5 inputs:
6 source_sha:
7 description: Exact 40-character source commit to build
8 required: true
9 type: string
10 version:
11 description: Workspace version without a v prefix
12 required: true
13 type: string
14 compiled_host_delivery:
15 description: Explicitly include qualified optional companions; Node stays default
16 required: false
17 default: false
18 type: boolean
19 compiled_host_run_id:
20 description: Green official CI run at this exact SHA containing Native image proof
21 required: false
22 default: ''
23 type: string
24 compiled_host_targets:
25 description: Comma-separated matching-native targets with qualified CI receipts
26 required: false
27 default: linux-x64,macos-arm64,windows-x64
28 type: string
29 retention_days:
30 description: Retention for Actions-only intermediate and assembled artifacts
31 required: false
32 default: 7
33 type: number
34
35 permissions:
36 contents: read
37 actions: read
38
39 env:
40 CARGO_TERM_COLOR: always
41 CARGO_INCREMENTAL: 0
42 RUSTFLAGS: -Dwarnings
43 # Build identity is the trusted workflow SHA. Callers pass source_sha only
44 # so `pin` can refuse a mismatch; it must not retarget checkout or caches.
45 CODEWHALE_BUILD_SHA: ${{ github.sha }}
46
47 jobs:
48 pin:
49 name: Pin caller SHA to this run
50 timeout-minutes: 10
51 runs-on: ubuntu-latest
52 steps:
53 - name: Require source_sha equals github.sha
54 env:
55 SOURCE_SHA: ${{ inputs.source_sha }}
56 run: |
57 set -euo pipefail
58 if [[ "${#SOURCE_SHA}" -ne 40 || "${SOURCE_SHA}" =~ [^0-9a-fA-F] ]]; then
59 echo "::error::source_sha must be a full 40-character commit SHA." >&2
60 exit 1
61 fi
62 expected="$(printf '%s' "${SOURCE_SHA}" | tr '[:upper:]' '[:lower:]')"
63 actual="$(printf '%s' "${GITHUB_SHA}" | tr '[:upper:]' '[:lower:]')"
64 if [[ "${actual}" != "${expected}" ]]; then
65 echo "::error::Reusable workflow SHA ${actual} does not match source_sha ${SOURCE_SHA}." >&2
66 exit 1
67 fi
68 - name: Require explicit matching-native qualification selection
69 if: inputs.compiled_host_delivery
70 env:
71 QUALIFICATION_RUN: ${{ inputs.compiled_host_run_id }}
72 HOST_TARGETS: ${{ inputs.compiled_host_targets }}
73 run: |
74 set -euo pipefail
75 [[ "${QUALIFICATION_RUN}" =~ ^[0-9]+$ ]] || { echo 'Enabled host delivery needs an exact green CI run id' >&2; exit 1; }
76 [[ -n "${HOST_TARGETS}" ]] || { echo 'No compiled host targets selected' >&2; exit 1; }
77 declare -A seen
78 IFS=',' read -ra selected <<< "${HOST_TARGETS}"
79 for target in "${selected[@]}"; do
80 case "${target}" in linux-x64|linux-arm64|macos-x64|macos-arm64|windows-x64|windows-arm64) ;; *) echo "Unsupported compiled host target ${target}" >&2; exit 1 ;; esac
81 [[ -z "${seen[${target}]:-}" ]] || { echo "Duplicate compiled host target ${target}" >&2; exit 1; }
82 seen["${target}"]=1
83 done
84
85 build:
86 name: Build ${{ matrix.platform }}
87 timeout-minutes: 90
88 # FreeBSD is a source-build target validated via `cargo check --target x86_64-unknown-freebsd -p codewhale-cli --locked`
89 # (see packaging/freebsd/README.md and docs/INSTALL.md#freebsd). The 7×1 prebuilt matrix stays 7 targets;
90 # FreeBSD has no prebuilt asset, no npm binary, and no matrix bloat — it builds from source.
91 strategy:
92 fail-fast: false
93 matrix:
94 include:
95 - os: ubuntu-latest
96 target: x86_64-unknown-linux-musl
97 platform: linux-x64
98 cli_binary: codewhale
99 shim_binary: codew
100 cli_artifact: codewhale-linux-x64
101 shim_artifact: codew-linux-x64
102 compat_tui_artifact: codewhale-tui-linux-x64
103 - os: ubuntu-24.04-arm
104 target: aarch64-unknown-linux-musl
105 platform: linux-arm64
106 cli_binary: codewhale
107 shim_binary: codew
108 cli_artifact: codewhale-linux-arm64
109 shim_artifact: codew-linux-arm64
110 compat_tui_artifact: codewhale-tui-linux-arm64
111 - os: ubuntu-latest
112 target: aarch64-linux-android
113 platform: android-arm64
114 cli_binary: codewhale
115 shim_binary: codew
116 cli_artifact: codewhale-android-arm64
117 shim_artifact: codew-android-arm64
118 compat_tui_artifact: codewhale-tui-android-arm64
119 - os: macos-latest
120 target: x86_64-apple-darwin
121 platform: macos-x64
122 cli_binary: codewhale
123 shim_binary: codew
124 cli_artifact: codewhale-macos-x64
125 shim_artifact: codew-macos-x64
126 compat_tui_artifact: codewhale-tui-macos-x64
127 - os: macos-latest
128 target: aarch64-apple-darwin
129 platform: macos-arm64
130 cli_binary: codewhale
131 shim_binary: codew
132 cli_artifact: codewhale-macos-arm64
133 shim_artifact: codew-macos-arm64
134 compat_tui_artifact: codewhale-tui-macos-arm64
135 - os: windows-latest
136 target: x86_64-pc-windows-msvc
137 platform: windows-x64
138 cli_binary: codewhale.exe
139 shim_binary: codew.exe
140 cli_artifact: codewhale-windows-x64.exe
141 shim_artifact: codew-windows-x64.exe
142 compat_tui_artifact: codewhale-tui-windows-x64.exe
143 - os: windows-11-arm
144 target: aarch64-pc-windows-msvc
145 platform: windows-arm64
146 cli_binary: codewhale.exe
147 shim_binary: codew.exe
148 cli_artifact: codewhale-windows-arm64.exe
149 shim_artifact: codew-windows-arm64.exe
150 compat_tui_artifact: codewhale-tui-windows-arm64.exe
151 runs-on: ${{ matrix.os }}
152 needs: pin
153 steps:
154 # No ref: — GITHUB_SHA only. CodeQL treats workflow_call checkout-with-ref
155 # and any ref named *sha* as an untrusted checkout (cache-poisoning).
156 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
157 - uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # master 2026-07-18
158 with:
159 toolchain: stable
160 targets: ${{ matrix.target }}
161 - uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
162 id: sccache
163 continue-on-error: true
164 - name: Enable sccache
165 if: steps.sccache.outcome == 'success'
166 shell: bash
167 run: |
168 {
169 echo "SCCACHE_GHA_ENABLED=true"
170 echo "RUSTC_WRAPPER=sccache"
171 echo "SCCACHE_IGNORE_SERVER_IO_ERROR=1"
172 } >> "${GITHUB_ENV}"
173 # Restore after the trusted lockfile is on disk. Key is OS + arch +
174 # explicit stable toolchain + rust-cache's Cargo.lock / rust-toolchain
175 # hash. Never interpolate github.event, github.ref, github.sha, or inputs.
176 - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
177 with:
178 cache-bin: false
179 prefix-key: v1-${{ runner.os }}-${{ runner.arch }}-stable
180 - name: Build static Linux binaries (musl)
181 if: endsWith(matrix.target, '-unknown-linux-musl')
182 shell: bash
183 run: |
184 sudo apt-get update
185 sudo apt-get install -y binutils musl-tools
186 rustup target add --toolchain stable ${{ matrix.target }}
187 cargo build --profile dist --locked --target ${{ matrix.target }} -p codewhale-cli
188 - name: Configure Android NDK linker
189 if: matrix.target == 'aarch64-linux-android' && runner.os == 'Linux'
190 shell: bash
191 env:
192 ANDROID_NDK_VERSION: 27.2.12479018
193 run: |
194 set -euo pipefail
195 sudo apt-get update
196 sudo apt-get install -y libclang-dev
197 ndk="${ANDROID_NDK_ROOT:-${ANDROID_NDK_HOME:-}}"
198 linker=""
199 if [[ -n "${ndk}" ]]; then
200 linker="${ndk}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
201 fi
202 if [[ -z "${linker}" || ! -x "${linker}" ]]; then
203 if ! command -v sdkmanager >/dev/null 2>&1; then
204 echo "sdkmanager is required to install Android NDK ${ANDROID_NDK_VERSION}" >&2
205 exit 1
206 fi
207 android_home="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
208 if [[ -z "${android_home}" ]]; then
209 echo "ANDROID_HOME or ANDROID_SDK_ROOT is required to install Android NDK ${ANDROID_NDK_VERSION}" >&2
210 exit 1
211 fi
212 yes | sdkmanager --licenses >/dev/null || true
213 sdkmanager --install "ndk;${ANDROID_NDK_VERSION}"
214 ndk="${android_home}/ndk/${ANDROID_NDK_VERSION}"
215 linker="${ndk}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
216 fi
217 ar="${ndk}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
218 if [[ ! -x "${linker}" ]]; then
219 echo "Android linker not found: ${linker}" >&2
220 exit 1
221 fi
222 if [[ ! -x "${ar}" ]]; then
223 echo "Android archiver not found: ${ar}" >&2
224 exit 1
225 fi
226 {
227 echo "ANDROID_NDK_ROOT=${ndk}"
228 echo "ANDROID_NDK_HOME=${ndk}"
229 echo "CC_aarch64_linux_android=${linker}"
230 echo "AR_aarch64_linux_android=${ar}"
231 echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=${linker}"
232 echo "BINDGEN_EXTRA_CLANG_ARGS_aarch64_linux_android=--target=aarch64-linux-android24 --sysroot=${ndk}/toolchains/llvm/prebuilt/linux-x86_64/sysroot"
233 } >> "${GITHUB_ENV}"
234 - name: Build
235 if: ${{ !endsWith(matrix.target, '-unknown-linux-musl') }}
236 shell: bash
237 run: cargo build --profile dist --locked --target ${{ matrix.target }} -p codewhale-cli
238 - name: Materialize codew command alias
239 shell: bash
240 run: |
241 bin_dir="target/${{ matrix.target }}/dist"
242 cp "${bin_dir}/${{ matrix.cli_binary }}" "${bin_dir}/${{ matrix.shim_binary }}"
243 cmp "${bin_dir}/${{ matrix.cli_binary }}" "${bin_dir}/${{ matrix.shim_binary }}"
244 - name: Verify static Linux binaries and launch on matching native runners
245 if: >-
246 endsWith(matrix.target, '-unknown-linux-musl') &&
247 ((startsWith(matrix.target, 'x86_64-') && runner.arch == 'X64') ||
248 (startsWith(matrix.target, 'aarch64-') && runner.arch == 'ARM64'))
249 shell: bash
250 run: |
251 set -euo pipefail
252 bin_dir="target/${{ matrix.target }}/dist"
253 for binary in "${{ matrix.cli_binary }}" "${{ matrix.shim_binary }}"; do
254 bin_path="${bin_dir}/${binary}"
255 if readelf -l "${bin_path}" | grep -Fq 'INTERP'; then
256 echo "Expected a static musl binary, but ${bin_path} has an ELF interpreter" >&2
257 exit 1
258 fi
259 "${bin_path}" --version
260 done
261 - name: Smoke binaries on matching native runners
262 if: >-
263 matrix.target != 'aarch64-linux-android' &&
264 ((startsWith(matrix.target, 'x86_64-') && runner.arch == 'X64') ||
265 (startsWith(matrix.target, 'aarch64-') && runner.arch == 'ARM64'))
266 shell: bash
267 run: |
268 bin_dir="target/${{ matrix.target }}/dist"
269 "${bin_dir}/${{ matrix.cli_binary }}" --version
270 "${bin_dir}/${{ matrix.shim_binary }}" --version
271 - name: Stage binaries
272 shell: bash
273 run: |
274 stage_binary() {
275 local binary="$1"
276 local artifact="$2"
277 local bin_path="target/${{ matrix.target }}/dist/${binary}"
278 if [[ ! -f "${bin_path}" ]]; then
279 echo "Binary not at ${bin_path}; searching target/ for ${binary}:" >&2
280 find target -name "${binary}" -type f
281 exit 1
282 fi
283 cp "${bin_path}" "${artifact}"
284 }
285
286 stage_binary "${{ matrix.cli_binary }}" "${{ matrix.cli_artifact }}"
287 stage_binary "${{ matrix.shim_binary }}" "${{ matrix.shim_artifact }}"
288 # Compatibility bridge for v0.9.4's hard-coded release
289 # completeness/updater contract. This is the same runtime, not a
290 # separately compiled or installed TUI command.
291 stage_binary "${{ matrix.cli_binary }}" "${{ matrix.compat_tui_artifact }}"
292 - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2
293 if: inputs.compiled_host_delivery && contains(format(',{0},', inputs.compiled_host_targets), format(',{0},', matrix.platform))
294 with:
295 bun-version: 1.4.0
296 - name: Stage exact qualified optional host image
297 if: inputs.compiled_host_delivery && contains(format(',{0},', inputs.compiled_host_targets), format(',{0},', matrix.platform))
298 shell: bash
299 env:
300 GH_TOKEN: ${{ github.token }}
301 QUALIFICATION_RUN: ${{ inputs.compiled_host_run_id }}
302 TARGET_PLATFORM: ${{ matrix.platform }}
303 RELEASE_VERSION: ${{ inputs.version }}
304 RUNTIME_CLOSURE_DIR: ${{ vars.CODEWHALE_COMPILED_HOST_CLOSURE_DIR }}
305 run: |
306 set -euo pipefail
307 proof_dir="${RUNNER_TEMP}/compiled-host-proof-${TARGET_PLATFORM}"
308 node scripts/release/fetch-compiled-host-proof.js --repo "${GITHUB_REPOSITORY}" --run-id "${QUALIFICATION_RUN}" --source-sha "${GITHUB_SHA}" --target "${TARGET_PLATFORM}" --output "${proof_dir}"
309 test -n "${RUNTIME_CLOSURE_DIR}" || { echo 'Enabled compiled-host delivery requires reviewed complete local runtime notices/relink-source closure' >&2; exit 1; }
310 bun_binary="$(bun -p 'process.execPath')"
311 extension=""
312 if [[ "${TARGET_PLATFORM}" == windows-* ]]; then extension=.exe; fi
313 node scripts/release/stage-compiled-host.mjs --bun "${bun_binary}" --compiled-image "${proof_dir}/codewhale-extension-host${extension}" --native-receipt "${proof_dir}/native-receipt.json" --runtime-closure "${RUNTIME_CLOSURE_DIR}/${TARGET_PLATFORM}.json" --target "${TARGET_PLATFORM}" --version "${RELEASE_VERSION}" --source-sha "${GITHUB_SHA}" --output-dir "compiled-host-${TARGET_PLATFORM}"
314 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
315 if: inputs.compiled_host_delivery && contains(format(',{0},', inputs.compiled_host_targets), format(',{0},', matrix.platform))
316 with:
317 name: codewhale-compiled-host-${{ matrix.platform }}
318 path: |
319 compiled-host-${{ matrix.platform }}/codewhale-extension-host-*
320 compiled-host-${{ matrix.platform }}/codewhale-extension-hosts.json
321 if-no-files-found: error
322 retention-days: ${{ inputs.retention_days }}
323 overwrite: true
324 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
325 with:
326 name: ${{ matrix.cli_artifact }}
327 path: ${{ matrix.cli_artifact }}
328 if-no-files-found: error
329 retention-days: ${{ inputs.retention_days }}
330 overwrite: true
331 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
332 with:
333 name: ${{ matrix.shim_artifact }}
334 path: ${{ matrix.shim_artifact }}
335 if-no-files-found: error
336 retention-days: ${{ inputs.retention_days }}
337 overwrite: true
338 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
339 with:
340 name: ${{ matrix.compat_tui_artifact }}
341 path: ${{ matrix.compat_tui_artifact }}
342 if-no-files-found: error
343 retention-days: ${{ inputs.retention_days }}
344 overwrite: true
345
346 bundle:
347 timeout-minutes: 15
348 needs: build
349 if: ${{ !cancelled() && needs.build.result == 'success' }}
350 runs-on: ubuntu-latest
351 steps:
352 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
353 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
354 with:
355 path: artifacts
356 pattern: '*'
357 - name: Collect only qualified requested compiled hosts
358 if: inputs.compiled_host_delivery
359 shell: bash
360 run: node scripts/release/stage-compiled-host.mjs --collect artifacts --output-dir artifacts/codewhale-compiled-hosts
361 - name: Create and checksum platform archives
362 shell: bash
363 env:
364 SOURCE_SHA: ${{ github.sha }}
365 run: |
366 set -euo pipefail
367 source_date_epoch="$(git show -s --format=%ct "${SOURCE_SHA}")"
368 if [[ ! "${source_date_epoch}" =~ ^[0-9]+$ ]]; then
369 echo "Could not read a Unix timestamp for source commit ${SOURCE_SHA}" >&2
370 exit 1
371 fi
372 SOURCE_DATE_EPOCH="${source_date_epoch}" \
373 bash scripts/release/create-release-bundles.sh artifacts bundles
374 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
375 with:
376 name: codewhale-bundles
377 path: |
378 bundles/*.tar.gz
379 bundles/*.zip
380 bundles/codewhale-bundles-sha256.txt
381 if-no-files-found: error
382 retention-days: ${{ inputs.retention_days }}
383 overwrite: true
384 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
385 if: inputs.compiled_host_delivery
386 with:
387 name: codewhale-compiled-hosts
388 path: artifacts/codewhale-compiled-hosts/*
389 if-no-files-found: error
390 retention-days: ${{ inputs.retention_days }}
391 overwrite: true
392
393 windows-installer:
394 timeout-minutes: 15
395 needs: build
396 if: ${{ !cancelled() && needs.build.result == 'success' }}
397 runs-on: windows-latest
398 steps:
399 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
400 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
401 with:
402 path: artifacts
403 pattern: '*windows-x64*'
404 - name: Install NSIS
405 shell: pwsh
406 run: choco install nsis -y --no-progress
407 - name: Build NSIS installer
408 shell: pwsh
409 run: |
410 $ErrorActionPreference = "Stop"
411 Copy-Item "artifacts\codewhale-windows-x64.exe\codewhale-windows-x64.exe" "scripts\installer\codewhale.exe"
412 Copy-Item "artifacts\codew-windows-x64.exe\codew-windows-x64.exe" "scripts\installer\codew.exe"
413 $makensis = "${env:ProgramFiles(x86)}\NSIS\makensis.exe"
414 if (!(Test-Path $makensis)) {
415 $makensis = "${env:ProgramFiles}\NSIS\makensis.exe"
416 }
417 if (!(Test-Path $makensis)) {
418 throw "makensis.exe not found after NSIS install"
419 }
420 $hostDefine = @()
421 $hostCatalog = "artifacts\codewhale-compiled-host-windows-x64\codewhale-extension-hosts.json"
422 if (Test-Path $hostCatalog) {
423 node -e "const h=require('./npm/codewhale/scripts/compiled-hosts'); const fs=require('fs'); const p=require('path'); const f=process.argv[1]; h.verifyDirectory(p.dirname(f), h.parseCatalog(fs.readFileSync(f),'${{ inputs.version }}'));" $hostCatalog
424 if ($LASTEXITCODE -ne 0) { throw 'Compiled host payload/catalog verification failed' }
425 $catalog = Get-Content -Raw $hostCatalog | ConvertFrom-Json
426 $hostEntry = @($catalog.hosts | Where-Object { $_.target -eq 'windows-x64' })
427 if ($hostEntry.Count -ne 1) { throw 'No unique qualified Windows host' }
428 $hostDir = Split-Path $hostCatalog
429 Copy-Item "$hostDir\$($hostEntry[0].asset)" "scripts\installer\codewhale-extension-host.exe"
430 Copy-Item "$hostDir\$($hostEntry[0].notices_asset)" "scripts\installer\codewhale-extension-host.LICENSES.txt"
431 Copy-Item "$hostDir\$($hostEntry[0].source_asset)" "scripts\installer\codewhale-extension-host.relink-source.tar.gz"
432 Copy-Item $hostCatalog "scripts\installer\codewhale-extension-host.release.json"
433 $hostDefine = @('/DCOMPILED_HOST=1')
434 }
435 Push-Location scripts\installer
436 & $makensis "/DVERSION=${{ inputs.version }}" @hostDefine "codewhale.nsi"
437 Pop-Location
438 if (!(Test-Path "scripts\installer\CodeWhaleSetup.exe")) {
439 throw "CodeWhaleSetup.exe was not produced"
440 }
441 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
442 with:
443 name: CodeWhaleSetup.exe
444 path: scripts/installer/CodeWhaleSetup.exe
445 if-no-files-found: error
446 retention-days: ${{ inputs.retention_days }}
447 overwrite: true
448
449 assemble:
450 timeout-minutes: 15
451 needs: [bundle, windows-installer]
452 if: ${{ !cancelled() && needs.bundle.result == 'success' && needs.windows-installer.result == 'success' }}
453 runs-on: ubuntu-latest
454 steps:
455 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
456 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
457 with:
458 node-version: 22
459 package-manager-cache: false
460 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
461 with:
462 path: intermediate-artifacts
463 pattern: '*'
464 - name: Assemble exact authoritative release inventory
465 run: node scripts/release/assemble-release-assets.js intermediate-artifacts release-assets
466 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
467 with:
468 name: codewhale-release-assets
469 path: release-assets/*
470 if-no-files-found: error
471 retention-days: ${{ inputs.retention_days }}
472 compression-level: 0
473 overwrite: true
474
475 smoke:
476 timeout-minutes: 15
477 needs: assemble
478 if: ${{ !cancelled() && needs.assemble.result == 'success' }}
479 runs-on: ubuntu-latest
480 steps:
481 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
482 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
483 with:
484 node-version: 22
485 package-manager-cache: false
486 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
487 with:
488 name: codewhale-release-assets
489 path: release-assets
490 - name: Verify 34-asset bridge inventory and checksum manifests (single binary)
491 run: node scripts/release/assemble-release-assets.js --verify release-assets
492 - name: Test release inventory contracts
493 run: |
494 node --test scripts/release/assemble-release-assets.test.js
495 node --test npm/codewhale/test/artifacts.test.js npm/codewhale/test/release-assets.test.js
496 - name: Render AUR metadata from candidate Linux archives
497 run: bash packaging/aur/render.sh release-assets "${RUNNER_TEMP}/codewhale-bin"
498 - name: Smoke packed npm wrapper against candidate assets
499 env:
500 CODEWHALE_SMOKE_ASSETS_DIR: ${{ github.workspace }}/release-assets
501 run: node scripts/release/npm-wrapper-smoke.js
502 - name: Record non-public candidate identity
503 shell: bash
504 run: |
505 {
506 echo "### Release artifact candidate"
507 echo ""
508 echo "- Source: \`${{ github.sha }}\`"
509 echo "- Version metadata: \`${{ inputs.version }}\`"
510 echo "- Inventory: 7 targets / 34 files (single binary; 7 legacy alias assets)"
511 echo "- Publication: none (Actions artifact \`codewhale-release-assets\` only)"
512 } >> "${GITHUB_STEP_SUMMARY}"
513
513 lines YAML