From 6b393955fdfaf6f964be1b758ab9124e424a6260 Mon Sep 17 00:00:00 2001 From: langowarny Date: Sun, 1 Mar 2026 03:39:41 +0900 Subject: [PATCH] chore: update release workflow to use single-target builds and create archives - Changed the build step to use 'single-target' instead of 'split' for GoReleaser. - Added steps to create tar.gz archives for built binaries and generate checksums. - Updated the GitHub release step to utilize the new archive format. --- .github/workflows/release.yml | 63 +++++++++++++++---- .../.openspec.yaml | 2 + .../design.md | 44 +++++++++++++ .../proposal.md | 26 ++++++++ .../specs/release-workflow/spec.md | 26 ++++++++ .../tasks.md | 17 +++++ openspec/specs/release-workflow/spec.md | 27 +++++--- 7 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/.openspec.yaml create mode 100644 openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/design.md create mode 100644 openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/proposal.md create mode 100644 openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/specs/release-workflow/spec.md create mode 100644 openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/tasks.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 47e0ffb9..e3cb3fc6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,11 +48,11 @@ jobs: with: install-only: true - - name: Build (split) + - name: Build (single-target) env: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} - run: goreleaser build --split --clean --timeout 60m + run: goreleaser build --single-target --clean --timeout 60m - name: Upload artifacts uses: actions/upload-artifact@v4 @@ -70,11 +70,6 @@ jobs: with: fetch-depth: 0 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - - name: Download all artifacts uses: actions/download-artifact@v4 with: @@ -82,12 +77,54 @@ jobs: pattern: build-* merge-multiple: true - - name: Set up GoReleaser - uses: goreleaser/goreleaser-action@v7 - with: - install-only: true + - name: Get version + id: version + run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" + + - name: Create archives and checksums + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + mkdir -p release + + # Find all built binaries and create tar.gz archives + for build_dir in dist/*/; do + # Skip non-directory entries + [ -d "$build_dir" ] || continue + + binary="$build_dir/lango" + [ -f "$binary" ] || continue + + # Extract dir name: e.g. lango_linux_amd64_v1 or lango-extended_darwin_arm64 + dirname=$(basename "$build_dir") - - name: Release (merge) + # Normalize: strip GOAMD64 suffix (_v1, _v2, etc.) + normalized=$(echo "$dirname" | sed 's/_v[0-9]*$//') + + # Build archive name: lango_{VERSION}_{os}_{arch}.tar.gz + # dirname format: {build_id}_{os}_{arch} + build_id=$(echo "$normalized" | cut -d_ -f1) + os_arch=$(echo "$normalized" | cut -d_ -f2-) + archive_name="${build_id}_${VERSION}_${os_arch}.tar.gz" + + # Create tar.gz with binary + docs + tar -czf "release/${archive_name}" \ + -C "$build_dir" lango \ + -C "$GITHUB_WORKSPACE" LICENSE README.md 2>/dev/null || \ + tar -czf "release/${archive_name}" \ + -C "$build_dir" lango + done + + # Generate checksums + cd release + sha256sum *.tar.gz > checksums.txt + + - name: Create GitHub Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: goreleaser continue --merge --timeout 60m + VERSION: ${{ steps.version.outputs.version }} + run: | + gh release create "$GITHUB_REF_NAME" \ + --title "lango v${VERSION}" \ + --generate-notes \ + release/* diff --git a/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/.openspec.yaml b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/.openspec.yaml new file mode 100644 index 00000000..34b5b231 --- /dev/null +++ b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-02-28 diff --git a/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/design.md b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/design.md new file mode 100644 index 00000000..ffcc5a71 --- /dev/null +++ b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/design.md @@ -0,0 +1,44 @@ +## Context + +The release workflow uses `goreleaser build --split` and `goreleaser continue --merge`, which are GoReleaser Pro-only features. The `goreleaser/goreleaser-action@v7` installs the OSS version, causing `unknown flag: --split` errors. This project requires CGO_ENABLED=1 (mattn/go-sqlite3), making cross-compilation impossible and requiring native per-platform builds. + +## Goals / Non-Goals + +**Goals:** +- Make the release workflow work with GoReleaser OSS (free tier) +- Maintain the same release artifact naming convention +- Keep the native runner matrix strategy for CGO cross-platform builds +- Produce identical release output (tar.gz archives + checksums) + +**Non-Goals:** +- Migrating to GoReleaser Pro +- Changing the `.goreleaser.yaml` build configuration +- Modifying the archive naming convention +- Adding new platforms or architectures + +## Decisions + +### Decision 1: Use `--single-target` instead of `--split` +`goreleaser build --single-target` is an OSS-compatible flag that builds only for the current GOOS/GOARCH. Combined with the existing matrix of native runners, this produces the same result as `--split` — each runner builds only its own platform binaries. + +**Alternative considered**: Running `go build` directly — rejected because it would duplicate ldflags/tags/env logic already defined in `.goreleaser.yaml`. + +### Decision 2: Manual archive + release instead of `--merge` +Since `goreleaser continue --merge` is Pro-only, the release job manually: +1. Finds built binaries in downloaded `dist/` directories +2. Normalizes GOAMD64 suffixes (e.g., `_v1`) from directory names +3. Creates tar.gz archives with the same naming convention +4. Generates SHA256 checksums +5. Uses `gh release create --generate-notes` for the GitHub Release + +**Alternative considered**: Using a separate release tool (e.g., `nfpm`) — rejected as overkill for tar.gz archives. + +### Decision 3: Remove Go setup from release job +The release job no longer runs GoReleaser, so Go is not needed. Only shell tools (`tar`, `sha256sum`, `gh`) are required. + +## Risks / Trade-offs + +- [Risk] GOAMD64 suffix normalization via `sed 's/_v[0-9]*$//'` could match unintended patterns → Mitigation: Pattern is specific to trailing `_v` + digits, matching GoReleaser's known convention +- [Risk] LICENSE/README.md may not exist in some builds → Mitigation: Fallback tar command without docs files +- [Risk] `sha256sum` not available on all runners → Mitigation: Release job runs on ubuntu-latest where it's always available +- [Trade-off] Changelog grouping from `.goreleaser.yaml` is no longer used; `--generate-notes` uses GitHub's default format → Acceptable since GitHub's auto-generated notes are sufficient diff --git a/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/proposal.md b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/proposal.md new file mode 100644 index 00000000..cd4dd29b --- /dev/null +++ b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/proposal.md @@ -0,0 +1,26 @@ +## Why + +The `release.yml` workflow uses `goreleaser build --split` and `goreleaser continue --merge`, which are **GoReleaser Pro-only** flags. The `goreleaser/goreleaser-action@v7` installs the OSS version, causing CI failures with `unknown flag: --split`. + +## What Changes + +- Replace `goreleaser build --split` with `goreleaser build --single-target` (OSS-compatible) in the build job +- Replace `goreleaser continue --merge` with manual archive creation + `gh release create` in the release job +- Remove unnecessary Go setup step from the release job +- Add version extraction, tar.gz archive creation, SHA256 checksum generation, and GitHub Release creation steps + +## Capabilities + +### New Capabilities + +(none) + +### Modified Capabilities + +- `release-workflow`: Replace Pro-only split/merge strategy with OSS-compatible single-target build + manual archive/release pipeline + +## Impact + +- **File**: `.github/workflows/release.yml` — build and release jobs restructured +- **No code changes**: `.goreleaser.yaml` unchanged; build settings (ldflags, CGO_ENABLED, tags) still used by `--single-target` +- **Release artifacts**: Same naming convention maintained (`lango_{VERSION}_{os}_{arch}.tar.gz`, `checksums.txt`) diff --git a/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/specs/release-workflow/spec.md b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/specs/release-workflow/spec.md new file mode 100644 index 00000000..07db494a --- /dev/null +++ b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/specs/release-workflow/spec.md @@ -0,0 +1,26 @@ +## MODIFIED Requirements + +### Requirement: Split build execution +Each matrix runner SHALL execute `goreleaser build --single-target --clean --timeout 60m` to produce binaries only for its native platform. The `--single-target` flag uses the GOOS and GOARCH environment variables set by the matrix to determine the build target. + +#### Scenario: Single-target build produces platform-specific artifacts +- **WHEN** `goreleaser build --single-target` runs on `macos-14` with GOOS=darwin GOARCH=arm64 +- **THEN** it SHALL produce darwin/arm64 binaries for both build IDs (lango and lango-extended) and upload the dist/ directory as artifacts + +### Requirement: Merge and release job +A separate `release` job SHALL download all build artifacts and create a GitHub Release with manually assembled archives and checksums. The job SHALL NOT require Go or GoReleaser installation. + +#### Scenario: Manual archive creation and release +- **WHEN** all 4 build jobs complete successfully +- **THEN** the release job SHALL: + 1. Download artifacts with `merge-multiple: true` + 2. Extract version from the git tag (strip `v` prefix) + 3. Find built binaries in dist/ subdirectories + 4. Normalize directory names by stripping GOAMD64 suffixes (e.g., `_v1`) + 5. Create tar.gz archives named `{build_id}_{VERSION}_{os}_{arch}.tar.gz` + 6. Generate SHA256 checksums in `checksums.txt` + 7. Create a GitHub Release using `gh release create` with `--generate-notes` + +#### Scenario: Archive naming convention +- **WHEN** archives are created for version 0.3.0 +- **THEN** they SHALL follow the naming pattern `lango_0.3.0_linux_amd64.tar.gz` and `lango-extended_0.3.0_linux_amd64.tar.gz` diff --git a/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/tasks.md b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/tasks.md new file mode 100644 index 00000000..47015374 --- /dev/null +++ b/openspec/changes/archive/2026-03-01-fix-goreleaser-split-ci/tasks.md @@ -0,0 +1,17 @@ +## 1. Build Job Fix + +- [x] 1.1 Replace `goreleaser build --split` with `goreleaser build --single-target --clean --timeout 60m` in the build job +- [x] 1.2 Update build step name from "Build (split)" to "Build (single-target)" + +## 2. Release Job Rewrite + +- [x] 2.1 Remove Go setup and GoReleaser setup steps from the release job +- [x] 2.2 Add version extraction step (strip `v` prefix from GITHUB_REF_NAME) +- [x] 2.3 Add archive creation step: iterate dist/ dirs, normalize GOAMD64 suffixes, create tar.gz archives +- [x] 2.4 Add SHA256 checksum generation step +- [x] 2.5 Replace `goreleaser continue --merge` with `gh release create --generate-notes` + +## 3. Verification + +- [x] 3.1 Run `goreleaser check` to validate .goreleaser.yaml remains valid +- [x] 3.2 Verify `--single-target` flag is available in GoReleaser OSS diff --git a/openspec/specs/release-workflow/spec.md b/openspec/specs/release-workflow/spec.md index a5e525ef..111777b6 100644 --- a/openspec/specs/release-workflow/spec.md +++ b/openspec/specs/release-workflow/spec.md @@ -2,7 +2,7 @@ ## Purpose -Defines the GitHub Actions release pipeline that uses a native runner matrix with split/merge strategy for CGO-dependent cross-platform binary builds on tag push. +Defines the GitHub Actions release pipeline that uses a native runner matrix with single-target builds and manual archive/release for CGO-dependent cross-platform binary builds on tag push. ## Requirements @@ -36,18 +36,29 @@ The workflow SHALL install `libsqlite3-dev` on Linux runners before building. - **THEN** it SHALL NOT run apt-get (macOS uses system frameworks) ### Requirement: Split build execution -Each matrix runner SHALL execute `goreleaser build --split --clean` to produce binaries only for its native platform. +Each matrix runner SHALL execute `goreleaser build --single-target --clean --timeout 60m` to produce binaries only for its native platform. The `--single-target` flag uses the GOOS and GOARCH environment variables set by the matrix to determine the build target. -#### Scenario: Split build produces platform-specific artifacts -- **WHEN** `goreleaser build --split` runs on `macos-14` -- **THEN** it SHALL produce darwin/arm64 binaries only and upload them as artifacts +#### Scenario: Single-target build produces platform-specific artifacts +- **WHEN** `goreleaser build --single-target` runs on `macos-14` with GOOS=darwin GOARCH=arm64 +- **THEN** it SHALL produce darwin/arm64 binaries for both build IDs (lango and lango-extended) and upload the dist/ directory as artifacts ### Requirement: Merge and release job -A separate `release` job SHALL download all build artifacts, merge them into `dist/`, and run `goreleaser continue --merge` to create the GitHub Release. +A separate `release` job SHALL download all build artifacts and create a GitHub Release with manually assembled archives and checksums. The job SHALL NOT require Go or GoReleaser installation. -#### Scenario: Artifact merge and release creation +#### Scenario: Manual archive creation and release - **WHEN** all 4 build jobs complete successfully -- **THEN** the release job SHALL download artifacts with `merge-multiple: true`, run `goreleaser continue --merge`, and create a GitHub Release with all 8 archives + checksums +- **THEN** the release job SHALL: + 1. Download artifacts with `merge-multiple: true` + 2. Extract version from the git tag (strip `v` prefix) + 3. Find built binaries in dist/ subdirectories + 4. Normalize directory names by stripping GOAMD64 suffixes (e.g., `_v1`) + 5. Create tar.gz archives named `{build_id}_{VERSION}_{os}_{arch}.tar.gz` + 6. Generate SHA256 checksums in `checksums.txt` + 7. Create a GitHub Release using `gh release create` with `--generate-notes` + +#### Scenario: Archive naming convention +- **WHEN** archives are created for version 0.3.0 +- **THEN** they SHALL follow the naming pattern `lango_0.3.0_linux_amd64.tar.gz` and `lango-extended_0.3.0_linux_amd64.tar.gz` ### Requirement: Write permissions for release The workflow SHALL request `contents: write` permission for creating GitHub Releases.