Add Jinja2 template validation test and pre-commit hook (#45)

* Remove escaped chars in comments

* Add Jinja2 template validation test and pre-commit hook

Catch template syntax errors early with a unit test that parses and
renders the default cloud-init template, and a pre-commit hook that
validates all templates in dropkit/templates/ on change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Riccardo Schirone
2026-02-24 14:23:39 +01:00
committed by GitHub
parent fde89df590
commit 0efeea52bb
3 changed files with 64 additions and 1 deletions
+10
View File
@@ -34,6 +34,16 @@ repos:
types: [python]
pass_filenames: false
# Jinja2 template validation
- repo: local
hooks:
- id: jinja2-check
name: jinja2 template syntax
entry: uv run python -c "import sys; from jinja2 import Template; [Template(open(f).read()) for f in sys.argv[1:]]"
language: system
files: ^dropkit/templates/.*\.yaml$
types: [file]
# Shell script linting
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.10.0
+1 -1
View File
@@ -1,5 +1,5 @@
#cloud-config
# Warning: This whole file will be evaluated by Jinja. Escape any special literals (`{{`, `{%`, `{#`).
# Warning: This whole file will be evaluated by Jinja. Escape any special literals
# Update and upgrade system packages
package_update: true
+53
View File
@@ -0,0 +1,53 @@
"""Tests for cloud-init template parsing and rendering."""
from jinja2 import Template, TemplateSyntaxError
from dropkit.config import Config
def _load_default_template() -> str:
"""Load the default cloud-init template content."""
path = Config.get_default_template_path()
return path.read_text()
def test_default_template_parses():
"""Verify the default template is valid Jinja2 syntax."""
content = _load_default_template()
try:
Template(content)
except TemplateSyntaxError as exc:
raise AssertionError(f"Template has Jinja2 syntax error: {exc}") from exc
def test_default_template_renders():
"""Verify the template renders with sample variables."""
content = _load_default_template()
template = Template(content)
rendered = template.render(
username="testuser",
full_name="Test User",
email="test@example.com",
ssh_keys=["ssh-ed25519 AAAAC3... test@host"],
tailscale_enabled=True,
)
assert "testuser" in rendered
assert "ssh-ed25519 AAAAC3... test@host" in rendered
assert "git config --global user.name 'Test User'" in rendered
assert "git config --global user.email 'test@example.com'" in rendered
assert "tailscale" in rendered
def test_default_template_renders_without_tailscale():
"""Verify the Tailscale section is absent when disabled."""
content = _load_default_template()
template = Template(content)
rendered = template.render(
username="testuser",
full_name="Test User",
email="test@example.com",
ssh_keys=["ssh-ed25519 AAAAC3... test@host"],
tailscale_enabled=False,
)
assert "testuser" in rendered
assert "tailscale.com/install.sh" not in rendered