kaishin 0.6.3

Universal self-update library for Rust CLIs, extracted from rvpm and renri.
Documentation

kaishin

Universal self-update library for Rust CLIs, extracted from rvpm and renri.

Features

  • GitHub Releases API integration
  • Automatic installation method detection (cargo install / dev build / direct binary)
  • Background update check with throttling via [Checker]
  • Silent background auto-update (Claude-Code style) via [Checker::auto_update] / [Checker::spawn_auto_update]
  • Customizable update banner
  • Interactive/Non-interactive update flow

Usage

Add this to your Cargo.toml:

[dependencies]

kaishin = "0.1"

Self-Update Command

use kaishin::{KaishinOptions, UpdateOptions, run_self_update};

#[tokio::main]
async fn main() -> Result<()> {
    let opts = KaishinOptions::new(
        "yukimemi",
        "my-tool",
        "my-tool",
        env!("CARGO_PKG_VERSION")
    );
    // If the crates.io package name differs from the binary name
    // (e.g. package `my-tool-cli` ships the binary `my-tool`), tell
    // the `cargo install` fallback which package to build:
    // let opts = opts.crate_name("my-tool-cli");
    let upd_opts = UpdateOptions::new()
        .yes(args.yes)
        .check_only(args.check)
        .non_interactive(args.non_interactive);

    // Run self-update command
    run_self_update(&opts, upd_opts).await?;
    // Note: In non-interactive mode (non_interactive: true), the updater will exit 
    // unless --yes (yes: true) is provided.

    Ok(())
}

Background Update Check

Using Checker to handle state and throttling automatically:

use kaishin::{KaishinOptions, Checker};

#[tokio::main]
async fn main() -> Result<()> {
    let opts = KaishinOptions::new("yukimemi", "rvpm", "rvpm", env!("CARGO_PKG_VERSION"));
    let checker = Checker::new("rvpm", opts);

    // 1. Check in background (with 24h throttle)
    if checker.should_check() {
        let checker_clone = checker.clone();
        tokio::spawn(async move {
            let _ = checker_clone.check_and_save().await;
        });
    }

    // ... run app ...

    // 2. Show banner at the end if update is available
    if let Some(latest) = checker.cached_update() {
        eprintln!("\n{}", checker.format_banner(&latest));
    }

    Ok(())
}

Silent Background Auto-Update

Like Claude Code, you can update silently in the background instead of just notifying. spawn_auto_update fires a detached task that checks, downloads, and replaces the binary with no prompts and no output. The running process keeps the old binary; the new version takes effect on the next launch.

use kaishin::{KaishinOptions, Checker};

#[tokio::main]
async fn main() -> Result<()> {
    let opts = KaishinOptions::new("yukimemi", "rvpm", "rvpm", env!("CARGO_PKG_VERSION"));
    let checker = Checker::new("rvpm", opts);

    // Fire-and-forget: self-throttled (24h by default), so it's safe to call
    // on every startup. Opt-out is up to you — gate this behind your own env
    // var or config if you want users to be able to disable it.
    if std::env::var_os("RVPM_NO_AUTOUPDATE").is_none() {
        checker.spawn_auto_update();
    }

    // ... run app ...
    Ok(())
}

Notes:

  • A dev build (under target/) is never overwritten — the call is a no-op.
  • For a cargo install-managed binary, auto-update only swaps the GitHub release asset; it never triggers a (slow, noisy) cargo install rebuild on this path. If no matching asset exists, the update is skipped.
  • Updates are serialised across processes by an OS advisory lock, so two instances starting at once won't both self-replace (and the lock is released automatically if a process exits or crashes).
  • Use [Checker::auto_update] directly (instead of spawn_auto_update) if you want to await the result and learn which version was installed.

License

MIT