diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index b6ce3b0f..43686eea 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -68,6 +68,20 @@ jobs: - name: Unit tests run: go test ./... + goreleaser-check: + name: goreleaser config valid + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Check .goreleaser.yaml + uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3 + with: + distribution: goreleaser + version: '~> v2' + args: check + generate-check: name: CRD generation up-to-date runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 87bf284f..b75a4fbf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,14 +1,15 @@ # Release Workflow # -# This workflow builds Go binaries for all platforms supported by obolup.sh and creates GitHub releases. +# Builds and publishes obol CLI releases via goreleaser (.goreleaser.yaml). # -# Supported platforms: -# - linux/amd64, linux/arm64 -# - darwin/amd64, darwin/arm64 -# -# Binary naming convention (matches obolup.sh): -# - obol_linux_amd64, obol_linux_arm64 -# - obol_darwin_amd64, obol_darwin_arm64 +# Artifacts per release: +# - Raw binaries: obol_linux_amd64, obol_linux_arm64, obol_darwin_amd64, +# obol_darwin_arm64 (exact names obolup.sh's download_release expects) +# - SHA256SUMS in sha256sum format (obolup.sh verifies binaries against it) +# - deb + rpm packages (linux amd64/arm64) +# - Homebrew formula pushed to ObolNetwork/homebrew-tap (stable tags only, +# and only when the HOMEBREW_TAP_GITHUB_TOKEN secret is configured — a +# fine-grained token with contents:write on the tap repo) # # How to use: # @@ -16,13 +17,8 @@ # $ git tag v0.1.0 # $ git push origin v0.1.0 # -# 2. The workflow automatically: -# - Builds all 4 binaries with version information -# - Creates a GitHub release -# - Uploads binaries and SHA256SUMS checksums -# - Generates release notes -# -# Before publishing the draft release: +# 2. The workflow builds everything and creates a DRAFT release with +# GitHub-generated notes. Before publishing the draft: # - Rewrite the generated body using .github/release-template.md # - Keep the generated "What's Changed", "New Contributors", and # "Full Changelog" sections at the bottom @@ -34,11 +30,8 @@ # # 4. Manual trigger (optional): # - Go to Actions tab → Release workflow → Run workflow -# - Enter a tag name (e.g., v0.1.0) -# -# The obolup.sh script uses these binaries for installation: -# - download_release() function expects binaries at the URL format above -# - Version information is injected via ldflags (matching obolup.sh build) +# - Enter an EXISTING tag name (e.g., v0.1.0); the workflow checks out +# that tag and releases it name: Release @@ -49,7 +42,7 @@ on: workflow_dispatch: inputs: tag: - description: 'Tag to release' + description: 'Tag to release (must already exist)' required: true type: string @@ -74,6 +67,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + ref: ${{ github.event.inputs.tag || github.ref_name }} - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -83,127 +77,45 @@ jobs: - name: Verify pins cover all component source run: .github/scripts/verify-x402-pins.sh - build: - name: Build binaries + release: + name: Build and release (goreleaser) + needs: [verify-image-pins] runs-on: ubuntu-latest - strategy: - matrix: - include: - - goos: linux - goarch: amd64 - - goos: linux - goarch: arm64 - - goos: darwin - goarch: amd64 - - goos: darwin - goarch: arm64 steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + fetch-tags: true + ref: ${{ github.event.inputs.tag || github.ref_name }} - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' - - name: Get version information - id: version - run: | - # Get version from tag (remove 'v' prefix) - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - TAG="${{ github.event.inputs.tag }}" - else - TAG="${{ github.ref_name }}" - fi - VERSION="${TAG#v}" - - # Get git commit (short hash) - GIT_COMMIT=$(git rev-parse --short HEAD) - - # Get build time (YYYYMMDDHHMMSS format) - BUILD_TIME=$(date -u +"%Y%m%d%H%M%S") - - # Check if repo is dirty - if ! git diff --quiet || ! git diff --cached --quiet; then - GIT_DIRTY="true" - else - GIT_DIRTY="false" - fi - - { - echo "version=$VERSION" - echo "git_commit=$GIT_COMMIT" - echo "build_time=$BUILD_TIME" - echo "git_dirty=$GIT_DIRTY" - } >> "$GITHUB_OUTPUT" - - - name: Build binary + # The Homebrew tap push needs a cross-repo token. When the secret is + # not configured, skip the brew pipe instead of failing the whole + # release — binaries, checksums, and packages still publish. + - name: Decide goreleaser args + id: args env: - GOOS: ${{ matrix.goos }} - GOARCH: ${{ matrix.goarch }} - CGO_ENABLED: 0 - run: | - # Build with ldflags (same as obolup.sh) - LDFLAGS="-X github.com/ObolNetwork/obol-stack/internal/version.Version=${{ steps.version.outputs.version }}" - LDFLAGS="$LDFLAGS -X github.com/ObolNetwork/obol-stack/internal/version.GitCommit=${{ steps.version.outputs.git_commit }}" - LDFLAGS="$LDFLAGS -X github.com/ObolNetwork/obol-stack/internal/version.BuildTime=${{ steps.version.outputs.build_time }}" - LDFLAGS="$LDFLAGS -X github.com/ObolNetwork/obol-stack/internal/version.GitDirty=${{ steps.version.outputs.git_dirty }}" - - # Build binary with naming convention from obolup.sh - OUTPUT="obol_${{ matrix.goos }}_${{ matrix.goarch }}" - go build -ldflags "$LDFLAGS" -o "$OUTPUT" ./cmd/obol - - # Make executable - chmod +x "$OUTPUT" - - - name: Upload artifact - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: obol_${{ matrix.goos }}_${{ matrix.goarch }} - path: obol_${{ matrix.goos }}_${{ matrix.goarch }} - if-no-files-found: error - - release: - name: Create GitHub Release - needs: [build, verify-image-pins] - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Download all artifacts - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - path: artifacts - merge-multiple: true - - - name: Get tag name - id: tag + TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "tag=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT" - else - echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" + ARGS="release --clean" + if [ -z "$TAP_TOKEN" ]; then + echo "::notice::HOMEBREW_TAP_GITHUB_TOKEN not configured — skipping Homebrew tap publish" + ARGS="$ARGS --skip=homebrew" fi + echo "args=$ARGS" >> "$GITHUB_OUTPUT" - - name: Generate checksums - run: | - cd artifacts - sha256sum obol_* > SHA256SUMS - cat SHA256SUMS - - - name: Create Release - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 + - name: Run goreleaser + uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3 with: - tag_name: ${{ steps.tag.outputs.tag }} - name: Release ${{ steps.tag.outputs.tag }} - draft: true - prerelease: false - generate_release_notes: true - files: | - artifacts/obol_* - artifacts/SHA256SUMS + distribution: goreleaser + version: '~> v2' + args: ${{ steps.args.outputs.args }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 00000000..742cce07 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,112 @@ +# goreleaser configuration for the obol CLI. +# +# Invariants that MUST hold (obolup.sh depends on them): +# - Release assets are raw binaries named obol__ +# (obolup.sh download_release fetches them by that exact name). +# - The checksum file is named SHA256SUMS in `sha256sum` format +# (" ") — obolup.sh verify_release_checksum parses it. +# - CGO stays disabled: release binaries use the Secure-Enclave stub on +# darwin, same as the previous inline CI build. +# +# Publishing shape: +# - GitHub release: draft, GitHub-native generated notes. Rewrite the +# narrative body from .github/release-template.md before publishing, +# keeping the generated sections at the bottom. +# - deb/rpm packages attached to the release (no apt repo yet). +# - Homebrew formula pushed to ObolNetwork/homebrew-tap on stable +# releases only (skip_upload: auto skips prereleases). The release +# workflow passes --skip=homebrew when the tap token secret is absent. +version: 2 + +project_name: obol + +builds: + - id: obol + main: ./cmd/obol + binary: obol + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + ldflags: + - -X github.com/ObolNetwork/obol-stack/internal/version.Version={{ .Version }} + - -X github.com/ObolNetwork/obol-stack/internal/version.GitCommit={{ .ShortCommit }} + - -X github.com/ObolNetwork/obol-stack/internal/version.BuildTime={{ .Now.Format "20060102150405" }} + - -X github.com/ObolNetwork/obol-stack/internal/version.GitDirty=false + mod_timestamp: "{{ .CommitTimestamp }}" + +archives: + # Raw binaries, not tarballs — obolup.sh downloads these by name. + - id: binaries + formats: [binary] + name_template: "obol_{{ .Os }}_{{ .Arch }}" + +checksum: + name_template: SHA256SUMS + algorithm: sha256 + +nfpms: + - id: packages + package_name: obol + file_name_template: "obol_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + vendor: Obol + homepage: https://obol.org/stack + maintainer: Obol + description: |- + Obol Stack CLI — run an AI agent business from your own machine. + Sell inference, agents, and HTTP services via x402 micropayments; + buy from other sellers; run local blockchain infrastructure. + license: Apache-2.0 + section: utils + formats: + - deb + - rpm + bindir: /usr/bin + +# Homebrew cask (goreleaser deprecated formula-style `brews` in favor of +# casks; casks are macOS-only — Linux brew users use the deb/rpm or the +# install script instead). +homebrew_casks: + - name: obol + repository: + owner: ObolNetwork + name: homebrew-tap + token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" + directory: Casks + homepage: https://obol.org/stack + description: "Run an AI agent business from your own machine — sell and buy services via x402 micropayments" + # auto = publish the cask for stable tags only; rc/pre-release tags + # never move what `brew install` resolves to. + skip_upload: auto + commit_author: + name: obol-release-bot + email: platform@obol.tech + commit_msg_template: "chore: bump {{ .ProjectName }} to {{ .Tag }}" + caveats: |- + The obol CLI manages its own Kubernetes toolchain. To finish setup: + obol stack init + obol stack up + Docker must be installed and running. + # Release binaries are not signed/notarized; strip the Gatekeeper + # quarantine bit on install (the goreleaser-documented pattern for + # unsigned cask binaries). + hooks: + post: + install: | + if system_command("/usr/bin/xattr", args: ["-h"]).exit_status == 0 + system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", staged_path] + end + +changelog: + # Same "What's Changed" body the previous softprops release step + # generated — the release-template rewrite flow is unchanged. + use: github-native + +release: + draft: true + prerelease: auto + name_template: "Release {{ .Tag }}"