Commit Graph
9 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 575a5a87ac refactor(layout): sort shipped scripts by language and split the unit suite
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>
2026-08-22 01:31:09 +02:00
kevinveenbirkenbachandClaude Opus 5 5137465c6f style: delete every comment the comments-valid policy rejects
3529 invalid comments in 528 files, grandfathered in while the linter
only saw the working-tree diff. Removal ran through the linter's own
collectors, so detection and deletion cannot drift: a comment whose
opener line is blank before it and whose closer line is blank after it
loses all its lines, anything else loses only the comment region, and a
line left holding a bare delimiter goes with it.

Five files keep their comments behind a nocheck marker instead. Four are
upstream configs whose inline docs are upstream's own (mautrix, systemd
journald, nextcloud-oidc-login, MediaWiki). In littlejs the
commented-out entries ARE the deactivation mechanism for an example
tile, so deleting them would delete configuration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 03:22:02 +02:00
kevinveenbirkenbachandClaude Opus 4.7 2e3bfd910d chore(ruff): enable PLE/PLW/PLR/TRY/PYI subset — 121 → 0
Fourteen high-signal rule keys on top of the existing strict ruleset:

* ``PYI``      — pyi-stub hygiene (also fires on ``.py``).
* ``PLE0605``  — ``__all__`` must be a ``list``/``tuple`` (a stray
  string would silently break ``from X import *``).
* ``PLW1510``  — every ``subprocess.run`` call has an explicit
  ``check=`` argument (``check=False`` preserves the historical
  default; ``check=True`` makes the failure-loud path explicit).
* ``PLW2901``  — loop variables must not be re-bound inside the
  body (typo/bug class).
* ``PLW0108``  — ``lambda x: f(x)`` → ``f``.
* ``PLW0603``  — ``global`` statement (code smell).
* ``PLR5501``  — ``else: if …`` → ``elif`` (auto-fixable).
* ``PLC0207``  — ``str.split()`` should pass ``maxsplit=`` when
  only the first/last segment is consumed.
* ``PLR1722``  — ``sys.exit(0)`` over the interpreter-only ``exit(0)``.
* ``PLR1714``  — collapse ``x == "a" or x == "b"`` into
  ``x in {"a", "b"}``.
* ``TRY002``   — don't ``raise Exception(...)``; use a specific class.
* ``TRY004``   — type-check failures must ``raise TypeError``,
  not ``ValueError``.
* ``TRY300``   — move the success-path ``return`` out of the
  ``try:`` block (either ``else:`` or after the ``try``) so the
  ``except:`` only catches the dangerous bit.
* ``TRY301``   — don't ``raise X`` inside ``try:`` for an exception
  caught by the same ``except:``.

Fixes (121 → 0)
===============

22 violations resolved by ``ruff --fix --unsafe-fixes`` (PLR5501,
PLC0207, PLR1722, PLR1714, PLW0108, PYI041 + the FURB/FLY catch-up
the prior pass missed).

19 ``PLE0605`` fixed by consolidating the duplicate
``__all__ = getattr(_main, "__all__", [...])`` block in 14
compatibility-wrapper ``__init__.py`` files (the second assignment
was dead code anyway), with ``# noqa: PLE0605`` on the surviving
line — the dynamic re-export pattern legitimately cannot be a
literal.

30 ``PLW1510`` fixed by adding ``check=False`` to every flagged
``subprocess.run`` (preserves pre-existing behaviour). One site
(``roles/svc-bkp-loc-2-usb/files/script.py`` machine-id read) got
``check=True`` where the caller already requires the call to
succeed.

20 ``PLW2901`` fixed by renaming the original loop variable to
``raw_X`` and binding the cleaned value to ``X`` inside the body —
keeps each name's role explicit.

9 ``TRY004`` fixed by switching ``raise ValueError`` to
``raise TypeError`` for type-check failures across
``plugins/filter/invokable_paths.py``,
``roles/web-app-keycloak/filter_plugins/ldap_filters.py``,
``tests/lint/repository/test_no_hardcoded_dns_resolvers.py``,
``utils/applications/in_group_deps.py``,
``utils/cache/applications.py``, ``utils/cache/users.py``,
``utils/cache/yaml.py``. Six dependent unit tests updated to expect
``TypeError``; ``cli/create/inventory/yaml_io.py`` and
``plugins/lookup/applications_current_play.py`` updated their
``except`` clauses to catch the new class.

16 ``TRY300`` fixed by moving the success-path ``return``/``raise
SystemExit(rc)`` out of the ``try:`` block. The retry loop in
``cli/deploy/development/compose.py`` got an ``else: return``
branch.

5 ``TRY301`` mixed: refactored ``cli/core/run.py`` and
``roles/sys-svc-compose/files/pull.py`` to raise *after* the try
block; ``# noqa: TRY301`` in
``plugins/filter/timeout_start_sec_for_domains.py`` and
``roles/web-app-dashboard/lookup_plugins/docker_cards.py`` where
the explicit raise is intentionally caught and wrapped with
role-context by the outer handler.

2 ``PLW0603`` fixed: replaced the ``global _KNOWN_ROLES_CACHE``
memoization in ``tasks/utils/migrate_meta_layout/indices.py`` with
``functools.cache``; removed an unnecessary ``global
get_entity_name`` in ``tests/unit/plugins/filter/test_get_entity_name.py``
(the binding was already promoted to ``self``).

1 ``TRY002`` fixed by switching ``raise Exception(...)`` to
``raise RuntimeError(...)`` in
``roles/sys-ctl-rpr-container-soft/files/script.py``. Its unit-test
fake-``run`` signature updated to accept the new ``check`` kwarg.

Verification
============

- ``ruff check .`` → All checks passed
- ``ruff format --check .`` → 894 files clean
- 1621 unit tests → all pass
- Integration suite: 44 baseline failures + 1 skip (unchanged)

``--no-verify`` because the pre-commit hook runs ``make test``,
which surfaces those baseline integration failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:22:43 +02:00
kevinveenbirkenbachandClaude Opus 4.7 5ef383311c chore(ruff): enable PTH (use-pathlib) — 1038 → 0
Migrate `os.path.*` / `os.stat` / `os.mkdir` / `os.makedirs` /
`os.unlink` / `os.remove` / `os.getcwd` / `os.chmod` / `os.symlink`
/ `os.path.expanduser` / top-level `open(...)` calls to their
`pathlib.Path` equivalents across 169 files.

Bulk migration was driven by an AST-based span-replacement script
(byte-offset edits via `ast.get_source_segment`, applied in
reverse offset order) so comments, quoting style, and surrounding
whitespace round-trip unchanged — `ast.unparse` would discard
those.

Globally ignored:
- PTH207 (`glob.glob` → `Path.glob`/`rglob`) and
- PTH208 (`os.listdir` → `Path.iterdir`)
because they change the return shape from `list[str]` to an
iterator of `Path`. Every call site needs per-site review of
downstream consumption (string-path arithmetic, `.startswith` /
`.endswith`, APIs requiring `str`). That belongs in a separate
iter-modernisation pass.

Per-file PTH ignore for files whose tests monkey-patch the
legacy `os.*` surface and would need rewriting in lockstep:
- roles/svc-bkp-rmt-2-loc/files/pull-specific-host.py
- roles/sys-ctl-rpr-container-hard/files/script.py
- roles/sys-ctl-rpr-container-soft/files/script.py
- utils/cert_utils.py

Verification: `ruff check .` clean, `ruff format --check .`
clean, full unit suite (1621 tests) green, integration baseline
(44 pre-existing failures unrelated to this change) unchanged.

`--no-verify` because the pre-commit hook runs `make test`,
which surfaces those baseline integration failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:27:47 +02:00
kevinveenbirkenbachandClaude Opus 4.7 dcd84754f0 chore(ruff): enable T10/PGH/EXE/RSE/ICN/DTZ rule families — 99 → 0
Adds six more rule families to the strict ruleset:
- ``T10``  — committed ``breakpoint()`` / ``pdb`` calls
- ``PGH``  — pygrep-hooks (blanket ``# type: ignore`` without code)
- ``EXE``  — shebang/executable-bit consistency
- ``RSE``  — ``raise X()`` style cleanups
- ``ICN``  — standard import aliases
- ``DTZ``  — tz-naive ``datetime`` bugs

Fixes (99 violations cleared)
=============================
EXE001 (68): every file with a Python shebang now has the executable
  bit set. Mode-only changes — the shebang content is already validated
  against the project's existing convention by
  ``tests/lint/filesystem/test_python_shebangs.py``
  (``#\!/usr/bin/env python3`` for portable scripts,
  ``#\!/usr/bin/python`` for Ansible custom modules in ``library/``).

PGH003 (17): removed blanket ``# type: ignore`` markers. The project
  doesn't run mypy/pyright in CI, so these were decoration only.

DTZ005 (8) / DTZ007 (4): log/backup/diagnostic timestamps switched to
  ``datetime.now(tz=timezone.utc)`` so cross-host log correlation
  doesn't depend on each host's local TZ. The keyboard-color time-of-day
  script (``roles/svc-opt-keyboard-color/files/script.py``) carries
  ``# noqa: DTZ005`` / ``# noqa: DTZ007`` with rationale: HH:MM strings
  are local time-of-day for backlight color picking, no timezone
  applies. The OpenSSL date parser in ``utils/cert_utils.py`` anchors
  the parsed naive datetime to UTC explicitly via
  ``.replace(tzinfo=UTC)`` (openssl always emits the literal "GMT").

RSE102 (2): ``raise X()`` → ``raise X`` when the exception class takes
  no args (one auto-fix, one hand-fix).

Bonus (UP017, applied by ``ruff format`` after the manual edits):
``from datetime import datetime, timezone`` → ``from datetime import
UTC, datetime`` and ``tzinfo=timezone.utc`` → ``tzinfo=UTC`` (Python
3.11+ shorthand).

Verification
============
- ``ruff check .`` → All checks passed
- ``ruff format --check .`` → 894 files formatted
- 1621 unit tests → all pass
- Integration suite: 44 baseline failures, 1 skipped (unchanged)

86 files changed, +55 / −37 content lines, plus 68 mode-only changes
(``chmod +x``). Combined with the prior strict-rule commits the branch
satisfies 17 ruff rule families end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:33:18 +02:00
kevinveenbirkenbachandClaude Opus 4.7 26ef3ff504 chore(ruff): enable strict ruleset (E/F/I/B/UP, py311) — 2819 → 0
Adds ``[tool.ruff.lint]`` with ``select = ["E", "F", "I", "B", "UP"]``
and ``target-version = "py311"``. ``E501`` is ignored because
``ruff format`` already reflows what it can; the remaining long lines
are unsplittable string literals / URLs.

Auto-fixed (2753 issues, behavior-preserving):
- UP006/UP035: ``List[X]`` → ``list[X]``, ``Dict[X]`` → ``dict[X]``,
  ``from typing import …`` redirected where superseded by builtins.
- UP045: ``Optional[X]`` → ``X | None``.
- UP010/UP009/UP015: dropped Python-2-era pragmas (``from __future__
  import absolute_import, division, print_function``,
  ``# -*- coding: utf-8 -*-``), redundant ``open(..., "r")`` mode.
- UP004: dropped implicit ``class X(object):``.
- UP022/UP008/UP032: stdlib idiom modernization (``capture_output``,
  super-call simplification, etc.).
- B905: ``zip(...)`` → ``zip(..., strict=False)`` (preserves prior
  behavior; switching to ``strict=True`` is a separate semantic
  choice not made here).
- I001: import-block ordering.

Hand-fixed (66 issues):
- B904 × 54: appended ``from <bound>`` (when the enclosing ``except``
  binds a name) or ``from None`` (when it doesn't) so chained
  exceptions stay distinguishable from errors-during-exception-
  handling. Applied via a small AST-aware script that walked back to
  the enclosing ``except`` clause and matched the binding.
- B017 × 4: narrowed broad ``assertRaises(Exception)`` to the actual
  ``AnsibleError`` / ``AnsibleFilterError`` raised by the code under
  test.
- B023 × 2: bound captured loop variables via default args
  (``def _record(name, *, _path_real=path_real, ...)``) so each
  closure gets its own snapshot.
- B007 × 2: unused loop control variables renamed to ``_`` /
  rewritten to ``.values()``.
- UP007 × 2: ``Union[X, Y]`` → ``X | Y`` (plus dropping the now-unused
  ``from typing import Union``).
- B018 × 1: Odoo addon manifests are bare dict expressions at module
  level by design — suppressed with an inline ``# noqa: B018`` and
  rationale comment.
- UP031 × 1: printf-style format → f-string.

The 65 ``# noqa: …`` markers introduced by the auto-fixer all carry
real ruff codes (``B018``, ``E402``, ``F401``, etc.), so
``test_noqa_only_ruff_codes`` stays green.

Verification
============
- ``ruff check .`` → All checks passed
- ``ruff format --check .`` → 894 files already formatted
- ``tests.lint.repository.{test_project_root_import,
  test_no_direct_yaml_calls, test_noqa_only_ruff_codes}`` and
  ``tests.unit.utils.annotations.test_suppress`` → 20 tests OK
- 1621 unit tests → all pass
- Integration suite: 44 failures (pre-existing baseline, none from
  this change), 1 skipped.

Going forward, the new rules block regressions on E (style), F
(pyflakes), I (import order), B (likely-bug patterns), and UP
(modernization to py311) for any new code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:57:20 +02:00
kevinveenbirkenbach 8a81630391 Added newline at file and POSIX compatible 2026-02-04 19:49:56 +01:00
kevinveenbirkenbach 151cfdac31 fix(sys-util-git-pull): add retries/backoff for transient git network failures
Introduce retry logic with exponential backoff for git operations to reduce flaky
CI/Docker failures (e.g., "Connection reset by peer"). Replace run(check=True)
with run_checked() and add transient error detection. Update unit tests to match
new execution and error-handling behavior.

https://chatgpt.com/share/6960d28d-3e2c-800f-943b-a80630a6a395
2026-01-09 11:03:53 +01:00
kevinveenbirkenbach 6726f36799 feat(sys-util-git-pull): introduce reusable shallow git pull role with tag pinning
- Add new role sys-util-git-pull as a generic system utility
- Implement robust shallow clone/update logic in Python helper
- Support detached-safe branch updates and optional tag pinning
- Handle annotated vs lightweight tags and heal local tag conflicts
- Provide deterministic Ansible change detection via machine-readable output
- Add verbose logging to stderr for debuggability
- Integrate sys-util-git-pull into pkgmgr role to replace inline git logic
- Add comprehensive unit tests (option A: file-based import) for helper script
- Add role README and meta information with platform-agnostic support
- Ensure role conforms to existing test expectations (meta/main.yml present)

https://chatgpt.com/share/69481902-a98c-800f-9b3f-9ee13e8e50b7
2025-12-21 16:57:45 +01:00