mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
Of the 76 open code scanning alerts, 39 point at code that can change. svc-ai-mcp-adapter: - A path argument that is empty or made only of dots is refused. Percent-encoding keeps a separator inside its segment, but "." and ".." need none, so an upstream that resolves dot segments could answer from a path the contract does not name. - parse_duration splits on the units. The pattern it used before rescanned a long run of digits from every start position. Tests and the README generators: - Every Jinja environment built with autoescape=False and a "noqa: S701" uses select_autoescape now, as six test modules and utils/templating/jinja.py already did. It escapes templates named *.html, *.htm and *.xml, which none of these call sites loads. - Dead code goes: a doubled "rel" block in the email lint, an unread _SKIP_DIRS, and a module that _resolve returned to no caller. - Two "except ...: pass" blocks become contextlib.suppress and assertRaises. - Nine test modules import unittest.mock one way instead of two, and one lookup test imports its module once. Closes #807 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
82 lines
2.7 KiB
Python
82 lines
2.7 KiB
Python
"""Derive the role-README section schema from the Jinja template.
|
|
|
|
``templates/roles/README.md.j2.tmpl`` is the single source of truth. The
|
|
required sections are the H2s the template emits with no optional context;
|
|
the optional sections are the extra H2s that appear once every
|
|
``{% if ... %}``-guarded variable is supplied. Rendering the template both
|
|
ways and diffing the H2 sets keeps the lint and the generator in lock-step
|
|
with the template.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import functools
|
|
|
|
import jinja2
|
|
|
|
from cli.build.docs.readme.sections import h2_titles
|
|
from utils.cache.files import PROJECT_ROOT
|
|
|
|
TEMPLATE_DIR = PROJECT_ROOT / "templates" / "roles"
|
|
TEMPLATE_NAME = "README.md.j2.tmpl"
|
|
|
|
_REQUIRED_CTX: dict = {
|
|
"application_id": "web-app-example",
|
|
"application_name": "Example",
|
|
"application_url": "https://example.com/",
|
|
"application_description": "is an example application.",
|
|
"application_overview": "This role deploys the example application.",
|
|
"application_features": [{"name": "Example", "description": "An example feature."}],
|
|
"application_author": "Kevin Veen-Birkenbach",
|
|
"cosmos_mermaid": 'flowchart LR\n a["a"] --> b["b"]',
|
|
}
|
|
|
|
_OPTIONAL_CTX: dict = {
|
|
"application_invokable": True,
|
|
"cosmos_intro": "Example cosmos intro.",
|
|
"application_author_url": "https://www.veen.world",
|
|
"application_use_cases": "Example use case.",
|
|
"application_developer_notes": [{"file": "Administration.md", "description": "notes."}],
|
|
"application_further_resources": [{"label": "Example", "url": "https://example.com/"}],
|
|
}
|
|
|
|
|
|
def _render(ctx: dict) -> str:
|
|
env = jinja2.Environment(
|
|
loader=jinja2.FileSystemLoader(str(TEMPLATE_DIR)),
|
|
keep_trailing_newline=True,
|
|
autoescape=jinja2.select_autoescape(),
|
|
)
|
|
return env.get_template(TEMPLATE_NAME).render(**ctx)
|
|
|
|
|
|
def render_required() -> str:
|
|
return _render(dict(_REQUIRED_CTX))
|
|
|
|
|
|
def render_full() -> str:
|
|
return _render({**_REQUIRED_CTX, **_OPTIONAL_CTX})
|
|
|
|
|
|
@functools.lru_cache(maxsize=1)
|
|
def required_sections() -> tuple[str, ...]:
|
|
"""H2 titles the template emits without any optional context (mandatory)."""
|
|
return tuple(h2_titles(render_required()))
|
|
|
|
|
|
@functools.lru_cache(maxsize=1)
|
|
def canonical_order() -> tuple[str, ...]:
|
|
"""Full H2 order (required + optional) as the template lays them out."""
|
|
return tuple(h2_titles(render_full()))
|
|
|
|
|
|
@functools.lru_cache(maxsize=1)
|
|
def optional_sections() -> tuple[str, ...]:
|
|
required = set(required_sections())
|
|
return tuple(title for title in canonical_order() if title not in required)
|
|
|
|
|
|
def credits_heading() -> str:
|
|
"""The last required section (Credits by convention)."""
|
|
return required_sections()[-1]
|