Update dependencies, improve CLI asset generation, and add man page
- Bump `git2` from 0.20.4 to 0.21.0 and `rusqlite` from 0.39.0 to 0.40.1 in Cargo.toml. - Refactor CLI asset generation in `cli_assets.rs` to write a man page directly instead of generating it with `clap_mangen`. - Implement a function to remove old man pages before writing the new one. - Add tests to ensure the generated man page documents all visible command paths. - Update the install script to copy the newly generated man page correctly. - Introduce a new man page for the `depot` command with comprehensive documentation.
This commit is contained in:
+60
-7
@@ -1,4 +1,4 @@
|
||||
//! Generation helpers for CLI man pages and shell completions.
|
||||
//! Generation helpers for CLI assets.
|
||||
|
||||
use crate::cli::Cli;
|
||||
use anyhow::{Context, Result};
|
||||
@@ -8,8 +8,9 @@ use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
const BIN_NAME: &str = "depot";
|
||||
const MAN_PAGE: &str = include_str!("../man/depot.1");
|
||||
|
||||
/// Generate all supported shell completion scripts and a man page into `out_dir`.
|
||||
/// Generate all supported shell completion scripts and the manual 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()))?;
|
||||
@@ -17,7 +18,7 @@ pub fn generate_cli_assets(out_dir: &Path) -> Result<()> {
|
||||
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_pages(out_dir)?;
|
||||
write_man_page(out_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -40,19 +41,59 @@ fn generate_completion(out_dir: &Path, shell: Shell, filename: &str) -> Result<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_man_pages(out_dir: &Path) -> Result<()> {
|
||||
clap_mangen::generate_to(command_for_generation(), out_dir)
|
||||
.context("Failed to generate clap man pages")?;
|
||||
fn write_man_page(out_dir: &Path) -> Result<()> {
|
||||
remove_old_depot_man_pages(out_dir)?;
|
||||
let output_path = out_dir.join("depot.1");
|
||||
fs::write(&output_path, MAN_PAGE)
|
||||
.with_context(|| format!("Failed to write man page to {}", output_path.display()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_old_depot_man_pages(out_dir: &Path) -> Result<()> {
|
||||
for entry in fs::read_dir(out_dir)
|
||||
.with_context(|| format!("Failed to read output directory {}", out_dir.display()))?
|
||||
{
|
||||
let path = entry
|
||||
.with_context(|| format!("Failed to inspect output directory {}", out_dir.display()))?
|
||||
.path();
|
||||
let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else {
|
||||
continue;
|
||||
};
|
||||
if file_name == "depot.1" || file_name.starts_with("depot-") && file_name.ends_with(".1") {
|
||||
fs::remove_file(&path)
|
||||
.with_context(|| format!("Failed to remove stale man page {}", path.display()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use clap::Command;
|
||||
|
||||
fn visible_command_paths(command: &Command) -> Vec<String> {
|
||||
command
|
||||
.get_subcommands()
|
||||
.filter(|subcommand| !subcommand.is_hide_set())
|
||||
.flat_map(|subcommand| {
|
||||
let name = subcommand.get_name();
|
||||
let nested = visible_command_paths(subcommand);
|
||||
if nested.is_empty() {
|
||||
vec![name.to_string()]
|
||||
} else {
|
||||
let mut paths = vec![name.to_string()];
|
||||
paths.extend(nested.into_iter().map(|nested| format!("{name} {nested}")));
|
||||
paths
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_cli_assets_writes_expected_files() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
std::fs::write(temp.path().join("depot-install.1"), "stale").unwrap();
|
||||
generate_cli_assets(temp.path()).unwrap();
|
||||
|
||||
let bash = temp.path().join("depot.bash");
|
||||
@@ -69,7 +110,19 @@ mod tests {
|
||||
assert!(zsh.exists());
|
||||
assert!(fish.exists());
|
||||
assert!(man.exists());
|
||||
assert!(man_pages > 1);
|
||||
assert_eq!(man_pages, 1);
|
||||
assert!(!std::fs::read_to_string(&man).unwrap().is_empty());
|
||||
assert!(!temp.path().join("depot-install.1").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manual_page_documents_visible_command_paths() {
|
||||
let command = command_for_generation();
|
||||
for path in visible_command_paths(&command) {
|
||||
assert!(
|
||||
MAN_PAGE.contains(&format!("depot {path}")),
|
||||
"manual page does not document `depot {path}`"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ use crate::metadata_time;
|
||||
use anyhow::{Context, Result};
|
||||
use rusqlite::{Connection, params};
|
||||
use sha2::{Digest, Sha256, Sha512};
|
||||
use std::collections::{HashMap, BTreeSet};
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::fs;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -2538,7 +2538,7 @@ pub fn mirrors_status(
|
||||
let head = repo.head().ok();
|
||||
let branch = head
|
||||
.as_ref()
|
||||
.and_then(|h| h.shorthand().map(|s| s.to_string()))
|
||||
.and_then(|h| h.shorthand().ok().map(|s| s.to_string()))
|
||||
.unwrap_or_else(|| "(no branch)".to_string());
|
||||
|
||||
// Latest commit OID
|
||||
|
||||
+5
-5
@@ -170,7 +170,7 @@ fn apply_cherry_picks(repo: &Repository, cherry_pick_revs: &[String]) -> Result<
|
||||
let tree = repo
|
||||
.find_tree(tree_id)
|
||||
.with_context(|| format!("Failed to find tree after cherry-pick {}", rev))?;
|
||||
let message = commit.summary().unwrap_or("cherry-pick");
|
||||
let message = commit.summary().ok().flatten().unwrap_or("cherry-pick");
|
||||
let new_head = repo
|
||||
.commit(
|
||||
None,
|
||||
@@ -336,7 +336,7 @@ fn repair_mirror_refs(repo: &Repository) -> Result<()> {
|
||||
fn sync_remote_tracking_heads(repo: &Repository) -> Result<()> {
|
||||
for reference_result in repo.references_glob("refs/remotes/origin/*")? {
|
||||
let reference = reference_result?;
|
||||
let Some(name) = reference.name() else {
|
||||
let Ok(name) = reference.name() else {
|
||||
continue;
|
||||
};
|
||||
if name == "refs/remotes/origin/HEAD" {
|
||||
@@ -368,7 +368,7 @@ fn ensure_valid_local_head(repo: &Repository) -> Result<()> {
|
||||
let mut candidates: Vec<String> = Vec::new();
|
||||
for reference_result in repo.references_glob("refs/heads/*")? {
|
||||
let reference = reference_result?;
|
||||
let Some(name) = reference.name() else {
|
||||
let Ok(name) = reference.name() else {
|
||||
continue;
|
||||
};
|
||||
candidates.push(name.to_string());
|
||||
@@ -517,7 +517,7 @@ fn resolve_remote_head_like<'a>(repo: &'a Repository) -> Result<Option<git2::Obj
|
||||
let mut candidates: Vec<(String, Oid)> = Vec::new();
|
||||
for reference_result in repo.references_glob("refs/remotes/origin/*")? {
|
||||
let reference = reference_result?;
|
||||
let Some(name) = reference.name() else {
|
||||
let Ok(name) = reference.name() else {
|
||||
continue;
|
||||
};
|
||||
if name == "refs/remotes/origin/HEAD" {
|
||||
@@ -1049,7 +1049,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
repo.head().unwrap().resolve().unwrap().name(),
|
||||
Some("refs/heads/main")
|
||||
Ok("refs/heads/main")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user