xtask 0.1.1

Reusable building blocks for Rust xtask automation.
Documentation

xtask

Reusable building blocks for Rust xtask crates.

This crate is deliberately not a framework for defining one universal xtask CLI. Each repository should keep its own clap command tree and product-specific build flow. xtask provides the small, reusable primitives that tend to be copied between repositories: repository paths, file checks, process helpers, artifact hashing, Apple signing, DMG packaging, Sparkle appcasts, and GitHub Actions outputs.

Feature flags

Feature Default Purpose
process yes xshell setup and command lookup helpers.
hash yes SHA-256 and human-readable artifact sizes.
github-actions no Append values to GITHUB_OUTPUT.
latest-json no Update simple channel-based latest.json manifests.
sparkle no Generate and merge Sparkle RSS appcasts.
apple no macOS-only Xcode, plist, codesign, provisioning profile, and notarization helpers.
dmg no macOS-only create-dmg wrapper. Implies apple and hash.

Apple modules are compiled on target_os = "macos" only. This keeps Linux CI and non-Apple xtask crates from accidentally depending on platform-specific APIs.

Repository root discovery

Library crates cannot use their own env!("CARGO_MANIFEST_DIR") to discover a downstream repository. Pass the manifest directory from your repository's xtask crate instead:

let root = xtask::repo::root_from_xtask_manifest(env!("CARGO_MANIFEST_DIR"))?;
# anyhow::Ok(())

Basic usage

use xtask::{fs, hash, repo};

let root = repo::root_from_xtask_manifest(env!("CARGO_MANIFEST_DIR"))?;
let app = root.join("target/release/MyApp.app");

fs::ensure_dir(&app)?;
let sha = hash::sha256_file(root.join("dist/MyApp.dmg"))?;
println!("sha256={sha}");
# anyhow::Ok(())

macOS signing example

#[cfg(target_os = "macos")]
fn sign_app(app: &std::path::Path) -> anyhow::Result<()> {
    let options = xtask::apple::CodesignOptions::runtime("Developer ID Application", app);
    xtask::apple::codesign(&options)?;
    xtask::apple::verify_signature(app)
}
# #[cfg(target_os = "macos")]
# sign_app(std::path::Path::new("MyApp.app"))?;
# anyhow::Ok(())

DMG example

#[cfg(target_os = "macos")]
fn package(app: std::path::PathBuf, output: std::path::PathBuf) -> anyhow::Result<()> {
    let options = xtask::dmg::CreateDmgOptions::new("MyApp", app, output);
    xtask::dmg::create(&options)
}
# #[cfg(target_os = "macos")]
# package("MyApp.app".into(), "MyApp.dmg".into())?;
# anyhow::Ok(())

Publishing

The package name xtask is intentionally short. Before publishing, run:

cargo publish --dry-run --all-features

Then publish with a crates.io token from an account that should own the crate.