Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c33c6a0
Document v0.1.0 readiness and roadmap
Jul 23, 2026
e4e655a
Align user guide with v0.1.0 behaviour
Jul 23, 2026
66dbc85
Test documentation examples in isolated environments
Jul 23, 2026
561f447
Restore rstest-bdd documentation example coverage
Jul 23, 2026
3c37d1c
Make documentation examples executable
Jul 23, 2026
f99c981
Reject unsupported rule dependencies
Jul 24, 2026
cb4c139
Add property coverage for documentation fences
Jul 24, 2026
3566e4e
Adopt the canonical CLI contract
Jul 24, 2026
8d28505
Deduplicate CLI documentation test helpers
Jul 24, 2026
aecc54e
Complete canonical JSON output contract
Jul 24, 2026
32f5717
Simplify JSON graph artefact routing
Jul 24, 2026
078a296
Align documentation and shared test helpers
Jul 24, 2026
d49cba9
Decouple JSON envelope metadata
Jul 24, 2026
9c8b697
Deduplicate JSON subcommand test setup
Jul 25, 2026
4f9e391
Align examples and JSON contract tests
Jul 25, 2026
5789ea9
Clarify JSON envelope documentation
Jul 26, 2026
426b45f
Address review feedback on docs and example coverage
Jul 26, 2026
1c9ce08
Align design docs with the implemented CLI contract
Jul 26, 2026
850ffb0
Resolve CI lint-integrity, concurrency, and doc-coherence findings
Jul 26, 2026
ac0c34e
Tighten JSON contract assertions and scope --json docs
Jul 26, 2026
60144f7
Replace last retired manifest subcommand example in design doc
Jul 26, 2026
c020b36
Deduplicate empty-stream assertions in JSON-diagnostics steps
Jul 26, 2026
2a2c88d
Deduplicate JSON subcommand success tests
Jul 26, 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
8 changes: 6 additions & 2 deletions .github/workflows/netsukefile-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ jobs:
command: "touch unused.txt"
MANIFEST
- name: Build dependent, inline, and foreach targets
run: ./target/debug/netsuke --verbose build --emit build.ninja dependent.txt inline-command.txt inline-script.txt a-foreach.txt b-foreach.txt
run: |
./target/debug/netsuke --verbose generate --output build.ninja
ninja -f build.ninja dependent.txt inline-command.txt inline-script.txt a-foreach.txt b-foreach.txt
- name: Assert dependent artefacts exist
env:
NINJA_MANIFEST: build.ninja
Expand All @@ -100,7 +102,9 @@ jobs:
NINJA_MANIFEST: build.ninja
run: scripts/assert-file-absent.sh skip-foreach.txt
- name: Run action target
run: ./target/debug/netsuke --verbose build --emit action.ninja say-hello
run: |
./target/debug/netsuke --verbose generate --output action.ninja
ninja -f action.ninja say-hello
- name: Assert action artefact exists
env:
NINJA_MANIFEST: action.ninja
Expand Down
271 changes: 123 additions & 148 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,202 +3,177 @@
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](
https://deepwiki.com/leynos/netsuke)

A modern, declarative build system compiler. YAML + Jinja in, Ninja out.
Nothing more. Nothing less.
*A friendly build-system compiler: YAML and Jinja in, Ninja out.*

## What is Netsuke?
Netsuke turns a readable `Netsukefile` into a validated, static Ninja build
graph. It keeps the dynamic work in a higher-level manifest and leaves fast,
incremental execution to [Ninja](https://ninja-build.org/).

**Netsuke** is a friendly build system that compiles structured manifests into
a Ninja build graph. It’s not a shell-script runner, a meta-task framework, or
a domain-specific CI layer. It’s `make`, if `make` hadn’t been invented in 1977.
______________________________________________________________________

### Key properties
## Why Netsuke?

- **Declarative**: Targets, rules, and dependencies described explicitly.
- **Dynamic when needed**: Jinja templating for loops, macros, conditionals,
file globbing.
- **Static where required**: Always compiles to a reproducible, fully static
dependency graph.
- **Unopinionated**: No magic for C, Rust, Python, JavaScript, or any other
blessed language.
- **Safe**: All variable interpolation is securely shell-escaped by default.
- **Fast**: Builds executed by [Ninja](https://ninja-build.org/), the fastest
graph executor we know of.
- **Readable manifests**: Describe rules, targets, dependencies, and defaults
in YAML instead of a tab-sensitive language.
- **Dynamic planning**: Use Jinja variables, macros, `foreach`, `when`, and
globbing before Netsuke creates the build graph.
- **Static execution**: Inspect the generated Ninja file or render the graph
before running any build command.
- **Useful diagnostics**: Get source-aware errors, localized output, progress
reporting, and canonical `--json` machine-readable command output.
- **No blessed toolchain**: Use the same manifest model for Rust, C, Python,
web projects, or anything else a command can build.

## Quick Example
______________________________________________________________________

```yaml
netsuke_version: "1.0"

vars:
cc: clang
cflags: -Wall -Werror

rules:
- name: compile
command: "{{ cc }} {{ cflags }} -c {{ ins }} -o {{ outs }}"
## Quick start

- name: link
command: "{{ cc }} {{ cflags }} {{ ins }} -o {{ outs }}"

targets:
- foreach: glob('src/*.c')
name: "build/{{ item | basename | with_suffix('.o') }}"
rule: compile
sources: "{{ item }}"

- name: app
rule: link
sources: "{{ glob('src/*.c') | map('basename') | map('with_suffix', '.o') }}"
```
### Prerequisites

Yes, it’s just YAML. Yes, that’s a Jinja `foreach`. No, you don’t need to define
`.PHONY` or remember what `$@` means. This is the present day. You deserve
better.
Netsuke currently requires:

## Key Concepts
- [Ninja](https://ninja-build.org/) on `PATH`;
- Rust 1.89 or later when installing from source.

### 🔨 Rules
### Installation

Rules are reusable command templates. Each one has exactly one of:
Until the v0.1.0 release is published, install the current source checkout with
Cargo:

- `command:` - a single shell string
- `script:` - a multi-line block
- (or) can be declared inline on a target
<!-- tested-example: readme-source-install -->

```yaml
rules:
- name: rasterise
script: |
inkscape --export-png={{ ins }} {{ outs }}
```sh
git clone https://github.com/leynos/netsuke.git
cd netsuke
cargo install --path .
```

### 🎯 Targets
### Your first build

Targets are things you want to build.
Create a new directory and add a file named `Netsukefile`:

```yaml
- name: build/logo.png
rule: rasterise
sources: assets/logo.svg
```
<!-- tested-example: readme-first-build-manifest -->

Targets can also define:
```yaml
netsuke_version: "1.0.0"

- `deps`: implicit dependencies — trigger rebuilds but are not passed to
`$in`/`{{ ins }}`; map to Ninja `|`
- `order_only_deps`: e.g. `mkdir -p build`
- `vars`: per-target variables
targets:
- name: hello.txt
command: "echo 'Hello from Netsuke!' > hello.txt"

You may also use `command:` or `script:` instead of referencing a `rule`.
defaults:
- hello.txt
```

## 🧪 Phony Targets and Actions
Run Netsuke, then inspect the result:

Phony targets behave like Make’s `.PHONY`:
<!-- tested-example: readme-first-build-commands -->

```yaml
- name: clean
phony: true
always: true
command: rm -rf build
```sh
netsuke
cat hello.txt
```

For cleaner structure, you may also define phony targets under an `actions:`
block:
The second command prints `Hello from Netsuke!`. See the
[quick-start guide](docs/quickstart.md) for variables, templates, and `foreach`.

```yaml
actions:
- name: test
command: pytest
```
______________________________________________________________________

All `actions` are treated as `{ phony: true, always: false }` by default.
## What works today

## 🧠 Templating
The core build-system compiler is implemented:

Netsuke uses [MiniJinja](https://docs.rs/minijinja) to render your manifest
before parsing.
- YAML 1.2 manifest parsing with duplicate-key and schema validation;
- Jinja variables, macros, `foreach`, `when`, globbing, environment helpers,
executable discovery, and opt-in network helpers;
- reusable rules, targets, actions, defaults, and explicit, implicit, and
order-only dependencies;
- a deterministic intermediate build graph with duplicate-output, missing-rule,
and cycle checks;
- Ninja generation and execution, plus `clean` and standalone manifest
generation;
- reproducible dependency graphs as Graphviz DOT or self-contained,
accessible HTML;
- layered configuration, localized output, accessibility preferences,
progress reporting, stage timings, and versioned JSON results or diagnostics;
- unit, behavioural, integration, property, snapshot, and initial Kani
verification coverage.

You can:
Release automation is configured to build packages for Linux, macOS, and
Windows, including platform help artefacts. v0.1.0 will be the first public
release of this work.

- Glob files: `{{ glob('src/**/*.c') }}`
- Read environment vars: `{{ env('CC') }}`
- Use filters: `{{ path | basename | with_suffix('.o') }}`
- Define reusable macros:
______________________________________________________________________

```yaml
macros:
- signature: "shout(msg)"
body: |
echo "{{ msg | upper }}"
```
## v0.1.0 status

Templating happens **before** parsing, so any valid output must be valid YAML.
v0.1.0 is a useful preview for early adopters, not a declaration that Netsuke
is finished or that every interface is stable. The compiler pipeline and
ordinary local-build workflow are substantial; the command-line interface,
configuration vocabulary, and advanced recipe model are still evolving.

## 🔐 Safety
Pin the Netsuke version in automation and expect some command names, flags,
diagnostic schemas, and manifest details to change before 1.0.

Shell commands are automatically escaped. Interpolation into `command:` or
`script:` will never yield a command injection vulnerability unless you
explicitly ask for `| raw`.
Known limitations include:

```yaml
command: "echo {{ dangerous_value }}" # Safe
command: "echo {{ dangerous_value | raw }}" # Unsafe (your problem now)
```
- recipes are shell strings; structured executable arguments and recipe
environment mappings are not implemented yet;
- literal shell dollar expressions still need Ninja-aware escaping in
manifests;
- compiler-generated dependency imports such as GCC depfiles are planned but
not yet part of the manifest model;
- `--json` emits exactly one versioned result or diagnostic document for each
command, but the schema may still change before 1.0;
- accessibility, terminal rendering, configuration precedence, and
cross-platform compiler invariants need broader verification.

## 🔧 CLI
A `Netsukefile` can execute commands and use impure template helpers. Treat it
with the same care as a `Makefile`: review untrusted manifests before running
them. Netsuke quotes supported path substitutions, but it is not a sandbox.

```shell
netsuke [build] [target1 target2 ...]
netsuke clean
netsuke graph
netsuke manifest FILE
```
______________________________________________________________________

- `netsuke` alone builds the `defaults:` targets from your manifest
- `netsuke graph` emits a Graphviz `.dot` of the build DAG
- `netsuke clean` runs `ninja -t clean`
- `netsuke manifest FILE` writes the Ninja manifest to `FILE` without invoking
Ninja
## The road ahead

You can also pass:
Work after the first release is organized around three priorities:

- `--file` to use an alternate manifest
- `--directory` to run in a different working dir
- `-j N` to control parallelism (passed through to Ninja)
- `-v`, `--verbose` to enable verbose logging
1. **Stabilize the command-line contract**: harden the canonical command and
flag names, non-interactive safeguards, stable exit codes, bounded output,
and versioned `--json` documents.
2. **Make recipes safer and clearer**: add structured executable arguments,
environment mappings, compiler dependency imports, backend dollar escaping,
and better conditional-action feedback.
3. **Strengthen confidence**: expand Kani and property-test coverage, verify
accessibility with assistive technology, and add regression coverage for
configuration precedence and terminal rendering.

Release builds include a `netsuke.1` manual page generated by `cargo-orthohelp`
from the CLI documentation metadata, providing the same flags and subcommands
documented via `--help`. Windows release artefacts also include PowerShell
external help for `Get-Help Netsuke -Full`. Manual page generation honours
`SOURCE_DATE_EPOCH` for reproducible dates. If the value is invalid, a warning
is emitted and the date falls back to `1970-01-01`. If `SOURCE_DATE_EPOCH` is
unset, the date deterministically falls back to `1970-01-01` without a warning.
The published crate does not include these files; packagers can source them
from release artefacts under `target/orthohelp/<target>/release/`.
Longer-term work explores machine-readable context, profiles, run history,
artefact delivery, and local-first feedback for human and agent workflows. The
[roadmap](docs/roadmap.md) tracks the detailed sequence and current progress.

## 🚧 Status
______________________________________________________________________

Netsuke is **under active development**. It’s not finished, but it’s buildable,
usable, and increasingly delightful.
## Learn more

Coming soon:
- [Quick-start guide](docs/quickstart.md) — build something in five minutes.
- [Users' guide](docs/users-guide.md) — manifest and command reference.
- [Design document](docs/netsuke-design.md) — architecture and design
rationale.
- [Developers' guide](docs/developers-guide.md) — development workflow and
quality gates.
- [Roadmap](docs/roadmap.md) — completed foundations and planned work.

- `graph --html` for interactive DAGs
- Extensible plugin system for filters/functions
- Toolchain presets (`cargo`, `node`, etc.)
______________________________________________________________________

## Why “Netsuke”?
## Licence

A **netsuke** is a small carved object used to fasten things securely to a
belt. It’s not the sword. It’s not the pouch. It’s the thing that connects them.
ISC — see [LICENSE](LICENSE) for details.

That’s what this is: a tidy connector between your intent and the tool that
gets it done.
______________________________________________________________________

## Licence
## Contributing

Netsuke is distributed under the
[ISC licence](https://opensource.org/licenses/ISC). You don't need a legal
thesis to use a build tool.
Contributions are welcome. Start with the
[developers' guide](docs/developers-guide.md); automated contributors should
also follow [AGENTS.md](AGENTS.md).
Loading
Loading