CLI Generation
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
Section titled “Distribution methods”brew install org/tap/cli.microsoft/winget-pkgs. Users install with winget install Publisher.Package..deb, .rpm, or .apk packages attached to GitHub Releases. Users download the package artifact from a release and install it manually.install.sh and install.ps1 scripts that download the latest or a pinned release from GitHub.go installGenerated release artifacts
Section titled “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
Section titled “GoReleaser”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
Section titled “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
Section titled “WinGet 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
Section titled “Linux 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
Section titled “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
Section titled “Checksum 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
Section titled “Creating a release”The generated GitHub Actions workflow runs GoReleaser when a version tag is pushed:
git tag v0.1.0git push origin v0.1.0This creates a GitHub Release and uploads the compiled archives and checksums.
Prerequisites
Section titled “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
Section titled “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
Section titled “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 | bashPETSTORE_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)
Section titled “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
Section titled “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
Section titled “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
Section titled “Linux package installation”After downloading a release artifact from GitHub Releases, install it with the matching package manager.
Debian/Ubuntu (.deb)
Section titled “Debian/Ubuntu (.deb)”sudo dpkg -i petstore_*_amd64.debRHEL/Fedora (.rpm)
Section titled “RHEL/Fedora (.rpm)”sudo rpm -i petstore_*_amd64.rpmAlpine (.apk)
Section titled “Alpine (.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
Section titled “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
Section titled “Shell completions after install”Generated CLIs include Cobra’s built-in completion command, so after installation users can enable completions with:
petstore completion bashpetstore completion zshpetstore completion fishpetstore completion powershellThis works regardless of whether the CLI was installed from a release archive, an install script, or go install.
Common pitfalls
Section titled “Common pitfalls”contents: write permission and that the repository token has enough scope to create releases and upload assets.go install shows (devel) version outputgo install does not apply the GoReleaser ldflags used in release builds.