Distribute a CLI
CLI distribution currently uses generated GoReleaser configuration and release
scripts rather than the standard speakeasy configure publishing workflow.
The CLI target generates the artifacts needed for cross-platform binary distribution: a GoReleaser configuration, a GitHub Actions release workflow, install scripts for Linux, macOS, and Windows, optional Homebrew tap publishing, optional WinGet publishing, and optional Linux package artifacts via nFPM.
Distribution methods
Generated release artifacts
With cli.generateRelease: true (the default), the generator produces:
.goreleaser.yaml.github/workflows/release.yamlscripts/install.shscripts/install.ps1checksums.txtplus a detached GPG signature for the checksum artifact at release time- optional Homebrew tap publishing when
cli.distribution.homebrew.enabled: true - optional WinGet publishing when
cli.distribution.winget.enabled: true - optional Linux package artifacts (
.deb,.rpm,.apk) whencli.distribution.nfpm.enabled: true
To manage releases with separate tooling, disable this behavior:
cli:
generateRelease: falseGoReleaser
The generated .goreleaser.yaml builds binaries for the major desktop/server targets:
The generated build metadata is derived from the CLI config, especially cliName. For example, with cliName: "petstore", GoReleaser will build from ./cmd/petstore and publish a binary named petstore.
Homebrew tap publishing
Enable Homebrew formula generation in gen.yaml:
cli:
generateRelease: true
distribution:
homebrew:
enabled: true
tap: myorg/homebrew-petstoreWhen enabled, GoReleaser updates a formula in the configured tap repository as part of the release.
One-time setup:
- Create a tap repository such as
github.com/myorg/homebrew-petstore - Add a
HOMEBREW_TAP_GITHUB_TOKENsecret to the CLI repository - For classic personal access tokens, grant
public_repofor public taps orrepofor private taps - For fine-grained personal access tokens, grant
Contents: Read and writeon the tap repository
End users install the CLI with:
brew install myorg/petstore/petstoreWinGet publishing
Enable WinGet manifest generation in gen.yaml:
cli:
generateRelease: true
distribution:
winget:
enabled: true
publisher: MyOrg
repositoryOwner: mygithubuser
publisherUrl: https://example.com
packageIdentifier: MyOrg.Petstore
license: Apache-2.0When enabled, GoReleaser generates a WinGet manifest, pushes a branch, and opens a PR against microsoft/winget-pkgs.
One-time setup:
- Fork
microsoft/winget-pkgsunder a GitHub account or organization - Set
distribution.winget.repositoryOwnerto the owner of that fork - Add a
WINGET_GITHUB_TOKENsecret to the CLI repository - Use a classic GitHub personal access token with
public_reposcope for the fork owner account - Ensure the configured
distribution.winget.packageIdentifiermatches the desired publisher/package name
End users install the CLI with:
winget install MyOrg.PetstoreLinux package artifacts with nFPM
Enable Linux package generation in gen.yaml:
cli:
generateRelease: true
distribution:
nfpm:
enabled: true
formats: deb,rpm
maintainer: "Speakeasy <oss@speakeasy.com>"
license: "Apache-2.0"When enabled, GoReleaser attaches Linux packages to the same GitHub Release as the archive artifacts.
Important: this does not create an apt or yum repository automatically. Users download the package from GitHub Releases and install it manually.
Example generated configuration
version: 2
builds:
- id: petstore
main: ./cmd/petstore
binary: petstore
env:
- CGO_ENABLED=0
goos: [linux, windows, darwin]
goarch: [amd64, arm64]
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.buildTime={{.Date}}
archives:
- id: petstore
formats: [tar.gz]
format_overrides:
- goos: windows
formats: [zip]
checksum:
name_template: checksums.txt
signs:
- artifacts: checksumChecksum signing
The generated release workflow signs the checksum file with GPG. This produces:
checksums.txtchecksums.txt.sig
Add these repository secrets before pushing a release tag:
CLI_GPG_SECRET_KEYCLI_GPG_PASSPHRASE
Use an ASCII-armored private key for CLI_GPG_SECRET_KEY. The generated workflow imports that key, exports its fingerprint to GPG_FINGERPRINT, and GoReleaser signs the checksum artifact.
Creating a release
The generated GitHub Actions workflow runs GoReleaser when a version tag is pushed:
git tag v0.1.0
git push origin v0.1.0This creates a GitHub Release and uploads the compiled archives and checksums.
Prerequisites
- GitHub Actions permissions: the release workflow needs
contents: write - Repository visibility/access: private repositories require appropriate access for anyone downloading release assets
- Checksum signing secrets: set
CLI_GPG_SECRET_KEYandCLI_GPG_PASSPHRASE.CLI_GPG_SECRET_KEYmust contain an ASCII-armored private key used to sign the generated checksum file. - Homebrew tap secret: when Homebrew publishing is enabled, set
HOMEBREW_TAP_GITHUB_TOKEN. For classic PATs usepublic_repo(public tap) orrepo(private tap). For fine-grained PATs grantContents: Read and writeon the tap repository. - WinGet secret: when WinGet publishing is enabled, set
WINGET_GITHUB_TOKENusing a classic PAT withpublic_reposcope for the account that owns thewinget-pkgsfork, and setdistribution.winget.repositoryOwnerto that fork owner.
Install scripts
The generated install scripts download a release archive from GitHub, extract the binary, and place it into a sensible install location.
Linux and macOS
curl -fsSL https://raw.githubusercontent.com/{org}/{repo}/main/scripts/install.sh | bashThe script supports two environment variables derived from the configured envVarPrefix:
<PREFIX>_INSTALL_DIR— override the install directory<PREFIX>_VERSION— install a specific release tag instead oflatest
Example:
PETSTORE_INSTALL_DIR=/opt/bin curl -fsSL .../install.sh | bash
PETSTORE_VERSION=v0.2.0 curl -fsSL .../install.sh | bashBy default, the installer tries /usr/local/bin first and falls back to ~/.local/bin when it does not have write access.
Windows (PowerShell)
iwr -useb https://raw.githubusercontent.com/{org}/{repo}/main/scripts/install.ps1 | iexThe PowerShell installer uses the same environment-variable pattern:
<PREFIX>_INSTALL_DIR<PREFIX>_VERSION
By default, it installs under %LOCALAPPDATA%\Programs\<cliName> and updates the user PATH when needed.
Homebrew installation
If Homebrew tap publishing is enabled for the CLI, users can install it with:
brew install myorg/petstore/petstoreThis requires the publisher to maintain a tap repository named homebrew-<name> and configure HOMEBREW_TAP_GITHUB_TOKEN in the CLI repository’s release workflow.
WinGet installation
If WinGet publishing is enabled for the CLI, Windows users can install it with:
winget install MyOrg.PetstoreThis requires the publisher to fork microsoft/winget-pkgs, set distribution.winget.repositoryOwner to that fork owner, configure WINGET_GITHUB_TOKEN in the CLI repository’s release workflow, and supply the required publisher/package metadata in gen.yaml.
Linux package installation
After downloading a release artifact from GitHub Releases, install it with the matching package manager.
Debian/Ubuntu (.deb)
sudo dpkg -i petstore_*_amd64.debRHEL/Fedora (.rpm)
sudo rpm -i petstore_*_amd64.rpmAlpine (.apk)
sudo apk add --allow-untrusted ./petstore_*_amd64.apkOnly document the package formats actually published for the CLI. End users should see installation steps that match the assets attached to the release they download.
go install
Users with the Go toolchain can install the CLI directly from the module:
go install {packageName}/cmd/{cliName}@latestExample:
go install github.com/acme/petstore-cli/cmd/petstore@latestThis command is derived from the packageName and cliName settings.
Note
go install does not inject the version metadata that the GoReleaser build
adds with linker flags. Version output may therefore appear as (devel).
Shell completions after install
Generated CLIs include Cobra’s built-in completion command, so after installation users can enable completions with:
petstore completion bash
petstore completion zsh
petstore completion fish
petstore completion powershellThis works regardless of whether the CLI was installed from a release archive, an install script, or go install.
Common pitfalls
Last updated on