mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
Replace 8 bash env modules with a single Python generator
(cli.meta.env) that materialises env/static.env + runtime context
into a gitignored .env. Each variable lives in its own handler
under utils/env/handlers/; defaults carry inline one-line `# ...`
comments.
Rename to the INFINITO_* schema:
- workflow inputs: APPS, SERVICES_DISABLED, CMD, DEBUG, WHITELIST,
BUNDLES, FULL_CYCLE, VARIANT, RUN_FLAGS, LIMIT_HOST, PARALLEL
- inventory: INVENTORY_DIR / INVENTORY_FILE / HOST_VARS_FILE,
PW_FILE -> INVENTORY_PASSWORD_FILE
- venv: VENV_BASE / VENV_FALLBACK / VENV_NAME
- new SPOT defaults: COMPILE_SILENCE, CONTAINER_NO_CA,
WAIT_HEALTH_TIMEOUT_S, PACKAGE_CACHE_FRONTEND_INIT_IMAGE,
promoted IMAGE_TAG from GHA-only to general passthrough
compose.yml hardening:
- inline \${VAR:-default} removed; strict \${VAR:?...} everywhere
- new integration lint blocks regressions
Tooling:
- scripts/install/all.sh owns the stamp-file install logic
(Makefile target delegates)
- scripts/meta/env/load.sh is the single shell entry point;
8 bash modules deleted
- new shell lint: no INFINITO_* defaults outside env/static.env
Tests:
- 93 new unit tests for utils/env/{builder,parser,runtime,writer}
- restructure tests/lint into repository/ (file-content rules) vs
filesystem/{shell,python,env}/ (filesystem-shape rules)
- drift + smoke integration tests against the generator
Docs:
- variables.md is now a pointer; per-variable docs live next to
the key in env/static.env / env/ci.env
- compose.yml.md drops the Default column
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
182 lines
6.8 KiB
Python
182 lines
6.8 KiB
Python
"""Lint guard: redundant boolean expressions in Jinja2 / Ansible YAML.
|
|
|
|
Two related anti-patterns are flagged here. Each is independently
|
|
suppressible via its own marker so a legitimate exception can opt out
|
|
of one without disabling the other.
|
|
|
|
1. ``redundant-bool-ternary`` — ``<bool-expr> | ternary('true','false')``
|
|
for any boolean-producing left side: the ``bool`` filter, an ``in``
|
|
/ ``not in`` / ``is`` / ``is not`` / comparison operator, a Jinja
|
|
``is defined`` / ``is none`` test, etc. The ``ternary`` is a no-op
|
|
on a value that is already a boolean — its only effect is to swap
|
|
``True`` / ``False`` for the lowercase string tokens
|
|
``'true'`` / ``'false'``, and every consumer the project actually
|
|
feeds these to (PHP ``filter_var(..., FILTER_VALIDATE_BOOLEAN)``,
|
|
JS ``String(x).toLowerCase() === "true"``, dotenv-style flags,
|
|
Ansible ``bool``-comparing conditionals) accepts ``True`` / ``False``
|
|
just as well. Convention::
|
|
|
|
{{ FOO | bool }}
|
|
{{ 'web-app-lam' in group_names }}
|
|
{{ (A == B) }}
|
|
|
|
The detector keys on the literal ``'true'`` / ``'false'`` payload
|
|
inside ``ternary(…)``. Real string conversions that happen to use
|
|
``ternary`` use other labels (``'yes'`` / ``'no'``, ``'1'`` / ``'0'``,
|
|
…) and are NOT matched. ``... | bool | string | lower`` is also NOT
|
|
matched — it lower-cases the output to the literal token
|
|
``true`` / ``false``, which IS sometimes required by Rails / Elixir
|
|
consumers.
|
|
|
|
2. ``redundant-bool-comparison`` — ``... | bool == True/False`` and the
|
|
lower-case / quoted variants (``== true``, ``== "false"``, …).
|
|
Comparing a boolean against a boolean literal is a tautology that
|
|
hides intent. Convention::
|
|
|
|
when: FOO | bool # truthy branch
|
|
when: not (FOO | bool) # falsy branch
|
|
|
|
Per-line opt-out::
|
|
|
|
# nocheck: redundant-bool-ternary
|
|
{# nocheck: redundant-bool-comparison #}
|
|
|
|
The marker grammar is documented at
|
|
``docs/contributing/actions/testing/suppression.md``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from utils.annotations.suppress import suppressed_line_numbers
|
|
from utils.cache.files import PROJECT_ROOT, iter_project_files, read_text
|
|
|
|
# `... | ternary('true','false')` — anywhere. The literal `'true'` /
|
|
# `'false'` payload is the signal that the upstream value is already a
|
|
# boolean (or boolean-equivalent) and the ternary is converting it back
|
|
# into the same truth value as a string.
|
|
_PATTERN_TERNARY: re.Pattern[str] = re.compile(
|
|
r"""\|\s*ternary\(\s*['"]true['"]\s*,\s*['"]false['"]\s*\)""",
|
|
)
|
|
|
|
# `| bool == ` followed by a bool literal (case-insensitive, optional quotes).
|
|
_PATTERN_COMPARISON: re.Pattern[str] = re.compile(
|
|
r"""\|\s*bool\s*==\s*(?:['"]?(?:true|false)['"]?)""",
|
|
re.IGNORECASE,
|
|
)
|
|
|
|
_SCAN_EXTENSIONS: tuple[str, ...] = (".j2", ".yml", ".yaml")
|
|
|
|
|
|
def _scan_paths() -> list[Path]:
|
|
"""Iterate every production template / YAML file via the shared
|
|
file-walk cache (tests dir excluded)."""
|
|
out: list[Path] = []
|
|
for s in iter_project_files(extensions=_SCAN_EXTENSIONS, exclude_tests=True):
|
|
p = Path(s)
|
|
try:
|
|
p.relative_to(PROJECT_ROOT)
|
|
except ValueError:
|
|
continue
|
|
out.append(p)
|
|
return sorted(out)
|
|
|
|
|
|
def _file_offenders(path: Path, pattern: re.Pattern[str], rule: str) -> list[str]:
|
|
"""Return ``[]`` if the file is clean, or a list of human-readable
|
|
``line N: <snippet>`` strings for each match of ``pattern`` on a
|
|
line not annotated with ``noqa: <rule>``.
|
|
"""
|
|
try:
|
|
src = read_text(str(path))
|
|
except (OSError, UnicodeDecodeError):
|
|
return []
|
|
|
|
lines = src.splitlines()
|
|
noqa_lines = suppressed_line_numbers(lines, rule)
|
|
|
|
offenders: list[str] = []
|
|
for idx, line in enumerate(lines, start=1):
|
|
if idx in noqa_lines:
|
|
continue
|
|
if pattern.search(line) is None:
|
|
continue
|
|
offenders.append(f"line {idx}: {line.strip()}")
|
|
return offenders
|
|
|
|
|
|
def _collect(pattern: re.Pattern[str], rule: str) -> dict[Path, list[str]]:
|
|
out: dict[Path, list[str]] = {}
|
|
for path in _scan_paths():
|
|
issues = _file_offenders(path, pattern, rule)
|
|
if issues:
|
|
out[path] = issues
|
|
return out
|
|
|
|
|
|
def _format_failure(
|
|
headline: str,
|
|
offenders: dict[Path, list[str]],
|
|
fix: str,
|
|
) -> str:
|
|
rel = lambda p: p.relative_to(PROJECT_ROOT) # noqa: E731
|
|
lines = [headline]
|
|
for path, issues in sorted(offenders.items()):
|
|
lines.append(f" - {rel(path)}:")
|
|
lines.extend(f" * {issue}" for issue in issues)
|
|
lines.append("")
|
|
lines.append(fix)
|
|
return "\n".join(lines)
|
|
|
|
|
|
class TestNoRedundantBoolPatterns(unittest.TestCase):
|
|
"""Reject ``| bool | ternary(…)`` and ``| bool == <bool-literal>``."""
|
|
|
|
def test_no_redundant_bool_ternary(self) -> None:
|
|
offenders = _collect(_PATTERN_TERNARY, "redundant-bool-ternary")
|
|
if not offenders:
|
|
return
|
|
self.fail(
|
|
_format_failure(
|
|
f"{len(offenders)} file(s) end a boolean expression with "
|
|
f"`| ternary('true','false')`. That ternary is a no-op on "
|
|
f"a value that is already a boolean — its only effect is "
|
|
f"to swap `True`/`False` for the lowercase string tokens "
|
|
f"`'true'`/`'false'`, which every consumer the project "
|
|
f"actually feeds them to accepts unchanged:",
|
|
offenders,
|
|
"Fix: drop the `| ternary('true','false')` segment. The "
|
|
"preceding expression already evaluates to the truth value "
|
|
"you want — `FOO | bool`, `'web-app-X' in group_names`, "
|
|
"`(A == B)`, `FOO is defined`, etc. If the consumer "
|
|
"truly requires the literal lowercase tokens 'true'/'false', "
|
|
"use `... | bool | string | lower` and annotate the line "
|
|
"with `noqa: redundant-bool-ternary` (with a one-line "
|
|
"comment explaining why).",
|
|
)
|
|
)
|
|
|
|
def test_no_redundant_bool_comparison(self) -> None:
|
|
offenders = _collect(_PATTERN_COMPARISON, "redundant-bool-comparison")
|
|
if not offenders:
|
|
return
|
|
self.fail(
|
|
_format_failure(
|
|
f"{len(offenders)} file(s) compare `... | bool` against a "
|
|
f"bool literal. The comparison is a no-op — `... | bool` "
|
|
f"already produces the boolean you are testing for:",
|
|
offenders,
|
|
"Fix: replace `FOO | bool == True` with `FOO | bool` and "
|
|
"`FOO | bool == False` with `not (FOO | bool)`. If a real "
|
|
"tri-state guard is intended, annotate the line with "
|
|
"`noqa: redundant-bool-comparison` and document why.",
|
|
)
|
|
)
|
|
|
|
|
|
if __name__ == "__main__": # pragma: no cover
|
|
unittest.main()
|