-
Notifications
You must be signed in to change notification settings - Fork 0
440 lines (389 loc) · 15.6 KB
/
Copy pathrelease.yml
File metadata and controls
440 lines (389 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# release.yml - Codegeist release artifact workflow.
#
# Purpose:
# - Validate release-shaped JVM and native artifacts on GitHub-hosted runners.
# - Publish a GitHub Release only for pushed v* tags.
#
# Inputs and side effects:
# - Branch validation derives the version from release/v* branches, including
# iteration branches such as release/v0.1.0-github-release-build and candidate
# branches such as release/v0.1.0-codegeist-rc-1.
# - workflow_dispatch may pass release_version for pre-tag validation.
# - Tag runs create or update a published GitHub Release and upload artifacts.
#
# Related files:
# - app/codegeist/cli/pom.xml
# - docs/developer/release/github-release-build.md
# - scripts/install/
name: Codegeist Release Build
on:
workflow_dispatch:
inputs:
release_version:
description: SemVer without leading v. Leave empty to derive from the selected ref.
required: false
type: string
push:
branches:
- "release/v*"
tags:
- "v*"
permissions:
contents: read
concurrency:
group: codegeist-release-${{ github.ref }}
cancel-in-progress: false
env:
JAVA_VERSION: "25"
GRAALVM_DISTRIBUTION: graalvm-community
NATIVE_SMOKE_TIMEOUT_SECONDS: "5"
FILE_EDIT_SMOKE_TIMEOUT_SECONDS: "90"
SHELL_ASK_SMOKE_TIMEOUT_SECONDS: "90"
jobs:
metadata:
name: Resolve release metadata
runs-on: ubuntu-latest
outputs:
release_version: ${{ steps.resolve.outputs.release_version }}
publish_release: ${{ steps.resolve.outputs.publish_release }}
steps:
- name: Resolve release version
id: resolve
shell: bash
env:
INPUT_RELEASE_VERSION: ${{ github.event.inputs.release_version || '' }}
REF_NAME: ${{ github.ref_name }}
REF_TYPE: ${{ github.ref_type }}
run: |
set -euo pipefail
version="${INPUT_RELEASE_VERSION#v}"
source="workflow input"
if [ -z "$version" ]; then
if [ "$REF_TYPE" = "tag" ] && [[ "$REF_NAME" =~ ^v(.+)$ ]]; then
version="${BASH_REMATCH[1]}"
source="tag"
elif [[ "$REF_NAME" =~ ^release/v([0-9]+[.][0-9]+[.][0-9]+)($|[-/]) ]]; then
version="${BASH_REMATCH[1]}"
source="release branch"
else
printf 'Could not derive a release version from ref %s.\n' "$REF_NAME" >&2
printf 'Use a release/v<major>.<minor>.<patch>-... branch or pass release_version.\n' >&2
exit 1
fi
fi
semver='^(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)(-[0-9A-Za-z][0-9A-Za-z.-]*)?$'
if ! [[ "$version" =~ $semver ]]; then
printf 'Release version must be SemVer without leading v: %s\n' "$version" >&2
exit 1
fi
publish_release=false
if [ "$REF_TYPE" = "tag" ]; then
expected_ref="v$version"
if [ "$REF_NAME" != "$expected_ref" ]; then
printf 'Tag %s does not match resolved release version %s.\n' "$REF_NAME" "$version" >&2
exit 1
fi
publish_release=true
fi
printf 'release_version=%s\n' "$version" >> "$GITHUB_OUTPUT"
printf 'publish_release=%s\n' "$publish_release" >> "$GITHUB_OUTPUT"
{
printf '### Release metadata\n'
printf '\n'
printf -- '- Version: `%s`\n' "$version"
printf -- '- Source: `%s`\n' "$source"
printf -- '- Ref: `%s`\n' "$REF_NAME"
printf -- '- Published GitHub Release: `%s`\n' "$publish_release"
} >> "$GITHUB_STEP_SUMMARY"
build-jvm:
name: Build JVM jar
runs-on: ubuntu-latest
needs: metadata
env:
RELEASE_VERSION: ${{ needs.metadata.outputs.release_version }}
steps:
- name: Checkout source
uses: actions/checkout@v7
- name: Set up GraalVM
uses: graalvm/setup-graalvm@v1
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.GRAALVM_DISTRIBUTION }}
github-token: ${{ secrets.GITHUB_TOKEN }}
cache: maven
- name: Run Maven tests
working-directory: app/codegeist/cli
shell: bash
run: mvn --batch-mode --no-transfer-progress -Drevision="$RELEASE_VERSION" test
- name: Build executable jar
working-directory: app/codegeist/cli
shell: bash
run: mvn --batch-mode --no-transfer-progress -Drevision="$RELEASE_VERSION" -DskipTests clean package
- name: Stage JVM jar asset
working-directory: app/codegeist/cli
shell: bash
run: |
set -euo pipefail
mkdir -p target/dist
jar --list --file target/codegeist.jar | grep --fixed-strings --line-regexp 'META-INF/LICENSE'
unzip -p target/codegeist.jar META-INF/LICENSE | cmp - ../../../LICENSE
cp -p target/codegeist.jar target/dist/codegeist-jvm.jar
- name: Upload JVM jar artifact
uses: actions/upload-artifact@v7
with:
name: codegeist-jvm
if-no-files-found: error
path: app/codegeist/cli/target/dist/codegeist-jvm.jar
build-native:
name: Build and smoke native ${{ matrix.platform }}
runs-on: ${{ matrix.os }}
needs:
- metadata
- build-jvm
strategy:
fail-fast: false
matrix:
include:
- platform: linux-x64
os: ubuntu-latest
extension: tar.gz
- platform: windows-x64
os: windows-latest
extension: zip
- platform: macos-x64
os: macos-15-intel
extension: tar.gz
env:
RELEASE_VERSION: ${{ needs.metadata.outputs.release_version }}
steps:
- name: Checkout source
uses: actions/checkout@v7
- name: Set up GraalVM
uses: graalvm/setup-graalvm@v1
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.GRAALVM_DISTRIBUTION }}
github-token: ${{ secrets.GITHUB_TOKEN }}
cache: maven
native-image-job-reports: "true"
- name: Build native executable
if: runner.os != 'Windows'
working-directory: app/codegeist/cli
shell: bash
run: mvn --batch-mode --no-transfer-progress -Drevision="$RELEASE_VERSION" -DskipTests -Pnative clean native:compile
- name: Build native executable with MSVC
if: runner.os == 'Windows'
working-directory: app/codegeist/cli
shell: pwsh
run: |
$vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe"
if (-not (Test-Path -LiteralPath $vswhere)) {
throw "vswhere.exe was not found: $vswhere"
}
$installationPath = & $vswhere "-latest" "-products" "*" "-requires" "Microsoft.VisualStudio.Component.VC.Tools.x86.x64" "-property" "installationPath"
if (-not $installationPath) {
throw "No Visual Studio installation with MSVC x64 tools was found."
}
$vsDevCmd = Join-Path $installationPath "Common7\Tools\VsDevCmd.bat"
if (-not (Test-Path -LiteralPath $vsDevCmd)) {
throw "VsDevCmd.bat was not found: $vsDevCmd"
}
$command = "`"$vsDevCmd`" -arch=x64 && mvn --batch-mode --no-transfer-progress -Drevision=$env:RELEASE_VERSION -DskipTests -Pnative clean native:compile"
cmd /d /s /c $command
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
$environmentCommand = "`"$vsDevCmd`" -arch=x64 >nul && set VCToolsRedistDir"
$environmentOutput = & cmd /d /s /c $environmentCommand
if ($LASTEXITCODE -ne 0) {
throw "Failed to resolve VCToolsRedistDir from the MSVC environment."
}
$redistSetting = $environmentOutput |
Where-Object { $_ -like "VCToolsRedistDir=*" } |
Select-Object -First 1
if (-not $redistSetting) {
throw "VCToolsRedistDir was not available after activating MSVC."
}
$vcToolsRedistDir = $redistSetting.Substring("VCToolsRedistDir=".Length)
"CODEGEIST_WINDOWS_VC_REDIST_DIR=$vcToolsRedistDir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Package and smoke native archive
working-directory: app/codegeist/cli
shell: pwsh
run: |
../../../scripts/tests/artifact-smoke.ps1 `
-Platform "${{ matrix.platform }}" `
-CliDir (Get-Location).Path `
-ExpectedVersion $env:RELEASE_VERSION `
-SmokeRoot (Join-Path (Get-Location).Path "target/smoke-test") `
-NativeTimeoutSeconds ([int]$env:NATIVE_SMOKE_TIMEOUT_SECONDS) `
-FileEditTimeoutSeconds ([int]$env:FILE_EDIT_SMOKE_TIMEOUT_SECONDS) `
-ShellAskTimeoutSeconds ([int]$env:SHELL_ASK_SMOKE_TIMEOUT_SECONDS) `
-WindowsVcRedistDir $env:CODEGEIST_WINDOWS_VC_REDIST_DIR
- name: Smoke platform install script
working-directory: app/codegeist/cli
shell: pwsh
run: |
../../../scripts/tests/install-script-smoke.ps1 `
-Platform "${{ matrix.platform }}" `
-CliDir (Get-Location).Path `
-ExpectedVersion $env:RELEASE_VERSION `
-SmokeRoot (Join-Path (Get-Location).Path "target/smoke-test/install-script/${{ matrix.platform }}") `
-InstallScriptDir (Resolve-Path "../../../scripts/install").Path
- name: Upload native artifact
uses: actions/upload-artifact@v7
with:
name: codegeist-${{ matrix.platform }}
if-no-files-found: error
path: app/codegeist/cli/target/dist/codegeist-${{ matrix.platform }}.${{ matrix.extension }}
stage-release-support:
name: Stage release support assets
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v7
- name: Stage release support assets
shell: bash
run: |
set -euo pipefail
bash -n \
scripts/install/codegeist-install-linux.sh \
scripts/install/codegeist-install-macos.sh
pwsh -NoProfile -Command '"scripts/install/codegeist-install-windows.ps1" | ForEach-Object { $tokens = $null; $errors = $null; [System.Management.Automation.Language.Parser]::ParseFile($_, [ref]$tokens, [ref]$errors) > $null; if ($errors.Count -gt 0) { throw $errors[0] } }'
mkdir -p dist
install_scripts=(
codegeist-install-linux.sh
codegeist-install-macos.sh
codegeist-install-windows.ps1
)
for script in "${install_scripts[@]}"; do
source="scripts/install/$script"
if [ ! -f "$source" ]; then
printf 'Install script not found: %s\n' "$source" >&2
exit 1
fi
cp -p "$source" "dist/$script"
done
cp -p LICENSE dist/LICENSE
- name: Upload release support artifact
uses: actions/upload-artifact@v7
with:
name: codegeist-release-support
if-no-files-found: error
path: |
dist/codegeist-install-*
dist/LICENSE
checksums:
name: Generate and verify checksums
runs-on: ubuntu-latest
needs:
- metadata
- build-jvm
- build-native
- stage-release-support
steps:
- name: Download release artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
- name: Generate SHA256SUMS
shell: bash
run: |
set -euo pipefail
mkdir -p dist
expected_assets=(
LICENSE
codegeist-jvm.jar
codegeist-linux-x64.tar.gz
codegeist-windows-x64.zip
codegeist-macos-x64.tar.gz
codegeist-install-linux.sh
codegeist-install-macos.sh
codegeist-install-windows.ps1
)
shopt -s nullglob
for asset in "${expected_assets[@]}"; do
matches=(artifacts/*/"$asset")
if [ "${#matches[@]}" -ne 1 ] || [ ! -f "${matches[0]}" ]; then
printf 'Expected exactly one workflow artifact named %s, found %s.\n' "$asset" "${#matches[@]}" >&2
exit 1
fi
cp -p "${matches[0]}" dist/
done
shopt -u nullglob
cd dist
checksum_file="SHA256SUMS.txt"
sha256sum "${expected_assets[@]}" > "$checksum_file"
sha256sum -c "$checksum_file"
{
printf '### Release assets\n'
printf '\n'
for asset in *; do
printf -- '- `%s`\n' "$asset"
done
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload checksum artifact
uses: actions/upload-artifact@v7
with:
name: codegeist-checksums
if-no-files-found: error
path: dist/SHA256SUMS.txt
release:
name: Create GitHub Release
if: needs.metadata.outputs.publish_release == 'true'
runs-on: ubuntu-latest
needs:
- metadata
- checksums
permissions:
contents: write
steps:
- name: Download release artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
- name: Stage release assets
shell: bash
env:
RELEASE_VERSION: ${{ needs.metadata.outputs.release_version }}
run: |
set -euo pipefail
mkdir -p release-assets
expected_assets=(
LICENSE
codegeist-jvm.jar
codegeist-linux-x64.tar.gz
codegeist-windows-x64.zip
codegeist-macos-x64.tar.gz
codegeist-install-linux.sh
codegeist-install-macos.sh
codegeist-install-windows.ps1
SHA256SUMS.txt
)
shopt -s nullglob
for asset in "${expected_assets[@]}"; do
matches=(artifacts/*/"$asset")
if [ "${#matches[@]}" -ne 1 ] || [ ! -f "${matches[0]}" ]; then
printf 'Expected exactly one release asset named %s, found %s.\n' "$asset" "${#matches[@]}" >&2
exit 1
fi
cp -p "${matches[0]}" release-assets/
done
shopt -u nullglob
cat > release-notes.md <<EOF
Codegeist $RELEASE_VERSION release.
Validation completed in this workflow run before upload:
- Maven test suite passed before packaging.
- JVM jar was packaged with the canonical license at META-INF/LICENSE and staged as a release asset without runtime artifact smoke.
- Linux x64, Windows x64, and macOS x64 native archives were built, unpacked, and smoke-tested with the canonical LICENSE, --version, --show-config, and ask-driven file-edit plus shell-tool side effects.
- Linux, macOS, and Windows install scripts were run against local release-shaped assets on their matching release runners, then staged as release assets for curl-based downloads.
- The standalone LICENSE release asset and all executable assets are covered by the SHA-256 checksum file, which was generated and verified before upload.
Install scripts are bootstrap helpers for the native archives. This release intentionally excludes package-manager publishing, signing, notarization, SBOM, and SLSA provenance.
EOF
- name: Upload GitHub release
uses: softprops/action-gh-release@v3
with:
draft: false
prerelease: ${{ contains(needs.metadata.outputs.release_version, '-') }}
name: Codegeist ${{ github.ref_name }}
body_path: release-notes.md
files: release-assets/*