Skip to content

Commit a859318

Browse files
authored
Merge pull request #1628 from cgwalters/fix-man
Fix man page generation
2 parents 2cb69c1 + 5090f1d commit a859318

File tree

6 files changed

+50
-23
lines changed

6 files changed

+50
-23
lines changed

.dockerignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
target
44
# This one can have large .qcow2 files written by coreos-assembler
55
.cosa
6-
# These directories don't contribute to our container build
7-
docs/
86
# TMT interprets these, not the container build
97
plans/
108
# These only affect flow outside of the container

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ COPY --from=src /src /src
6363
WORKDIR /src
6464
# See https://www.reddit.com/r/rust/comments/126xeyx/exploring_the_problem_of_faster_cargo_docker/
6565
# We aren't using the full recommendations there, just the simple bits.
66-
RUN --mount=type=cache,target=/build/target --mount=type=cache,target=/var/roothome <<EORUN
66+
RUN --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome <<EORUN
6767
set -xeuo pipefail
6868
make
6969
make install-all DESTDIR=/out

crates/xtask/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ path = "src/xtask.rs"
1414
[dependencies]
1515
# Workspace dependencies
1616
anyhow = { workspace = true }
17+
anstream = { workspace = true }
1718
camino = { workspace = true }
1819
chrono = { workspace = true, features = ["std"] }
1920
fn-error-context = { workspace = true }
21+
owo-colors = { workspace = true }
2022
serde = { workspace = true, features = ["derive"] }
2123
serde_json = { workspace = true }
2224
tempfile = { workspace = true }

crates/xtask/src/man.rs

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
66
use anyhow::{Context, Result};
77
use camino::Utf8Path;
8+
use fn_error_context::context;
89
use serde::{Deserialize, Serialize};
9-
use std::fs;
10+
use std::{fs, io::Write};
1011
use xshell::{cmd, Shell};
1112

1213
/// Represents a CLI option extracted from the JSON dump
@@ -50,10 +51,22 @@ pub struct CliPositional {
5051
}
5152

5253
/// Extract CLI structure by running the JSON dump command
54+
#[context("Extracting CLI")]
5355
pub fn extract_cli_json(sh: &Shell) -> Result<CliCommand> {
54-
let json_output = cmd!(sh, "cargo run --features=docgen -- internals dump-cli-json")
55-
.read()
56-
.context("Running CLI JSON dump command")?;
56+
// If we have a release binary, assume that we should compile
57+
// in release mode as hopefully we'll have incremental compilation
58+
// enabled.
59+
let releasebin = Utf8Path::new("target/release/bootc");
60+
let release = releasebin
61+
.try_exists()
62+
.context("Querying release bin")?
63+
.then_some("--release");
64+
let json_output = cmd!(
65+
sh,
66+
"cargo run {release...} --features=docgen -- internals dump-cli-json"
67+
)
68+
.read()
69+
.context("Running CLI JSON dump command")?;
5770

5871
let cli_structure: CliCommand =
5972
serde_json::from_str(&json_output).context("Parsing CLI JSON output")?;
@@ -253,14 +266,15 @@ pub fn update_markdown_with_options(
253266
}
254267

255268
/// Discover man page files and infer their command paths from filenames
269+
#[context("Querying man page mappings")]
256270
fn discover_man_page_mappings(
257271
cli_structure: &CliCommand,
258272
) -> Result<Vec<(String, Option<Vec<String>>)>> {
259273
let man_dir = Utf8Path::new("docs/src/man");
260274
let mut mappings = Vec::new();
261275

262276
// Read all .md files in the man directory
263-
for entry in fs::read_dir(man_dir)? {
277+
for entry in fs::read_dir(man_dir).context("Reading docs/src/man")? {
264278
let entry = entry?;
265279
let path = entry.path();
266280

@@ -278,7 +292,7 @@ fn discover_man_page_mappings(
278292
.ok_or_else(|| anyhow::anyhow!("Invalid filename"))?;
279293

280294
// Check if the file contains generation markers
281-
let content = fs::read_to_string(&path)?;
295+
let content = fs::read_to_string(&path).with_context(|| format!("Reading {path:?}"))?;
282296
if !content.contains("<!-- BEGIN GENERATED OPTIONS -->")
283297
&& !content.contains("<!-- BEGIN GENERATED SUBCOMMANDS -->")
284298
{
@@ -331,6 +345,7 @@ fn find_command_path_for_filename(
331345
}
332346

333347
/// Sync all man pages with their corresponding CLI commands
348+
#[context("Syncing man pages")]
334349
pub fn sync_all_man_pages(sh: &Shell) -> Result<()> {
335350
let cli_structure = extract_cli_json(sh)?;
336351

@@ -382,12 +397,14 @@ pub fn sync_all_man_pages(sh: &Shell) -> Result<()> {
382397
}
383398

384399
/// Generate man pages from hand-written markdown sources
400+
#[context("Generating manpages")]
385401
pub fn generate_man_pages(sh: &Shell) -> Result<()> {
386402
let man_src_dir = Utf8Path::new("docs/src/man");
387403
let man_output_dir = Utf8Path::new("target/man");
388404

389405
// Ensure output directory exists
390-
sh.create_dir(man_output_dir)?;
406+
sh.create_dir(man_output_dir)
407+
.with_context(|| format!("Creating {man_output_dir}"))?;
391408

392409
// First, sync the markdown files with current CLI options
393410
sync_all_man_pages(sh)?;
@@ -396,7 +413,7 @@ pub fn generate_man_pages(sh: &Shell) -> Result<()> {
396413
let version = get_package_version()?;
397414

398415
// Convert each markdown file to man page format
399-
for entry in fs::read_dir(man_src_dir)? {
416+
for entry in fs::read_dir(man_src_dir).context("Reading manpages")? {
400417
let entry = entry?;
401418
let path = entry.path();
402419

@@ -421,7 +438,7 @@ pub fn generate_man_pages(sh: &Shell) -> Result<()> {
421438
let output_file = man_output_dir.join(format!("{}.{}", base_name, section));
422439

423440
// Read markdown content and replace version placeholders
424-
let content = fs::read_to_string(&path)?;
441+
let content = fs::read_to_string(&path).with_context(|| format!("Reading {path:?}"))?;
425442
let content_with_version = content.replace("<!-- VERSION PLACEHOLDER -->", &version);
426443

427444
// Check if we need to regenerate by comparing input and output modification times
@@ -437,16 +454,14 @@ pub fn generate_man_pages(sh: &Shell) -> Result<()> {
437454

438455
if should_regenerate {
439456
// Create temporary file with version-replaced content
440-
let temp_path = format!("{}.tmp", path.display());
441-
fs::write(&temp_path, content_with_version)?;
457+
let mut tmpf = tempfile::NamedTempFile::new_in(path.parent().unwrap())?;
458+
tmpf.write_all(content_with_version.as_bytes())?;
459+
let tmpf = tmpf.path();
442460

443-
cmd!(sh, "go-md2man -in {temp_path} -out {output_file}")
461+
cmd!(sh, "go-md2man -in {tmpf} -out {output_file}")
444462
.run()
445463
.with_context(|| format!("Converting {} to man page", path.display()))?;
446464

447-
// Clean up temporary file
448-
fs::remove_file(&temp_path)?;
449-
450465
println!("Generated {}", output_file);
451466
}
452467
}
@@ -458,6 +473,7 @@ pub fn generate_man_pages(sh: &Shell) -> Result<()> {
458473
}
459474

460475
/// Get version from Cargo.toml
476+
#[context("Querying package version")]
461477
fn get_package_version() -> Result<String> {
462478
let cargo_toml =
463479
fs::read_to_string("crates/lib/Cargo.toml").context("Reading crates/lib/Cargo.toml")?;
@@ -596,6 +612,7 @@ TODO: Add practical examples showing how to use this command.
596612
}
597613

598614
/// Apply post-processing fixes to generated man pages
615+
#[context("Fixing man pages")]
599616
fn apply_man_page_fixes(sh: &Shell, dir: &Utf8Path) -> Result<()> {
600617
// Fix apostrophe rendering issue
601618
for entry in fs::read_dir(dir)? {
@@ -608,7 +625,7 @@ fn apply_man_page_fixes(sh: &Shell, dir: &Utf8Path) -> Result<()> {
608625
.map_or(false, |e| e.chars().all(|c| c.is_numeric()))
609626
{
610627
// Check if the file already has the fix applied
611-
let content = fs::read_to_string(&path)?;
628+
let content = fs::read_to_string(&path).with_context(|| format!("Reading {path:?}"))?;
612629
if content.starts_with(".ds Aq \\(aq\n") {
613630
// Already fixed, skip
614631
continue;

crates/xtask/src/xtask.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@ const TAR_REPRODUCIBLE_OPTS: &[&str] = &[
2727
];
2828

2929
fn main() {
30+
use std::io::Write as _;
31+
32+
use owo_colors::OwoColorize;
3033
if let Err(e) = try_main() {
31-
eprintln!("error: {e:?}");
34+
let mut stderr = anstream::stderr();
35+
// Don't panic if writing fails.
36+
let _ = writeln!(stderr, "{}{:#}", "error: ".red(), e);
3237
std::process::exit(1);
3338
}
3439
}
@@ -56,7 +61,10 @@ fn try_main() -> Result<()> {
5661
}
5762
}
5863
// Otherwise verify we're in the toplevel
59-
if !Utf8Path::new("ADOPTERS.md").try_exists()? {
64+
if !Utf8Path::new("ADOPTERS.md")
65+
.try_exists()
66+
.context("Checking for toplevel")?
67+
{
6068
anyhow::bail!("Not in toplevel")
6169
}
6270
}
@@ -69,11 +77,11 @@ fn try_main() -> Result<()> {
6977
.iter()
7078
.find_map(|(k, f)| (*k == cmd).then_some(*f))
7179
.unwrap_or(print_help);
72-
f(&sh)?;
80+
return f(&sh);
7381
} else {
7482
print_help(&sh)?;
83+
Ok(())
7584
}
76-
Ok(())
7785
}
7886

7987
fn gitrev_to_version(v: &str) -> String {

0 commit comments

Comments
 (0)