diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..57a15f3 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,506 @@ +# 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, for example +# release/v0.1.0-github-release-build -> 0.1.0. +# - 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 +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 + JAR_SMOKE_TIMEOUT_SECONDS: "15" + NATIVE_SMOKE_TIMEOUT_SECONDS: "5" + +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..-... 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 and smoke JVM jar + runs-on: ubuntu-latest + needs: metadata + env: + RELEASE_VERSION: ${{ needs.metadata.outputs.release_version }} + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - 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: Smoke version command and stage jar asset + working-directory: app/codegeist/cli + shell: bash + run: | + set -euo pipefail + + mkdir -p target/dist target/smoke-test + jar_asset="target/dist/codegeist-$RELEASE_VERSION-jvm-any.jar" + cp -p target/codegeist.jar "$jar_asset" + + log_file="$PWD/target/smoke-test/codegeist-jvm.log" + actual="$(LOG_FILE="$log_file" timeout "${JAR_SMOKE_TIMEOUT_SECONDS}s" java -jar "$jar_asset" --version 2>&1)" + + if [ "$actual" != "$RELEASE_VERSION" ]; then + printf 'Expected jar version %s, got %s\n' "$RELEASE_VERSION" "$actual" >&2 + exit 1 + fi + + if [ ! -s "$log_file" ]; then + printf 'Expected non-empty jar smoke log: %s\n' "$log_file" >&2 + exit 1 + fi + + printf 'JVM jar smoke passed: %s\n' "$jar_asset" + + - name: Upload JVM jar artifact + uses: actions/upload-artifact@v4 + with: + name: codegeist-${{ needs.metadata.outputs.release_version }}-jvm-any + if-no-files-found: error + path: app/codegeist/cli/target/dist/codegeist-${{ needs.metadata.outputs.release_version }}-jvm-any.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@v4 + + - 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 + } + + - name: Package and smoke native archive + if: runner.os != 'Windows' + working-directory: app/codegeist/cli + shell: bash + run: | + set -euo pipefail + + platform="${{ matrix.platform }}" + package_name="codegeist-$RELEASE_VERSION-$platform" + dist_dir="$PWD/target/dist" + package_dir="$dist_dir/$package_name" + archive="$dist_dir/$package_name.tar.gz" + smoke_dir="$PWD/target/smoke-test" + + if [ ! -x target/codegeist ]; then + printf 'Native executable is missing or not executable: target/codegeist\n' >&2 + exit 1 + fi + + rm -rf "$package_dir" "$archive" "$smoke_dir" + mkdir -p "$package_dir" "$smoke_dir" + cp -p target/codegeist "$package_dir/codegeist" + + shopt -s nullglob + if [ "$platform" = "linux-x64" ]; then + sidecars=(target/lib*.so) + else + sidecars=(target/*.dylib) + fi + shopt -u nullglob + + if [ "${#sidecars[@]}" -gt 0 ]; then + cp -p "${sidecars[@]}" "$package_dir/" + fi + + tar -C "$dist_dir" -czf "$archive" "$package_name" + + temp_dir="$(mktemp -d)" + trap 'rm -rf "$temp_dir"' EXIT + tar -xzf "$archive" -C "$temp_dir" + + python3 - "$temp_dir/$package_name" "$smoke_dir/codegeist-$platform-native.log" "$NATIVE_SMOKE_TIMEOUT_SECONDS" "$RELEASE_VERSION" <<'PY' + import os + import subprocess + import sys + + package_dir, log_file, timeout_seconds, expected = sys.argv[1:5] + env = os.environ.copy() + env["LOG_FILE"] = log_file + + try: + completed = subprocess.run( + ["./codegeist", "--version"], + cwd=package_dir, + env=env, + text=True, + capture_output=True, + timeout=int(timeout_seconds), + ) + except subprocess.TimeoutExpired: + print(f"Native version smoke timed out after {timeout_seconds}s", file=sys.stderr) + sys.exit(1) + + actual = (completed.stdout + completed.stderr).rstrip("\r\n") + if completed.returncode != 0: + print(f"Native version smoke failed with exit code {completed.returncode}: {actual}", file=sys.stderr) + sys.exit(completed.returncode) + + if actual != expected: + print(f"Expected native version {expected}, got {actual}", file=sys.stderr) + sys.exit(1) + + if not os.path.exists(log_file) or os.path.getsize(log_file) == 0: + print(f"Expected non-empty native smoke log: {log_file}", file=sys.stderr) + sys.exit(1) + PY + + printf 'Native archive smoke passed: %s\n' "$archive" + + - name: Package and smoke Windows native archive + if: runner.os == 'Windows' + working-directory: app/codegeist/cli + shell: pwsh + run: | + $cliDir = (Get-Location).Path + $distDir = Join-Path $cliDir "target/dist" + $smokeDir = Join-Path $cliDir "target/smoke-test" + $packageName = "codegeist-$env:RELEASE_VERSION-windows-x64" + $packageDir = Join-Path $distDir $packageName + $archive = Join-Path $distDir "$packageName.zip" + $nativeExe = Join-Path $cliDir "target/codegeist.exe" + + if (-not (Test-Path -LiteralPath $nativeExe)) { + throw "Native executable was not written: $nativeExe" + } + + Remove-Item -Recurse -Force -LiteralPath $packageDir -ErrorAction SilentlyContinue + Remove-Item -Force -LiteralPath $archive -ErrorAction SilentlyContinue + Remove-Item -Recurse -Force -LiteralPath $smokeDir -ErrorAction SilentlyContinue + New-Item -ItemType Directory -Force -Path $packageDir | Out-Null + New-Item -ItemType Directory -Force -Path $smokeDir | Out-Null + + Copy-Item -LiteralPath $nativeExe -Destination (Join-Path $packageDir "codegeist.exe") -Force + Get-ChildItem -LiteralPath (Join-Path $cliDir "target") -Filter "*.dll" -File -ErrorAction SilentlyContinue | + ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination $packageDir -Force } + + Compress-Archive -Path $packageDir -DestinationPath $archive -Force + + $tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("codegeist-smoke-" + [guid]::NewGuid().ToString("N")) + New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null + + try { + Expand-Archive -LiteralPath $archive -DestinationPath $tempRoot -Force + $runDir = Join-Path $tempRoot $packageName + $packageExe = Join-Path $runDir "codegeist.exe" + if (-not (Test-Path -LiteralPath $packageExe)) { + throw "Packaged native executable was not found after unzip: $packageExe" + } + + $stdoutFile = Join-Path $smokeDir "codegeist-windows-native.out" + $stderrFile = Join-Path $smokeDir "codegeist-windows-native.err" + $logFile = Join-Path $smokeDir "codegeist-windows-native.log" + Remove-Item -Force -LiteralPath $stdoutFile, $stderrFile, $logFile -ErrorAction SilentlyContinue + + $env:LOG_FILE = $logFile + $process = Start-Process -FilePath $packageExe -ArgumentList "--version" -WorkingDirectory $runDir -NoNewWindow -PassThru -RedirectStandardOutput $stdoutFile -RedirectStandardError $stderrFile + if (-not $process.WaitForExit([int]$env:NATIVE_SMOKE_TIMEOUT_SECONDS * 1000)) { + $process.Kill() + $process.WaitForExit() + throw "Native version smoke timed out after $env:NATIVE_SMOKE_TIMEOUT_SECONDS seconds" + } + + $stdout = if (Test-Path -LiteralPath $stdoutFile) { Get-Content -LiteralPath $stdoutFile -Raw } else { "" } + $stderr = if (Test-Path -LiteralPath $stderrFile) { Get-Content -LiteralPath $stderrFile -Raw } else { "" } + $actual = ($stdout + $stderr).TrimEnd("`r", "`n") + + if ($process.ExitCode -ne 0) { + throw "Native version smoke failed with exit code $($process.ExitCode): $actual" + } + + if ($actual -ne $env:RELEASE_VERSION) { + throw "Expected native version $env:RELEASE_VERSION, got $actual" + } + + if (-not (Test-Path -LiteralPath $logFile) -or (Get-Item -LiteralPath $logFile).Length -eq 0) { + throw "Expected non-empty native smoke log: $logFile" + } + } + finally { + Remove-Item -Recurse -Force -LiteralPath $tempRoot -ErrorAction SilentlyContinue + } + + Write-Host "Native archive smoke passed: $archive" + + - name: Upload native artifact + uses: actions/upload-artifact@v4 + with: + name: codegeist-${{ needs.metadata.outputs.release_version }}-${{ matrix.platform }} + if-no-files-found: error + path: app/codegeist/cli/target/dist/codegeist-${{ needs.metadata.outputs.release_version }}-${{ matrix.platform }}.${{ matrix.extension }} + + checksums: + name: Generate and verify checksums + runs-on: ubuntu-latest + needs: + - metadata + - build-jvm + - build-native + env: + RELEASE_VERSION: ${{ needs.metadata.outputs.release_version }} + steps: + - name: Download release artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Generate SHA256SUMS + shell: bash + run: | + set -euo pipefail + + mkdir -p dist + count=0 + for file in artifacts/*/codegeist-"$RELEASE_VERSION"*; do + if [ ! -f "$file" ]; then + continue + fi + cp -p "$file" dist/ + count=$((count + 1)) + done + + if [ "$count" -eq 0 ]; then + printf 'No release artifacts were downloaded.\n' >&2 + exit 1 + fi + + cd dist + checksum_file="codegeist-$RELEASE_VERSION-SHA256SUMS.txt" + sha256sum codegeist-"$RELEASE_VERSION"* > "$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@v4 + with: + name: codegeist-${{ needs.metadata.outputs.release_version }}-checksums + if-no-files-found: error + path: dist/codegeist-${{ needs.metadata.outputs.release_version }}-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@v4 + 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 + count=0 + for file in artifacts/*/codegeist-"$RELEASE_VERSION"*; do + if [ ! -f "$file" ]; then + continue + fi + cp -p "$file" release-assets/ + count=$((count + 1)) + done + + if [ "$count" -eq 0 ]; then + printf 'No release assets were downloaded.\n' >&2 + exit 1 + fi + + cat > release-notes.md <'` + - `git ls-remote --tags origin ''` + - `gh release view ''` + Stop if any of these show an existing tag or release. +7. Fetch `origin` and verify that local `main` and `origin/main` point to the same + commit, or fast-forward local `main` to `origin/main` when it is safe and the + worktree is clean. Do not create a merge commit. +8. Verify `.github/workflows/release.yml` exists on `main`. +9. Start pre-tag validation on `main`: + +```bash +gh workflow run release.yml --ref main -f release_version= +``` + +10. Locate the created run with `gh run list --workflow release.yml --branch main` + or the run id returned by GitHub CLI, then wait for it: + +```bash +gh run watch --exit-status +``` + +11. Stop if pre-tag validation does not conclude with `success`. +12. Create and push an annotated release tag from the validated `main` commit: + +```bash +git tag -a -m "Codegeist " +git push origin +``` + +13. Locate and watch the tag-triggered release run. It must conclude with + `success`. +14. Verify the GitHub Release exists, is not a draft, and has the expected tag: + +```bash +gh release view --json tagName,isDraft,isPrerelease,url,assets +``` + +15. Verify the expected assets are present: + +```text +codegeist--jvm-any.jar +codegeist--linux-x64.tar.gz +codegeist--windows-x64.zip +codegeist--macos-x64.tar.gz +codegeist--SHA256SUMS.txt +``` + +16. Download the release assets into a temporary directory under `/tmp/opencode` + and verify checksums: + +```bash +gh release download --dir +sha256sum -c codegeist--SHA256SUMS.txt +``` + +17. Report the release URL, tag, validated workflow run ids, assets, checksum + result, and any warnings such as GitHub Actions deprecation notices. + +## Rules + +- Do not create the tag before pre-tag validation passes. +- Do not publish from branch or `workflow_dispatch` runs. +- Do not use `git reset`, force-push, delete tags, or overwrite a release. +- Do not continue when an expected asset or checksum is missing. +- Do not mark the release complete until the GitHub Release is published and the + downloaded checksums verify. diff --git a/.oc_local/opencode.json b/.oc_local/opencode.json index eb57140..a68460f 100644 --- a/.oc_local/opencode.json +++ b/.oc_local/opencode.json @@ -2,6 +2,7 @@ "$schema": "https://opencode.ai/config.json", "instructions": [ ".oc_local/rules/architecture-doc.md", + ".oc_local/rules/codegeist-release.md", ".oc_local/rules/codegeist-task-specification.md", ".oc_local/rules/third-party-analysis-workflow.md" ] diff --git a/.oc_local/rules/codegeist-release.md b/.oc_local/rules/codegeist-release.md new file mode 100644 index 0000000..e14fa3f --- /dev/null +++ b/.oc_local/rules/codegeist-release.md @@ -0,0 +1,64 @@ +# Codegeist Release Workflow + +Use this rule for Codegeist GitHub release work, especially `/codegeist-release`, +`.github/workflows/release.yml`, release tags, release assets, and checksum +verification. + +## Release Shape + +- Use SemVer tags with a leading `v`, for example `v0.1.0`. +- Pass the Maven release version without the leading `v`, for example + `-Drevision=0.1.0`. +- Keep the default Maven revision as `0.1.0-SNAPSHOT` between release runs. +- Use a `release/v*` branch for workflow development and branch validation before + merging release automation to `main`. + +## Required Validation Order + +1. Validate workflow changes on a `release/v*` branch. Branch runs must build, + smoke, checksum, and upload workflow artifacts without publishing a GitHub + Release. +2. Merge the validated branch to `main`. +3. Run pre-tag validation from `main` with `workflow_dispatch` and + `release_version=`. +4. Create and push the annotated `v*` tag only after pre-tag validation passes. +5. Let the tag-triggered workflow publish the GitHub Release automatically. +6. Verify the published release assets and checksums after the tag run passes. + +## Publication Policy + +- Only pushed `v*` tags may publish GitHub Releases. +- `release/v*` branch runs and `workflow_dispatch` runs must not publish releases. +- Tag runs publish releases automatically; they must not leave the release as a + draft. +- Do not publish manually uploaded assets that bypass the workflow. +- Do not create or push the final tag if the pre-tag validation run fails, + remains cancelled, or is skipped without an explicit release decision. + +## Expected Assets + +Each release must include exactly the expected Codegeist artifact family for the +selected version: + +```text +codegeist--jvm-any.jar +codegeist--linux-x64.tar.gz +codegeist--windows-x64.zip +codegeist--macos-x64.tar.gz +codegeist--SHA256SUMS.txt +``` + +Verify `codegeist--SHA256SUMS.txt` against the downloaded release assets +before reporting the release as complete. + +## Safety Rules + +- Run `gh auth status` before using `gh workflow`, `gh run`, or `gh release`. +- Confirm the tag does not already exist locally, remotely, or as a GitHub Release + before creating it. +- Keep the worktree clean before tagging. +- Prefer annotated tags for human-facing Codegeist releases. +- Never use `git reset`, force-push, or delete tags as part of the normal release + command. If a bad release tag or release exists, stop and ask for an explicit + recovery decision. +- Keep release docs and project memory synchronized when workflow behavior changes. diff --git a/.oc_local/rules/codegeist-task-specification.md b/.oc_local/rules/codegeist-task-specification.md index 319341e..a6e91a0 100644 --- a/.oc_local/rules/codegeist-task-specification.md +++ b/.oc_local/rules/codegeist-task-specification.md @@ -88,6 +88,10 @@ This overlay adds only Codegeist-specific guidance. Keep generic phase behavior before creating the final `v*` release tag. Keep local Windows validation on a real Windows VM over SSH or a matching GitHub Windows runner; do not add local compatibility-layer smoke paths for Windows release validation. +- For publishing a Codegeist GitHub Release, prefer `/codegeist-release v` + after the validated release workflow is on `main`. The command owns pre-tag + validation, annotated tag creation, automatic tag-run publication, and published + asset checksum verification. - For Spring Shell command-line arguments such as `--version`, keep the current default command path noninteractive with `spring.shell.interactive.enabled=false` until a task intentionally implements diff --git a/README.md b/README.md index d8395d3..187e860 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ vision: - a Spring Shell `--version` command backed by Spring Boot build metadata - a GraalVM native-image Maven profile and local native smoke check - local Linux and Windows smoke scripts under `scripts/tests/` +- a GitHub Actions release workflow for branch validation, pre-tag validation, + tag-triggered published releases, checksums, and Linux/Windows/macOS native + smokes - repo-local agent workflow rules, commands, and configuration - lightweight project memory in `docs/memory-bank/chat.md` @@ -128,6 +131,26 @@ Native release downloads are planned as platform archives, not true single-file executables. See `docs/developer/release/native-distribution-packaging.md` for the Linux `tar.gz`, Windows `zip`, sidecar-library, and no-single-executable rationale. +## GitHub Release Build + +The GitHub release workflow lives at `.github/workflows/release.yml`. + +It validates versioned release artifacts on GitHub-hosted runners: + +- `codegeist--jvm-any.jar` +- `codegeist--linux-x64.tar.gz` +- `codegeist--windows-x64.zip` +- `codegeist--macos-x64.tar.gz` +- `codegeist--SHA256SUMS.txt` + +Push a versioned release branch such as +`release/v0.1.0-github-release-build` to test the workflow without publishing. +After the workflow is on `main`, use `gh workflow run release.yml --ref main -f +release_version=0.1.0` for pre-tag validation. A pushed `v*` tag starts the same +workflow and publishes the assets to a GitHub Release. + +See `docs/developer/release/github-release-build.md` for the full operator flow. + ## Getting Started 1. Clone the repository with `git clone --recurse-submodules ` so the nested `.opencode` and `.devcontainer` checkouts are available from the start. @@ -171,8 +194,7 @@ If an older checkout is missing nested submodules, initialize them with ## Status -The repository is still early, but it now has a real application entrypoint and -an end-to-end local build/run workflow in the devcontainer. It also has local -Linux and Windows smoke-test entrypoints for the current `--version` artifact -contract. The next release-readiness step is GitHub-hosted release automation for -Linux, Windows, and macOS artifacts. +The repository is still early, but it now has a real application entrypoint, an +end-to-end local build/run workflow in the devcontainer, local Linux and Windows +smoke-test entrypoints, and GitHub-hosted release automation for the current +`--version` artifact contract. diff --git a/app/codegeist/cli/pom.xml b/app/codegeist/cli/pom.xml index b9291ec..afe31b0 100644 --- a/app/codegeist/cli/pom.xml +++ b/app/codegeist/cli/pom.xml @@ -12,13 +12,14 @@ ai.codegeist codegeist - 0.1.0-SNAPSHOT + ${revision} ${project.artifactId} Bootstrap application for codegeist.ai 25 + 0.1.0-SNAPSHOT ${java.version} 0.10.6 2.0.0-M6 diff --git a/docs/developer/README.md b/docs/developer/README.md index cf5d337..4ef8eef 100644 --- a/docs/developer/README.md +++ b/docs/developer/README.md @@ -11,6 +11,8 @@ constraints here. ## Release Documents +- `release/github-release-build.md` - GitHub-hosted release workflow triggers, + artifacts, branch validation, pre-tag validation, and published release behavior. - `release/local-build-smoke.md` - local Linux and Windows build-smoke entrypoints and final local smoke-suite usage. - `release/native-distribution-packaging.md` - native archive layout, sidecar diff --git a/docs/developer/architecture/architecture.md b/docs/developer/architecture/architecture.md index bad8c79..0b359d0 100644 --- a/docs/developer/architecture/architecture.md +++ b/docs/developer/architecture/architecture.md @@ -42,6 +42,7 @@ The current application build is defined by `app/codegeist/cli/pom.xml`. | Spring AI Agent Utils | BOM and core artifact `0.7.0` | | GraalVM | Native Maven profile using `native-maven-plugin` `0.10.6` | | Packaging | Spring Boot executable jar named `target/codegeist.jar` | +| Release CI | `.github/workflows/release.yml` validates versioned JVM and native artifacts on GitHub-hosted Linux, Windows, and macOS runners, and publishes GitHub Releases only from `v*` tags | | Tests | Spring Boot context-load test, Spring-context command test, focused version output tests, native version smoke, local Linux smoke, Windows QEMU smoke, and final local smoke suite | Spring AI provider starters are not present. Spring AI Agent Utils is present as a @@ -50,6 +51,8 @@ dependency baseline, but no Agent Utils runtime utility is wired into the app ye ## Implemented File Layout ```text +.github/workflows/ + release.yml app/codegeist/cli/ pom.xml Taskfile.yml @@ -183,6 +186,32 @@ download or VM prerequisites. | `task final-smoke-suite` | Runs `scripts/tests/final-smoke-suite.sh` | Local Linux and Windows smoke suite; both platforms must pass by default | | `task run` | `java -jar target/codegeist.jar` after `build` | Starts the packaged Spring Boot application | +## GitHub Release Flow + +`.github/workflows/release.yml` is the implemented GitHub-hosted release build +path. It accepts three trigger shapes: + +- push to `release/v*` for branch validation without publishing; +- `workflow_dispatch` for pre-tag validation or reruns without publishing; +- push to `v*` tags for release-cycle automation and GitHub Release publication. + +The workflow resolves a non-SNAPSHOT SemVer release version, passes it to Maven as +`-Drevision=`, runs Maven tests before packaging, builds and smoke-tests a +versioned JVM jar, then builds native archives on GitHub-hosted Linux x64, Windows +x64, and macOS x64 runners. The Windows native job activates the MSVC tools +environment before running Maven native compilation. The checksum job generates and +verifies `codegeist--SHA256SUMS.txt`; the release job uploads the jar, +native archives, and checksum file to a published GitHub Release only for matching +`v*` tags. + +The implemented release artifact names are: + +- `codegeist--jvm-any.jar` +- `codegeist--linux-x64.tar.gz` +- `codegeist--windows-x64.zip` +- `codegeist--macos-x64.tar.gz` +- `codegeist--SHA256SUMS.txt` + ## Not Implemented Yet The following concepts are discussed in strategy docs but are not implemented in diff --git a/docs/developer/release/github-release-build.md b/docs/developer/release/github-release-build.md new file mode 100644 index 0000000..47a6122 --- /dev/null +++ b/docs/developer/release/github-release-build.md @@ -0,0 +1,147 @@ +# GitHub Release Build + +GitHub-hosted release automation builds, packages, smoke-tests, checksums, and +uploads Codegeist release artifacts. + +## Scope + +The workflow lives at `.github/workflows/release.yml` and covers the implemented +Spring Boot CLI module under `app/codegeist/cli`. It validates the current +`--version` behavior on generated JVM and native artifacts. It does not create +installers, signing, notarization, SBOM, SLSA provenance, package-manager +publishing, or runtime behavior beyond the existing command. + +## Triggers + +| Trigger | Purpose | Publishes a GitHub Release | +| --- | --- | --- | +| Push to `release/v*` | Branch-based release workflow validation before merging into `main`. | No | +| `workflow_dispatch` | Pre-tag validation or operator rerun from GitHub CLI, API, or UI. | No | +| Push tag `v*` | Release-cycle automation after pre-tag validation passes. | Yes, as a published release | + +Branch validation derives the release version from the branch name. The first +release automation branch is: + +```text +release/v0.1.0-github-release-build +``` + +That branch resolves to Maven and artifact version `0.1.0`. Tag runs require the +tag name to match the resolved version, for example `v0.1.0`. + +`workflow_dispatch` can pass an explicit SemVer value without the leading `v`: + +```bash +gh workflow run release.yml --ref main -f release_version=0.1.0 +``` + +GitHub only exposes `workflow_dispatch` after the workflow file exists on the +default branch. Before the workflow is merged to `main`, validate it by pushing a +`release/v*` branch. + +## Artifact Contract + +The workflow produces versioned release assets: + +| Asset | Source job | Smoke command | +| --- | --- | --- | +| `codegeist--jvm-any.jar` | Ubuntu JVM job | `java -jar codegeist--jvm-any.jar --version` | +| `codegeist--linux-x64.tar.gz` | Ubuntu native job | unpack and run `./codegeist --version` | +| `codegeist--windows-x64.zip` | Windows native job | unzip and run `codegeist.exe --version` | +| `codegeist--macos-x64.tar.gz` | macOS native job | unpack and run `./codegeist --version` | +| `codegeist--SHA256SUMS.txt` | Checksum job | `sha256sum -c` before upload | + +Native archives keep the executable and required GraalVM sidecar libraries in one +directory. See `native-distribution-packaging.md` for the archive layout and +sidecar-library rationale. + +## Workflow Gates + +The implemented jobs run these gates in order: + +1. Resolve and validate the release version. +2. Run the Maven test suite with `-Drevision=`. +3. Build the executable JVM jar and smoke `--version`. +4. Build native executables on GitHub-hosted Linux, Windows, and macOS runners. +5. Activate the MSVC tools environment on Windows before Maven native compile. +6. Package native archives with sidecar libraries. +7. Unpack each native archive into a fresh temporary directory and smoke + `--version` from the extracted directory. +8. Generate and verify `codegeist--SHA256SUMS.txt`. +9. Upload all assets as workflow artifacts. +10. On `v*` tag runs only, upload the same assets to a published GitHub Release. + +## Branch Validation Flow + +Use this before merging release workflow changes to `main`: + +```bash +git checkout -b release/v0.1.0-github-release-build +git push -u origin release/v0.1.0-github-release-build +``` + +The push starts the workflow without creating a GitHub Release. Inspect the run: + +```bash +gh run list --workflow release.yml --branch release/v0.1.0-github-release-build +gh run watch --exit-status +``` + +Branch-run artifacts are downloaded from the workflow run, not from GitHub +Releases. Use the run page's `Artifacts` section or GitHub CLI: + +```bash +gh run download -n codegeist-0.1.0-linux-x64 -D downloads/linux +gh run download -n codegeist-0.1.0-windows-x64 -D downloads/windows +gh run download -n codegeist-0.1.0-jvm-any -D downloads/jvm +``` + +The Linux artifact contains `codegeist-0.1.0-linux-x64.tar.gz`; the Windows +artifact contains `codegeist-0.1.0-windows-x64.zip`. Extract the archive and keep +the executable beside its sidecar libraries. + +## Pre-Tag Validation Flow + +After the workflow exists on `main`, run the same build and smoke matrix without a +release tag: + +```bash +gh auth status +gh workflow run release.yml --ref main -f release_version=0.1.0 +gh run watch --exit-status +``` + +The pre-tag run must pass before creating the final release tag unless a release +decision explicitly records why it was skipped. Pre-tag validation does not publish +a GitHub Release. + +## Tag Release Flow + +When branch validation and pre-tag validation have passed, create and push the +release tag: + +```bash +git tag -a v0.1.0 -m "Codegeist v0.1.0" +git push origin v0.1.0 +``` + +The tag push starts the release workflow automatically. The release job creates or +updates a published GitHub Release and uploads the jar, native archives, and +checksum file. + +The repo-local OpenCode command wraps the full release sequence: + +```text +/codegeist-release v0.1.0 +``` + +It runs pre-tag validation, creates and pushes the annotated tag after validation +passes, waits for the tag-triggered workflow, then verifies the published release +assets and checksums. + +## Status And Skips + +Normal release validation expects Linux x64, Windows x64, and macOS x64 jobs to +pass. If a GitHub-hosted platform smoke is skipped or fails, record the platform, +artifact, command, concrete reason, and follow-up owner in the task or release +decision before publishing. diff --git a/docs/developer/release/native-distribution-packaging.md b/docs/developer/release/native-distribution-packaging.md index 9b1d057..a0c1507 100644 --- a/docs/developer/release/native-distribution-packaging.md +++ b/docs/developer/release/native-distribution-packaging.md @@ -11,7 +11,7 @@ Use one downloadable archive per platform and architecture: | --- | --- | --- | | Linux x64 | `codegeist--linux-x64.tar.gz` | `codegeist` plus required `.so` libraries in one directory. | | Windows x64 | `codegeist--windows-x64.zip` | `codegeist.exe` plus required `.dll` libraries in one directory. | -| macOS x64 | `codegeist--macos-x64.tar.gz` | Future native binary plus required dynamic libraries in one directory. | +| macOS x64 | `codegeist--macos-x64.tar.gz` | Native binary plus required dynamic libraries in one directory. | | macOS arm64 | `codegeist--macos-aarch64.tar.gz` | Future native binary plus required dynamic libraries in one directory. | The archive is the single download artifact. The extracted directory is the runtime diff --git a/docs/developer/specification/build-release-and-binary-smoke-strategy.md b/docs/developer/specification/build-release-and-binary-smoke-strategy.md index e05b848..6579bdd 100644 --- a/docs/developer/specification/build-release-and-binary-smoke-strategy.md +++ b/docs/developer/specification/build-release-and-binary-smoke-strategy.md @@ -1,24 +1,24 @@ # Build Release And Binary Smoke Strategy -Planned release strategy for Codegeist GitHub Releases, cross-platform artifacts, -and platform-native binary smoke validation. +Release strategy for Codegeist GitHub Releases, cross-platform artifacts, and +platform-native binary smoke validation. ## Purpose And Status -This document defines how Codegeist should build, package, publish, and verify -release artifacts once release automation is implemented. It is a strategy and -handoff document only. It does not add GitHub Actions workflows, release scripts, -Maven plugins, Taskfile commands, Java source, tests, installers, signing keys, +This document defines how Codegeist builds, packages, publishes, and verifies +release artifacts. The current implementation is `.github/workflows/release.yml`, +which validates release-shaped artifacts on GitHub-hosted runners and publishes +GitHub Releases for `v*` tag runs. It does not add installers, signing keys, notarization setup, package-manager manifests, or runtime behavior. -Use this guide before implementing release CI, release scripts, native packaging -checks, startup budgets, or platform smoke suites. Current implementation remains -the single Spring Boot CLI module under `app/codegeist/cli`. +Use this guide before changing release CI, release scripts, native packaging +checks, startup budgets, or platform smoke suites. Current application +implementation remains the single Spring Boot CLI module under `app/codegeist/cli`. ## Current Baseline -The repository currently has local developer build and smoke entrypoints, not a -GitHub release pipeline. +The repository currently has local developer build and smoke entrypoints and a +GitHub-hosted release workflow. | Area | Current state | | --- | --- | @@ -28,13 +28,13 @@ GitHub release pipeline. | Spring Shell | `4.0.2` dependency baseline; `--version` command implemented | | Spring AI | BOM `2.0.0-M6`; no provider starters or model calls yet | | Spring AI Agent Utils | BOM and core dependency `0.7.0` | -| JVM package | Spring Boot executable jar named `target/codegeist.jar` | +| JVM package | Spring Boot executable jar named `target/codegeist.jar`; release asset `codegeist--jvm-any.jar` | | Native package | GraalVM native Maven profile using `native-maven-plugin` `0.10.6` | | Local commands | `task test`, `task build`, `task native`, `task native-smoke`, `task local-linux-smoke`, `task qemu-windows-smoke`, `task final-smoke-suite`, `task run` | +| GitHub release workflow | `.github/workflows/release.yml` for `release/v*` branch validation, `workflow_dispatch` pre-tag validation, and `v*` tag release publication | -No GitHub release workflow, platform artifact matrix, checksum generation, -artifact signing, notarization, installer generation, or package-manager -publishing exists yet. +No artifact signing, notarization, installer generation, SBOM, SLSA provenance, or +package-manager publishing exists yet. The current local smoke suite lives under `scripts/tests/` and verifies the implemented `--version` behavior on local Linux artifacts and, when configured, a @@ -44,9 +44,8 @@ Windows QEMU VM over SSH. GitHub Releases are the deployment target for Codegeist release artifacts. -A future release workflow should publish artifacts only through a GitHub Release -draft or published release associated with the release tag. Each release should -include: +The implemented release workflow publishes artifacts only through a GitHub Release +associated with a `v*` release tag. Each release includes: - JVM jar artifact. - Platform-native archive artifacts when the platform build is available. @@ -84,17 +83,18 @@ The JVM jar and native distribution archives have separate responsibilities. | Artifact | Example name | Built from | Verification posture | | --- | --- | --- | --- | -| JVM jar | `codegeist-.jar` | `task build` / Maven package | Release-blocking once runtime behavior exists. | -| Linux native archive | `codegeist--linux-x64.tar.gz` | Native compile and package on Linux x64 | Release target; blocking once runner, toolchain, package script, and smoke command are stable. | -| Windows native archive | `codegeist--windows-x64.zip` | Native compile and package on Windows x64 | Release target; blocking once runner, toolchain, package script, and smoke command are stable. | -| macOS Intel native archive | `codegeist--macos-x64.tar.gz` | Native compile and package on macOS x64 | Release target; skip only with explicit runner/toolchain reason. | +| JVM jar | `codegeist--jvm-any.jar` | Maven package on Ubuntu | Release-blocking for the current `--version` artifact contract. | +| Linux native archive | `codegeist--linux-x64.tar.gz` | Native compile and package on Linux x64 | Release-blocking in the implemented workflow. | +| Windows native archive | `codegeist--windows-x64.zip` | Native compile and package on Windows x64 | Release-blocking in the implemented workflow. | +| macOS Intel native archive | `codegeist--macos-x64.tar.gz` | Native compile and package on macOS x64 | Release-blocking in the implemented workflow. | | macOS Apple Silicon native archive | `codegeist--macos-aarch64.tar.gz` | Native compile and package on macOS arm64 | Compatibility target; skip only with explicit runner/toolchain reason. | -| Checksums | `SHA256SUMS` or per-artifact `.sha256` files | Platform-neutral checksum step | Required for every uploaded artifact. | +| Checksums | `codegeist--SHA256SUMS.txt` | Platform-neutral checksum step | Required for every uploaded artifact. | -Use names that include the project, version, operating system, and architecture. -Do not reuse `target/codegeist.jar`, `target/codegeist`, or `target/codegeist.exe` -as final release asset names because they do not identify version or platform and -do not package required sidecar libraries. +Use names that include the project, version, platform, and architecture. The JVM +jar uses `jvm-any` because it is not tied to one operating system or CPU. Do not +reuse `target/codegeist.jar`, `target/codegeist`, or `target/codegeist.exe` as +final release asset names because they do not identify version or platform and do +not package required sidecar libraries. Native release artifacts are archives, not true single executable files. The archive is the user download. The extracted directory is the runtime unit because @@ -104,8 +104,8 @@ full packaging rationale. ## Verification Gates -Future release CI should use ordered gates so a failure explains which part of the -release is unsafe. +Release CI uses ordered gates so a failure explains which part of the release is +unsafe. 1. Source hygiene: checkout tag, verify clean workspace, run `git --no-pager diff --check` for generated release changes when applicable. @@ -122,16 +122,14 @@ release is unsafe. run the packaged executable on its own platform with the same bounded startup policy. 8. Artifact integrity: generate checksums and verify every checksum before upload. -9. Release draft validation: create or update a GitHub Release draft and attach all - artifacts, checksums, and validation summaries. -10. Publication: publish the GitHub Release only when required gates passed and all - skips or failures have approved release notes. +9. Release publication: on `v*` tag runs only, create or update a published GitHub + Release and attach all artifacts and checksums. +10. Post-release verification: download the published assets and verify + `codegeist--SHA256SUMS.txt` before reporting the release complete. -The JVM jar should be the first release-blocking artifact. Native archives are -named release targets and should become blocking for each platform as soon as that -platform's runner, GraalVM toolchain, package naming, unpack step, and smoke -command are stable. Until then, missing native checks must be recorded as -`skipped`, not left implicit. +The JVM jar is a release-blocking artifact. Linux x64, Windows x64, and macOS x64 +native archives are release-blocking in the implemented GitHub workflow. Missing +native checks must be recorded as `skipped`, not left implicit. ## Binary Smoke Scenarios @@ -155,11 +153,10 @@ Required smoke checks once the corresponding CLI behavior exists: - Artifact integrity: verify the downloaded artifact checksum before execution. - Timeout handling: fail the smoke if the process hangs beyond the smoke budget. -Planned command shapes for future CI jobs: +Implemented CI smoke command shapes: ```bash -java -jar codegeist-.jar --version -java -jar codegeist-.jar --help +java -jar codegeist--jvm-any.jar --version tar -xzf codegeist--linux-x64.tar.gz -C /tmp/codegeist-smoke cd /tmp/codegeist-smoke/codegeist--linux-x64 ./codegeist --version @@ -175,13 +172,14 @@ Set-Location $env:TEMP\codegeist-smoke\codegeist--windows-x64 .\codegeist.exe --version ``` -These examples are planned smoke shapes. They must not be reported as executed -until the corresponding release job or local verification really runs them. +These examples reflect the implemented release workflow's `--version` smoke shape. +Do not report broader commands such as `--help` as executed until the CLI owns that +behavior and the corresponding release job really runs it. ## Implemented Local Smoke Suite -The implemented local suite is a pre-release validation aid until GitHub Actions -release jobs exist. +The implemented local suite is a pre-release validation aid alongside GitHub +Actions release jobs. | Script | Current behavior | | --- | --- | @@ -193,7 +191,7 @@ release jobs exist. The local suite is intentionally not a release publisher. It does not upload artifacts, generate checksums, create GitHub Releases, sign binaries, or replace -the future GitHub-hosted Linux, Windows, and macOS release matrix. +the GitHub-hosted Linux, Windows, and macOS release matrix. ## Timing And Budgets @@ -220,8 +218,7 @@ Initial provisional budgets until baseline measurements exist: | Platform smoke suite | 60 seconds per artifact | Excludes native compilation time. | Treat these as release smoke budgets, not ordinary unit-test budgets. A later task -should replace them with measured values after the first stable release matrix is -implemented. +can replace them with measured values after several stable release matrix runs. ## Skip And Failure Policy @@ -261,47 +258,41 @@ Owner: future release automation task Release decision: non-blocking for this pre-matrix release; must be listed in release notes ``` -## GitHub Actions Handoff +## Implemented GitHub Actions Matrix -A later task should implement the workflow. This document only defines the shape. - -Illustrative matrix outline: +`.github/workflows/release.yml` implements the first release matrix: ```yaml strategy: matrix: include: - - os: ubuntu-latest - artifact_suffix: linux-x64.tar.gz - native: true - - os: windows-latest - artifact_suffix: windows-x64.zip - native: true - - os: macos-latest - artifact_suffix: macos-x64.tar.gz - native: true - - os: macos-latest - artifact_suffix: macos-aarch64.tar.gz - native: skip-until-runner-confirmed + - 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 ``` -The future workflow should keep build, package, unpacked smoke, checksum, -release-draft, and publish steps visible as separate stages. It should upload logs -as CI artifacts when useful, but release notes should contain only concise -validation summaries. +The workflow keeps build, package, unpacked smoke, checksum, and release publish +steps visible as separate stages. Release notes should contain concise validation +summaries instead of raw logs. ## Release Candidate Checklist Before publishing or approving a release candidate, verify: - Release tag is selected and matches artifact version names. -- `task test` passed from `app/codegeist/cli`. +- Maven tests passed from `app/codegeist/cli` with the selected `-Drevision`. - JVM jar was built, renamed for release, checksumed, and smoke tested. - Each platform-native archive is either unpacked and smoke tested on its own platform or recorded as `skipped` or `failed` with required details. - Checksum verification passed before artifact upload. - Release notes list supported platforms and skipped or failed platform checks. -- GitHub Release draft contains the expected artifacts and checksum files. +- Published GitHub Release contains the expected artifacts and checksum files. - Signing, notarization, SBOM, provenance, installer, or package-manager gaps are listed as non-blocking or blocking according to the current release task. - Startup and smoke durations are summarized for every executed smoke check. @@ -312,16 +303,15 @@ Before publishing or approving a release candidate, verify: Likely follow-up owners: -- A future packaging task should validate the JVM package, native archive shape, - sidecar-library collection, startup behavior, and unpacked executable smoke - behavior when the implementation reaches packaging-readiness. -- A later release automation task should create GitHub Actions workflows, artifact - naming scripts, native archive packaging, checksum generation, release - draft/upload behavior, and platform smoke jobs. +- A future packaging hardening task can add macOS arm64 once runner capacity and + toolchain behavior are confirmed. +- A future release hardening task can add signing, notarization, SBOM, SLSA + provenance, and installer/package-manager artifacts when they become release + goals. - A later CLI task should add stable `--help` and broader no-side-effect command behavior. Release smoke checks can already assert the implemented `--version` command. When any of those tasks implements real behavior, update `docs/developer/architecture/architecture.md` so it continues to describe current -state rather than planned release strategy. +state rather than stale release strategy. diff --git a/docs/memory-bank/chat.md b/docs/memory-bank/chat.md index 2cc9b8e..9be904c 100644 --- a/docs/memory-bank/chat.md +++ b/docs/memory-bank/chat.md @@ -36,6 +36,21 @@ `target/dist/codegeist--linux-x64.tar.gz`, unpacks it into a fresh temp directory, runs packaged `./codegeist --version`, and writes `target/smoke-test/codegeist.log`. +- Branch `release/v0.1.0-github-release-build` adds `.github/workflows/release.yml` + for GitHub-hosted release validation. Pushes to `release/v*` validate without + publishing, `workflow_dispatch` supports pre-tag validation with + `release_version=0.1.0`, and pushed `v*` tags publish versioned assets to a + GitHub Release. Branch run `26535014716` passed JVM, Linux x64, Windows x64, + macOS x64, and checksum jobs; the release job was correctly skipped on the branch + run. +- `app/codegeist/cli/pom.xml` now uses CI-friendly `${revision}` with local default + `0.1.0-SNAPSHOT`; release CI passes `-Drevision=0.1.0` so artifact smokes print + `0.1.0`. +- GitHub release assets are `codegeist--jvm-any.jar`, + `codegeist--linux-x64.tar.gz`, + `codegeist--windows-x64.zip`, + `codegeist--macos-x64.tar.gz`, and + `codegeist--SHA256SUMS.txt`. - `scripts/tests/final-smoke-suite.sh` is the local final smoke entrypoint. It runs Linux direct smoke and automated Windows QEMU/SSH smoke. Default mode requires both platforms to pass; `--allow-skips` is developer-only. The suite @@ -76,8 +91,8 @@ is solved with the current Spring Shell `--version` behavior. - `docs/tasks/T005_add-cross-platform-release-and-qemu-smoke/` is the active release-readiness task group. `T005_01` is solved with local Linux/Windows - build-smoke entrypoints under `scripts/tests/`; `T005_02` remains the - GitHub-hosted release build follow-up. + build-smoke entrypoints under `scripts/tests/`; `T005_02` is solved on + `release/v0.1.0-github-release-build` with passing GitHub branch validation. - The previous T003 source-generation child tasks `T003_05` through `T003_12` were removed with their generated specification documents because they encouraged placeholder Java instead of tested behavior. @@ -123,8 +138,9 @@ for packaging, release, platform, or binary-smoke work. - For the active T005 release work, validate Linux and Windows locally before the release path where practical, use GitHub-hosted runners for Linux, Windows, and - macOS release builds, and use `gh` pre-tag validation before creating the final - `v*` release tag. + macOS release builds, and use `/codegeist-release v0.1.0` or the equivalent `gh` + pre-tag validation before creating the final `v*` release tag. Tag runs publish + the GitHub Release automatically. - Keep test and smoke helper scripts under `scripts/tests/`. Local Windows release validation uses a real Windows QEMU VM over SSH or a matching GitHub Windows runner; do not add local compatibility-layer smoke paths. @@ -166,5 +182,7 @@ - Revisit `docs/developer/specification/native-packaging-posture.md` and `build-release-and-binary-smoke-strategy.md` when release automation or binary smoke work starts. -- Solve `T005_02` next: GitHub Actions release automation and release publication - comes after the local Linux/Windows build-smoke entrypoints. +- After merging `release/v0.1.0-github-release-build` to `main`, run + `/codegeist-release v0.1.0`. The command validates `main`, creates and pushes the + annotated tag, waits for the tag workflow, and verifies the published release + assets and checksums. diff --git a/docs/tasks/T005_add-cross-platform-release-and-qemu-smoke/tasks/T005_02_add-github-release-build.md b/docs/tasks/T005_add-cross-platform-release-and-qemu-smoke/tasks/T005_02_add-github-release-build.md index b9f9fa4..3689a28 100644 --- a/docs/tasks/T005_add-cross-platform-release-and-qemu-smoke/tasks/T005_02_add-github-release-build.md +++ b/docs/tasks/T005_add-cross-platform-release-and-qemu-smoke/tasks/T005_02_add-github-release-build.md @@ -1,6 +1,6 @@ # T005_02 Add GitHub Release Build -Status: open +Status: solved Parent: `../task.md` @@ -30,8 +30,8 @@ Windows, and macOS using GitHub-hosted runners. archives. - Publish release assets to GitHub Releases with versioned artifact names. - Generate and publish SHA-256 checksums for all release assets. -- Create the GitHub Release as a draft unless the solve phase intentionally - chooses and documents a different publication policy. +- Publish the GitHub Release automatically from the tag-triggered workflow after + pre-tag validation passes. - Update current-state architecture and user/developer documentation for the implemented GitHub release path. @@ -71,7 +71,7 @@ Windows, and macOS using GitHub-hosted runners. running GraalVM `native-image` through Maven. - Release asset names include project, version, platform, and architecture. - A checksum file is generated, verified, and uploaded with the release assets. -- The workflow uploads all expected artifacts to a draft GitHub Release. +- The workflow uploads all expected artifacts to a published GitHub Release. - Release documentation describes how to run the workflow and what artifacts it produces. @@ -109,7 +109,7 @@ Expected CI verification: ```text Manual workflow run or v* tag release run completes Linux, Windows, and macOS build, native archive packaging, unpacked smoke, checksum, artifact upload, and -draft release creation jobs. +release publication jobs. ``` Expected pre-tag development verification: @@ -130,6 +130,64 @@ release workflow without manually opening the GitHub Actions UI. If a GitHub platform smoke is skipped, record `skipped` with the concrete reason, platform, artifact, command, and follow-up owner. +## Implementation Notes + +- Created the implementation on branch `release/v0.1.0-github-release-build` so + `main` stays unchanged while the workflow is tested. +- Added `.github/workflows/release.yml` for `release/v*` branch validation, + `workflow_dispatch` pre-tag validation, and `v*` tag release publication. +- The workflow derives version `0.1.0` from branch + `release/v0.1.0-github-release-build`, accepts `release_version=0.1.0` for + `workflow_dispatch`, and passes Maven `-Drevision=0.1.0` so `--version` prints + the release version instead of `0.1.0-SNAPSHOT`. +- Added CI-friendly Maven revision support in `app/codegeist/cli/pom.xml` while + keeping the local default version `0.1.0-SNAPSHOT`. +- The workflow builds and smokes `codegeist--jvm-any.jar`, + `codegeist--linux-x64.tar.gz`, + `codegeist--windows-x64.zip`, and + `codegeist--macos-x64.tar.gz`, then generates and verifies + `codegeist--SHA256SUMS.txt`. +- GitHub Release upload is guarded to `v*` tag runs only and publishes the release + automatically. Branch and `workflow_dispatch` runs validate artifacts without + publishing. +- Added `docs/developer/release/github-release-build.md` and updated current-state + architecture, release strategy, native packaging notes, developer docs, README, + and project memory for the implemented workflow. + +## Verification Notes + +- `git --no-pager diff --check` passed. +- `.github/workflows/release.yml` parsed successfully with Python `yaml.safe_load`. +- `actionlint` was not installed in the local environment, so actionlint validation + was skipped locally. +- From `app/codegeist/cli`, the following local release-version path passed: + +```bash +mvn --batch-mode --no-transfer-progress test +mvn --batch-mode --no-transfer-progress -Drevision=0.1.0 -DskipTests package +java -jar target/codegeist.jar --version +``` + +- The jar smoke printed `0.1.0`, proving the release workflow can override the + default `0.1.0-SNAPSHOT` project version with Maven `-Drevision=0.1.0`. +- GitHub branch validation passed on + `release/v0.1.0-github-release-build`: + `https://github.com/codegeist-ai/codegeist/actions/runs/26534205715`. +- The passing branch run validated metadata resolution, Maven tests, + JVM jar package and smoke, Linux x64 native package and smoke, Windows x64 native + package and smoke, macOS x64 native package and smoke, checksum generation, and + checksum verification. +- The GitHub Release job was correctly skipped because the validation run was + a `release/v*` branch push, not a `v*` tag push. +- The first attempted branch run, + `https://github.com/codegeist-ai/codegeist/actions/runs/26532524977`, proved JVM, + Linux, and Windows behavior but was cancelled after the old `macos-13` runner + label left the macOS job queued. The workflow now uses `macos-15-intel` for the + macOS x64 job. +- Full pre-tag validation with `workflow_dispatch` and automatic release + publication on a `v0.1.0` tag remain release-cycle steps after this branch is + merged to `main`. + ## Planning Notes - Keep GitHub workflow steps visible: tests, jar package, jar smoke, native build,