Conversation
Reviewer's GuideAdds five example Netsukefile YAML templates demonstrating common build and processing workflows by defining version, variables, templated rules, dynamic target generation, and default targets in standalone manifest files. Class diagram for Netsukefile manifest structureclassDiagram
class Manifest {
+string netsuke_version
+dict vars
+list rules
+list targets
+list defaults
}
class Rule {
+string name
+string command
+string description
}
class Target {
+string name
+string rule
+list sources
+list deps
}
Manifest "1" o-- "*" Rule : contains
Manifest "1" o-- "*" Target : contains
Rule <|-- CompileRule
Rule <|-- LinkRule
Rule <|-- DevelopRule
Rule <|-- ThumbRule
Rule <|-- RasteriseRule
Rule <|-- BuildRule
Rule <|-- SetupRule
Rule <|-- InstallRule
Rule <|-- TestRule
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Summary by CodeRabbit
Summary by CodeRabbit
WalkthroughIntroduce five new example YAML configuration files for the Netsuke build system. Each file demonstrates automated workflows for distinct domains: document compilation, C application builds, photo processing, Python package management, and SVG-to-PNG conversion. Define variables, build rules, dynamic targets, and default outputs for each scenario. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Netsuke
participant Shell
User->>Netsuke: Invoke build (e.g., default target)
Netsuke->>Shell: Execute defined build rules (e.g., pandoc, gcc, darktable-cli, pip, inkscape)
Shell-->>Netsuke: Return command results
Netsuke-->>User: Output build artefacts (PDF, executable, JPEGs, test results, PNGs)
Poem
✨ Finishing Touches🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Hey @leynos - I've reviewed your changes and found some issues that need to be addressed.
- Consider adding a directory-creation step (e.g.
mkdir -p {{ outdir }}) in the rules that write tobuild/...so the target dirs exist before files are generated. - It’d be clearer to standardise on one templating helper (either
basename+replaceor justreplace) across all examples for consistent path manipulation.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider adding a directory-creation step (e.g. `mkdir -p {{ outdir }}`) in the rules that write to `build/...` so the target dirs exist before files are generated.
- It’d be clearer to standardise on one templating helper (either `basename`+`replace` or just `replace`) across all examples for consistent path manipulation.
## Individual Comments
### Comment 1
<location> `examples/photo_album.yml:4` </location>
<code_context>
+netsuke_version: "1.0"
+
+vars:
+ style: "{{ env('DT_STYLE') | default('natural') }}"
+ outdir: build/jpg
+
</code_context>
<issue_to_address>
Potential for inconsistent output if DT_STYLE changes between builds.
Incorporate the style value into the output path or dependency graph to ensure images are rebuilt when DT_STYLE changes.
</issue_to_address>
### Comment 2
<location> `examples/photo_album.yml:13` </location>
<code_context>
+ description: Develop RAW image
+
+ - name: thumb
+ command: "convert {ins} -resize 200x200 {outs}"
+ description: Create thumbnail
+
</code_context>
<issue_to_address>
Thumbnail resizing may not preserve aspect ratio.
Using '-resize 200x200' may stretch non-square images. To maintain aspect ratio, use '200x200^' or add '-gravity center -extent 200x200'.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
- name: thumb
command: "convert {ins} -resize 200x200 {outs}"
description: Create thumbnail
=======
- name: thumb
command: "convert {ins} -resize 200x200^ -gravity center -extent 200x200 {outs}"
description: Create thumbnail
>>>>>>> REPLACE
</suggested_fix>
### Comment 3
<location> `examples/python_package.yml:8` </location>
<code_context>
+
+rules:
+ - name: install
+ command: "{{ python }} -m pip install -e ."
+ description: Install package
+ - name: test
</code_context>
<issue_to_address>
No isolation of Python environment for install rule.
Consider running the install step within a virtual environment to prevent conflicts with the global Python environment.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Actionable comments posted: 8
♻️ Duplicate comments (2)
examples/photo_album.yml (1)
17-24: Same YAML-lint issue as inc_app.ymlWrap the Jinja
% forblocks in a literal scalar to silence CI linting while keeping the template logic.examples/svg_to_png.yml (1)
12-16: YAML-lint complains about raw JinjaAs with the other templates, shield the
% forblock behind a scalar to keep the file YAML-valid during static analysis.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
examples/book.yml(1 hunks)examples/c_app.yml(1 hunks)examples/photo_album.yml(1 hunks)examples/python_package.yml(1 hunks)examples/svg_to_png.yml(1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
examples/c_app.yml
[error] 18-18: syntax error: found character '%' that cannot start any token
(syntax)
examples/photo_album.yml
[error] 17-17: syntax error: found character '%' that cannot start any token
(syntax)
examples/svg_to_png.yml
[error] 12-12: syntax error: found character '%' that cannot start any token
(syntax)
|
@sourcery-ai resolve |
|
@sourcery-ai review |
There was a problem hiding this comment.
Hey @leynos - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `examples/book.yml:14` </location>
<code_context>
+targets:
+ - name: "{{ outdir }}/manuscript.pdf"
+ rule: build
+ sources: "{{ glob('chapters/*.md') }}"
+
+defaults:
</code_context>
<issue_to_address>
Quoting the glob result may cause YAML to treat it as a string, not a list.
Remove the quotes around the glob expression to ensure YAML interprets it as a list, not a string.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
examples/book.yml(1 hunks)examples/c_app.yml(1 hunks)examples/photo_album.yml(1 hunks)examples/python_package.yml(1 hunks)examples/svg_to_png.yml(1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
examples/photo_album.yml
[error] 17-17: syntax error: found character '%' that cannot start any token
(syntax)
examples/svg_to_png.yml
[error] 12-12: syntax error: found character '%' that cannot start any token
(syntax)
🔇 Additional comments (2)
examples/c_app.yml (1)
10-14: Link flags now match compile flags – good work
{{ cflags }}is propagated to the link rule, ensuring diagnostics and optimisation consistency across the entire build.examples/photo_album.yml (1)
16-29: Fix YAML syntax error – Jinja control lines must sit inside a string
Raw{% ... %}lines at 17-24 and 27-29 break YAML parsing (yaml-linterror). Wrap the block with a multi-line string so the file stays valid before Jinja renders.-targets: -{% for raw in glob('photos/*.nef') %} +targets: | + {% for raw in glob('photos/*.nef') %} - name: "{{ outdir }}/{{ raw | basename | replace('.nef', '.jpg') }}" rule: develop sources: "{{ raw }}" - name: "{{ outdir }}/thumbs/{{ raw | basename | replace('.nef', '.jpg') }}" rule: thumb sources: "{{ outdir }}/{{ raw | basename | replace('.nef', '.jpg') }}" -{% endfor %} + {% endfor %} -defaults: -{% for raw in glob('photos/*.nef') %} - - "{{ outdir }}/thumbs/{{ raw | basename | replace('.nef', '.jpg') }}" -{% endfor %} +defaults: | + {% for raw in glob('photos/*.nef') %} + - "{{ outdir }}/thumbs/{{ raw | basename | replace('.nef', '.jpg') }}" + {% endfor %}Likely an incorrect or invalid review comment.
| targets: | | ||
| {% for src in glob('src/*.c') %} | ||
| - name: "{{ objdir }}/{{ src | basename | replace('.c', '.o') }}" | ||
| rule: compile | ||
| sources: "{{ src }}" | ||
| {% endfor %} | ||
| - name: app |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify that Netsuke re-parses the templated target list
Wrapping the Jinja loop inside a literal block (|) removes YAML-lint errors, but it also turns targets into a scalar string. Netsuke must post-process that string into real YAML; otherwise the build graph will be empty.
Run:
Ensure the runtime Jinja pass writes a second-stage YAML that Netsuke loads.
🏁 Script executed:
#!/bin/bash
# Expect: 'targets' key in the rendered YAML is an array, not a string.
grep -nA2 -B2 '^targets:' examples/c_app.ymlLength of output: 218
Run a Jinja render simulation to verify the targets type:
🏁 Script executed:
#!/bin/bash
set -e
pip install jinja2 pyyaml
python3 - << 'EOF'
import jinja2, yaml, glob
env = jinja2.Environment()
template = open('examples/c_app.yml').read()
rendered = env.from_string(template).render(objdir='build', glob=glob)
loaded = yaml.safe_load(rendered)
print(type(loaded['targets']).__name__)
EOFLength of output: 1103
Register missing Jinja filters and re-run the YAML load simulation:
🏁 Script executed:
#!/bin/bash
set -e
pip install jinja2 pyyaml
python3 - << 'EOF'
import jinja2, yaml, glob, os
env = jinja2.Environment()
env.filters['basename'] = os.path.basename
env.filters['replace'] = lambda s, a, b: s.replace(a, b)
template = open('examples/c_app.yml').read()
rendered = env.from_string(template).render(objdir='build', glob=glob)
loaded = yaml.safe_load(rendered)
print(type(loaded.get('targets')).__name__)
EOFLength of output: 873
Register glob.glob correctly and re-run the YAML load test:
🏁 Script executed:
#!/bin/bash
set -e
pip install jinja2 pyyaml
python3 - << 'EOF'
import jinja2, yaml, glob, os
# Load template from examples/
env = jinja2.Environment(loader=jinja2.FileSystemLoader('examples'))
env.globals['glob'] = glob.glob
env.filters['basename'] = os.path.basename
env.filters['replace'] = lambda s, a, b: s.replace(a, b)
# Render and parse
template = env.get_template('c_app.yml').render(objdir='build')
data = yaml.safe_load(template)
# Output type of targets
print(type(data.get('targets')).__name__)
EOFLength of output: 420
Ensure targets renders as a YAML sequence, not a string
The Jinja literal block (|) makes targets a single string (yaml.safe_load yields str). Netsuke must re-parse that block as YAML to build a graph. Remove the scalar indicator so Jinja emits a real sequence:
File: examples/c_app.yml
Lines: 18–24
- targets: |
- {% for src in glob('src/*.c') %}
- - name: "{{ objdir }}/{{ src | basename | replace('.c', '.o') }}"
- rule: compile
- sources: "{{ src }}"
- {% endfor %}
- - name: app
+ targets:
+ {% for src in glob('src/*.c') %}
+ - name: "{{ objdir }}/{{ src | basename | replace('.c', '.o') }}"
+ rule: compile
+ sources: "{{ src }}"
+ {% endfor %}
+ - name: app- Drop the
|aftertargets: - Unindent the loop so each
-is part of the sequence
Run yaml.safe_load on the rendered output to confirm targets is a list, not a string.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| targets: | | |
| {% for src in glob('src/*.c') %} | |
| - name: "{{ objdir }}/{{ src | basename | replace('.c', '.o') }}" | |
| rule: compile | |
| sources: "{{ src }}" | |
| {% endfor %} | |
| - name: app | |
| targets: | |
| {% for src in glob('src/*.c') %} | |
| - name: "{{ objdir }}/{{ src | basename | replace('.c', '.o') }}" | |
| rule: compile | |
| sources: "{{ src }}" | |
| {% endfor %} | |
| - name: app |
🤖 Prompt for AI Agents
In examples/c_app.yml around lines 18 to 24, the `targets` key is followed by a
literal block indicator `|` causing the entire block to be parsed as a single
string instead of a YAML sequence. To fix this, remove the `|` after `targets:`
and unindent the Jinja for-loop so that each `-` is aligned as a list item under
`targets`. After making these changes, verify by running `yaml.safe_load` on the
rendered output to ensure `targets` is parsed as a list, not a string.
| command: "{{ python }} -m venv {{ venv }}" | ||
| description: Create virtual environment |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Upgrade venv tooling during creation
Add --upgrade-deps to python -m venv so the virtual environment ships with the latest pip and setuptools, avoiding old-toolchain surprises.
- command: "{{ python }} -m venv {{ venv }}"
+ command: "{{ python }} -m venv --upgrade-deps {{ venv }}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| command: "{{ python }} -m venv {{ venv }}" | |
| description: Create virtual environment | |
| command: "{{ python }} -m venv --upgrade-deps {{ venv }}" | |
| description: Create virtual environment |
🤖 Prompt for AI Agents
In examples/python_package.yml at lines 9 to 10, the command creating the
virtual environment should include the --upgrade-deps flag to ensure the latest
pip and setuptools are installed. Modify the command to add --upgrade-deps after
python -m venv so the environment uses up-to-date tooling and avoids issues with
outdated packages.
| targets: | ||
| {% for svg in glob('assets/svg/*.svg') %} | ||
| - name: "{{ outdir }}/{{ svg | basename | replace('.svg', '.png') }}" | ||
| rule: rasterise | ||
| sources: "{{ svg }}" | ||
| {% endfor %} | ||
|
|
||
| defaults: | ||
| {% for svg in glob('assets/svg/*.svg') %} | ||
| - "{{ outdir }}/{{ svg | basename | replace('.svg', '.png') }}" | ||
| {% endfor %} |
There was a problem hiding this comment.
Wrap Jinja loop to restore YAML validity
{% for svg … %} lines at 12-16 and 19-21 cause the same % token error. Encapsulate the loop in a literal block.
-targets:
-{% for svg in glob('assets/svg/*.svg') %}
+targets: |
+ {% for svg in glob('assets/svg/*.svg') %}
- name: "{{ outdir }}/{{ svg | basename | replace('.svg', '.png') }}"
rule: rasterise
sources: "{{ svg }}"
-{% endfor %}
+ {% endfor %}
-defaults:
-{% for svg in glob('assets/svg/*.svg') %}
- - "{{ outdir }}/{{ svg | basename | replace('.svg', '.png') }}"
-{% endfor %}
+defaults: |
+ {% for svg in glob('assets/svg/*.svg') %}
+ - "{{ outdir }}/{{ svg | basename | replace('.svg', '.png') }}"
+ {% endfor %}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| targets: | |
| {% for svg in glob('assets/svg/*.svg') %} | |
| - name: "{{ outdir }}/{{ svg | basename | replace('.svg', '.png') }}" | |
| rule: rasterise | |
| sources: "{{ svg }}" | |
| {% endfor %} | |
| defaults: | |
| {% for svg in glob('assets/svg/*.svg') %} | |
| - "{{ outdir }}/{{ svg | basename | replace('.svg', '.png') }}" | |
| {% endfor %} | |
| targets: | | |
| {% for svg in glob('assets/svg/*.svg') %} | |
| - name: "{{ outdir }}/{{ svg | basename | replace('.svg', '.png') }}" | |
| rule: rasterise | |
| sources: "{{ svg }}" | |
| {% endfor %} | |
| defaults: | | |
| {% for svg in glob('assets/svg/*.svg') %} | |
| - "{{ outdir }}/{{ svg | basename | replace('.svg', '.png') }}" | |
| {% endfor %} |
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 12-12: syntax error: found character '%' that cannot start any token
(syntax)
🤖 Prompt for AI Agents
In examples/svg_to_png.yml around lines 11 to 21, the Jinja for loops are
causing YAML parsing errors due to the unescaped % tokens. To fix this, wrap
each Jinja for loop block inside a YAML literal block (using | or >) to preserve
the Jinja syntax as a literal string and restore YAML validity. This will
prevent the YAML parser from misinterpreting the Jinja syntax.
| - name: build | ||
| command: "mkdir -p $(dirname {outs}) && pandoc {ins} -o {outs}" | ||
| description: Compile document | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Expose PDF engine selection
Permit authors to tweak the output engine without editing the rule. Pipe an optional pdf_engine var through to pandoc.
- command: "mkdir -p $(dirname {outs}) && pandoc {ins} -o {outs}"
+ command: "mkdir -p $(dirname {outs}) && pandoc {ins} -o {outs} {{ pdf_engine | default('') }}"Add to vars:
pdf_engine: "--pdf-engine=xelatex"🤖 Prompt for AI Agents
In examples/book.yml around lines 7 to 10, the pandoc command does not allow
selecting the PDF engine dynamically. Modify the command to include an optional
pdf_engine variable by adding it to the vars section as pdf_engine:
"--pdf-engine=xelatex" and update the command to append {pdf_engine} so authors
can specify the PDF engine without editing the rule directly.
|
Stale |
Summary
Testing
cargo fmt --allmdformat-allcargo clippy --all-targets --all-features -- -D warnings(fails: lint_groups_priority, print-stdout)cargo test --all-targets --all-featureshttps://chatgpt.com/codex/tasks/task_e_687239b30c848322b1d0cb24c9549f52
Summary by Sourcery
New Features: