Every script a role ships now lives under `files/<language>/`, and the unit
suite is split the same way: `tests/unit/python/` beside new sibling trees for
the other languages. `tests/lint/filesystem/test_role_files_layout.py` keeps it
that way; directories whose shape is dictated from outside carry a `.nocheck`
naming the constraint (Docker build contexts, WordPress mu-plugins, the Joomla
extension, Odoo addons, Playwright suites, the `files/test/test.sh` harness
that `test-e2e-cli` resolves generically).
This cannot be applied in halves. Moving a file and repointing what names it
are the same change: 14 references resolve only at deploy time, so the tree is
green either before or after, never between.
Those 14 were found by static resolution over every reference class, not by the
test suite -- a full `make quality-high` was green while all of them were
broken:
* 7 `src:` values built from a role variable holding a bare filename
(svc-bkp-remote-2-local, decidim, espocrm, friendica, joomla, pixelfed x2).
The variable doubles as the destination name inside the container, so the
language segment belongs at the call site, not in the variable.
* `sys-service/tasks/04_base.yml` resolved every consumer's systemd script
through `first_found(..., errors='strict')` over `files/script.{sh,py}`.
18 roles ship exactly that and have no template fallback: the play would
have died on each.
* `sys-front-inj-javascript` fell back to `<role>/files/javascript.js`; the
interface now reads `files/javascript/javascript.js`.
* `sys-front-inj-logout/templates/head_sub.j2` built the cache-busting mtime
path through `path_absolute(... 'files', ...)`.
* 4 `ansible.builtin.script:` invocations naming a bare command
(pull.py, prune.sh, set_postgres_superuser_password.sh, ganesha-serve.sh).
* `dev-python/files/shell/install.sh` pointed at a sibling role's script.
Depth-dependent paths inside the moved trees were bumped accordingly: 59
`parents[N]` computations, 12 relative climbs, and the per-role recover CLI,
whose path `cli/administration/recover/recoverers.py` still assembled from the
pre-move location.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
format_debian_entry indented only the first body line per entry, leaving the rest at column 0. dpkg's changelog parser treats un-indented change lines as badly formatted, loses the entry boundary and reports 'cannot parse maintainer email address ""', failing the .deb build in build-ci-images. Every non-empty body line is now indented by two spaces.
trim_and_archive additionally dropped the '# Changelog' H1 preamble on every rewrite (it rebuilt content from the version entries only), which would break the MD041 markdown lint on the next trim. The preamble before the first version header is now preserved.
Regenerated packaging/debian/changelog and packaging/fedora/infinito-nexus.spec from CHANGELOG.md (untrimmed). Also adds the missing YAML document-start to web-app-pihole/tasks/main.yml.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Trim CHANGELOG.md to the active window and move older entries (4.0.3 through 8.0.4) into docs/changelog/; regenerate the Debian and Fedora package changelogs from the kept entries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Break cli/contributing/changelog/archive/__main__.py into four
single-responsibility modules under the same package:
* versioning.py — pad_version / unpad_version / archive_filename
(the canonical <padded-semver>-<date>.md filename schema).
* archive_dir.py — existing_archives, archived_releases_from_directory,
build_index_section (filesystem operations on docs/changelog/).
* changelog_md.py — split_into_entries, md_body_after_header,
trim_and_archive (CHANGELOG.md parsing and trimming, plus the
always-rebuild-index logic).
* package_mirror.py — mirror_to_debian_changelog,
mirror_to_rpm_spec_changelog (Debian and RPM regeneration from
CHANGELOG.md kept entries, with the trailing notice that points at
docs.infinito.nexus and lists archived versions as plain text).
__main__.py shrinks to argparse plus a thin orchestration layer that
parses CHANGELOG.md once, runs trim_and_archive, reads the populated
archive directory, and mirrors into the package files.
Add `mirror_to_debian_changelog` and `mirror_to_rpm_spec_changelog`
content-idempotency: skip the write when the regenerated content
already matches what is on disk so re-runs leave file mtimes
unchanged. Verified end-to-end: a follow-up run on a fully-processed
tree leaves all three files (CHANGELOG.md, Debian changelog, RPM
spec) byte-identical and mtime-stable.
Replace `datetime.strptime` calls with `date.fromisoformat` so the
DTZ007 lint stays clean: we only need calendar-date parsing for
display formatting, not timezone-aware moments.
Tests follow the same split: one test_<module>.py per source module,
40 tests in total. Shared fixtures live in _helpers.py (underscore
prefix exempts it from the test-file-naming lint), and each test
class explicitly mixes TempRepoMixin with unittest.TestCase so the
test-files-have-tests AST lint sees the direct unittest base.
Update docs/contributing/actions/release.md so the procedure
description matches the current behaviour: package changelogs are
"mirrored" (not "trimmed") from CHANGELOG.md, and the idempotence
section calls out byte-idempotency on each of the three files.
Run the CLI once against the live tree to bring packaging/debian/
changelog and packaging/fedora/infinito-nexus.spec back into sync
with CHANGELOG.md and the docs/changelog/ archive directory.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>