Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
63ed915
Add optional description field to manifest targets
Aug 9, 2026
02792a1
Add netsuke help targets subcommand
Aug 9, 2026
3706808
Cover help targets with snapshot, integration, and BDD tests
Aug 9, 2026
537955f
Document target descriptions and the help targets command
Aug 9, 2026
539cb34
Mark the issue-551 execplan complete
Aug 9, 2026
af9750c
Wrap the PR link in the execplan to satisfy markdownlint
Aug 9, 2026
e964857
Merge the help-topic BDD verification helpers
Aug 9, 2026
6d2c34d
Deduplicate the help catalogue snapshot test setup
Aug 9, 2026
1d0ccea
Extract manifest render description and recipe helpers
leynos Aug 10, 2026
e72dbf2
Align help targets columns by Unicode display width
leynos Aug 10, 2026
f40cb73
Avoid trailing spaces in help catalogues
Aug 11, 2026
0df3475
Resolve help catalogue review findings
Aug 11, 2026
05249f5
Correct missing description comment
Aug 11, 2026
47bfe48
Localize nested help topics
Aug 11, 2026
1522b6d
Correct documentation for target descriptions
leynos Aug 12, 2026
05df1a7
Correct Polish and Portuguese help text
leynos Aug 12, 2026
0528ee9
Refactor help topic localization mapping
leynos Aug 12, 2026
59204cc
Document target discovery and runner boundaries
leynos Aug 12, 2026
18ff2d7
Document side-effect-free help rendering
leynos Aug 12, 2026
2303ce7
Harden help target rendering (#551)
leynos Aug 12, 2026
1e8c6ee
Reconcile target discovery documentation and translations (#551)
leynos Aug 12, 2026
55c1193
Describe the targets help topic (#551)
leynos Aug 12, 2026
8f181da
Document v0.1.0 help and localisation updates
leynos Aug 12, 2026
0991671
Use dotted help localisation key in execplan
leynos Aug 12, 2026
9522818
Harden targets help queries (#551)
leynos Aug 12, 2026
5616757
Rename target descriptions execplan (#551)
leynos Aug 12, 2026
9c3e775
Polish localized help copy
leynos Aug 12, 2026
16ade1b
Correct locale review wording
leynos Aug 12, 2026
e38ffd4
Document restricted help-targets rendering (#551)
leynos Aug 12, 2026
3ea3f38
Restrict help query data disclosure (#551)
leynos Aug 12, 2026
e455aa3
Clarify target help diagnostics (#551)
leynos Aug 12, 2026
e5afbff
Refine Czech and Romanian help status (#551)
leynos Aug 12, 2026
8bff44c
Deduplicate help-target rejection tests (#551)
leynos Aug 13, 2026
6b5ed7d
Document target discovery descriptions
leynos Aug 13, 2026
edc82a6
Document help-target query boundaries (#551)
leynos Aug 13, 2026
e869dbc
Restrict target-help manifest queries (#551)
leynos Aug 14, 2026
7feba4a
Fix migration guide spacing (#551)
leynos Aug 14, 2026
a002fff
Harden target-help catalogue checks (#551)
leynos Aug 14, 2026
793b718
Extract foreach catalogue assertion (#551)
leynos Aug 14, 2026
4133b0e
Align release help with target catalogue (#551)
leynos Aug 14, 2026
c840edb
Keep terminal control detection const-compatible (#551)
leynos Aug 14, 2026
252aefd
Document release shell completion sidecars
leynos Aug 14, 2026
2de05cb
Generate release shell completions (#551)
leynos Aug 14, 2026
a34b13f
Clarify help target validation (#551)
leynos Aug 14, 2026
4ede996
Isolate help target query dependencies (#551)
leynos Aug 15, 2026
19b4475
Instrument help target queries (#551)
leynos Aug 15, 2026
7497b69
Document help-target query telemetry
leynos Aug 15, 2026
bd2b2da
Separate help target query effects (#551)
leynos Aug 15, 2026
a3220ea
Repair post-rebase module layout (#551)
leynos Aug 15, 2026
6a6d003
Consolidate help-target telemetry tests (#551)
leynos Aug 15, 2026
ba3d7f0
Clarify target compatibility documentation
leynos Aug 15, 2026
5711980
Harden localized help diagnostics (#551)
leynos Aug 15, 2026
fc26f72
Tighten telemetry labels and locale text (#551)
leynos Aug 16, 2026
0ba3fd8
Refresh help targets ExecPlan (#551)
leynos Aug 16, 2026
30a4037
Repair rebase integration gaps (#551)
leynos Aug 16, 2026
a11b52b
Bound help contention test (#551)
leynos Aug 16, 2026
59c7cb1
Reject unsafe help target names (#551)
leynos Aug 16, 2026
6a74574
Repair manifest configuration rebase (#551)
leynos Aug 16, 2026
1903b78
Deduplicate help diagnostic tests (#551)
leynos Aug 16, 2026
43e6681
Keep runner module within review limit (#551)
leynos Aug 16, 2026
c7f65f6
Remove duplicated AST design section
leynos Aug 16, 2026
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
27 changes: 27 additions & 0 deletions .github/release-staging.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,33 @@ destination = "LICENSE"
output = "license_path"
required = true

# These files are generated by build.rs from Cli::command(), so each released
# archive carries completion data that matches the binary it contains.
[[common.artefacts]]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
source = "target/generated-completions/{target}/release/netsuke.bash"
destination = "completions/bash/netsuke"
required = true

[[common.artefacts]]
source = "target/generated-completions/{target}/release/netsuke.elv"
destination = "completions/elvish/netsuke.elv"
required = true

[[common.artefacts]]
source = "target/generated-completions/{target}/release/netsuke.fish"
destination = "completions/fish/netsuke.fish"
required = true

[[common.artefacts]]
source = "target/generated-completions/{target}/release/_netsuke.ps1"
destination = "completions/powershell/_netsuke.ps1"
required = true

[[common.artefacts]]
source = "target/generated-completions/{target}/release/_netsuke"
destination = "completions/zsh/_netsuke"
required = true

[targets.linux-x86_64]
platform = "linux"
arch = "x86_64"
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ name = "netsuke"
path = "src/main.rs"

[package.metadata.ortho_config]
root_type = "netsuke::cli::CliConfig"
root_type = "netsuke::cli::ReleaseHelpCli"
locales = [
"ar",
"cs",
Expand Down Expand Up @@ -132,10 +132,12 @@ wait-timeout = "0.2"
url = "^2.5.0"
ortho_config = { version = "0.9.0", features = ["serde_json"] }
sys-locale = "0.3.2"
unicode-width = "0.2.1"

[build-dependencies]
cap-std = "3.4.4"
clap = { version = "4.5.0", features = ["derive"] }
clap_complete = "4.5.0"
clap_mangen = "0.3.0"
ortho_config = { version = "0.9.0", features = ["serde_json"] }
serde = { version = "1", features = ["derive"] }
Expand Down
42 changes: 37 additions & 5 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
//! Build script for Netsuke.
//!
//! This script performs two main tasks:
//! This script performs three main tasks:
//! - Generate the CLI manual page into `target/generated-man/<target>/<profile>` for release
//! packaging.
//! - Generate Bash, Elvish, Fish, PowerShell, and Zsh completion files into
//! `target/generated-completions/<target>/<profile>` from the same Clap command tree.
//! - Audit localization keys declared in `src/localization/keys.rs` against the Fluent bundles
//! in `locales/*/messages.ftl`, failing the build if any declared key is missing from a
//! locale.
use cap_std::{ambient_authority, fs::Dir};
use clap::CommandFactory;
use clap_complete::aot::{Shell, generate_to};
use clap_mangen::Man;
use std::{
env,
Expand Down Expand Up @@ -119,10 +122,10 @@ fn manual_date() -> String {
clippy::disallowed_methods,
reason = "TARGET and PROFILE are set by Cargo for the build script alone; nothing else knows the triple and profile being built, so they cannot be passed in"
)]
fn out_dir_for_target_profile() -> PathBuf {
fn out_dir_for_target_profile(artefact: &str) -> PathBuf {
let target = env::var("TARGET").unwrap_or_else(|_| "unknown-target".into());
let profile = env::var("PROFILE").unwrap_or_else(|_| "unknown-profile".into());
PathBuf::from(format!("target/generated-man/{target}/{profile}"))
PathBuf::from(format!("target/{artefact}/{target}/{profile}"))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

fn write_man_page(data: &[u8], dir: &Path, page_name: &str) -> std::io::Result<PathBuf> {
Expand All @@ -144,6 +147,7 @@ fn write_man_page(data: &[u8], dir: &Path, page_name: &str) -> std::io::Result<P
fn emit_rerun_directives() {
println!("cargo:rerun-if-changed=src/cli/build_support.rs");
println!("cargo:rerun-if-changed=src/cli/config.rs");
println!("cargo:rerun-if-changed=src/cli/help.rs");
println!("cargo:rerun-if-changed=src/cli/parser.rs");
println!("cargo:rerun-if-changed=src/cli/parsing.rs");
println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION");
Expand Down Expand Up @@ -205,9 +209,37 @@ fn generate_man_page(out_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}

fn generate_completions(out_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
let working_dir = Dir::open_ambient_dir(".", ambient_authority())?;
working_dir.create_dir_all(out_dir)?;
let cli_command = cli::Cli::command();
let name = cli_command
.get_bin_name()
.unwrap_or_else(|| cli_command.get_name())
.to_owned();

for shell in [
Shell::Bash,
Shell::Elvish,
Shell::Fish,
Shell::PowerShell,
Shell::Zsh,
] {
let mut completion_command = cli::Cli::command();
generate_to(shell, &mut completion_command, &name, out_dir)?;
}

// Publish the directory so tests can inspect the exact generated artefacts
// rather than recreating the generator's path and file-name conventions.
println!(
"cargo:rustc-env=NETSUKE_GENERATED_COMPLETIONS_DIR={}",
out_dir.display()
);
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
emit_rerun_directives();
build_l10n_audit::audit_localization_keys()?;
let out_dir = out_dir_for_target_profile();
generate_man_page(&out_dir)
generate_man_page(&out_dir_for_target_profile("generated-man"))?;
generate_completions(&out_dir_for_target_profile("generated-completions"))
}
85 changes: 82 additions & 3 deletions docs/developers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,73 @@ as the durable architecture record.

[adr-003-cli]: adr-003-agent-consistent-human-first-cli.md

## Ninja child-process APIs and help-runner boundary

The public Ninja process helpers are re-exported from `netsuke::runner`.
`CommandEnv` is an explicit, composable set of child-process overrides:
`CommandEnv::inherit()` leaves the parent environment in place,
`with_var` overrides one variable, and `with_path` replaces the child's
`PATH`. The parent process is never mutated. `NinjaBuildRequest` and
`NinjaToolRequest` borrow the program, CLI settings, generated build file,
target list or tool name, and `CommandEnv` needed for one invocation.

The legacy `run_ninja` and `run_ninja_tool` helpers retain their existing
signatures and inherit the parent environment. Callers that need an isolated
child use `run_ninja_with` or `run_ninja_tool_with` with one of the request
types. Keep environment selection at this process boundary: do not add
process-wide environment mutation to callers or tests.

`netsuke help targets` is deliberately a different runner path. The dispatch
layer routes `HelpTopic::Targets` to `src/runner/help.rs`, which resolves and
runs the manifest loading, expansion, and rendering stages, then always builds
and validates a `BuildGraph` before rendering the deterministic
action-then-target catalogue. An invalid graph aborts before the catalogue is
rendered. It must not generate a Ninja file, call a Ninja subprocess, execute a
recipe, or create build outputs. Its Jinja environment is a restricted,
side-effect-free query surface. It allowlists only the lexical path filters
`basename`, `dirname`, `with_suffix`, and `relative_to`, the collection filters
`uniq`, `flatten`, and `group_by`, and the clock-independent `timedelta`
function. It rejects `env()` and `glob()`, file tests, filesystem metadata
filters such as `size` and `linecount`, `hash`, `digest`, `contents`, `realpath`,
and `expanduser`, executable discovery through `which` and
`command_available`, network and command helpers (`fetch`, `shell`, and
`grep`), and the clock-dependent `now()` function. Normal build manifest
rendering still registers the full standard library; this restriction applies
only to query rendering.

The query allowlist has one owner: `register_manifest_query`. Query loading
does not construct `StdlibConfig`; the registration function composes the
allowlist directly. Reuse its lexical path, collection, and time registration
helpers only when a helper's result depends on template inputs rather than the
host. Do not add a host-observing helper to the shared query registration path;
assess and record any future allowlist change here. The no-topic and
named-command help paths render clap help directly and do not load a manifest.
Keep future help topics within this boundary rather than coupling read-only
inspection to `runner::process`.

### Help-target query telemetry

`src/runner/help_telemetry.rs` is the observability boundary around the pure
manifest and catalogue query within `netsuke help targets`.
`instrument_help_targets` wraps that query and records the fixed metrics
`netsuke_runner_help_targets_total` and
`netsuke_runner_help_targets_duration_seconds`. It also opens the
`runner.help_targets` span and emits a bounded `Completed help targets query`
event when the query finishes. The command boundary in `src/runner/help.rs`
owns status reporting and rendering after the query succeeds.

Telemetry labels use only the fixed `outcome` values `success` and `error`, and
the fixed `error_category` values `none`, `manifest_not_found`, and `other`.
The wrapper never records manifest-controlled names, descriptions, paths, or
other details. Metric descriptions are registered once per process, through a
`Once`, so repeated queries do not re-register them.

Telemetry tests use `metrics::with_local_recorder` with a
`metrics_util::DebuggingRecorder`, together with the local tracing subscriber
capture helper. They assert the counter, duration sample, and completion event
for a successful fixture query, a missing-manifest failure, and an invalid
manifest failure classified as the non-`RunnerError` `other` category.

## Localization

`src/locale_catalogues.rs` is the authoritative registry of shipped catalogues.
Expand Down Expand Up @@ -783,9 +850,14 @@ the policy, rejects tracked drift, and scans every tracked Markdown file.

## Release help tooling

Release builds generate help artefacts explicitly with `cargo-orthohelp`,
rather than from `build.rs`. The build script remains responsible for the
localization key audit only. Release automation installs the pinned tool with:
Release builds generate their manual and PowerShell help explicitly with
`cargo-orthohelp`, rather than consuming the ordinary-build help artefacts from
`build.rs`. The metadata root is `netsuke::cli::ReleaseHelpCli`, which combines
`CliConfig` field metadata with the Clap command surface, including
`help targets`, so the release manual and PowerShell help remain aligned with
the CLI. During ordinary Cargo builds, `build.rs` generates the local manual
page and shell completions, and audits the localization keys. Release
automation installs the pinned tool with:

```bash
cargo install cargo-orthohelp --version 0.9.0 --locked
Expand All @@ -808,6 +880,13 @@ PowerShell external help under
date from `SOURCE_DATE_EPOCH`, falling back to `1970-01-01` when unset or
invalid.

Shell completions are generated separately by `build.rs` from
`Cli::command()` for Bash, Elvish, Fish, PowerShell, and Zsh. Release staging
copies these portable completion sidecars into each standalone archive under
`completions/<shell>/`. They remain separate files for users to copy into the
completion location documented by their shell; package installation does not
claim to install them.

Keep `[package.metadata.ortho_config]` in `Cargo.toml` aligned with the CLI
when adding, renaming, or removing user-facing options. Changes to CLI
documentation metadata should be covered by `rstest` workflow/script contract
Expand Down
Loading
Loading