From 76569b7c9e47a1974fdb8391a66a7c9bc76c9505 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Thu, 30 Apr 2026 05:30:31 -0700 Subject: [PATCH] ci(release): switch release.yml to GoReleaser + race-detector tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After this PR the release pipeline produces a real out-of-box install story for molecule-cli — multi-OS binaries with checksums, archived with shell completions, plus a CI gate that catches races in the new connect orchestrator. What changed: - `.github/workflows/release.yml` * Vet now scans `./...` (was: three packages); silently let regressions in internal/backends/ + internal/connect/ ship. * Test now runs `-race -count=1 ./...` (was: just `cmd/molecule` without race). The connect orchestrator runs heartbeat + poll goroutines concurrently — a race here would corrupt cursor state. * Release job switches from inline `go build` per matrix entry to `goreleaser release --clean`. Same multi-OS output, plus auto-generated changelog, checksum files, and one config file that future channels (brew tap, scoop, choco) hook into without new workflow steps. * `goreleaser check` runs first so a broken .goreleaser.yaml fails fast at validation, not partway through a build. * Path filter expanded so .goreleaser.yaml edits trigger CI. - `.goreleaser.yaml` * Pre-generate shell completions in the before: hook so the archive can include them. (`molecule completion ` still works at runtime — this just ships the files for users who prefer a drop-in setup.) * Update archive `formats:` (plural) for goreleaser v2 — `format:` was deprecated. * Drop the redundant per-archive checksum block; the top-level `checksum:` covers it. * Header comment rewritten to reflect that this is now the canonical release path (was: "wire it up when ready"). Test plan: - [x] yaml parses for both files - [x] `go test -race -count=1 ./...` green - [ ] CI on this PR exercises the new test job (vet ./..., -race ./...) - [ ] First tag push (v0.1.0) exercises the release job After merge, cutting v0.1.0 is: git tag v0.1.0 && git push origin v0.1.0 # → Release artifacts auto-built and published to GitHub Releases This is M1.4 of [RFC #10](https://github.com/Molecule-AI/molecule-cli/issues/10). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 71 ++++++++++++++++++++++------------- .goreleaser.yaml | 27 +++++++------ 2 files changed, 60 insertions(+), 38 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2082dc8..7bcb818 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,16 @@ name: Release Go binaries + +# Two paths land here: +# pull_request — runs the test job (vet + race-detector tests across the +# whole module) so every PR proves the binary still builds and passes +# all tests, not just cmd/molecule. +# push tags v* — runs test + GoReleaser to cut multi-OS binaries (linux/ +# darwin/windows × amd64/arm64), checksums, and a GitHub Release. The +# release config lives in .goreleaser.yaml. +# +# Why GoReleaser over inline `go build`: checksums, release notes from +# git commits, and one config file that future channels (Homebrew tap, +# scoop bucket, Chocolatey) hook into without adding new workflow steps. on: push: tags: ['v*'] @@ -7,6 +19,8 @@ on: - '**.go' - 'go.mod' - 'go.sum' + - '.github/workflows/release.yml' + - '.goreleaser.yaml' permissions: contents: write @@ -17,40 +31,45 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 - with: { go-version: '1.25' } + with: + go-version: '1.25' + cache: true - name: Tidy run: go mod tidy && git diff --exit-code go.sum + # Vet covers the whole module — was previously scoped to three + # packages, which silently let regressions in internal/backends/ + # or internal/connect/ ship. - name: Vet - run: go vet ./cmd/molecule/... ./internal/client/... ./internal/cmd/... + run: go vet ./... + # Race detector required: the connect orchestrator runs + # heartbeat + poll goroutines concurrently. A race here would + # corrupt cursor state. - name: Test - run: go test ./cmd/molecule/... + run: go test -race -count=1 ./... release: runs-on: ubuntu-latest needs: [test] - strategy: - matrix: - include: - - goos: linux - goarch: amd64 - - goos: linux - goarch: arm64 - - goos: darwin - goarch: amd64 - - goos: darwin - goarch: arm64 - - goos: windows - goarch: amd64 + if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: { go-version: '1.25' } - - name: Build - run: | - GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} \ - go build -o molecule-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goos == 'windows' && '.exe' || '' }} \ - ./cmd/molecule - - uses: softprops/action-gh-release@v2 with: - files: molecule-* - if: startsWith(github.ref, 'refs/tags/v') + # GoReleaser needs full history for its commit-based + # changelog. fetch-depth: 0 pulls everything. + fetch-depth: 0 + - uses: actions/setup-go@v5 + with: + go-version: '1.25' + cache: true + - name: Validate goreleaser config + uses: goreleaser/goreleaser-action@v6 + with: + version: '~> v2' + args: check + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + version: '~> v2' + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 1907f23..851ca25 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,16 +1,14 @@ # GoReleaser configuration for molecule-cli # https://goreleaser.com/install/ # -# .goreleaser.yaml is the main config file for GoReleaser. -# This file must be committed. GoReleaser reads it from the repo root. +# Used by .github/workflows/release.yml on tag push (refs/tags/v*). +# Produces multi-OS binaries (linux/darwin/windows × amd64/arm64), +# tar.gz/zip archives that include pre-generated shell completions, +# a sha256 checksums file, and a GitHub Release. # # Run locally: -# goreleaser check # validate the config -# goreleaser snapshot --clean --snapshot-dir ./.snapshot # dry-run build -# -# CI: GitHub Actions runs plain `go build` per target (see .github/workflows/release.yml). -# GoReleaser would be used for a more sophisticated release (changelog from commits, -# multiple formats, Homebrew formula update, checksum files). Wire it up here when ready. +# goreleaser check — validate the config +# goreleaser release --snapshot --clean — dry-run build (no upload) version: 2 @@ -20,6 +18,13 @@ env: before: hooks: - go mod tidy + # Pre-generate shell completions so the archive ships them. Users + # can drop completions/molecule.bash into ~/.bash_completion.d/, etc. + # `molecule completion ` also still works at runtime. + - mkdir -p completions + - sh -c 'go run ./cmd/molecule completion bash > completions/molecule.bash' + - sh -c 'go run ./cmd/molecule completion zsh > completions/_molecule' + - sh -c 'go run ./cmd/molecule completion fish > completions/molecule.fish' builds: - id: molecule @@ -40,15 +45,13 @@ builds: archives: - id: default - format: tar.gz + formats: [tar.gz] format_overrides: - goos: windows - format: zip + formats: [zip] files: - src: completions/**/* dst: completions - checksum: - name_template: 'molecule_{{.Os}}_{{.Arch}}_{{.Version}}_checksums.txt' snapshot: name_template: "{{.Tag}}-snapshot"