feat: add CLI asset generation for shell completions and man page

This commit is contained in:
2026-02-28 01:01:33 -06:00
parent ce5fefa833
commit b8a2cfa9be
9 changed files with 177 additions and 2 deletions
Generated
+27
View File
@@ -226,6 +226,15 @@ dependencies = [
"strsim",
]
[[package]]
name = "clap_complete"
version = "4.5.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c757a3b7e39161a4e56f9365141ada2a6c915a8622c408ab6bb4b5d047371031"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "4.5.55"
@@ -244,6 +253,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
[[package]]
name = "clap_mangen"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ea63a92086df93893164221ad4f24142086d535b3a0957b9b9bea2dc86301"
dependencies = [
"clap",
"roff",
]
[[package]]
name = "colorchoice"
version = "1.0.4"
@@ -369,6 +388,8 @@ dependencies = [
"ar",
"bzip2",
"clap",
"clap_complete",
"clap_mangen",
"fd-lock",
"filetime",
"flate2",
@@ -1608,6 +1629,12 @@ dependencies = [
"web-sys",
]
[[package]]
name = "roff"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
[[package]]
name = "rpassword"
version = "7.4.0"
+2
View File
@@ -37,6 +37,8 @@ petgraph = "0.8.3"
fd-lock = "4.0.4"
reqwest = { version = "0.13.2", default-features = false, features = ["blocking", "native-tls"] }
filetime = "0.2.27"
clap_complete = "4.5.66"
clap_mangen = "0.2.31"
[dev-dependencies]
tempfile = "=3.25.0"
+4
View File
@@ -19,6 +19,10 @@ Depot is a source-based package manager designed for Linux. It focuses on reprod
cargo build --release
```
When installing via Meson (`meson install`), Depot now generates and installs:
- Shell completions for Bash, Zsh, and Fish
- A `depot(1)` man page
### Installing a Package
To install a package from a spec file:
+10
View File
@@ -125,3 +125,13 @@ install_data(
'contrib/README.md',
install_dir: join_paths(get_option('datadir'), 'doc', meson.project_name()),
)
meson.add_install_script(
sh,
join_paths(src_root, 'tools', 'meson', 'install-cli-assets.sh'),
join_paths(build_root, 'depot'),
join_paths(get_option('datadir'), 'bash-completion', 'completions'),
join_paths(get_option('datadir'), 'zsh', 'site-functions'),
join_paths(get_option('datadir'), 'fish', 'vendor_completions.d'),
join_paths(get_option('mandir'), 'man1'),
)
+7
View File
@@ -112,6 +112,13 @@ pub enum Commands {
},
/// Show current configuration
Config,
/// Generate shell completion scripts and a man page into an output directory.
#[command(hide = true)]
GenerateArtifacts {
/// Output directory for generated files
#[arg(long, value_name = "DIR")]
out_dir: PathBuf,
},
/// Create a new package specification interactively
MakeSpec {
/// Output file path (defaults to <name>.toml)
+74
View File
@@ -0,0 +1,74 @@
//! Generation helpers for CLI man pages and shell completions.
use crate::cli::Cli;
use anyhow::{Context, Result};
use clap::CommandFactory;
use clap_complete::Shell;
use std::fs;
use std::path::Path;
const BIN_NAME: &str = "depot";
/// Generate all supported shell completion scripts and a man page into `out_dir`.
pub fn generate_cli_assets(out_dir: &Path) -> Result<()> {
fs::create_dir_all(out_dir)
.with_context(|| format!("Failed to create output directory {}", out_dir.display()))?;
generate_completion(out_dir, Shell::Bash, "depot.bash")?;
generate_completion(out_dir, Shell::Zsh, "_depot")?;
generate_completion(out_dir, Shell::Fish, "depot.fish")?;
generate_man_page(out_dir, "depot.1")?;
Ok(())
}
fn command_for_generation() -> clap::Command {
Cli::command().name(BIN_NAME)
}
fn generate_completion(out_dir: &Path, shell: Shell, filename: &str) -> Result<()> {
let mut command = command_for_generation();
let output_path = out_dir.join(filename);
let mut buffer = Vec::new();
clap_complete::generate(shell, &mut command, BIN_NAME, &mut buffer);
fs::write(&output_path, buffer).with_context(|| {
format!(
"Failed to write {} completion to {}",
shell,
output_path.display()
)
})?;
Ok(())
}
fn generate_man_page(out_dir: &Path, filename: &str) -> Result<()> {
let output_path = out_dir.join(filename);
let mut buffer = Vec::new();
clap_mangen::Man::new(command_for_generation())
.render(&mut buffer)
.context("Failed to render clap man page")?;
fs::write(&output_path, buffer)
.with_context(|| format!("Failed to write man page {}", output_path.display()))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generate_cli_assets_writes_expected_files() {
let temp = tempfile::tempdir().unwrap();
generate_cli_assets(temp.path()).unwrap();
let bash = temp.path().join("depot.bash");
let zsh = temp.path().join("_depot");
let fish = temp.path().join("depot.fish");
let man = temp.path().join("depot.1");
assert!(bash.exists());
assert!(zsh.exists());
assert!(fish.exists());
assert!(man.exists());
assert!(!std::fs::read_to_string(&man).unwrap().is_empty());
}
}
+6 -2
View File
@@ -1,7 +1,7 @@
use crate::cli::{Cli, Commands, RepoCommands, RepoKindArg};
use crate::{
builder, config, cross, db, deps, index, install, locking, package, planner, signing, source,
staging, ui,
builder, cli_assets, config, cross, db, deps, index, install, locking, package, planner,
signing, source, staging, ui,
};
use anyhow::{Context, Result};
use std::fs;
@@ -1949,6 +1949,10 @@ pub fn run(cli: Cli) -> Result<()> {
}
}
},
Commands::GenerateArtifacts { out_dir } => {
cli_assets::generate_cli_assets(&out_dir)?;
ui::success(format!("Generated CLI assets in {}", out_dir.display()));
}
Commands::Config => {
let config = config::Config::for_rootfs(&cli.rootfs);
let config_lock = locking::open_lock(&config)?;
+1
View File
@@ -3,6 +3,7 @@
mod builder;
mod cli;
mod cli_assets;
mod commands;
mod config;
mod cross;
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -ne 5 ]; then
echo "usage: $0 <depot-bin> <bash-dir> <zsh-dir> <fish-dir> <man1-dir>" >&2
exit 2
fi
depot_bin="$1"
bash_dir="$2"
zsh_dir="$3"
fish_dir="$4"
man1_dir="$5"
if [ ! -x "$depot_bin" ]; then
echo "error: depot binary is not executable: $depot_bin" >&2
exit 1
fi
if [ -z "${MESON_INSTALL_DESTDIR_PREFIX:-}" ]; then
echo "error: MESON_INSTALL_DESTDIR_PREFIX is not set" >&2
exit 1
fi
resolve_dest() {
case "$1" in
/*) printf '%s%s\n' "${DESTDIR:-}" "$1" ;;
*) printf '%s/%s\n' "$MESON_INSTALL_DESTDIR_PREFIX" "$1" ;;
esac
}
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
"$depot_bin" generate-artifacts --out-dir "$tmpdir"
bash_dest="$(resolve_dest "$bash_dir")"
zsh_dest="$(resolve_dest "$zsh_dir")"
fish_dest="$(resolve_dest "$fish_dir")"
man_dest="$(resolve_dest "$man1_dir")"
mkdir -p "$bash_dest" "$zsh_dest" "$fish_dest" "$man_dest"
cp "$tmpdir/depot.bash" "$bash_dest/depot"
cp "$tmpdir/_depot" "$zsh_dest/_depot"
cp "$tmpdir/depot.fish" "$fish_dest/depot.fish"
cp "$tmpdir/depot.1" "$man_dest/depot.1"