cargo-promote (0.1.3)
Installation
[registry]
default = "gitea"
[registries.gitea]
index = "sparse+ " # Sparse index
# index = " " # Git
[net]
git-fetch-with-cli = truecargo add cargo-promote@0.1.3About this package
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
cargo install --git https://github.com/89jobrien/cargo-promote
Commands
Publishing
# Publish current crate to the first pipeline stage
cargo-promote publish
# Publish a specific workspace member
cargo-promote publish -p my-crate
# Publish to a named registry directly
cargo-promote publish --registry my-registry
# Select a named pipeline
cargo-promote publish --pipeline staging-only
# Publish even if the version already exists on the registry
cargo-promote publish --force
# Publish + promote through all pipeline stages
cargo-promote ship -p my-crate
# Ship with auto-confirm and force
cargo-promote ship -p my-crate -y --force
# Publish all crates under a directory in dependency order
cargo-promote publish-all --path ~/dev --dry-run
cargo-promote publish-all --force --skip "sandbox,experiments"
cargo-promote publish-all --registry my-registry --allow-dirty
Promotion
# Promote from one pipeline stage to the next
cargo-promote promote -p my-crate --from my-registry
# Promote with auto-confirm and dry-run
cargo-promote promote -p my-crate --from my-registry -y --dry-run
# Bump version and create promote.lock
cargo-promote bump
# Merge branch stage forward
cargo-promote branch --from develop
Deferrals
Defer a promotion for later confirmation (e.g. after manual QA):
# Defer a registry promotion
cargo-promote defer --from my-registry
# Defer a branch promotion
cargo-promote defer --branch --from develop
# Defer with a notification command
cargo-promote defer --from my-registry --command notify-send "promotion deferred"
# Confirm or reject a pending deferral
cargo-promote confirm <ticket>
cargo-promote confirm <ticket> --reason "passed QA"
cargo-promote reject <ticket> --reason "failed QA"
# List deferrals
cargo-promote deferrals
cargo-promote deferrals --pending
Info
# List crates on a registry
cargo-promote list
cargo-promote list --registry crates-io
# Show local crate versions
cargo-promote status
Configuration
Place a promote.toml in your crate or workspace root:
# Automatically bump version before publishing (patch | minor | major)
autobump = "patch"
[registries.my-registry]
cargo_name = "my-registry" # name in ~/.cargo/config.toml
api_url = "http://<host>/api/packages/<user>/cargo"
[registries.crates-io]
confirm = true # prompt before publishing
[pipelines.default]
stages = ["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:
autobump = "patch"
[packages.my-internal-crate]
publish = false # skip during publish-all
[packages.important-lib]
autobump = "minor" # override global autobump
pipeline = "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.
[forge]
type = "gitea" # "gitea" or "github"
url = "https://gitea.example.com"
owner = "your-org"
repo = "your-project"
token_env = "GITEA_TOKEN" # env var holding the API token
Branch Pipeline
For environment-based promotion through git branches:
autobump = "patch"
[pipeline]
stages = ["develop", "staging", "production"]
release_branch = "production" # default: last stage
[registries.my-registry]
cargo_name = "my-registry"
api_url = "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:
[registries.my-registry]
index = "sparse+http://<host>/api/packages/<user>/cargo/"
[registry]
default = "my-registry"
Add your token to ~/.cargo/credentials.toml:
[registries.my-registry]
token = "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
cargo build --release
cargo test
cargo clippy
Dependencies
| ID | Version |
|---|---|
| anyhow | ^1.0 |
| cargo_metadata | ^0.18 |
| chrono | ^0.4 |
| clap | ^4 |
| secrecy | ^0.10 |
| semver | ^1 |
| serde | ^1.0 |
| serde_json | ^1.0 |
| serde_yaml | ^0.9 |
| sha2 | ^0.10 |
| thiserror | ^2 |
| toml | ^0.8 |
| toml_edit | ^0.22 |
| ureq | ^2 |
| tempfile | ^3.0 |