Shape: CLI

A CLI tool typically ships TWO ways at once: a tarball on GitHub Releases (with platform-specific binaries) AND a package on a system package manager (Homebrew for macOS, with extension points for Cargo / Scoop / Snap / etc.). The release surface is a tag push -- the GitHub Releases page + the Homebrew tap update happen on the same trigger.

When this shape applies

  • You author a command-line tool that users install rather than visit.
  • You want a GitHub Release with cross-platform binaries AND a Homebrew tap update on the same tag push.
  • Optionally, you also publish to other ecosystems (cargo, nuget, rubygems) via the custom adapter pattern.

project-config.yaml

For the primary channels (Homebrew + GitHub Releases), pick one as the canonical distribution.channel and use the GitHub Releases workflow to drive both. Homebrew first since it's the user-facing install vector for the macOS audience:

environments:
  - id: release
    kind: branch-only
    branch: main

distribution:
  channel: homebrew
  registry: https://github.com/myorg/homebrew-tap
  manifest: Formula/mytool.rb
  release_workflow: gaia-release.yml
  tap: myorg/homebrew-tap

The homebrew channel requires the tap sub-field. For a tool that publishes to GitHub Releases AND tap, the GitHub Releases publish runs first inside release_workflow; the Homebrew tap update is a downstream job that runs after the release exists.

Alternatively, if GitHub Releases IS the primary surface:

distribution:
  channel: github-releases
  registry: https://github.com/myorg/mytool
  manifest: Cargo.toml
  release_workflow: gaia-release.yml
  repo: myorg/mytool

The github-releases channel requires the repo sub-field.

Phase 5 routing

With kind: branch-only + distribution:, the Phase 5 router emits publish-primary and suggests /gaia-publish. /gaia-deploy and /gaia-post-deploy both refuse (the tool ships as an artifact, not as a deployed service).

CI overlays

CLI projects often need a matrix build (Linux + macOS + Windows). Add the matrix job via gaia-ci.user-jobs.yml:

jobs:
  build-matrix:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - run: cargo build --release

Publish to homebrew + github-releases

/gaia-publish

The orchestrator dispatches the channel-matching adapter (publish-homebrew or publish-github-releases), which invokes the configured release_workflow. For the combined homebrew + GitHub Releases case, structure your workflow as a sequence:

  • Build cross-platform binaries -> upload as GitHub Release assets.
  • Compute SHA-256 hashes of the binaries.
  • Update the Homebrew Formula in the tap repo with the new version + hashes.

Other ecosystems (the custom channel)

Ecosystems not first-class in this iteration -- cargo (Rust), nuget (.NET), cran (R), rubygems (Ruby), hex (Elixir), helm-chart (Kubernetes) -- use the custom channel and point at a custom adapter:

distribution:
  channel: custom
  registry: https://crates.io
  manifest: Cargo.toml
  release_workflow: gaia-release.yml
  adapter_name: cargo-publish

Then create .gaia/custom/adapters/publish-cargo-publish/ with an adapter-manifest.yaml + a run.sh that drives cargo publish. See the custom-adapter extension contract for the full schema.

You'll know it worked when

  • /gaia-config-validate exits 0; the channel-specific sub-field (tap for homebrew, repo for github-releases) is present.
  • /gaia-help suggests /gaia-publish on the Phase 5 row.
  • A /gaia-publish run creates a GitHub Release at the version in manifest and updates the Homebrew tap (or runs the custom adapter for non-first-class channels).
  • End users can install via brew install myorg/tap/mytool (homebrew) or download the release tarball directly.