Netsuke is a modern, declarative build system designed to be intuitive, fast,
and safe. Think of it not just as a make replacement, but as a build system
compiler. You describe your build process in a human-readable YAML manifest
(Netsukefile), leveraging the power of Jinja templating for dynamic logic.
Netsuke then compiles this high-level description into an optimized build plan
executed by the high-performance Ninja build
system.
Core Philosophy:
-
Declarative: Define what you want to build, not how step-by-step.
-
Dynamic where needed: Use Jinja for variables, loops (
foreach), conditionals (when), file globbing (glob), and more. -
Static Execution Plan: All dynamic logic is resolved before execution, resulting in a static Ninja file for fast, reproducible builds.
-
Safety First: Automatic shell escaping prevents command injection vulnerabilities.
-
Fast Execution: Leverages Ninja for efficient dependency tracking and parallel execution.
Netsuke is typically built from source using Cargo:
cargo build --release
# The executable will be in target/release/netsuke
Refer to the project's README.md or release pages for pre-compiled binaries
if available. Ensure the ninja executable is also installed and available in
your system's PATH.
The primary way to use Netsuke is through its command-line interface (CLI). The
default command is build.
-
Create a
Netsukefile: Define your build rules and targets in a YAML file namedNetsukefilein your project root. -
Run Netsuke: Execute the
netsukecommand.
netsuke # Builds default targets defined in Netsukefile
netsuke build target_name another_target # Builds specific targets
If no Netsukefile is found, Netsuke will provide a helpful error message:
Error: No `Netsukefile` found in the current directory.
Hint: Run `netsuke --help` to see how to specify or create a manifest.
A different manifest path can be specified using the -f or --file option:
netsuke -f path/to/manifest.ymlFor a step-by-step introduction, see the Quick Start guide.
The Netsukefile is a YAML file describing the build process.
Netsuke targets YAML 1.2 and forbids duplicate keys in manifests. If the same mapping key appears more than once (even if a YAML parser would normally accept it with “last key wins” behaviour), Netsuke treats this as an error.
# Mandatory: Specifies the manifest format version
netsuke_version: "1.0.0"
# Optional: Global variables accessible throughout the manifest
vars:
cc: gcc
src_dir: src
# Optional: Reusable Jinja macros
macros:
- signature: "compile_cmd(input, output)"
body: |
{{ cc }} -c {{ input }} -o {{ output }}
# Optional: Reusable command templates
rules:
- name: link
command: "{{ cc }} {{ ins }} -o {{ outs }}"
# Optional: Phony targets often used for setup or meta-tasks
# Implicitly phony: true, always: false
actions:
- name: clean
command: "rm -rf build *.o"
# Required: Defines the build targets (artefacts to produce)
targets:
- name: build/main.o
# A target can define its command inline...
command: "{{ compile_cmd('src/main.c', 'build/main.o') }}"
sources: src/main.c
- name: my_app
# ...or reference a rule
rule: link
sources: build/main.o
# Optional: Targets to build if none are specified on the command line
defaults:
- my_app
-
netsuke_version(Required): Semantic version string (e.g.,"1.0.0") indicating the manifest schema version. Parsed usingsemver. -
vars(Optional): A mapping (dictionary) of global variables. Values can be strings, numbers, booleans, or lists, accessible within Jinja expressions. -
macros(Optional): A list of Jinja macro definitions. Each item has asignature(e.g.,"my_macro(arg1, arg2='default')") and a multi-linebody. -
rules(Optional): A list of named, reusable command templates. -
targets(Required): A list defining the primary build outputs (files or logical targets). -
actions(Optional): Similar totargets, but entries here are implicitly treated asphony: true(meaning they don't necessarily correspond to a file and should run when requested). Useful for tasks likecleanortest. -
defaults(Optional): A list of target names (fromtargetsoractions) to build whennetsukeis run without specific targets.
Rules encapsulate reusable build commands or scripts.
rules:
- name: compile # Unique identifier for the rule
# Recipe: Exactly one of 'command', 'script', or 'rule'
command: "{{ cc }} {{ cflags }} -c {{ ins }} -o {{ outs }}"
# Optional: Displayed during the build
description: "Compiling {{ outs }}"
# Optional: Ninja dependency file info (gcc or msvc format)
deps: gcc
-
name: Unique string identifier. -
recipe: The action to perform. Defined by one of:-
command: A single shell command string. May contain{{ ins }}(space-separated inputs) and{{ outs }}(space-separated outputs). These specific placeholders are substituted after Jinja rendering but before hashing the action. All other Jinja interpolations happen first. The final command must be parseable byshlex(POSIX mode). -
script: A multi-line script (using YAML|). If it starts with#!, it's executed directly. Otherwise, it's run via/bin/sh -e(or PowerShell on Windows) by default. Interpolated variables are automatically shell-escaped unless| rawis used. -
rule: References another rule by name (less common within a rule definition).
-
-
description(Optional): A user-friendly message printed by Ninja when this rule runs. Can contain{{ ins }}/{{ outs }}. -
deps(Optional): Specifies dependency file format (gccormsvc) for C/C++ header dependencies, generating Ninja'sdepfileanddepsattributes.
Targets define what to build or what action to perform.
targets:
# Example 1: Building an object file using a rule
- name: build/utils.o # Output file(s). Can be a string or list.
rule: compile # Rule to use (mutually exclusive with command/script)
sources: src/utils.c # Input file(s). String or list.
deps: # Explicit dependencies (targets built before this one)
- build/utils.h
vars: # Target-local variables, override globals
cflags: "-O0 -g"
# Example 2: Linking an executable using an inline command
- name: my_app
command: "{{ cc }} build/main.o build/utils.o -o my_app"
sources: # Implicit dependencies derived from command/rule usage
- build/main.o
- build/utils.o
order_only_deps: # Dependencies built before, but changes don't trigger rebuild
- build_directory # e.g., Ensure 'build/' exists
# Example 3: A phony action (can also be in top-level 'actions:')
- name: package
phony: true # Doesn't represent a file, always considered out-of-date
command: "tar czf package.tar.gz my_app"
deps: my_app # Depends on the 'my_app' target
# Example 4: A target that always runs
- name: timestamp
always: true # Runs every time, regardless of inputs/outputs
command: "date > build/timestamp.txt"
-
name: Output file path(s). Can be a string or a list (StringOrList). -
recipe: How to build the target. Defined by one ofrule,command, orscript(mutually exclusive). -
sources: Input file path(s) (StringOrList). If a source matches another target'sname, an implicit dependency is created. -
deps(Optional): Explicit target dependencies (StringOrList). Changes trigger rebuilds. -
order_only_deps(Optional): Dependencies that must run first but whose changes don't trigger rebuilds (StringOrList). Maps to Ninja||. -
vars(Optional): Target-specific variables that override globalvars. -
phony(Optional, defaultfalse): Treat target as logical, not a file. Always out-of-date if requested. -
always(Optional, defaultfalse): Re-run the command every timenetsukeis invoked, regardless of dependency changes.
StringOrList: Fields like name, sources, deps, and order_only_deps
accept either a single string or a YAML list of strings for convenience.
Netsuke uses the MiniJinja engine to add dynamic capabilities to your manifest.
-
Variables:
{{ my_variable }} -
Expressions:
{{ 1 + 1 }},{{ sources | map('basename') }} -
Control Structures (within specific keys like
foreach,when, or insidemacros):{% if enable %}…{% endif %},{% for item in list %}…{% endfor %}
Important: Structural Jinja ({% %}) is generally not allowed directly
within the YAML structure outside of macros. Logic should primarily be within
string values or dedicated keys like foreach and when.
Netsuke processes the manifest in stages:
-
Initial YAML Parse: Load the raw file into an intermediate structure (like
serde_json::Value). -
Template Expansion (
foreach,when): Evaluateforeachexpressions to generate multiple target definitions. Theitem(and optionalindex) become available in the context. Evaluatewhenexpressions to conditionally include/exclude targets. -
Deserialisation to AST: Convert the expanded intermediate structure into Netsuke's typed Rust structs (
NetsukeManifest,Target, etc.). -
Final Rendering: Render Jinja expressions only within string fields (like
command,description,name,sourcesetc.) using the combined context (globals + target vars + iteration vars).
These keys enable generating multiple similar targets programmatically.
targets:
# Generate a target for each .c file in src/
- foreach: glob('src/*.c') # Jinja expression returning an iterable
# Only include if the filename isn't 'skip.c'
when: item | basename != 'skip.c'
# 'item' and 'index' (0-based) are available in the context
name: "build/{{ item | basename | with_suffix('.o') }}"
rule: compile
sources: "{{ item }}"
vars:
compile_flags: "-O{{ index + 1 }}" # Example using index
-
foreach: A Jinja expression evaluating to a list (or any iterable). A target definition will be generated for each item. -
when(Optional): A Jinja expression evaluating to a boolean. If false, the target generated for the currentitemis skipped.
Define reusable Jinja logic in the top-level macros section.
macros:
- signature: "cc_cmd(src, obj, flags='')" # Jinja macro signature
body: | # Multi-line body
{{ cc }} {{ flags }} -c {{ src | shell_escape }} -o {{ obj | shell_escape }}
targets:
- name: build/main.o
command: "{{ cc_cmd('src/main.c', 'build/main.o', flags=cflags) }}"
sources: src/main.c
Netsuke provides a rich set of built-in Jinja functions, filters, and tests to simplify common build tasks. These are automatically available in your manifest templates.
-
env(name, default=None): Reads an environment variable. Fails ifnameis unset and nodefaultis provided. Example:{{ env('CC', 'gcc') }} -
glob(pattern): Expands a filesystem glob pattern into a sorted list of files (directories are excluded). Handles*,**,?,[]. Case-sensitive. Example:{{ glob('src/**/*.c') }} -
fetch(url, cache=False): Downloads content from a URL. Ifcache=True, caches the result in.netsuke/fetchwithin the workspace based on URL hash. Enforces a configurable maximum response size (default 8 MiB); requests abort with an error quoting the configured threshold when the limit is exceeded. Cached downloads stream directly to disk and remove partial files on error. Configure the limit withStdlibConfig::with_fetch_max_response_bytes. Marks template as impure. -
now(offset=None): Returns the current time as a timezone-aware object (defaults to UTC).offsetcan be '+HH:MM' or 'Z'. Exposes.iso8601,.unix_timestamp,.offset. -
timedelta(...): Creates a duration object (e.g., for age comparisons). Acceptsweeks,days,hours,minutes,seconds,milliseconds,microseconds,nanoseconds. Exposes.iso8601,.seconds,.nanoseconds.
Apply filters using the pipe | operator: {{ value | filter_name(args...) }}
Path & File Filters:
-
basename:{{ 'path/to/file.txt' | basename }}->"file.txt" -
dirname:{{ 'path/to/file.txt' | dirname }}->"path/to" -
with_suffix(new_suffix, count=1, sep='.'): Replaces the lastcountdot-separated extensions.{{ 'archive.tar.gz' | with_suffix('.zip', 2) }}->"archive.zip" -
relative_to(base_path): Makes a path relative.{{ '/a/b/c' | relative_to('/a/b') }}->"c" -
realpath: Canonicalizes path, resolving symlinks. -
expanduser: Expands~to the home directory. -
contents(encoding='utf-8'): Reads file content as a string. -
size: File size in bytes. -
linecount: Number of lines in a text file. -
hash(alg='sha256'): Full hex digest of file content (supportssha256,sha512;md5,sha1iflegacy-digestsfeature enabled). -
digest(len=8, alg='sha256'): Truncated hex digest. -
shell_escape: Crucial for security. Safely quotes a string for use as a single shell argument. Use this whenever interpolating paths or variables into commands unless you are certain they are safe.
Collection Filters:
-
uniq: Removes duplicate items from a list, preserving order. -
flatten: Flattens a nested list.{{ [[1], [2, 3]] | flatten }}->[1, 2, 3] -
group_by(attribute): Groups items in a list of dicts/objects by an attribute's value. -
map(attribute='...')/map('filter', ...): Applies attribute access or another filter to each item in a list. -
filter(attribute='...')/filter('test', ...): Selects items based on attribute value or a test function. -
join(sep): Joins list items into a string. -
sort: Sorts list items.
Command Filters (Impure):
-
shell(command_string): Pipes the input value (string or bytes) as stdin tocommand_stringexecuted via the system shell (sh -corcmd /C). Returns stdout. Marks the template as impure. Example:{{ user_list | shell('grep admin') }}. The captured stdout is limited to 1 MiB by default; configure a different budget withStdlibConfig::with_command_max_output_bytes. Exceeding the limit raises anInvalidOperationerror that quotes the configured threshold. Templates can pass an options mapping such as{'mode': 'tempfile'}to stream stdout into a temporary file instead. The file path is returned to the template and remains bounded byStdlibConfig::with_command_max_stream_bytes(default 64 MiB). -
grep(pattern, flags=None): Filters input lines matchingpattern.flagscan be a string (e.g.,'-i') or list of strings. Implemented viashell. Marks template as impure. The same output and streaming limits apply whengrepemits large result sets.
Executable Discovery (which):
whichfilter/function: Resolves executables using the currentPATHwithout marking the template as impure. Example:{{ 'clang++' | which }}returns the first matching binary; the function alias{{ which('clang++') }}is available if piping would be awkward.- Keyword arguments:
all(defaultfalse): Return every match, ordered byPATH.canonical(defaultfalse): Resolve symlinks and deduplicate entries by their canonical path.fresh(defaultfalse): Bypass the resolver cache for this lookup while keeping previous entries available for future renders.cwd_mode(auto|always|never, defaultauto): Control whether emptyPATHsegments (and, on Windows, the implicit current-directory search) are honoured. Use"always"to force the working directory into the search order whenPATHis empty.
- Errors include actionable diagnostic codes such as
netsuke::jinja::which::not_foundalong with a preview of the scannedPATH. Supplying unknown keyword arguments or invalid values raisesnetsuke::jinja::which::args.
Impurity: Filters like shell and functions like fetch interact with the
outside world. Netsuke tracks this "impurity". Impure templates might affect
caching or reproducibility analysis in future versions. Use impure helpers
judiciously.
Use tests with the is keyword: {% if path is file %}
-
file,dir,symlink: Checks filesystem object type (without following links). -
readable,writable,executable: Checks permissions for the current user. -
absolute,relative: Checks path type.
Netsuke's CLI provides commands to manage your build.
netsuke [OPTIONS] [COMMAND] [TARGETS...]
-
-f, --file <FILE>: Path to theNetsukefile(default:Netsukefile). -
-C, --directory <DIR>: Change to directoryDIRbefore doing anything. -
-j, --jobs <N>: Set the number of parallel jobs Ninja should run (default: Ninja's default). -
-v, --verbose: Enable verbose diagnostic logging and completion timing summaries. -
--locale <LOCALE>: Localize CLI help and error messages (for exampleen-USores-ES).
Netsuke's fetch helper is guarded by a configurable network policy. The
policy is configured by these global options:
-
--fetch-allow-scheme <SCHEME>: Allow additional URL schemes beyond the defaults. -
--fetch-allow-host <HOST>: Allow the provided hostnames when default deny is enabled (wildcards such as*.example.comare supported). -
--fetch-block-host <HOST>: Always block the provided hostnames (wildcards supported), even if they are allowlisted. -
--fetch-default-deny: Deny all hosts by default and only permit the allowlist.
-
build(default): Compiles the manifest and runs Ninja to build the specifiedTARGETS(or thedefaultsif none are given).--emit <FILE>: Write the generatedbuild.ninjafile to<FILE>and keep it, instead of using a temporary file. When-C/--directoryis set, relative--emitpaths are resolved under<DIR>.
-
manifest <FILE>: Generates thebuild.ninjafile and writes it to<FILE>without executing Ninja. Use-to stream the generated Ninja file to stdout (for examplenetsuke manifest - | sed ...). When-C/--directoryis set, relative manifest output paths are resolved under<DIR>. -
clean: Removes build artefacts by runningninja -t clean. Requires rules/targets to be properly configured for cleaning in Ninja (often viaphonytargets). -
graph: Generates the build dependency graph by runningninja -t graphon the generatedbuild.ninja, outputting DOT to stdout (suitable for Graphviz). Future versions may support other formats like--html.
Netsuke layers configuration in this order, with later entries overriding earlier ones: defaults, configuration files, environment variables, and command-line flags.
Configuration files are discovered using OrthoConfig. Netsuke honours
NETSUKE_CONFIG_PATH first, then searches $XDG_CONFIG_HOME/netsuke, each
entry in $XDG_CONFIG_DIRS (falling back to /etc/xdg on Unix-like targets),
Windows application data directories, $HOME/.config/netsuke,
$HOME/.netsuke.toml, and finally the project root.
Environment variables use the NETSUKE_ prefix (for example,
NETSUKE_JOBS=8). Use __ to separate nested keys when matching structured
configuration.
Use --locale <LOCALE>, NETSUKE_LOCALE, or a locale = "..." entry in a
configuration file to select localized CLI copy and error messages. Locale
precedence is: command-line flag, environment variable, configuration file,
then the system default. The same locale applies to user-facing runtime
diagnostics, including manifest parse failures, stdlib template errors, and
runner failures. Spanish (es-ES) is included as a reference translation;
unsupported locales fall back to English (en-US).
For information on contributing translations, see the Translator Guide.
JSON diagnostics follow the same OrthoConfig layering:
- CLI flag:
--diag-json - Environment variable:
NETSUKE_DIAG_JSON=true|false - Configuration file:
diag_json = true|false
For startup failures that happen before configuration files can be loaded, Netsuke honours the CLI flag and environment variable immediately. A configuration file cannot request JSON for errors raised while that same file is being discovered or parsed.
Netsuke separates its output into two streams for scriptability:
- stderr: Status messages, progress indicators, stage summaries, and diagnostics
- stdout: Subprocess output (for example,
ninja -t graphproduces DOT graphs on stdout)
This separation allows reliable piping and redirection:
# Capture build graph without status noise
netsuke graph > build.dot
# Capture progress log without build output
netsuke build 2> progress.log
# Suppress status messages entirely
netsuke --progress false buildWhen using the manifest subcommand with - as the output path, the generated
Ninja file streams to stdout while status messages remain on stderr:
# Pipe generated Ninja file for inspection
netsuke manifest - | grep 'rule 'Use --diag-json when a caller needs machine-readable diagnostics on stderr
instead of the human-oriented text renderer.
When JSON diagnostics are enabled:
- Failing commands write exactly one JSON document to
stderr. - Successful commands write nothing to
stderr. - Progress lines, timing summaries, emoji prefixes, and tracing logs are
suppressed so
stderrremains machine-readable. stdoutbehaviour is unchanged. For example,netsuke --diag-json manifest -still streams the generated Ninja file tostdout.
The current schema version is 1. The document shape is:
schema_version: Integer schema version for compatibility checks.generator: Object containingnameandversion.diagnostics: Array of diagnostic objects.
Each diagnostic object contains:
message: Localized summary line.code: Stable diagnostic code, ornullwhen unavailable.severity: One oferror,warning, oradvice.help: Localized remediation hint, ornull.url: Optional documentation URL.causes: Ordered error-cause chain.source: Optional source descriptor currently containingname.primary_span: The first labelled span, ornull.labels: All labelled spans withlabel,offset,length,line,column,end_line,end_column, andsnippet.related: Nested diagnostics rendered using the same schema.
Example:
{
"schema_version": 1,
"generator": {
"name": "netsuke",
"version": "0.1.0"
},
"diagnostics": [
{
"message": "Manifest parse failed.",
"code": "netsuke::manifest::parse",
"severity": "error",
"help": "YAML does not permit tabs; use spaces for indentation.",
"url": null,
"causes": [
"YAML parse error at line 2, column 2: tabs disallowed within this context",
"tabs disallowed within this context (block indentation) at line 2, column 2"
],
"source": {
"name": "Netsukefile"
},
"primary_span": {
"label": "invalid YAML",
"offset": 10,
"length": 1,
"line": 2,
"column": 2,
"end_line": 2,
"end_column": 2,
"snippet": "-"
},
"labels": [
{
"label": "invalid YAML",
"offset": 10,
"length": 1,
"line": 2,
"column": 2,
"end_line": 2,
"end_column": 2,
"snippet": "-"
}
],
"related": []
}
]
}Netsuke supports an accessible output mode that uses static, labelled status lines suitable for screen readers and dumb terminals.
Accessible mode is auto-enabled when:
TERMis set todumbNO_COLORis set (any value)
Accessible mode can be forced on or off:
- CLI flag:
--accessible trueor--accessible false - Environment variable:
NETSUKE_ACCESSIBLE=true - Configuration file:
accessible = true
Explicit configuration always takes precedence over auto-detection, in either
direction (--accessible false disables accessible mode even when NO_COLOR
is set).
When accessible mode is active, each pipeline stage produces a labelled status line on stderr:
Stage 1/6: Reading manifest file
Stage 2/6: Parsing YAML document
Stage 3/6: Expanding template directives
Stage 4/6: Deserializing and rendering manifest values
Stage 5/6: Building and validating dependency graph
Stage 6/6: Synthesizing Ninja plan and executing Build
Task 1/2: cc -c src/a.c
Task 2/2: cc -c src/b.c
Build complete.
In standard mode, Netsuke uses indicatif stage summaries when progress is
enabled. During Stage 6, Netsuke parses Ninja status lines ([current/total])
and emits task progress updates. When stdout is not a teletype terminal (TTY),
task progress automatically falls back to textual updates, so continuous
integration (CI) and redirected logs remain readable.
When verbose mode is enabled, Netsuke appends a completion timing summary on stderr after a successful run:
Build complete.
Stage timing summary:
- Stage 1/6: Reading manifest file: 12ms
- Stage 2/6: Parsing YAML document: 4ms
- Stage 3/6: Expanding template directives: 7ms
- Stage 4/6: Deserializing and rendering manifest values: 6ms
- Stage 5/6: Building and validating dependency graph: 3ms
- Stage 6/6: Synthesizing Ninja plan and executing Build: 18ms
Total pipeline time: 50ms
Without --verbose, timing lines are not emitted. Failed runs also suppress
timing summary output, even when verbose mode is enabled.
Progress output can be controlled via OrthoConfig layering:
- CLI flag:
--progress trueor--progress false - Environment variable:
NETSUKE_PROGRESS=true|false - Configuration file:
progress = true|false
When progress is disabled, Netsuke suppresses stage and task progress output in both standard and accessible modes. If verbose mode is enabled at the same time, only the completion timing summary remains visible on successful runs.
Netsuke resolves a CLI theme through the same layered configuration model as its other user-facing preferences:
- CLI flag:
--theme auto|unicode|ascii - Environment variable:
NETSUKE_THEME=auto|unicode|ascii - Configuration file:
theme = "auto" | "unicode" | "ascii"
Theme precedence is:
- Explicit
theme - Legacy
no_emoji = true NETSUKE_NO_EMOJIpresentNO_COLORpresent- Output mode default (
unicodefor standard output,asciifor accessible output)
auto keeps the mode-sensitive default. unicode forces Unicode symbols even
in accessible mode, while ascii forces ASCII-safe symbols everywhere.
Netsuke still supports the legacy no-emoji compatibility flag for users who already rely on it:
- CLI flag:
--no-emoji true - Environment variable:
NETSUKE_NO_EMOJI(any value, including empty) - Configuration file:
no_emoji = true
Only --no-emoji true acts as a hard override; --no-emoji false and omitting
the flag both defer to environment variable detection. NETSUKE_NO_EMOJI uses
presence-based semantics, so setting it to any value (including "false" or
"0") still selects the ASCII theme unless an explicit theme overrides it.
In all output modes, Netsuke uses semantic text prefixes so meaning is never conveyed solely by colour. The active theme swaps only the glyph set:
- Unicode theme:
✖ Error:,⚠ Warning:,✔ Success:,ℹ Info:,⏱ Timing: - ASCII theme:
X Error:,! Warning:,+ Success:,i Info:,T Timing:
-
0: Success. -
Non-zero: Failure (e.g., manifest parsing error, template error, build command failure).
Refer to the examples/ directory in the Netsuke repository for practical
manifest files:
-
basic_c.yml: A simple C project compilation. Demonstratesvars,rules,targets, anddefaults. -
website.yml: Builds a static website from Markdown files using Pandoc. Showsglob,foreach, and path manipulation filters (basename,with_suffix). -
photo_edit.yml: Processes RAW photos withdarktable-cliand creates a gallery. Usesglob,foreach, andactions. -
visual_design.yml: Rasterizes SVG files using Inkscape. Usesenvfunction with a default. -
writing.yml: Compiles a multi-chapter book from Markdown to PDF via LaTeX. Shows more complex dependencies and sorting of glob results.
These examples illustrate how to combine Netsuke's features to manage different kinds of build workflows effectively.
Netsuke aims for clear and actionable error messages.
-
YAML Errors: If
Netsukefileis invalid YAML,serde-saphyrprovides location info (line, column). Netsuke may add hints for common issues (e.g., tabs vs spaces). -
Schema Errors: If the YAML structure doesn't match the expected schema (e.g., missing
targets, unknown keys), errors indicate the structural problem. -
Template Errors: Jinja errors during
foreach,when, or string rendering include context (e.g., undefined variable, invalid filter usage) and potentially location information. -
IR Validation Errors: Errors found during graph construction (e.g., missing rules, duplicate outputs, circular dependencies) are reported before execution.
-
Build Failures: If a command run by Ninja fails, Netsuke reports the failure, typically showing the command's output/error streams captured by Ninja.
Use the -v flag for more detailed error context or internal logging.
-
Command Injection: Netsuke automatically shell-escapes variables interpolated into
command:strings unless the| rawJinja filter is explicitly used. Avoid| rawunless you fully trust the variable's content. File paths fromglobor placeholders like{{ ins }}/{{ outs }}are quoted safely. -
script:Execution: Scripts run via the specified interpreter (defaulting tosh -e). Ensure scripts handle inputs safely. -
Impure Functions/Filters:
env(),glob(),fetch(),shell(),grep()interact with the environment or network. Be mindful when using them with untrusted manifest parts. Future versions might offer sandboxing options.
Always review Netsukefile manifests, especially those from untrusted sources,
before building.