Skip to content

Generate Ninja build file on the fly#55

Merged
leynos merged 4 commits intomainfrom
codex/implement-manifest-to-ninja-pipeline
Aug 5, 2025
Merged

Generate Ninja build file on the fly#55
leynos merged 4 commits intomainfrom
codex/implement-manifest-to-ninja-pipeline

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Aug 5, 2025

Summary

  • generate Ninja script from manifest to a temporary file or emit location and run Ninja with -f
  • adjust CLI and docs for new --emit option and emit subcommand
  • expand runner tests for emitting and persisting Ninja manifests

Testing

  • make fmt
  • make lint
  • make test
  • make markdownlint
  • make nixie (fails: error: too many arguments. Expected 0 arguments but got 1.)

https://chatgpt.com/codex/tasks/task_e_68924b2aa17c8322ad8b42ce4cdff36d

Summary by Sourcery

Allow dynamic generation of Ninja build files with options to emit or use temporary files when running the build

New Features:

  • Add --emit option to build command to write and keep the generated Ninja file
  • Introduce emit subcommand to output the Ninja file without invoking Ninja

Enhancements:

  • Generate Ninja content via a new helper and write to a secure temporary file by default
  • Refactor run_ninja to accept a build file path and pass it to Ninja with -f
  • Clean up temporary Ninja files after running the build

Build:

  • Add tempfile and serial_test dependencies for test support

Documentation:

  • Update CLI docs to describe the --emit flag and new emit subcommand

Tests:

  • Expand runner tests to cover ephemeral and persisted Ninja file behaviors and error handling
  • Update CLI and cucumber tests for emit option and subcommand scenarios

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Aug 5, 2025

Reviewer's Guide

This PR refactors the runner to generate Ninja manifests on the fly—either writing to a temporary file by default or to a user-specified path—by extracting generation logic into a helper, extending the CLI with a new --emit option and Emit subcommand, updating documentation and tests to cover both transient and persistent emission, and adjusting run_ninja to invoke Ninja with the proper build file.

Sequence diagram for build command with and without --emit

sequenceDiagram
    actor User
    participant CLI
    participant Runner
    participant FileSystem
    participant Ninja
    User->>CLI: netsuke build [--emit FILE] [targets]
    CLI->>Runner: run(cli)
    Runner->>Runner: generate_ninja(cli)
    alt --emit FILE specified
        Runner->>FileSystem: write Ninja file to FILE
        Runner->>Ninja: run_ninja -f FILE [targets]
    else default (no --emit)
        Runner->>FileSystem: create temp Ninja file
        Runner->>Ninja: run_ninja -f temp_file [targets]
        Runner->>FileSystem: delete temp Ninja file
    end
Loading

Sequence diagram for emit subcommand

sequenceDiagram
    actor User
    participant CLI
    participant Runner
    participant FileSystem
    User->>CLI: netsuke emit FILE
    CLI->>Runner: run(cli)
    Runner->>Runner: generate_ninja(cli)
    Runner->>FileSystem: write Ninja file to FILE
    Note over Runner,FileSystem: No build is run
Loading

Class diagram for updated CLI command structure

classDiagram
    class Cli {
        file: PathBuf
        directory: Option<PathBuf>
        jobs: Option<usize>
        verbose: bool
        command: Option<Commands>
    }
    class Commands {
    }
    class Build {
        emit: Option<PathBuf>
        targets: Vec<String>
    }
    class Emit {
        file: PathBuf
    }
    Commands <|-- Build
    Commands <|-- Emit
    Cli --> Commands
Loading

Class diagram for runner logic refactor

classDiagram
    class Runner {
        +run(cli: &Cli) -> io::Result<()> 
        +generate_ninja(cli: &Cli) -> io::Result<String>
        +run_ninja(program: &Path, cli: &Cli, build_file: &Path, targets: &[String]) -> io::Result<()> 
    }
Loading

File-Level Changes

Change Details Files
Refactor runner to support on-the-fly and emitted Ninja manifests
  • Extract generate_ninja helper to centralize manifest loading and translation
  • In Build command, branch on emit: write to specified path or create a tempfile and clean it up afterwards
  • Add Emit command handler to generate and write the manifest without invoking Ninja
  • Update run_ninja signature to accept a build_file path and pass -f build_file to Ninja invocation
src/runner.rs
tests/steps/process_steps.rs
Extend CLI and documentation for --emit and emit subcommand
  • Add emit: Option field to Build, introduce Emit variant in Commands
  • Update clap derive annotations in src/cli.rs and corresponding examples in docs/netsuke-design.md
  • Add Cucumber step definitions and feature scenarios for emit behavior
  • Update CLI parsing tests to include cases for --emit and emit subcommand
src/cli.rs
docs/netsuke-design.md
tests/steps/cli_steps.rs
tests/features/cli.feature
tests/cli_tests.rs
Enhance runner_tests to validate emission behavior
  • Replace manual temp scripts with support::fake_ninja in tests
  • Add tests for default build (temporary file removed), build with emit (file retained), and emit subcommand
  • Ensure run_ninja_not_found and serial execution of Ninja-based tests
tests/runner_tests.rs
Add tempfile dependency for temporary Ninja files
  • Include tempfile crate in Cargo.toml to manage temp Ninja outputs
Cargo.toml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 5, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Summary by CodeRabbit

  • New Features

    • Added support for emitting the generated Ninja manifest to a specified file via a new --emit option in the build command and an emit subcommand.
    • Introduced new CLI subcommands: clean, graph, and emit for improved build management.
  • Bug Fixes

    • Enhanced error handling for missing or invalid Ninja build files.
  • Documentation

    • Updated CLI documentation to reflect new subcommands and options for Ninja manifest management.
  • Tests

    • Expanded test coverage for CLI parsing, Ninja file emission, and error scenarios related to build files.
  • Chores

    • Updated dependencies and refined test utilities for better reliability and maintainability.

Walkthrough

Update the CLI, runner, and related documentation to add new subcommands and options for emitting Ninja manifest files. Refactor the build logic to support writing the manifest to user-specified locations or temporary files. Extend and adapt tests and step definitions to cover the new CLI surface and behaviour.

Changes

Cohort / File(s) Change Summary
CLI Extension & Refactor
src/cli.rs, docs/netsuke-design.md
Add emit option to Build command; introduce new Emit subcommand; update documentation and CLI parsing logic accordingly.
Runner Logic & Manifest Generation
src/runner.rs
Refactor build logic to support optional emit path; add generate_ninja helper; update run_ninja to accept explicit build file; implement Emit subcommand logic.
Cargo Dependency Update
Cargo.toml
Move tempfile from dev-dependencies to main dependencies; update version to 3.8.0.
CLI Tests & Step Definitions
tests/cli_tests.rs, tests/features/cli.feature, tests/steps/cli_steps.rs
Add and update test cases and steps for new emit option and Emit subcommand; adjust helpers for new command structure.
Runner Tests
tests/runner_tests.rs
Refactor and extend tests to cover new emit functionality; improve test isolation and coverage; remove unused helpers.
Process Step Adjustment
tests/steps/process_steps.rs
Update run_ninja invocation to explicitly provide build file path.
Test Support Enhancements
tests/support/mod.rs
Add fake ninja executable that checks for build file presence and validity.
Feature Tests
tests/features/ninja_process.feature
Add scenarios testing ninja invocation failure when build file is missing or invalid.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant Runner
    participant FileSystem
    participant Ninja

    User->>CLI: netsuke build --emit out.ninja target
    CLI->>Runner: Commands::Build { emit: Some(out.ninja), targets: [...] }
    Runner->>Runner: generate_ninja()
    Runner->>FileSystem: Write Ninja manifest to out.ninja
    Runner->>Ninja: Run Ninja with -f out.ninja
    Ninja-->>Runner: Build output
    Runner-->>CLI: Exit

    User->>CLI: netsuke emit out.ninja
    CLI->>Runner: Commands::Emit { file: out.ninja }
    Runner->>Runner: generate_ninja()
    Runner->>FileSystem: Write Ninja manifest to out.ninja
    Runner-->>CLI: Exit
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

Emit a file, or build anew,
The CLI now does what you ask it to do!
With tempfiles or paths, your manifest stays,
Ninja obeys in so many ways.
Tests march in step, the docs shine bright—
Command-line magic, working right!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55dac5d and 360972a.

📒 Files selected for processing (11)
  • Cargo.toml (1 hunks)
  • docs/netsuke-design.md (4 hunks)
  • src/cli.rs (3 hunks)
  • src/runner.rs (5 hunks)
  • tests/cli_tests.rs (1 hunks)
  • tests/features/cli.feature (1 hunks)
  • tests/features/ninja_process.feature (1 hunks)
  • tests/runner_tests.rs (3 hunks)
  • tests/steps/cli_steps.rs (5 hunks)
  • tests/steps/process_steps.rs (4 hunks)
  • tests/support/mod.rs (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
Cargo.toml

📄 CodeRabbit Inference Engine (AGENTS.md)

Cargo.toml: Use explicit version ranges in Cargo.toml and keep dependencies up-to-date.
Mandate caret requirements for all dependencies: All crate versions specified in Cargo.toml must use SemVer-compatible caret requirements (e.g., some-crate = "1.2.3").
Prohibit unstable version specifiers: The use of wildcard (*) or open-ended inequality (>=) version requirements is strictly forbidden. Tilde requirements (~) should only be used where a dependency must be locked to patch-level updates for a specific, documented reason.

Files:

  • Cargo.toml
**/*.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.rs: Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.
Prefer semantic error enums: Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary: Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library: Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.

Files:

  • tests/cli_tests.rs
  • tests/support/mod.rs
  • tests/steps/process_steps.rs
  • src/cli.rs
  • tests/steps/cli_steps.rs
  • tests/runner_tests.rs
  • src/runner.rs

⚙️ CodeRabbit Configuration File

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.

Files:

  • tests/cli_tests.rs
  • tests/support/mod.rs
  • tests/steps/process_steps.rs
  • src/cli.rs
  • tests/steps/cli_steps.rs
  • tests/runner_tests.rs
  • src/runner.rs
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
Proactively update the relevant file(s) in the docs/ directory to reflect the latest state when new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve.

Files:

  • docs/netsuke-design.md
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Documentation must use en-GB-oxendict spelling and grammar, except for the naming of the "LICENSE" file.
Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Tables and headings in Markdown must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/netsuke-design.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/netsuke-design.md
🪛 GitHub Check: build-test
tests/support/mod.rs

[warning] 58-58:
Diff in /home/runner/work/netsuke/netsuke/tests/support/mod.rs


[warning] 30-30:
Diff in /home/runner/work/netsuke/netsuke/tests/support/mod.rs


[warning] 58-58:
Diff in /home/runner/work/netsuke/netsuke/tests/support/mod.rs


[warning] 30-30:
Diff in /home/runner/work/netsuke/netsuke/tests/support/mod.rs

🪛 GitHub Actions: CI
tests/support/mod.rs

[error] 30-30: Prettier formatting check failed. Diff detected in attribute formatting for #[allow(unfulfilled_lint_expectations, reason = ...)]. Run 'cargo fmt --all -- --write' to fix code style issues.


[error] 58-58: Prettier formatting check failed. Diff detected in attribute formatting for #[allow(unfulfilled_lint_expectations, reason = ...)]. Run 'cargo fmt --all -- --write' to fix code style issues.


[error] 84-84: Prettier formatting check failed. Diff detected in attribute formatting for #[allow(unfulfilled_lint_expectations, reason = ...)]. Run 'cargo fmt --all -- --write' to fix code style issues.

🔇 Additional comments (29)
Cargo.toml (1)

19-19: LGTM!

The tempfile dependency correctly moved to main dependencies with explicit version specification to support the new temporary Ninja file generation functionality.

tests/support/mod.rs (1)

29-59: Well-implemented test utility for build file validation.

The fake ninja executable correctly validates build file existence using shell scripting and proper error handling.

src/cli.rs (3)

75-75: Correctly initialised default emit field.

The default Build command properly initialises the new emit field to None.


88-91: Well-designed optional emit parameter.

The emit field correctly extends the Build command with optional Ninja manifest output functionality.


102-107: Clean implementation of Emit subcommand.

The new Emit subcommand provides a clear interface for manifest generation without build execution.

tests/features/cli.feature (2)

26-31: Comprehensive test coverage for build emit option.

The scenario correctly validates parsing of the build command with the new --emit option.


33-37: Proper test coverage for emit subcommand.

The scenario appropriately tests the standalone emit subcommand functionality.

tests/features/ninja_process.feature (2)

21-26: Excellent error condition coverage for missing build file.

The scenario properly tests ninja process failure when the build file is missing.


28-34: Thorough testing of invalid build file condition.

The scenario correctly validates error handling when a directory exists instead of a regular build file.

tests/cli_tests.rs (1)

12-37: LGTM! Comprehensive test coverage for emit functionality.

The test cases correctly include the emit field in all Commands::Build variants and add proper coverage for the new --verbose, --emit, and emit subcommand functionality. The test structure maintains consistency and follows good testing practices.

tests/steps/process_steps.rs (3)

37-42: LGTM! New step definition supports build file validation testing.

The new fake_ninja_check step definition correctly uses the helper function to create a fake ninja executable that validates build file paths, supporting the enhanced testing scenarios.


56-69: LGTM! Helper steps support comprehensive test scenarios.

The new step definitions cli_uses_temp_dir and build_dir_exists provide necessary test setup functionality for validating build file handling and directory-based test scenarios.


84-89: LGTM! Updated to use new API correctly.

The changes to use Path::new(ninja) instead of the full module path and the creation of BuildTargets::new(vec![]) correctly align with the updated run_ninja function signature requirements.

tests/steps/cli_steps.rs (4)

19-22: LGTM! Correct initialisation of emit field.

The apply_cli function correctly initialises the emit field to None for the default Commands::Build variant, maintaining consistency with the updated CLI structure.


34-40: LGTM! Function signature updated correctly.

The extract_build function now returns both targets and emit path as a tuple, correctly reflecting the updated Commands::Build variant structure.


90-97: LGTMS! New step definition for emit command testing.

The command_is_emit step definition correctly matches against the Commands::Emit variant, supporting comprehensive test coverage for the new emit subcommand.


135-155: LGTM! Comprehensive emit testing step definitions.

The new step definitions emit_path and emit_command_path provide thorough testing capabilities for both the --emit option in build commands and the standalone emit subcommand. The functions correctly extract and validate the emit paths from their respective command variants.

docs/netsuke-design.md (1)

1404-1427: LGTM! Clear documentation of new CLI behaviour.

The documentation correctly describes the enhanced build command behaviour with the --emit option and clearly explains the new clean, graph, and emit subcommands. The descriptions are accurate and user-friendly.

tests/runner_tests.rs (5)

19-23: LGTM! Correctly updated for new CLI structure.

The test properly initialises the emit field to None in the Commands::Build variant, maintaining consistency with the updated CLI interface.


31-52: LGTM! Good test coverage for ninja executable not found scenario.

The new test run_ninja_not_found correctly verifies error handling when the ninja executable is missing, using the appropriate std::io::ErrorKind::NotFound assertion.


54-90: LGTM! Test correctly verifies default build behaviour.

The renamed test run_executes_ninja_without_persisting_file correctly verifies that the default build command creates a temporary ninja file that is removed after execution. The use of #[serial] prevents PATH environment variable conflicts.


92-133: LGTM! Comprehensive test for emit functionality.

The test run_build_with_emit_keeps_file thoroughly verifies that specifying the --emit option writes and retains the ninja manifest file while ensuring no default build.ninja file remains. The test correctly uses serial execution and environment cleanup.


135-165: LGTM! Good test coverage for emit subcommand.

The test run_emit_subcommand_writes_file correctly verifies that the emit subcommand writes the ninja manifest without executing ninja (by clearing PATH). The test properly uses serial execution and environment cleanup.

src/runner.rs (6)

19-65: Well-structured wrapper types

The semantic wrapper types properly encapsulate string-heavy parameters with clear intent. The #[must_use] attributes and minimal API surface are appropriate.


71-108: Clean command dispatch with proper error context

The refactored run function properly handles both temporary and persisted Ninja files with appropriate error context throughout.


110-127: Properly extracted helper with rich error context

The write_and_log function cleanly separates concerns and provides detailed error messages.


128-160: Well-extracted Ninja generation logic

The generate_ninja function properly encapsulates manifest loading and transformation with comprehensive error context at each step.


161-221: Consistent application of wrapper types in argument handling

The sensitive argument functions properly use the CommandArg wrapper while maintaining their original logic.


222-309: Robust Ninja invocation with proper path handling

The updated run_ninja function correctly canonicalizes the build file path with fallback and properly uses the new wrapper types. The #[expect] attribute is appropriately scoped with clear justification.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/implement-manifest-to-ninja-pipeline

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and found some issues that need to be addressed.

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/runner.rs:88` </location>
<code_context>
+/// let ninja = generate_ninja(&cli).expect("generate");
+/// assert!(ninja.contains("rule"));
+/// ```
+fn generate_ninja(cli: &Cli) -> io::Result<String> {
+    let manifest_path = cli
+        .directory
</code_context>

<issue_to_address>
The error handling in generate_ninja may obscure the original error source.

Using io::Error::other erases error details. Consider using thiserror, anyhow, or at least include the original error message for better debugging.

Suggested implementation:

```rust
+use anyhow::{Context, Result};
+
+fn generate_ninja(cli: &Cli) -> Result<String> {
+    let manifest_path = cli
+        .directory

```

- You will need to add `anyhow` to your `Cargo.toml` dependencies if it is not already present.
- Update all internal error handling in `generate_ninja` to use `?` for propagation, and optionally `.context("...")` to add custom error messages.
- Update all call sites of `generate_ninja` to handle `anyhow::Result` instead of `io::Result`.
</issue_to_address>

### Comment 2
<location> `src/runner.rs:187` </location>
<code_context>
     if let Some(jobs) = cli.jobs {
         cmd.arg("-j").arg(jobs.to_string());
     }
+    cmd.arg("-f").arg(build_file);
     cmd.args(targets);
     cmd.stdout(Stdio::piped());
</code_context>

<issue_to_address>
Passing the build file path to Ninja may require normalization.

If build_file is a temporary file, verify its path is valid and accessible from Ninja's working directory. Consider using an absolute or canonical path to avoid issues with relative paths.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    cmd.arg("-f").arg(build_file);
=======
    // Canonicalize the build_file path to ensure Ninja receives an absolute path.
    let build_file_path = match build_file.canonicalize() {
        Ok(abs_path) => abs_path,
        Err(_) => build_file.to_path_buf(), // fallback to original if canonicalization fails
    };
    cmd.arg("-f").arg(&build_file_path);
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `tests/steps/process_steps.rs:65` </location>
<code_context>
     } else {
         std::path::Path::new("ninja")
     };
-    match runner::run_ninja(program, cli, &[]) {
+    match runner::run_ninja(program, cli, Path::new("build.ninja"), &[]) {
         Ok(()) => {
             world.run_status = Some(true);
</code_context>

<issue_to_address>
Process step updated to pass build file path to run_ninja.

Please add tests for scenarios where the build file is missing or invalid.
</issue_to_address>

### Comment 4
<location> `src/runner.rs:32` </location>
<code_context>
-            run_ninja(Path::new("ninja"), cli, &targets)
+        Commands::Build { targets, emit } => {
+            let ninja_content = generate_ninja(cli)?;
+            if let Some(path) = emit {
+                fs::write(&path, ninja_content.as_bytes()).map_err(io::Error::other)?;
+                info!("Generated Ninja file at {}", path.display());
</code_context>

<issue_to_address>
Consider extracting the repeated file-writing and logging logic into a helper function to simplify the control flow in the run function.

Here’s one way to collapse all three “write‐file → log → maybe run Ninja” sites into a tiny helper, then use a single `match emit` in your `Build` arm and a trivial call in `Emit`:

```rust
// add at top of runner.rs
fn write_and_log(path: &Path, content: &str) -> io::Result<()> {
    fs::write(path, content).map_err(io::Error::other)?;
    info!("Generated Ninja file at {}", path.display());
    Ok(())
}
```

Then simplify your `run()` to something like:

```rust
pub fn run(cli: &Cli) -> io::Result<()> {
    match cli.command.clone().unwrap() {
        Commands::Build { targets, emit } => {
            let ninja = generate_ninja(cli)?;
            match emit {
                Some(path) => {
                    write_and_log(&path, &ninja)?;
                    run_ninja(Path::new("ninja"), cli, &path, &targets)
                }
                None => {
                    let mut tmp = Builder::new()
                        .prefix("netsuke.")
                        .suffix(".ninja")
                        .tempfile()
                        .map_err(io::Error::other)?;
                    write_and_log(tmp.path(), &ninja)?;
                    run_ninja(Path::new("ninja"), cli, tmp.path(), &targets)
                }
            }
        }
        Commands::Emit { file } => {
            let ninja = generate_ninja(cli)?;
            write_and_log(&file, &ninja)
        }
        // ...
    }
}
```

This:

- Extracts the common write + info into `write_and_log`.
- Collapses the two Build‐branches into one `match emit`.
- Makes `Emit` a one‐liner.
</issue_to_address>

### Comment 5
<location> `docs/netsuke-design.md:1351` </location>
<code_context>
-use clap::Subcommand;
-#[rustfmt::skip]
-use std::path::PathBuf;
+use clap::{Parser, Subcommand}; use std::path::PathBuf;

 #[derive(Parser)]
</code_context>

<issue_to_address>
Rust code block line exceeds 120 columns; please wrap to comply with code block wrapping rule.

The line combining multiple imports in the Rust code block exceeds the 120 column limit for code blocks. Please wrap the line so that no line in the code block exceeds 120 columns.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/runner.rs Outdated
Comment thread src/runner.rs Outdated
Comment thread tests/steps/process_steps.rs
Comment thread src/runner.rs
Comment thread docs/netsuke-design.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c3b9fd and 908c16f.

📒 Files selected for processing (9)
  • Cargo.toml (1 hunks)
  • docs/netsuke-design.md (4 hunks)
  • src/cli.rs (3 hunks)
  • src/runner.rs (6 hunks)
  • tests/cli_tests.rs (1 hunks)
  • tests/features/cli.feature (1 hunks)
  • tests/runner_tests.rs (4 hunks)
  • tests/steps/cli_steps.rs (5 hunks)
  • tests/steps/process_steps.rs (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
Cargo.toml

📄 CodeRabbit Inference Engine (AGENTS.md)

Cargo.toml: Use explicit version ranges in Cargo.toml and keep dependencies up-to-date.
Mandate caret requirements for all dependencies: All crate versions specified in Cargo.toml must use SemVer-compatible caret requirements (e.g., some-crate = "1.2.3").
Prohibit unstable version specifiers: The use of wildcard (*) or open-ended inequality (>=) version requirements is strictly forbidden. Tilde requirements (~) should only be used where a dependency must be locked to patch-level updates for a specific, documented reason.

Files:

  • Cargo.toml
**/*.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.rs: Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.
Prefer semantic error enums: Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary: Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library: Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.

Files:

  • tests/steps/process_steps.rs
  • src/cli.rs
  • tests/cli_tests.rs
  • tests/steps/cli_steps.rs
  • tests/runner_tests.rs
  • src/runner.rs

⚙️ CodeRabbit Configuration File

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.

Files:

  • tests/steps/process_steps.rs
  • src/cli.rs
  • tests/cli_tests.rs
  • tests/steps/cli_steps.rs
  • tests/runner_tests.rs
  • src/runner.rs
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
Proactively update the relevant file(s) in the docs/ directory to reflect the latest state when new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve.

Files:

  • docs/netsuke-design.md
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Documentation must use en-GB-oxendict spelling and grammar, except for the naming of the "LICENSE" file.
Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Tables and headings in Markdown must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/netsuke-design.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/netsuke-design.md
🧬 Code Graph Analysis (2)
tests/steps/process_steps.rs (1)
src/runner.rs (1)
  • run_ninja (169-235)
src/runner.rs (3)
src/manifest.rs (1)
  • from_path (49-54)
src/ir.rs (1)
  • from_manifest (108-119)
src/ninja_gen.rs (1)
  • generate (37-73)
🔇 Additional comments (24)
tests/steps/process_steps.rs (2)

6-6: LGTM!

Addition of Path import is necessary for the updated run_ninja function call.


65-65: LGTM!

Correctly updated the run_ninja call to include the explicit build file path parameter, aligning with the new function signature.

tests/features/cli.feature (1)

26-37: LGTM!

Well-structured test scenarios that comprehensively cover the new emit functionality for both the --emit option and standalone emit subcommand.

tests/cli_tests.rs (1)

12-37: LGTM!

Comprehensive test coverage that correctly adapts existing cases to include the new emit field and adds proper test cases for the new emit functionality. Good use of rstest parameterisation as per coding guidelines.

src/cli.rs (2)

75-90: LGTM!

Well-designed CLI extension that adds the optional emit field to the Build command with proper default initialisation.


102-108: LGTM!

Clean implementation of the new Emit subcommand with clear documentation and appropriate field structure.

tests/steps/cli_steps.rs (4)

20-22: LGTM!

Correctly adds the new emit field to the default Build command with None as expected.


34-40: LGTM!

Function signature correctly updated to return both targets and emit field as a tuple, matching the expanded Commands::Build structure.


90-97: LGTM!

New step definition follows the established pattern for command verification and correctly matches the Commands::Emit variant.


115-115: LGTM!

Correctly destructures the tuple from extract_build to access targets while ignoring the emit field.

docs/netsuke-design.md (3)

1351-1351: LGTM!

Good consolidation of related imports from the same crates, improving readability.


1376-1391: LGTM!

Documentation accurately reflects the CLI structure changes, clearly explaining the new --emit option and additional subcommands with consistent formatting.


1402-1425: LGTM!

Clear and accurate documentation of the new emit functionality, properly explaining both the --emit option behavior and the emit subcommand's purpose.

tests/runner_tests.rs (5)

4-4: LGTM!

Necessary import for serial test execution to avoid race conditions when modifying environment variables.


20-23: LGTM!

Correctly updates the Commands::Build usage to include the new emit field with appropriate None value.


37-95: LGTM!

Good addition of error case testing with run_ninja_not_found and improved test naming. The assertion ensuring no ninja file persists correctly validates the temporary file behavior.


97-138: LGTM!

Comprehensive test for emit functionality with proper environment setup, file validation, and cleanup. Good use of platform-specific testing with cfg(unix).


140-169: LGTM!

Well-designed test for the emit subcommand that cleverly ensures ninja isn't invoked by clearing PATH, while properly validating file creation and cleanup.

src/runner.rs (6)

12-12: LGTM!

Necessary import addition for the updated run_ninja function signature.


15-15: LGTM!

Required import for temporary file creation functionality.


25-66: LGTM!

Well-structured implementation that correctly handles both emit and temporary file scenarios. Good use of tempfile::Builder for secure temporary file creation and proper resource management.


68-98: LGTM!

Well-designed helper function with clear single responsibility, proper error handling, and useful debug logging. Good separation of concerns from the main run logic.


157-174: LGTM!

Good improvement to make the build file path explicit rather than implicit. The signature change enhances flexibility while maintaining clear documentation.


187-187: LGTM!

Correctly uses the explicit build file parameter with the -f flag for Ninja execution.

Comment thread Cargo.toml Outdated
Comment thread tests/steps/cli_steps.rs Outdated
Copy link
Copy Markdown

@codescene-delta-analysis codescene-delta-analysis Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Failed
Prevent hotspot decline (1 hotspot with String Heavy Function Arguments)
Enforce advisory code health rules (1 file with String Heavy Function Arguments)

Gates Passed
4 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Prevent hotspot decline Violations Code Health Impact
runner.rs 1 rule in this hotspot 10.00 → 9.69 Suppress
Enforce advisory code health rules Violations Code Health Impact
runner.rs 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

Comment thread src/runner.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 908c16f and cd05940.

📒 Files selected for processing (6)
  • docs/netsuke-design.md (4 hunks)
  • src/runner.rs (5 hunks)
  • tests/features/ninja_process.feature (1 hunks)
  • tests/runner_tests.rs (3 hunks)
  • tests/steps/process_steps.rs (4 hunks)
  • tests/support/mod.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.rs: Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.
Prefer semantic error enums: Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary: Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library: Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.

Files:

  • tests/support/mod.rs
  • tests/steps/process_steps.rs
  • src/runner.rs
  • tests/runner_tests.rs

⚙️ CodeRabbit Configuration File

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.

Files:

  • tests/support/mod.rs
  • tests/steps/process_steps.rs
  • src/runner.rs
  • tests/runner_tests.rs
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
Proactively update the relevant file(s) in the docs/ directory to reflect the latest state when new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve.

Files:

  • docs/netsuke-design.md
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Documentation must use en-GB-oxendict spelling and grammar, except for the naming of the "LICENSE" file.
Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Tables and headings in Markdown must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/netsuke-design.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/netsuke-design.md
🔇 Additional comments (18)
tests/support/mod.rs (1)

40-47: LGTM! Excellent use of concat! macro.

The multi-line shell script construction follows the coding guidelines perfectly by using concat!() rather than escaping newlines with backslashes.

tests/features/ninja_process.feature (1)

21-34: LGTM! Comprehensive error scenario coverage.

The new test scenarios effectively cover critical edge cases for build file validation. The scenarios appropriately test both missing files and invalid file types (directory instead of regular file), with clear step definitions and proper error expectations.

tests/steps/process_steps.rs (4)

6-7: LGTM! Clean import additions.

The new imports for std::fs and std::path::Path are properly added and necessary for the new step definitions.


37-42: LGTM! Well-structured step definition.

The fake_ninja_check step definition properly integrates with the support module and follows the established pattern of the existing fake_ninja step.


56-69: LGTM! Comprehensive test setup steps.

Both cli_uses_temp_dir and build_dir_exists step definitions are well-implemented and provide the necessary test setup for the new build file validation scenarios.


88-88: LGTM! Properly addresses past review feedback.

The explicit build file path parameter addresses the previous review comment about adding tests for missing/invalid build files. The change aligns with the updated run_ninja signature.

tests/runner_tests.rs (6)

4-4: LGTM! Proper test isolation with serial execution.

Adding serial_test::serial import enables proper test isolation for environment variable modifications.


19-23: LGTM! Consistent CLI structure updates.

The Commands::Build variant properly includes the new emit: None field, maintaining consistency with the updated CLI structure.


31-51: LGTM! Comprehensive error handling test.

The run_ninja_not_found test properly validates the NotFound error condition when the ninja executable doesn't exist.


54-55: LGTM! Proper test serialisation for environment safety.

The #[serial] attribute ensures safe modification of the PATH environment variable without interfering with concurrent tests.


91-132: LGTM! Thorough emit functionality testing.

The run_build_with_emit_keeps_file test comprehensively validates that the emit functionality creates the expected file with proper content whilst not leaving a default build.ninja file.


134-163: LGTM! Complete emit subcommand coverage.

The run_emit_subcommand_writes_file test properly validates the standalone emit subcommand functionality without requiring ninja execution.

docs/netsuke-design.md (2)

378-393: LGTM! Comprehensive CLI documentation updates.

The updated CLI structure properly documents all new commands and options, including the --emit flag for the Build command and the new Clean, Graph, and Emit subcommands. The documentation accurately reflects the implementation.


1351-1351: Approve CLI documentation updates

The consolidated import at line 1351 is 23 characters long, well under the 120-column limit. All CLI commands and the new emit option are documented accurately and match the implementation.

src/runner.rs (4)

9-16: Good adoption of anyhow for error handling

The switch to anyhow::Result with Context improves error reporting clarity, addressing the previous review suggestion effectively.


24-50: Well-structured refactoring with helper functions

The extraction of generate_ninja and write_and_log helpers effectively addresses the previous complexity concerns. The control flow is now cleaner and more maintainable.


78-109: Excellent error handling with context

The function properly implements the suggested anyhow error handling with descriptive context messages. The extraction of this logic improves modularity.


180-204: Robust path handling implementation

The canonicalization of the build file path with fallback successfully addresses the previous review suggestion about path normalization.

Comment thread src/runner.rs
Comment thread tests/support/mod.rs Outdated
Comment thread src/runner.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/runner.rs (1)

121-125: Use anyhow::Result for consistency.

Change the return type to Result<()> (anyhow) and add context to the file write operation for consistency with the rest of the module.

-fn write_and_log(path: &Path, content: &NinjaContent) -> io::Result<()> {
-    fs::write(path, content.as_str())?;
+fn write_and_log(path: &Path, content: &NinjaContent) -> Result<()> {
+    fs::write(path, content.as_str())
+        .with_context(|| format!("Failed to write Ninja file to {}", path.display()))?;
     info!("Generated Ninja file at {}", path.display());
     Ok(())
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd05940 and 2401c87.

📒 Files selected for processing (3)
  • src/runner.rs (5 hunks)
  • tests/runner_tests.rs (3 hunks)
  • tests/steps/process_steps.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.rs: Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.
Prefer semantic error enums: Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary: Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library: Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.

Files:

  • tests/steps/process_steps.rs
  • tests/runner_tests.rs
  • src/runner.rs

⚙️ CodeRabbit Configuration File

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.

Files:

  • tests/steps/process_steps.rs
  • tests/runner_tests.rs
  • src/runner.rs
🔇 Additional comments (18)
tests/steps/process_steps.rs (4)

37-42: LGTM!

The new step definition properly delegates to the support module and follows the established pattern.


56-62: LGTM!

The function correctly sets up the CLI to use the temporary directory for testing scenarios.


64-69: LGTM!

The function correctly creates a directory to test error handling when the build file is not a regular file.


88-89: LGTM!

The changes correctly adapt to the updated run_ninja API with explicit build file path and BuildTargets wrapper.

tests/runner_tests.rs (4)

9-29: LGTM!

The test correctly verifies error handling for invalid manifest versions with the updated Commands::Build structure.


31-52: LGTM!

The test correctly verifies error handling when the Ninja executable is not found, using the new API with BuildTargets and explicit build file path.


54-90: LGTM!

The test correctly verifies that the Ninja file is not persisted when emit is None, and properly handles PATH manipulation with serial test isolation.


92-133: LGTM!

The test thoroughly verifies that the Ninja file is persisted at the specified path when using --emit, and that no default build.ninja is created.

src/runner.rs (10)

19-34: LGTM!

The NinjaContent wrapper provides semantic meaning for Ninja file content with appropriate #[must_use] attributes.


36-47: LGTM!

The CommandArg wrapper provides semantic meaning for command arguments with appropriate #[must_use] attributes.


49-64: LGTM!

The BuildTargets wrapper provides semantic meaning for build target collections with appropriate methods and #[must_use] attributes.


71-93: LGTM!

The run function correctly handles both ephemeral and persistent Ninja file scenarios based on the emit option.


147-158: LGTM!

The generate_ninja function correctly loads the manifest, builds the graph, and generates Ninja content with proper error context.


167-170: LGTM!

The function correctly identifies sensitive keywords using the CommandArg wrapper.


179-181: LGTM!

The function correctly delegates to contains_sensitive_keyword using the CommandArg wrapper.


194-203: LGTM!

The function correctly redacts sensitive information while preserving keys, using the CommandArg wrapper throughout.


217-219: LGTM!

The function correctly applies redaction to all arguments using the CommandArg wrapper.


235-308: LGTM!

The run_ninja function correctly handles the build file path with canonicalization fallback, uses the new wrapper types, and properly redacts sensitive arguments before logging.

Comment thread tests/runner_tests.rs
Comment thread tests/steps/process_steps.rs
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Aug 5, 2025

@coderabbitai Please create a new GitHub issue for the following:

tests/steps/process_steps.rs (1)

    56-92: Manifest handling logic addresses pipeline integration requirements.

    The code properly:

        Resolves manifest paths relative to the temporary directory
        Creates manifest files when they don't exist using the write_manifest helper
        Updates CLI configuration to use the resolved path
        Cleans up manifest files outside the temp directory

    However, there are several issues:

        Mutable borrow issue: Line 56 takes a mutable reference to CLI, but line 71 attempts to clone from it whilst still borrowed mutably.

        Missing error context: File operations lack proper error context as required by the coding guidelines.

        Overly complex path resolution: The manifest path handling could be simplified.

    Apply this diff to fix the mutable borrow and improve error handling:

    -    let cli = world.cli.as_mut().expect("cli");
    -
    -    // Ensure a manifest exists at the path expected by the CLI.
    -    let dir = world.temp.as_ref().expect("temp dir");
    -    let manifest_path = if cli.file.is_absolute() {
    -        cli.file.clone()
    -    } else {
    -        dir.path().join(&cli.file)
    -    };
    -    if !manifest_path.exists() {
    -        let mut file = NamedTempFile::new_in(dir.path()).expect("manifest");
    -        support::write_manifest(&mut file);
    -        // Persist the temporary file to the desired manifest path.
    -        file.persist(&manifest_path).expect("persist manifest");
    -    }
    -    cli.file.clone_from(&manifest_path);
    +    let dir = world.temp.as_ref().expect("temp dir");
    +    let manifest_path = {
    +        let cli = world.cli.as_ref().expect("cli");
    +        if cli.file.is_absolute() {
    +            cli.file.clone()
    +        } else {
    +            dir.path().join(&cli.file)
    +        }
    +    };
    +    
    +    if !manifest_path.exists() {
    +        let mut file = NamedTempFile::new_in(dir.path())
    +            .expect("Failed to create temporary manifest file");
    +        support::write_manifest(&mut file);
    +        file.persist(&manifest_path)
    +            .expect("Failed to persist manifest file");
    +    }
    +    
    +    let cli = world.cli.as_mut().expect("cli");
    +    cli.file.clone_from(&manifest_path);

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 5, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@leynos I've created a GitHub issue for the code improvement you identified in tests/steps/process_steps.rs. The issue documents the mutable borrow problem, missing error context, and overly complex path resolution, along with your suggested fix. The issue has been assigned to you and includes references back to this PR and discussion.


🏁 Script executed:

#!/bin/bash
# First, let's examine the current code in tests/steps/process_steps.rs around lines 56-92
cat tests/steps/process_steps.rs | sed -n '50,100p'

Length of output: 1869


🏁 Script executed:

#!/bin/bash
# Create a GitHub issue for the code improvement
gh issue create \
  --title "Fix mutable borrow issue and improve error handling in tests/steps/process_steps.rs" \
  --body "## Problem

The manifest handling logic in \`tests/steps/process_steps.rs\` (lines 56-92) has several issues that need to be addressed:

### Issues Identified

1. **Mutable borrow issue**: Line 56 takes a mutable reference to CLI, but line 71 attempts to clone from it whilst still borrowed mutably.

2. **Missing error context**: File operations lack proper error context as required by the coding guidelines.

3. **Overly complex path resolution**: The manifest path handling could be simplified.

## Current Code

The problematic code in lines 56-92 of \`tests/steps/process_steps.rs\`:

\`\`\`rust
let cli = world.cli.as_mut().expect(\"cli\");

// Ensure a manifest exists at the path expected by the CLI.
let dir = world.temp.as_ref().expect(\"temp dir\");
let manifest_path = if cli.file.is_absolute() {
    cli.file.clone()
} else {
    dir.path().join(&cli.file)
};
if !manifest_path.exists() {
    let mut file = NamedTempFile::new_in(dir.path()).expect(\"manifest\");
    support::write_manifest(&mut file);
    // Persist the temporary file to the desired manifest path.
    file.persist(&manifest_path).expect(\"persist manifest\");
}
cli.file.clone_from(&manifest_path);
\`\`\`

## Proposed Solution

Apply the following changes to fix the mutable borrow issue and improve error handling:

\`\`\`rust
let dir = world.temp.as_ref().expect(\"temp dir\");
let manifest_path = {
    let cli = world.cli.as_ref().expect(\"cli\");
    if cli.file.is_absolute() {
        cli.file.clone()
    } else {
        dir.path().join(&cli.file)
    }
};

if !manifest_path.exists() {
    let mut file = NamedTempFile::new_in(dir.path())
        .expect(\"Failed to create temporary manifest file\");
    support::write_manifest(&mut file);
    file.persist(&manifest_path)
        .expect(\"Failed to persist manifest file\");
}

let cli = world.cli.as_mut().expect(\"cli\");
cli.file.clone_from(&manifest_path);
\`\`\`

## Benefits

- Fixes the mutable borrow issue by properly scoping the CLI references
- Improves error messages with more descriptive context
- Simplifies the path resolution logic
- Maintains the same functionality while being more robust

## References

- Original PR: https://github.com/leynos/netsuke/pull/55
- Discussion: https://github.com/leynos/netsuke/pull/55#discussion_r$(date +%s)" \
  --assignee leynos

Length of output: 2470


@leynos I've successfully created GitHub issue #57 for the code improvement you identified in tests/steps/process_steps.rs. The issue documents:

  • The mutable borrow problem where line 56 takes a mutable reference but line 71 attempts to clone while still borrowed
  • Missing error context in file operations
  • Overly complex path resolution logic
  • Your proposed solution with improved error handling and proper reference scoping

The issue is available at: #57

The issue has been assigned to you and includes references back to this PR and discussion for full context.

@leynos leynos force-pushed the codex/implement-manifest-to-ninja-pipeline branch from 55dac5d to 360972a Compare August 5, 2025 23:40
@leynos leynos merged commit cfa6347 into main Aug 5, 2025
2 of 4 checks passed
@leynos leynos deleted the codex/implement-manifest-to-ninja-pipeline branch August 5, 2025 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant