smithery.ai

cli-distribution

Distribution and packaging patterns including shell completions, man pages, cross-compilation, and release automation. Use when preparing CLI tools for distribution.

First seen Mar 24, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 12,011 B
  • docs SUMMARY.md 189 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

CLI Distribution Skill

Patterns and best practices for distributing Rust CLI applications to users.

Shell Completion Generation

Using clap_complete

use clap::{CommandFactory, Parser};
use clap_complete::{generate, Generator, Shell};
use std::io;

#[derive(Parser)]
struct Cli {
    /// Generate shell completions
    #[arg(long = "generate", value_enum)]
    generator: Option<Shell>,

    // ... other fields
}

fn print_completions<G: Generator>(gen: G, cmd: &mut clap::Command) {
    generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
}

fn main() {
    let cli = Cli::parse();

    if let Some(generator) = cli.generator {
        let mut cmd = Cli::command();
        print_completions(generator, &mut cmd);
        return;
    }

    // ... rest of application
}

Installation Instructions by Shell

Bash:

# Generate and save
myapp --generate bash > /etc/bash_completion.d/myapp

# Or add to ~/.bashrc
eval "$(myapp --generate bash)"

Zsh:

# Generate and save
myapp --generate zsh > ~/.zfunc/_myapp

# Add to ~/.zshrc
fpath=(~/.zfunc $fpath)
autoload -Uz compinit && compinit

Fish:

# Generate and save
myapp --generate fish > ~/.config/fish/completions/myapp.fish

# Or load directly
myapp --generate fish | source

PowerShell:

# Add to $PROFILE
Invoke-Expression (& myapp --generate powershell)

Dynamic Completions

For commands with dynamic values (like listing resources):

use clap::CommandFactory;
use clap_complete::{generate, Generator};

pub fn generate_with_values<G: Generator>(
    gen: G,
    resources: &[String],
) -> String {
    let mut cmd = Cli::command();

    // Add dynamic values to completion
    if let Some(subcommand) = cmd.find_subcommand_mut("get") {
        for resource in resources {
            subcommand = subcommand.arg(
                clap::Arg::new("resource")
                    .value_parser(clap::builder::PossibleValuesParser::new(resource))
            );
        }
    }

    let mut buf = Vec::new();
    generate(gen, &mut cmd, "myapp", &mut buf);
    String::from_utf8(buf).unwrap()
}

Man Page Generation

Using clap_mangen

[dependencies]
clap_mangen = "0.2"
use clap::CommandFactory;
use clap_mangen::Man;
use std::io;

fn generate_man_page() {
    let cmd = Cli::command();
    let man = Man::new(cmd);
    man.render(&mut io::stdout()).unwrap();
}

Build Script for Man Pages

// build.rs
use clap::CommandFactory;
use clap_mangen::Man;
use std::fs;
use std::path::PathBuf;

include!("src/cli.rs");

fn main() {
    let out_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target/man");
    fs::create_dir_all(&out_dir).unwrap();

    let cmd = Cli::command();
    let man = Man::new(cmd);
    let mut buffer = Vec::new();
    man.render(&mut buffer).unwrap();

    fs::write(out_dir.join("myapp.1"), buffer).unwrap();
}

Install Man Page

# System-wide
sudo cp target/man/myapp.1 /usr/local/share/man/man1/

# User-local
mkdir -p ~/.local/share/man/man1
cp target/man/myapp.1 ~/.local/share/man/man1/

Cross-Compilation

Target Triples

Common targets for CLI distribution:

# Linux
x86_64-unknown-linux-gnu      # GNU Linux
x86_64-unknown-linux-musl     # MUSL Linux (static)
aarch64-unknown-linux-gnu     # ARM64 Linux

# macOS
x86_64-apple-darwin           # Intel Mac
aarch64-apple-darwin          # Apple Silicon

# Windows
x86_64-pc-windows-msvc        # Windows MSVC
x86_64-pc-windows-gnu         # Windows GNU

Cross-Compilation with cross

# Install cross
cargo install cross

# Build for Linux from any platform
cross build --release --target x86_64-unknown-linux-gnu

# Build static binary with MUSL
cross build --release --target x86_64-unknown-linux-musl

GitHub Actions for Cross-Compilation

# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  build:
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            artifact_name: myapp
            asset_name: myapp-linux-amd64

          - os: ubuntu-latest
            target: x86_64-unknown-linux-musl
            artifact_name: myapp
            asset_name: myapp-linux-musl-amd64

          - os: macos-latest
            target: x86_64-apple-darwin
            artifact_name: myapp
            asset_name: myapp-macos-amd64

          - os: macos-latest
            target: aarch64-apple-darwin
            artifact_name: myapp
            asset_name: myapp-macos-arm64

          - os: windows-latest
            target: x86_64-pc-windows-msvc
            artifact_name: myapp.exe
            asset_name: myapp-windows-amd64.exe

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v3

      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Build
        run: cargo build --release --target ${{ matrix.target }}

      - name: Upload binaries
        uses: actions/upload-artifact@v3
        with:
          name: ${{ matrix.asset_name }}
          path: target/${{ matrix.target }}/release/${{ matrix.artifact_name }}

Binary Size Optimization

Cargo.toml optimizations

[profile.release]
opt-level = "z"     # Optimize for size
lto = true          # Link-time optimization
codegen-units = 1   # Better optimization
strip = true        # Strip symbols
panic = "abort"     # Smaller panic handler

Additional size reduction

# Install upx
brew install upx  # macOS
apt install upx   # Linux

# Compress binary
upx --best --lzma target/release/myapp

Before/After example:

Original:  2.5 MB
Optimized: 1.2 MB (strip = true)
UPX:       400 KB (upx --best --lzma)

Package Distribution

Homebrew (macOS/Linux)

Create a formula:

# Formula/myapp.rb
class Myapp < Formula
  desc "Description of your CLI tool"
  homepage "https://github.com/username/myapp"
  url "https://github.com/username/myapp/archive/v1.0.0.tar.gz"
  sha256 "abc123..."
  license "MIT"

  depends_on "rust" => :build

  def install
    system "cargo", "install", "--locked", "--root", prefix, "--path", "."

    # Install shell completions
    generate_completions_from_executable(bin/"myapp", "--generate")

    # Install man page
    man1.install "target/man/myapp.1"
  end

  test do
    assert_match "myapp 1.0.0", shell_output("#{bin}/myapp --version")
  end
end

Debian Package (.deb)

Using cargo-deb:

cargo install cargo-deb

# Create debian package
cargo deb

# Package will be in target/debian/myapp_1.0.0_amd64.deb

Cargo.toml metadata:

[package.metadata.deb]
maintainer = "Your Name <[email protected]>"
copyright = "2024, Your Name"
license-file = ["LICENSE", "4"]
extended-description = """
A longer description of your CLI tool
that spans multiple lines."""
depends = "$auto"
section = "utility"
priority = "optional"
assets = [
    ["target/release/myapp", "usr/bin/", "755"],
    ["README.md", "usr/share/doc/myapp/", "644"],
    ["target/completions/myapp.bash", "usr/share/bash-completion/completions/", "644"],
    ["target/man/myapp.1", "usr/share/man/man1/", "644"],
]

Docker Distribution

# Dockerfile
FROM rust:1.75 as builder
WORKDIR /app
COPY . .
RUN cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/myapp /usr/local/bin/myapp
ENTRYPOINT ["myapp"]

Multi-stage with MUSL (smaller image):

FROM rust:1.75-alpine as builder
RUN apk add --no-cache musl-dev
WORKDIR /app
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl

FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/myapp /myapp
ENTRYPOINT ["/myapp"]

Cargo-binstall Support

Add metadata for faster installation:

[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }{ binary-ext }"
bin-dir = "{ bin }{ binary-ext }"
pkg-fmt = "bin"

Users can then install with:

cargo binstall myapp

Auto-Update

Using self_update crate

[dependencies]
self_update = "0.39"
use self_update::cargo_crate_version;

fn update() -> Result<()> {
    let status = self_update::backends::github::Update::configure()
        .repo_owner("username")
        .repo_name("myapp")
        .bin_name("myapp")
        .show_download_progress(true)
        .current_version(cargo_crate_version!())
        .build()?
        .update()?;

    println!("Update status: `{}`!", status.version());
    Ok(())
}

Update Command

#[derive(Subcommand)]
enum Commands {
    /// Update to the latest version
    Update,
}

fn handle_update() -> Result<()> {
    println!("Checking for updates...");

    match update() {
        Ok(_) => {
            println!("Updated successfully! Please restart the application.");
            Ok(())
        }
        Err(e) => {
            eprintln!("Update failed: {}", e);
            eprintln!("Download manually: https://github.com/username/myapp/releases");
            Err(e)
        }
    }
}

Release Automation

Cargo-release

cargo install cargo-release

# Dry run
cargo release --dry-run

# Release patch version
cargo release patch --execute

# Release minor version
cargo release minor --execute

GitHub Release Action

# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - uses: taiki-e/create-gh-release-action@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          changelog: CHANGELOG.md

  upload-assets:
    needs: release
    strategy:
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
          - target: x86_64-apple-darwin
          - target: x86_64-pc-windows-msvc

    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v3

      - uses: taiki-e/upload-rust-binary-action@v1
        with:
          bin: myapp
          target: ${{ matrix.target }}
          token: ${{ secrets.GITHUB_TOKEN }}

Best Practices

  1. Provide multiple installation methods - Cargo, Homebrew, apt, etc.
  2. Generate completions - Essential for good UX
  3. Create man pages - Professional documentation
  4. Test cross-platform - Build for all major platforms
  5. Optimize binary size - Users appreciate smaller downloads
  6. Automate releases - Use CI/CD for consistent builds
  7. Version clearly - Semantic versioning
  8. Sign binaries - Build trust (especially on macOS)
  9. Provide checksums - Verify download integrity
  10. Document installation - Clear, platform-specific instructions

Distribution Checklist

  • Shell completions generated (bash, zsh, fish, powershell)
  • Man pages created
  • Cross-compiled for major platforms
  • Binary size optimized
  • Release artifacts uploaded to GitHub
  • Installation instructions in README
  • Homebrew formula (if applicable)
  • Debian package (if applicable)
  • Docker image (if applicable)
  • Checksums provided
  • Changelog maintained
  • Version bumped properly

References