Release Process
llmenv follows semantic versioning and automates release distribution via GitHub Actions.
Overview
Releases are triggered by pushing a version tag (v*) to the main branch only. The release workflow:
- Builds binaries for macOS (arm64, x86_64) and Linux (x86_64)
- Generates SHA256 checksums for all binaries (automatic)
- Publishes to crates.io (requires valid
CARGO_REGISTRY_TOKENsecret) - Creates a GitHub Release with pre-built binaries and checksums attached
Changelog
CHANGELOG.md follows Keep a Changelog and
Semantic Versioning. There is exactly one rule that
matters and it is easy to get wrong:
A version section only exists once that version has been git-tagged. Until then, everything lives under
## [Unreleased].
The Cargo.toml version and the changelog must never run ahead of the tags.
If git tag -l shows no vX.Y.Z tag, there is no [X.Y.Z] changelog section
and Cargo.toml is not bumped to it. (This repo previously accumulated
phantom 1.0.0/1.1.0/1.2.0 sections with no tags behind them — don't
recreate that.)
While developing (every change)
Add an entry under ## [Unreleased] in the appropriate category. Do not
touch the Cargo.toml version and do not create a new version heading.
Categories (Keep a Changelog): Added, Changed, Deprecated, Removed,
Fixed, Security. This repo also uses Documentation. Reference the issue/PR
number in the entry, e.g. (#63).
## [Unreleased]
### Added
- New `llmenv foo` subcommand that does X. (#81)
When cutting a release (and only then)
- Decide the version
X.Y.Zfrom the nature of the[Unreleased]entries (breaking → major, feature → minor, fix-only → patch). - Rename
## [Unreleased]to## [X.Y.Z] - YYYY-MM-DDand add a fresh empty## [Unreleased]above it. - Bump
versioninCargo.tomltoX.Y.Zand runcargo buildsoCargo.lockupdates in the same commit. - Commit (
chore: release X.Y.Z), then proceed to Version Tags below to tag and push.
The version bump, changelog rename, and lockfile update land together in the release commit — never piecemeal across unrelated changes.
Version Tags
Create a tag in the format vX.Y.Z and push it to the main branch:
# Version already bumped in Cargo.toml + CHANGELOG.md (see Changelog section)
cargo build --release # Test locally first
git tag -a vX.Y.Z -m "Release vX.Y.Z"
git push origin main vX.Y.Z # Push to main branch
Important: Tags must be pushed from the main branch. Releases triggered from feature branches are blocked by the workflow.
Binary Distribution
GitHub Releases
Pre-built binaries are attached to each release on GitHub:
llmenv-linux-x86_64— Linux x86_64llmenv-macos-x86_64— macOS Intel (x86_64)llmenv-macos-aarch64— macOS Apple Silicon (arm64)checksums.txt— SHA256 checksums for all binaries
Verify Binary Integrity
Each release includes a checksums.txt file. Verify downloaded binaries:
sha256sum -c checksums.txt
All binaries are automatically checksummed during the release build.
crates.io
The Rust crate is published to crates.io. Install with:
cargo install llmenv
Prerequisites:
- A valid
CARGO_REGISTRY_TOKENmust be set as a GitHub Actions secret - Generate tokens at https://crates.io/me
Homebrew
A Homebrew tap is maintained at phaedrus1992/homebrew-tap.
Install:
brew install phaedrus1992/tap/llmenv
Update:
brew upgrade llmenv
Maintenance
Adding a new platform
To add a new platform (e.g., Windows, aarch64 Linux):
-
Update
.github/workflows/release.yml:- Add a new matrix entry under
build-binaries - Set
os,target,asset_name
- Add a new matrix entry under
-
Test locally:
rustup target add <target>cargo build --release --target <target> -
Update Homebrew formula if a new macOS target is added:
- Modify Formula/llmenv.rb in
phaedrus1992/homebrew-tap - Add conditional blocks for the new architecture
- Modify Formula/llmenv.rb in
Rollback
If a release needs to be pulled:
# Mark as pre-release on GitHub (manual UI)
# Unpublish from crates.io (requires crates.io owner access)
cargo yank --vers X.Y.Z
# Remove from Homebrew (PR to phaedrus1992/homebrew-tap)
Secrets Configuration
The release workflow requires:
CARGO_REGISTRY_TOKEN— crates.io API token (scoped to publish only)
Set this in the repository settings under Secrets and variables → Actions.
Security notes:
- The token is passed via environment variable (never command-line arguments)
- GitHub Actions automatically masks secret values in logs
- Always use fine-grained tokens with minimal scope (publish-only)
Future Work: SLSA Provenance and Homebrew Automation
The following enhancements are tracked for future releases:
SLSA Build Provenance
- Integrate
slsa-framework/slsa-github-generatorfor cryptographic proof of build chain - Attach SLSA provenance to GitHub releases
- Enables users to verify binaries were built from claimed source by GitHub Actions
Homebrew Automation
- Auto-generate and update Formula/llmenv.rb SHA256 hashes after release
- Reduce manual steps and error potential in the homebrew-tap repo
- Trigger automation from llmenv release workflow
Troubleshooting
Release workflow doesn't trigger
- Verify tag was pushed to the main branch
- Workflow only runs when tag is pushed to main, not feature branches
- Check GitHub Actions tab for workflow run details
Publish fails with "unauthorized"
- Verify
CARGO_REGISTRY_TOKENis valid and haspublishscope - Check token hasn't expired
Binary artifacts missing from release
- Check the
build-binariesjob succeeded in Actions tab - Verify artifact paths match in
create-release - Checksums should always be generated automatically
Checksum verification fails
- Ensure you're on the same system/shell as the release CI
- Download the binary and checksums.txt from the same release
- Run
sha256sum -c checksums.txtin the download directory