Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/lint-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
166 changes: 39 additions & 127 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
# 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:
#
# 1. Create and push a release tag:
# $ 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
Expand All @@ -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

Expand All @@ -49,7 +42,7 @@ on:
workflow_dispatch:
inputs:
tag:
description: 'Tag to release'
description: 'Tag to release (must already exist)'
required: true
type: string

Expand All @@ -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
Expand All @@ -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 }}
112 changes: 112 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -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_<os>_<arch>
# (obolup.sh download_release fetches them by that exact name).
# - The checksum file is named SHA256SUMS in `sha256sum` format
# ("<hash> <filename>") — 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 <platform@obol.tech>
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 }}"