返回 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 retention_days:
15 description: Retention for Actions-only intermediate and assembled artifacts
16 required: false
17 default: 7
18 type: number
19
20 permissions:
21 contents: read
22
23 env:
24 CARGO_TERM_COLOR: always
25 CARGO_INCREMENTAL: 0
26 RUSTFLAGS: -Dwarnings
27 DEEPSEEK_BUILD_SHA: ${{ inputs.source_sha }}
28
29 jobs:
30 build:
31 name: Build ${{ matrix.platform }}
32 strategy:
33 fail-fast: false
34 matrix:
35 include:
36 - os: ubuntu-latest
37 target: x86_64-unknown-linux-musl
38 platform: linux-x64
39 cli_binary: codewhale
40 shim_binary: codew
41 tui_binary: codewhale-tui
42 cli_artifact: codewhale-linux-x64
43 shim_artifact: codew-linux-x64
44 tui_artifact: codewhale-tui-linux-x64
45 - os: ubuntu-24.04-arm
46 target: aarch64-unknown-linux-gnu
47 platform: linux-arm64
48 cli_binary: codewhale
49 shim_binary: codew
50 tui_binary: codewhale-tui
51 cli_artifact: codewhale-linux-arm64
52 shim_artifact: codew-linux-arm64
53 tui_artifact: codewhale-tui-linux-arm64
54 - os: ubuntu-latest
55 target: aarch64-linux-android
56 platform: android-arm64
57 cli_binary: codewhale
58 shim_binary: codew
59 tui_binary: codewhale-tui
60 cli_artifact: codewhale-android-arm64
61 shim_artifact: codew-android-arm64
62 tui_artifact: codewhale-tui-android-arm64
63 - os: macos-latest
64 target: x86_64-apple-darwin
65 platform: macos-x64
66 cli_binary: codewhale
67 shim_binary: codew
68 tui_binary: codewhale-tui
69 cli_artifact: codewhale-macos-x64
70 shim_artifact: codew-macos-x64
71 tui_artifact: codewhale-tui-macos-x64
72 - os: macos-latest
73 target: aarch64-apple-darwin
74 platform: macos-arm64
75 cli_binary: codewhale
76 shim_binary: codew
77 tui_binary: codewhale-tui
78 cli_artifact: codewhale-macos-arm64
79 shim_artifact: codew-macos-arm64
80 tui_artifact: codewhale-tui-macos-arm64
81 - os: windows-latest
82 target: x86_64-pc-windows-msvc
83 platform: windows-x64
84 cli_binary: codewhale.exe
85 shim_binary: codew.exe
86 tui_binary: codewhale-tui.exe
87 cli_artifact: codewhale-windows-x64.exe
88 shim_artifact: codew-windows-x64.exe
89 tui_artifact: codewhale-tui-windows-x64.exe
90 - os: windows-11-arm
91 target: aarch64-pc-windows-msvc
92 platform: windows-arm64
93 cli_binary: codewhale.exe
94 shim_binary: codew.exe
95 tui_binary: codewhale-tui.exe
96 cli_artifact: codewhale-windows-arm64.exe
97 shim_artifact: codew-windows-arm64.exe
98 tui_artifact: codewhale-tui-windows-arm64.exe
99 runs-on: ${{ matrix.os }}
100 steps:
101 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
102 with:
103 ref: ${{ inputs.source_sha }}
104 - uses: dtolnay/rust-toolchain@2c7215f132e9ebf062739d9130488b56d53c060c # master 2026-07-18
105 with:
106 toolchain: stable
107 targets: ${{ matrix.target }}
108 - uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
109 id: sccache
110 continue-on-error: true
111 - name: Enable sccache
112 if: steps.sccache.outcome == 'success'
113 shell: bash
114 run: |
115 {
116 echo "SCCACHE_GHA_ENABLED=true"
117 echo "RUSTC_WRAPPER=sccache"
118 echo "SCCACHE_IGNORE_SERVER_IO_ERROR=1"
119 } >> "${GITHUB_ENV}"
120 - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
121 with:
122 cache-bin: false
123 - name: Install Linux ARM64 system dependencies
124 if: matrix.target == 'aarch64-unknown-linux-gnu'
125 run: |
126 for i in 1 2 3 4 5; do
127 sudo apt-get update && break
128 echo "apt-get update failed (attempt $i); retrying in 15s"
129 sleep 15
130 done
131 sudo apt-get install -y libdbus-1-dev pkg-config
132 - name: Build static Linux x64 binaries (musl)
133 if: matrix.target == 'x86_64-unknown-linux-musl'
134 shell: bash
135 run: |
136 sudo apt-get update
137 sudo apt-get install -y musl-tools
138 rustup target add --toolchain stable x86_64-unknown-linux-musl
139 cargo build --profile dist --locked --target x86_64-unknown-linux-musl -p codewhale-cli -p codewhale-tui
140 - name: Configure Android NDK linker
141 if: matrix.target == 'aarch64-linux-android' && runner.os == 'Linux'
142 shell: bash
143 env:
144 ANDROID_NDK_VERSION: 27.2.12479018
145 run: |
146 set -euo pipefail
147 sudo apt-get update
148 sudo apt-get install -y libclang-dev
149 ndk="${ANDROID_NDK_ROOT:-${ANDROID_NDK_HOME:-}}"
150 linker=""
151 if [[ -n "${ndk}" ]]; then
152 linker="${ndk}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
153 fi
154 if [[ -z "${linker}" || ! -x "${linker}" ]]; then
155 if ! command -v sdkmanager >/dev/null 2>&1; then
156 echo "sdkmanager is required to install Android NDK ${ANDROID_NDK_VERSION}" >&2
157 exit 1
158 fi
159 android_home="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
160 if [[ -z "${android_home}" ]]; then
161 echo "ANDROID_HOME or ANDROID_SDK_ROOT is required to install Android NDK ${ANDROID_NDK_VERSION}" >&2
162 exit 1
163 fi
164 yes | sdkmanager --licenses >/dev/null || true
165 sdkmanager --install "ndk;${ANDROID_NDK_VERSION}"
166 ndk="${android_home}/ndk/${ANDROID_NDK_VERSION}"
167 linker="${ndk}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
168 fi
169 ar="${ndk}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
170 if [[ ! -x "${linker}" ]]; then
171 echo "Android linker not found: ${linker}" >&2
172 exit 1
173 fi
174 if [[ ! -x "${ar}" ]]; then
175 echo "Android archiver not found: ${ar}" >&2
176 exit 1
177 fi
178 {
179 echo "ANDROID_NDK_ROOT=${ndk}"
180 echo "ANDROID_NDK_HOME=${ndk}"
181 echo "CC_aarch64_linux_android=${linker}"
182 echo "AR_aarch64_linux_android=${ar}"
183 echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=${linker}"
184 echo "BINDGEN_EXTRA_CLANG_ARGS_aarch64_linux_android=--target=aarch64-linux-android24 --sysroot=${ndk}/toolchains/llvm/prebuilt/linux-x86_64/sysroot"
185 } >> "${GITHUB_ENV}"
186 - name: Build
187 if: matrix.target != 'x86_64-unknown-linux-musl'
188 shell: bash
189 run: cargo build --profile dist --locked --target ${{ matrix.target }} -p codewhale-cli -p codewhale-tui
190 - name: Smoke binaries on matching native runners
191 if: >-
192 matrix.target != 'aarch64-linux-android' &&
193 ((startsWith(matrix.target, 'x86_64-') && runner.arch == 'X64') ||
194 (startsWith(matrix.target, 'aarch64-') && runner.arch == 'ARM64'))
195 shell: bash
196 run: |
197 bin_dir="target/${{ matrix.target }}/dist"
198 "${bin_dir}/${{ matrix.cli_binary }}" --version
199 "${bin_dir}/${{ matrix.shim_binary }}" --version
200 "${bin_dir}/${{ matrix.tui_binary }}" --version
201 - name: Stage binaries
202 shell: bash
203 run: |
204 stage_binary() {
205 local binary="$1"
206 local artifact="$2"
207 local bin_path="target/${{ matrix.target }}/dist/${binary}"
208 if [[ ! -f "${bin_path}" ]]; then
209 echo "Binary not at ${bin_path}; searching target/ for ${binary}:" >&2
210 find target -name "${binary}" -type f
211 exit 1
212 fi
213 cp "${bin_path}" "${artifact}"
214 }
215
216 stage_binary "${{ matrix.cli_binary }}" "${{ matrix.cli_artifact }}"
217 stage_binary "${{ matrix.shim_binary }}" "${{ matrix.shim_artifact }}"
218 stage_binary "${{ matrix.tui_binary }}" "${{ matrix.tui_artifact }}"
219 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
220 with:
221 name: ${{ matrix.cli_artifact }}
222 path: ${{ matrix.cli_artifact }}
223 if-no-files-found: error
224 retention-days: ${{ inputs.retention_days }}
225 overwrite: true
226 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
227 with:
228 name: ${{ matrix.shim_artifact }}
229 path: ${{ matrix.shim_artifact }}
230 if-no-files-found: error
231 retention-days: ${{ inputs.retention_days }}
232 overwrite: true
233 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
234 with:
235 name: ${{ matrix.tui_artifact }}
236 path: ${{ matrix.tui_artifact }}
237 if-no-files-found: error
238 retention-days: ${{ inputs.retention_days }}
239 overwrite: true
240
241 bundle:
242 needs: build
243 if: ${{ !cancelled() && needs.build.result == 'success' }}
244 runs-on: ubuntu-latest
245 steps:
246 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
247 with:
248 ref: ${{ inputs.source_sha }}
249 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
250 with:
251 path: artifacts
252 pattern: '*'
253 - name: Create and checksum platform archives
254 shell: bash
255 run: bash scripts/release/create-release-bundles.sh artifacts bundles
256 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
257 with:
258 name: codewhale-bundles
259 path: |
260 bundles/*.tar.gz
261 bundles/*.zip
262 bundles/codewhale-bundles-sha256.txt
263 if-no-files-found: error
264 retention-days: ${{ inputs.retention_days }}
265 overwrite: true
266
267 windows-installer:
268 needs: build
269 if: ${{ !cancelled() && needs.build.result == 'success' }}
270 runs-on: windows-latest
271 steps:
272 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
273 with:
274 ref: ${{ inputs.source_sha }}
275 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
276 with:
277 path: artifacts
278 pattern: '*windows-x64.exe'
279 - name: Install NSIS
280 shell: pwsh
281 run: choco install nsis -y --no-progress
282 - name: Build NSIS installer
283 shell: pwsh
284 run: |
285 $ErrorActionPreference = "Stop"
286 Copy-Item "artifacts\codewhale-windows-x64.exe\codewhale-windows-x64.exe" "scripts\installer\codewhale.exe"
287 Copy-Item "artifacts\codew-windows-x64.exe\codew-windows-x64.exe" "scripts\installer\codew.exe"
288 Copy-Item "artifacts\codewhale-tui-windows-x64.exe\codewhale-tui-windows-x64.exe" "scripts\installer\codewhale-tui.exe"
289 $makensis = "${env:ProgramFiles(x86)}\NSIS\makensis.exe"
290 if (!(Test-Path $makensis)) {
291 $makensis = "${env:ProgramFiles}\NSIS\makensis.exe"
292 }
293 if (!(Test-Path $makensis)) {
294 throw "makensis.exe not found after NSIS install"
295 }
296 Push-Location scripts\installer
297 & $makensis "/DVERSION=${{ inputs.version }}" "codewhale.nsi"
298 Pop-Location
299 if (!(Test-Path "scripts\installer\CodeWhaleSetup.exe")) {
300 throw "CodeWhaleSetup.exe was not produced"
301 }
302 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
303 with:
304 name: CodeWhaleSetup.exe
305 path: scripts/installer/CodeWhaleSetup.exe
306 if-no-files-found: error
307 retention-days: ${{ inputs.retention_days }}
308 overwrite: true
309
310 assemble:
311 needs: [bundle, windows-installer]
312 if: ${{ !cancelled() && needs.bundle.result == 'success' && needs.windows-installer.result == 'success' }}
313 runs-on: ubuntu-latest
314 steps:
315 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
316 with:
317 ref: ${{ inputs.source_sha }}
318 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
319 with:
320 node-version: 20
321 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
322 with:
323 path: intermediate-artifacts
324 pattern: '*'
325 - name: Assemble exact authoritative release inventory
326 run: node scripts/release/assemble-release-assets.js intermediate-artifacts release-assets
327 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
328 with:
329 name: codewhale-release-assets
330 path: release-assets/*
331 if-no-files-found: error
332 retention-days: ${{ inputs.retention_days }}
333 compression-level: 0
334 overwrite: true
335
336 smoke:
337 needs: assemble
338 if: ${{ !cancelled() && needs.assemble.result == 'success' }}
339 runs-on: ubuntu-latest
340 steps:
341 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
342 with:
343 ref: ${{ inputs.source_sha }}
344 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
345 with:
346 node-version: 20
347 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
348 with:
349 name: codewhale-release-assets
350 path: release-assets
351 - name: Verify 34-asset inventory and checksum manifests
352 run: node scripts/release/assemble-release-assets.js --verify release-assets
353 - name: Test release inventory contracts
354 run: |
355 node --test scripts/release/assemble-release-assets.test.js
356 node --test npm/codewhale/test/artifacts.test.js npm/codewhale/test/release-assets.test.js
357 - name: Smoke packed npm wrapper against candidate assets
358 env:
359 CODEWHALE_SMOKE_ASSETS_DIR: ${{ github.workspace }}/release-assets
360 run: node scripts/release/npm-wrapper-smoke.js
361 - name: Record non-public candidate identity
362 shell: bash
363 run: |
364 {
365 echo "### Release artifact candidate"
366 echo ""
367 echo "- Source: \`${{ inputs.source_sha }}\`"
368 echo "- Version metadata: \`${{ inputs.version }}\`"
369 echo "- Inventory: 7 targets / 34 files"
370 echo "- Publication: none (Actions artifact \`codewhale-release-assets\` only)"
371 } >> "${GITHUB_STEP_SUMMARY}"
372
372 lines YAML