cargo-promote
Crate publishing and promotion pipeline for Rust projects. Publishes to private registries (Gitea, GitHub) and optionally crates.io, with configurable pipelines, per-package overrides, deferred promotions, and forge integration.
Install
Commands
Publishing
# Publish current crate to the first pipeline stage
# Publish a specific workspace member
# Publish to a named registry directly
# Select a named pipeline
# Publish even if the version already exists on the registry
# Publish + promote through all pipeline stages
# Ship with auto-confirm and force
# Publish all crates under a directory in dependency order
Promotion
# Promote from one pipeline stage to the next
# Promote with auto-confirm and dry-run
# Bump version and create promote.lock
# Merge branch stage forward
Deferrals
Defer a promotion for later confirmation (e.g. after manual QA):
# Defer a registry promotion
# Defer a branch promotion
# Defer with a notification command
# Confirm or reject a pending deferral
# List deferrals
Info
# List crates on a registry
# Show local crate versions
Configuration
Place a promote.toml in your crate or workspace root:
# Automatically bump version before publishing (patch | minor | major)
= "patch"
[]
= "my-registry" # name in ~/.cargo/config.toml
= "http://<host>/api/packages/<user>/cargo"
[]
= true # prompt before publishing
[]
= ["my-registry", "crates-io"]
Registry Options
Each [registries.<name>] entry supports:
| Field | Description |
|---|---|
cargo_name |
Name as known to cargo publish --registry |
api_url |
HTTP API URL for querying crate listings |
confirm |
Prompt for confirmation before publishing (bool) |
Per-Package Overrides
Override autobump, pipeline, or publishing behavior per crate:
= "patch"
[]
= false # skip during publish-all
[]
= "minor" # override global autobump
= "staging-only" # use a different pipeline
Forge Integration
Connect to a Gitea or GitHub forge for PR-based deferral workflows
and release creation. When configured, defer creates a PR on the
forge, and confirm comments and closes it automatically. The
Forge trait also supports create_release for tag-based releases.
[]
= "gitea" # "gitea" or "github"
= "https://gitea.example.com"
= "your-org"
= "your-project"
= "GITEA_TOKEN" # env var holding the API token
Branch Pipeline
For environment-based promotion through git branches:
= "patch"
[]
= ["develop", "staging", "production"]
= "production" # default: last stage
[]
= "my-registry"
= "http://<host>/api/packages/<user>/cargo"
The pipeline works as a CI chain:
- Push to
develop-- CI runs -- on green,cargo-promote bumpwrites version andpromote.lock, thencargo-promote branch --from developmerges tostaging - CI on
staging-- on green,cargo-promote branch --from stagingmerges toproduction cargo-promote publishonproductiontags the release -- a tag-triggered CI workflow handles registry publishing
promote.lock (YAML) tracks the version and a SHA-256 hash of
publishable files: Cargo.toml, Cargo.lock, and all *.rs files
under src/. The hash is verified at every branch hop to ensure
nothing mutated mid-pipeline.
Registry Auto-Discovery
cargo-promote automatically discovers registries from
.cargo/config.toml files by walking ancestor directories and
checking $CARGO_HOME. Entries in promote.toml take precedence
over discovered registries.
Architecture
Hexagonal architecture with domain ports (traits) and infrastructure adapters:
- Domain types:
CrateRef,CrateInfo,Pipeline,Stage,PublishOpts,Deferral,DeferralKind,DeferralStatus,PromoteLock,ManifestDescription - Ports (traits):
Publisher,RegistryQuery,PipelineRunner,BranchMerger,RemotePusher,Tagger,TokenResolver,Notifier,Forge - Adapters:
CargoPublisher,GiteaRegistry,GitHubRegistry,GiteaForge,LocalGit(implementsBranchMerger,RemotePusher,Tagger),CargoTokenResolver,SpawnNotifier - API:
Apifacade withApiBuilderfor dependency injection - Config:
Configwith per-package overrides and registry auto-discovery from.cargo/config.toml
Cargo Registry Setup
Register your private registry in ~/.cargo/config.toml:
[]
= "sparse+http://<host>/api/packages/<user>/cargo/"
[]
= "my-registry"
Add your token to ~/.cargo/credentials.toml:
[]
= "Bearer <your-token>"
Default Behavior
Without a promote.toml, cargo-promote uses built-in defaults:
- Registry: a single registry named
cratebox(override the URL and owner withREGISTRY_URLandREGISTRY_USERenv vars) - Pipeline: cratebox -> crates-io (crates-io requires confirmation)
publish-allskips a built-in set of repo names (override with--skip)
For any real use, create a promote.toml to define your own
registries and pipelines.
Build