Releasing AgentOps¶
This document describes the release process for the ao CLI and AgentOps plugin.
Overview¶
Releases are triggered by git tags and use a publisher-only workflow:
git tag vX.Y.Z
↓
┌──────────────────────────────────────────────────────┐
│ Local gate (authoritative) │
├──────────────────────────────────────────────────────┤
│ ./scripts/ci-local-release.sh │
│ - validation + tests + smoke checks │
│ - hook install + ao rpi smoke │
│ - SBOM + security report artifacts │
└──────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────┐
│ release.yml (publisher only) │
├──────────────────────────────────────────────────────┤
│ - GoReleaser publish │
│ - GitHub Release notes + assets │
│ - Homebrew update │
│ - SLSA provenance attestation │
└──────────────────────────────────────────────────────┘
Making a Release¶
1. Pre-release Checklist¶
- Local CI release gate passes (
./scripts/ci-local-release.sh; once the target version is known, rerun as./scripts/ci-local-release.sh --release-version X.Y.Z) - Local gate artifacts generated (
.agents/releases/local-ci/<timestamp>/includes SBOM + security report) - All tests pass locally (
cd cli && make test) - CI green on main (check Actions tab)
- Version number follows semver (vX.Y.Z)
- CHANGELOG.md updated with release notes
- plugin.json version matches tag
- No uncommitted changes on main
- Homebrew token is valid (check secrets)
1a. Release Size Check¶
Releases should contain at most 15 commits per minor version bump. v2.2.0 had 26 commits — too large to review, debug, or bisect effectively.
If the commit count since the last tag exceeds 15: - Split into multiple releases (e.g., vX.Y.Z for first batch, vX.Y.(Z+1) for remainder) - Or ensure each commit is well-scoped and the changelog covers all changes
Check commit count:
git log $(git describe --tags --abbrev=0)..HEAD --oneline | wc -l
The pre-flight validation script (scripts/validate-release.sh) will warn (not fail) when this threshold is exceeded.
2. Update CHANGELOG¶
Follow Keep a Changelog format:
## [X.Y.Z] - YYYY-MM-DD
### Added
- Feature description
### Changed
- Change description
### Fixed
- Fix description
3. Create and Push Tag¶
# Ensure you're on main and up to date
git checkout main
git pull
# Create annotated tag
git tag -a vX.Y.Z -m "Release vX.Y.Z"
# Push tag (triggers release workflow)
git push origin vX.Y.Z
4. Monitor the Publisher Workflow¶
Watch the release at: https://github.com/boshu2/agentops/actions
The workflow runs one publish job: 1. publish - Builds and publishes artifacts, updates Homebrew, uploads SBOM/security report, signs attestation
5. Verify the Release¶
After the workflow completes:
# Update Homebrew
brew update
# Upgrade ao
brew upgrade agentops
# Verify version
ao version
# Should show: ao version X.Y.Z
6. Verify Integrity (checksums + provenance)¶
# Download and verify checksums
curl -sL https://github.com/boshu2/agentops/releases/download/vX.Y.Z/checksums.txt
shasum -a 256 -c checksums.txt --ignore-missing
# Verify SLSA provenance (requires gh CLI)
gh attestation verify ao-darwin-arm64.tar.gz --repo boshu2/agentops
Release Artifacts¶
Each release produces:
| Artifact | Description |
|---|---|
ao-darwin-amd64.tar.gz |
macOS Intel binary |
ao-darwin-arm64.tar.gz |
macOS Apple Silicon binary |
ao-linux-amd64.tar.gz |
Linux x86_64 binary |
ao-linux-arm64.tar.gz |
Linux ARM64 binary |
checksums.txt |
SHA256 checksums for all archives |
sbom-cyclonedx-go-mod.json |
Publishable CycloneDX SBOM for Go dependencies |
security-gate-summary.json |
Security scan summary (gitleaks/semgrep/gosec/trivy/etc.) |
| SLSA attestation | Build provenance (verifiable via gh attestation verify) |
Release Notes¶
Release notes are auto-generated by GoReleaser with: - Header with install/upgrade instructions and integrity verification - Changelog grouped by type (features, fixes, docs, other) - Footer with full changelog diff link
The template lives in .goreleaser.yml under release.header and release.footer.
Validation Checks¶
Release validation is local-first and enforced by:
./scripts/ci-local-release.sh
This local gate runs doc checks, manifest/schema checks, smoke/integration checks, hook and ao rpi smoke paths, binary validation, SBOM generation, and security scans.
For command variants and expected release-E2E smoke markers, see Release E2E Checklist.
Failure Modes¶
Local Gate Fails¶
If ./scripts/ci-local-release.sh fails, do not tag or publish.
To fix:
1. Identify the issue from the workflow logs
2. Fix the code
3. Delete the tag: git tag -d vX.Y.Z && git push origin :refs/tags/vX.Y.Z
4. Re-run the local gate until all checks pass
5. Create and push the tag
Publish Fails¶
If publisher CI fails after a local gate pass, the release may be in a partial state.
To fix:
1. Check if GitHub release was created (may need manual cleanup)
2. Check if Homebrew formula was pushed
3. If the original tag-push workflow is still running for that tag, wait for it to finish before starting anything else
4. For post-tag commits, use scripts/retag-release.sh vX.Y.Z
5. Use workflow_dispatch only to rerun an existing tag after the original tag-triggered workflow has already finished and you are not pushing a new tag in parallel
6. scripts/retag-release.sh already waits on the tag-push workflow. It should not launch a second manual publish run for the same tag.
Why this matters: a second publish for the same tag can collide on release assets and fail with GitHub 422 already_exists errors if the first publish already created or uploaded assets.
Homebrew Token Expired¶
The HOMEBREW_TAP_GITHUB_TOKEN secret is validated before publish. If it fails:
- Generate a new PAT at https://github.com/settings/tokens
- Scope:
public_repo(for homebrew-agentops) - Update the secret in repository settings
Manual Release (workflow_dispatch)¶
Use workflow_dispatch only when you need to rerun an existing tag release after the original tag-triggered workflow has already completed or failed.
Do not use workflow_dispatch as the primary path for a fresh release. The primary path is still git push origin vX.Y.Z.
Do not launch a manual run in parallel with a tag-push run for the same tag.
If you have post-tag commits that should become part of the same version, use:
scripts/retag-release.sh vX.Y.Z
If you only need to retry the existing tag with no new commits:
- Go to Actions → Release workflow
- Click "Run workflow"
- Enter the tag (e.g.,
vX.Y.Z) - Click "Run workflow"
Local Testing¶
Before tagging, you can test the build locally:
# Run CI-equivalent local release gate (required)
./scripts/ci-local-release.sh
# Once the target version is known, bind the artifacts to that version for the audit
./scripts/ci-local-release.sh --release-version X.Y.Z
./scripts/resolve-release-artifacts.sh X.Y.Z
# Publishable local artifacts will be written to:
# .agents/releases/local-ci/<timestamp>/
# - sbom-vX.Y.Z.cyclonedx.json
# - sbom-vX.Y.Z.spdx.json
# - security-gate-full.json
# - release-artifacts.json
# Install goreleaser
brew install goreleaser
# Validate config
goreleaser check
# Build snapshot (doesn't require tag)
goreleaser build --snapshot --clean --single-target
# Test the binary
./dist/ao_darwin_arm64/ao version
./dist/ao_darwin_arm64/ao --help
./dist/ao_darwin_arm64/ao status
# Run doc-release gate
./tests/docs/validate-doc-release.sh
Dependency Automation Policy¶
Dependency automation is managed by Dependabot and follows this policy:
- Scope: Go modules (
/cli/go.mod) and GitHub Actions (/.github/workflows/*.yml) - Cadence: Weekly on Monday (separate schedules for Go and Actions)
- Grouping: Minor and patch updates are grouped per ecosystem
- Major updates: Opened as separate PRs for explicit review
- Security updates: Treated as priority work and merged outside normal cadence when validated
- PR limits: Capped to keep queue manageable and avoid review overload
Configuration Files¶
| File | Purpose |
|---|---|
.goreleaser.yml |
Build config, checksums, release notes, Homebrew formula |
.github/workflows/release.yml |
Publisher-only release workflow (publish + assets + attestation) |
.github/workflows/validate.yml |
CI validation (includes doc-release stabilization gate) |
.github/workflows/nightly.yml |
Nightly tests with failure alerts |
.github/dependabot.yml |
Dependabot policy and schedules for Go modules + GitHub Actions |
scripts/validate-release.sh |
Binary validation script |
tests/docs/validate-doc-release.sh |
Unified doc-release gate (links + skills + message freeze) |
Homebrew Tap¶
The Homebrew formula is automatically pushed to: https://github.com/boshu2/homebrew-agentops
Users install with:
brew tap boshu2/agentops
brew install agentops
Security¶
- Checksums: SHA256 checksums for all archives (
checksums.txt) - SLSA provenance: Build attestation via
actions/attest-build-provenance - Dependabot: Automated dependency updates for Go modules and GitHub Actions
- Security scan: Secrets and dangerous pattern detection on every PR
See SECURITY.md for vulnerability reporting.
Troubleshooting¶
"ao version" shows "dev"¶
The ldflags version injection failed. Check .goreleaser.yml:
ldflags:
- -s -w -X main.version={{ .Version }}
./scripts/ci-local-release.sh should catch this before tagging.
Binary artifact missing or renamed¶
GoReleaser archive naming doesn't match extraction pattern. Check:
- .goreleaser.yml archives section
- Release asset names in the GitHub Release page
Homebrew formula not updated¶
- Check
HOMEBREW_TAP_GITHUB_TOKENis valid - Check workflow logs for push errors
- Verify formula at homebrew-agentops repo
Attestation verification fails¶
# Ensure gh CLI is authenticated
gh auth status
# Try verification with verbose output
gh attestation verify <file> --repo boshu2/agentops --verbose