Production-ready Rust CLI template. Clone → rename → build. Ships with clap v4, tokio, tracing, config, colored output, Docker, and CI.
$ mycli hello Bhupendra --count 3
[1/3] Hello, Bhupendra!
[2/3] Hello, Bhupendra!
[3/3] Hello, Bhupendra!
$ mycli fetch https://httpbin.org/get --headers
Status: 200 OK
Headers:
content-type: application/json
...
$ mycli config
Current Configuration
Config file: ~/.config/mycli/config.toml
[app]
name = mycli
log_level = info
[http]
timeout = 30s
| Feature | Crate | Notes |
|---|---|---|
| CLI parsing | clap v4 |
derive macros, subcommands, env vars |
| Async runtime | tokio |
full feature set |
| Structured logging | tracing + tracing-subscriber |
env-filter, JSON mode |
| Error handling | anyhow + thiserror |
library vs. binary errors |
| Configuration | config |
TOML file + env vars with MYCLI__ prefix |
| Colored output | colored |
cross-platform, respects NO_COLOR |
| Progress spinners | indicatif |
spinner for long operations |
| HTTP client | reqwest |
async, rustls, JSON |
| Docker | multi-stage | builder + slim runtime, non-root user |
| CI | GitHub Actions | test + clippy + fmt, macOS/Linux/Windows matrix |
Click "Use this template" on GitHub, or:
git clone https://github.com/bhupendra05/rust-cli-starter.git myapp
cd myapp
# Rename the binary
sed -i 's/mycli/myapp/g' Cargo.toml src/cli.rs
cargo buildcargo build # debug build
cargo build --release # optimized binary → target/release/mycli
cargo run -- hello # run directlycargo install --path .
mycli --helpdocker build -t mycli .
docker run --rm mycli hello --count 3rust-cli-starter/
├── src/
│ ├── main.rs # Entry point — arg parse, init, dispatch
│ ├── cli.rs # Clap Cli struct + Commands enum + dispatch fn
│ ├── config.rs # AppConfig — TOML + env var loading
│ ├── error.rs # thiserror CliError enum with exit codes
│ ├── output.rs # tracing init, success/error/info helpers
│ └── commands/
│ ├── mod.rs
│ ├── hello.rs # Example sync command
│ ├── fetch.rs # Example async command (HTTP)
│ └── config.rs # Show current config
├── Dockerfile # Multi-stage: builder + slim runtime
├── config.example.toml # Config file template
├── Cargo.toml
└── .github/workflows/
└── ci.yml # Test + clippy + fmt + security audit
- Create
src/commands/mycommand.rs:
use anyhow::Result;
pub async fn run(arg: &str) -> Result<()> {
println!("Running with: {}", arg);
Ok(())
}- Add to
src/commands/mod.rs:
pub mod mycommand;- Add to the
Commandsenum insrc/cli.rs:
/// My new command
MyCommand {
arg: String,
},- Add dispatch arm:
Commands::MyCommand { arg } => {
commands::mycommand::run(&arg).await
}Config is loaded from (in priority order):
- Defaults (hardcoded in
AppConfig::default()) - Config file:
~/.config/mycli/config.toml(or--config path/to/file.toml) - Environment variables:
MYCLI__APP__LOG_LEVEL=debug(double underscore separator)
# Copy example config
mkdir -p ~/.config/mycli
cp config.example.toml ~/.config/mycli/config.toml
# Or use env vars
MYCLI__HTTP__TIMEOUT_SECONDS=60 mycli fetch https://slow-api.example.commycli -v hello # INFO level
mycli -vv hello # DEBUG level
mycli -vvv hello # TRACE level
# JSON logs for production
mycli --json-output hello
# Override with RUST_LOG
RUST_LOG=debug mycli helloMIT © bhupendra05