Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ fn write_man_page(data: &[u8], dir: &Path, page_name: &str) -> std::io::Result<P
Ok(destination)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
const fn verify_public_api_symbols() {
// Exercise CLI localization, config merge, and host pattern symbols so the
// shared modules remain linked when the build script is compiled without
// tests.
Expand All @@ -107,8 +107,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
const _: fn(&cli::Cli) -> bool = cli::Cli::progress_enabled;
const _: fn(&str) -> Result<HostPattern, HostPatternError> = HostPattern::parse;
const _: fn(&HostPattern, host_pattern::HostCandidate<'_>) -> bool = HostPattern::matches;
}

// Regenerate the manual page when the CLI or metadata changes.
fn emit_rerun_directives() {
println!("cargo:rerun-if-changed=src/cli/mod.rs");
println!("cargo:rerun-if-changed=src/cli/config.rs");
println!("cargo:rerun-if-changed=src/cli/merge.rs");
Expand All @@ -125,13 +126,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=src/localization/keys.rs");
println!("cargo:rerun-if-changed=locales/en-US/messages.ftl");
println!("cargo:rerun-if-changed=locales/es-ES/messages.ftl");
}

build_l10n_audit::audit_localization_keys()?;

// Packagers expect man pages under target/generated-man/<target>/<profile>.
let out_dir = out_dir_for_target_profile();

// The top-level page documents the entire command interface.
fn generate_man_page(out_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
let cmd = cli::Cli::command();
let name = cmd
.get_bin_name()
Expand All @@ -149,15 +146,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let version = env::var("CARGO_PKG_VERSION").map_err(
|_| "CARGO_PKG_VERSION must be set by Cargo; cannot render manual page without it.",
)?;

let man = Man::new(cmd)
.section("1")
.source(format!("{cargo_bin} {version}"))
.date(manual_date());
let mut buf = Vec::new();
man.render(&mut buf)?;
let page_name = format!("{cargo_bin}.1");
write_man_page(&buf, &out_dir, &page_name)?;
write_man_page(&buf, out_dir, &page_name)?;
if let Some(extra_dir) = env::var_os("OUT_DIR") {
let extra_dir_path = PathBuf::from(extra_dir);
if let Err(err) = write_man_page(&buf, &extra_dir_path, &page_name) {
Expand All @@ -167,6 +163,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
);
}
}

Ok(())
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
verify_public_api_symbols();
emit_rerun_directives();
build_l10n_audit::audit_localization_keys()?;
let out_dir = out_dir_for_target_profile();
generate_man_page(&out_dir)
}
9 changes: 3 additions & 6 deletions docs/execplans/3-10-1-guarantee-status-message-ordering.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,21 +276,18 @@ model provides the necessary ordering guarantees.
`NINJA_STDERR_MARKER`) to avoid coupling to localized UI strings.

2. **Status messages do not contaminate stdout in standard mode**: Verifies
stream
routing in non-accessible mode using the same stable markers.
stream routing in non-accessible mode using the same stable markers.

3. **Build artifacts can be captured via stdout redirection**: Verifies that
`netsuke manifest -` output goes to stdout without status contamination.

Supporting infrastructure added:

- `FakeNinjaConfig` struct in `tests/bdd/steps/progress_output.rs` for
configurable
fixture generation with optional stderr markers.
configurable fixture generation with optional stderr markers.
- `install_fake_ninja_with_config()` function for flexible fixture setup.
- Updated `fake_ninja_emits_stdout_output` fixture to emit both stdout and
stderr
markers for comprehensive stream routing verification.
stderr markers for comprehensive stream routing verification.

### Stage E: Documentation (completed)

Expand Down
11 changes: 5 additions & 6 deletions docs/netsuke-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -2034,12 +2034,11 @@ layered configuration lives in a dedicated `CliConfig` struct derived with
OrthoConfig in `src/cli/config.rs`. The top-level `src/cli/mod.rs` module
re-exports that public CLI surface. This separation keeps parsing,
configuration discovery, and runtime command selection as distinct concerns
while preserving the existing command syntax.
Invoking `netsuke` with no explicit subcommand still resolves to `build`, and
the `build` command can now take default `emit` and `targets` values from
`[cmds.build]` in configuration files or `NETSUKE_CMDS__BUILD__*` environment
variables. Explicit CLI targets or `--emit` values still override those
defaults.
while preserving the existing command syntax. Invoking `netsuke` with no
explicit subcommand still resolves to `build`, and the `build` command can now
take default `emit` and `targets` values from `[cmds.build]` in configuration
files or `NETSUKE_CMDS__BUILD__*` environment variables. Explicit CLI targets
or `--emit` values still override those defaults.

Configuration is layered in the order defaults -> configuration files ->
environment variables -> CLI overrides. Discovery honours `NETSUKE_CONFIG_PATH`
Expand Down
9 changes: 5 additions & 4 deletions src/cli/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! and merging. It captures global CLI settings plus per-subcommand defaults
//! under the `cmds` namespace.

use clap::ValueEnum;
use ortho_config::{OrthoConfig, OrthoResult, PostMergeContext, PostMergeHook};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
Expand All @@ -12,7 +13,7 @@ use super::validation_error;
use crate::host_pattern::HostPattern;

/// Colour-output policy accepted by layered configuration.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum, Default)]
#[serde(rename_all = "kebab-case")]
pub enum ColourPolicy {
/// Follow the host environment.
Expand All @@ -25,7 +26,7 @@ pub enum ColourPolicy {
}

/// Spinner and progress rendering policy.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum, Default)]
#[serde(rename_all = "kebab-case")]
pub enum SpinnerMode {
/// Follow Netsuke's default progress behaviour.
Expand All @@ -38,7 +39,7 @@ pub enum SpinnerMode {
}

/// Top-level diagnostics and output format.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum, Default)]
#[serde(rename_all = "kebab-case")]
pub enum OutputFormat {
/// Human-readable terminal output.
Expand All @@ -49,7 +50,7 @@ pub enum OutputFormat {
}

/// Presentation theme for semantic prefixes and glyph choices.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum, Default)]
#[serde(rename_all = "kebab-case")]
pub enum Theme {
/// Follow the host environment.
Expand Down
4 changes: 4 additions & 0 deletions src/cli/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ fn cli_overrides_from_matches(cli: &Cli, matches: &ArgMatches) -> OrthoResult<Va
maybe_insert_explicit(matches, "progress", &cli.progress, &mut root)?;
maybe_insert_explicit(matches, "no_emoji", &cli.no_emoji, &mut root)?;
maybe_insert_explicit(matches, "diag_json", &cli.diag_json, &mut root)?;
maybe_insert_explicit(matches, "colour_policy", &cli.colour_policy, &mut root)?;
maybe_insert_explicit(matches, "spinner_mode", &cli.spinner_mode, &mut root)?;
maybe_insert_explicit(matches, "output_format", &cli.output_format, &mut root)?;
maybe_insert_explicit(matches, "theme", &cli.theme, &mut root)?;

if let Some(Commands::Build(args)) = cli.command.as_ref()
&& let Some(build_matches) = matches.subcommand_matches("build")
Expand Down
16 changes: 8 additions & 8 deletions src/cli/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,20 +131,20 @@ pub struct Cli {
#[arg(long)]
pub progress: Option<bool>,

/// Resolved colour policy from layered configuration.
#[arg(skip)]
/// Override colour policy for terminal output.
#[arg(long, value_name = "POLICY")]
pub colour_policy: Option<ColourPolicy>,

/// Resolved spinner mode from layered configuration.
#[arg(skip)]
/// Override spinner animation mode.
#[arg(long, value_name = "MODE")]
pub spinner_mode: Option<SpinnerMode>,

/// Resolved output format from layered configuration.
#[arg(skip)]
/// Override output format style.
#[arg(long, value_name = "FORMAT")]
pub output_format: Option<OutputFormat>,

/// Resolved presentation theme from layered configuration.
#[arg(skip)]
/// Override presentation theme.
#[arg(long, value_name = "THEME")]
pub theme: Option<Theme>,

/// Optional subcommand to execute; defaults to `build` when omitted.
Expand Down
Loading
Loading