Files
computer-playbook/utils/install/lint/packages.py
kevinveenbirkenbachandClaude Opus 4.8 8a586ad373 feat(lint): validate generated distro changelogs + harden the mirror generator
Adds a lint-packages target/workflow that parses the generated packaging metadata with each ecosystem's native parser (dpkg-parsechangelog, rpmspec, bash -n for the PKGBUILD), failing on any parser stderr so warnings cannot slip through. Wired into the make lint fan-out and ci-orchestrator's lint gate; an install group provisions the parsers (best-effort, cross-distro).

Hardens package_mirror so the generated changelogs parse without warnings: every RPM body line gets a '- ' continuation prefix (a column-0 '*' was read as a new %changelog entry -> 'bad date'), and the archived-releases notice is folded into the oldest kept entry instead of trailing after the final entry (free text there made dpkg-parsechangelog warn). Regenerates packaging/debian/changelog and the Fedora spec accordingly.

Adds a test-lint check (test_package_changelog_completeness) asserting both package changelogs list every CHANGELOG.md + docs/changelog release and that their inline window matches CHANGELOG.md, plus unit regressions for the RPM continuation prefix and the Debian footer fold.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 21:36:30 +02:00

50 lines
1.9 KiB
Python

"""Best-effort install of the distro-packaging lint validators.
``scripts/lint/packages.sh`` validates the generated packaging metadata
with each ecosystem's native parser. This installer provisions those
parsers wherever the platform's package manager can: on Debian (apt) the
lint container gets ``dpkg-parsechangelog`` (``dpkg-dev``), ``rpmspec``
(``rpm``) and ``rpmlint``; on Arch (pacman) a dev host gets ``namcap``.
Every install is best-effort and per-command: cross-distro tools are
often unavailable, and ``scripts/lint/packages.sh`` skips absent ones, so
a failed candidate MUST NOT break the lint bootstrap. The validators are
intentionally kept out of the install stamp's required-tool set so a
non-Debian environment never loops trying to install a tool it cannot.
This group is installed explicitly by the ``lint-packages`` make target
(an explicit group bypasses the stamp), so the commands are provisioned
every run rather than only on the first all-mode install.
"""
from __future__ import annotations
import contextlib
from utils.install.primitives import which
from utils.install.system_pkg import (
detect_package_manager,
install_package_candidates,
)
_VALIDATOR_PACKAGES: dict[str, dict[str, list[str]]] = {
"dpkg-parsechangelog": {"apt-get": ["dpkg-dev"], "pacman": ["dpkg"]},
"rpmspec": {"apt-get": ["rpm"], "dnf": ["rpm-build"], "yum": ["rpm-build"]},
"rpmlint": {"apt-get": ["rpmlint"], "dnf": ["rpmlint"], "pacman": ["rpmlint"]},
"namcap": {"pacman": ["namcap"]},
}
def ensure() -> None:
manager: str | None = None
with contextlib.suppress(Exception):
manager = detect_package_manager()
if manager is None:
return
for command, by_manager in _VALIDATOR_PACKAGES.items():
candidates = by_manager.get(manager)
if not candidates or which(command):
continue
with contextlib.suppress(Exception):
install_package_candidates(manager, candidates)