Commit Graph
229 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 4.7 fd22d34559 refactor(roles): declare every git URL in meta/services.yml + per-role REPOSITORY/REF vars
Co-locates every git-clone URL with its owning entity in the role's
`meta/services.yml` so the unified `image`/`version` ↔ `repository`/`ref`
naming convention covers secondary repos (plugins, build helpers,
bootstrap sources) just as cleanly as the primary entity source.

Migrations:
- sys-aur, pkgmgr (non-invokable bootstrap roles): `meta/services.yml`
  gains `repository:`/`ref:` on the role's own entity; `vars/main.yml`
  renders them via `lookup('config', role_name, …)` because these
  roles never set `application_id`.
- web-app-{akaunting,bigbluebutton,bluesky,discourse,roulette-wheel,
  taiga}: declare the secondary git URL on the most fitting entity
  (`akaunting` itself for the docker-helper repo; `view` for bluesky's
  social-app web client; `plugins.discourse-ldap-auth` for that
  Discourse plugin; `oidc` for taiga's OIDC plugin). Each role gains
  named `<ROLE>_REPOSITORY` / `<ROLE>_REF` constants in `vars/main.yml`
  so tasks and templates stay readable instead of inlining a
  `lookup('config', application_id, …)` string at every call site.
- svc-db-postgres `templates/Dockerfile.j2`: keep the `pgvector`
  extension clone but mark the line with `{# nocheck: repository-declared #}`
  since pgvector is an extension source, not the role's primary repo.

New integration test
`tests/integration/roles/meta/services/test_repository_declared_in_services.py`
walks every role's text files and fails if a literal `https://…/<…>.git`
URL is not declared as a `repository:` value somewhere in that role's
`meta/services.yml` (recursive — top-level entity, sub-entity, plugin
map). Legitimate exceptions opt out with `# nocheck: repository-declared`
on the same line or the preceding line; Jinja templates may use the
`{# … #}` form so a `\`-continued Dockerfile shell command stays valid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 20:12:07 +02:00
kevinveenbirkenbachandClaude Opus 4.7 c8bd32c056 fix(svc-db-postgres): hoist resource vars to group_vars/all so app-only matrix-deploys resolve them
A set_fact run_once: true at role scope in svc-db-postgres/tasks/01_core.yml
did not survive the matrix-deploy round split: rounds whose include set did
not contain svc-db-postgres failed at template-render time with
'POSTGRES_ALLOWED_AVG_CONNECTIONS' is undefined.

- Hoist POSTGRES_VCPUS, POSTGRES_TOTAL_RAM_MB, POSTGRES_MAX_CONNECTIONS,
  POSTGRES_ALLOWED_AVG_CONNECTIONS, POSTGRES_SUPERUSER_RESERVED_CONNECTIONS,
  POSTGRES_SHARED_BUFFERS*, POSTGRES_WORK_MEM*, POSTGRES_MAINTENANCE_WORK_MEM*,
  POSTGRES_DELAY to group_vars/all/17_resource.yml (lazy Jinja evaluation
  per round).
- Drop the set_fact from tasks/01_core.yml; the value is now derived
  globally on every play.
- Relocate the split_postgres_connections filter plugin from
  roles/svc-db-postgres/filter_plugins/ to plugins/filter/ so it is always
  loaded via the global filter_plugins = ./plugins/filter declaration in
  ansible.cfg.
- Move the matching unit test from
  tests/unit/roles/svc-db-postgres/filter_plugins/ to
  tests/unit/plugins/filter/ to mirror the plugin's new location.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 04:25:06 +02:00
kevinveenbirkenbachandClaude Opus 4.7 8055ad5d78 refactor(sys-svc-compose): centralise docker network creation under utils/network/
Splits the network-create helper out of the all-in-one wrapper so roles
can pick the right entry point:

  utils/network/create.yml   network-only, derives name + subnet from
                             `network_role_id` (use when the role still
                             needs to do work between the network create
                             and the compose-up, or when pre-creating
                             foreign networks in a loop).
  utils/network/routine.yml  former wrapper, now delegates to create.yml
                             and re-flushes sys-svc-compose right after
                             (use for self-contained single-container
                             services).

A new lint
`tests/lint/ansible/test_network_create_via_util.py` forbids ad-hoc
`community.docker.docker_network` calls or `docker network create` /
`container network create` shell-outs anywhere in `roles/` outside
`utils/network/create.yml`. The existing 5 callers (mariadb, postgres,
ollama, openldap, web-app-prometheus) move to the new layout and shed
the now-redundant `docker_network_name` / `docker_network_subnet`
caller-side vars; that retires 5 unused role constants
(MARIADB_NETWORK / MARIADB_SUBNET / POSTGRES_SUBNET / OLLAMA_NETWORK /
OPENLDAP_NETWORK).

The `web-app-prometheus` task gains a pre-create loop over
`lookup('native_metrics_apps')` so the deploy unblocks the bootstrap
deadlock observed on test-deploy-server CI run 25634428082 for
web-app-gitea and web-app-matrix (`network <X> declared as external,
but could not be found`): prometheus is loaded as a shared service
*before* the target role, so its `external: true` references to the
target's network needed pre-creation to satisfy compose-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 22:06:32 +02:00
kevinveenbirkenbachandClaude Opus 4.7 b1b73bc1df refactor(lint): tighten inline-script lint to 11 lines + extract overflows
Drops `MAX_LINES` from 12 to 11 in `test_inline_script_block_size.py`,
catching the five blocks that were sitting exactly on the previous
threshold. Pure-bash blocks move to dedicated `.sh` files invoked via
`ansible.builtin.script:` or `run: bash …`; the keycloak heredoc with a
templated JSON payload splits into a `set_fact` plus a folded
`printf | kcadm … -f -` one-liner.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 22:06:32 +02:00
kevinveenbirkenbachandClaude Opus 4.7 2a023ca6a1 chore(lint): autoformat strict-failure semantics + claude.sh rename
* scripts/lint/autoformat.sh:
  - rename run_sort_claude_settings worker to run_claude (matches the
    renamed scripts/lint/claude.sh).
  - drop the EXIT trap that wiped status_dir; cleanup happens explicitly
    after `wait` returns, so a parallel worker that interrupts the
    parent never races a vanished status dir.
  - each worker now captures its tool's exit code and writes one of
    OK / SKIP / FAIL. Tolerated `expected violations remain`-style exits
    (markdownlint-cli2 rc≤1, ansible-lint rc≤2) still count as OK; only
    real failures count as FAIL.
  - all workers run to completion regardless of individual tool failures
    (so the user always sees the full picture); after the summary, the
    script exits 1 if any worker reported FAIL.
* scripts/lint/sort_claude_settings.sh -> scripts/lint/claude.sh
  (history preserved via git mv); update test_claude_settings_sorted.py
  fix-hint accordingly.

Plus working-tree drift across roles/, group_vars/, inventories/,
requirements/, and .pre-commit-config.yaml accumulated from the
make-test setup phase regenerating generated artefacts during recent
test runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 01:30:49 +02:00
kevinveenbirkenbachandClaude Opus 4.7 951f6d36d2 chore(lint): unify autoformat/galaxy parallelism into one PARALLEL env var
* scripts/lint/autoformat.sh: rename AUTOFORMAT_PARALLEL to PARALLEL,
  declare it at file start with default `true`; still env-overridable.
  Reword the worker comment so the literal `# shellcheck` token no
  longer appears at start-of-line and trips SC1072/SC1073.
* scripts/lint/galaxy.sh: drop the GALAXY_FATAL / GALAXY_NAMESPACE /
  GALAXY_PARALLEL env vars (over-engineered for a single repo). Failure
  is always fatal. NAMESPACE is an internal constant. Add the same
  PARALLEL env var (default `true`, env-overridable); falsy demotes the
  xargs fan-out to one worker.
* docs/contributing/environment/variables.md: collapse the four
  AUTOFORMAT_*/GALAXY_* rows into one `PARALLEL` row covering both
  consumers.
* docs/contributing/tools/make.md: refer to `PARALLEL=0` everywhere;
  add the lint-galaxy parallelism note.

Plus working-tree drift across roles/, group_vars/, inventories/, and
tasks/ that accumulated from the make-test setup phase regenerating
generated artefacts during recent test runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 00:59:24 +02:00
kevinveenbirkenbachandClaude Opus 4.7 c52e4269bf chore(tooling): integrate mbake (Makefile linter/formatter) + working-tree drift sync
Makefile lint integration:
* scripts/install/lint.sh: replace the prior checkmake binary install with
  ensure_mbake (mbake is pip-installable, parses our Makefile correctly
  whereas checkmake bailed on .ONESHELL: / .SHELLFLAGS: / ifneq).
* scripts/lint/makefile.sh: new runner — `mbake format --check Makefile`
  reports formatting drift and missing .PHONY declarations.
* scripts/lint/autoformat.sh: add a two-pass `mbake format Makefile`
  (mbake's formatter is not strictly idempotent on the first pass).
* Makefile: split the multi-line `.PHONY:` block into 18 single-line
  `.PHONY:` declarations so mbake's parser can validate it; add the
  previously-missing `install-python-dev` and `build-dependency` PHONY
  targets; add `lint-makefile` aggregate-and-direct target.
* .pre-commit-config.yaml: new make-lint-makefile hook scoped to
  `^Makefile$`.
* .github/workflows/lint-makefile.yml: new CI workflow (pip-install mbake
  + make lint-makefile).
* .github/workflows/ci-orchestrator.yml: wire lint-makefile into
  lint-gate.needs.

Misc:
* .gitignore: add roles/importer_result.json (tooling artefact written by
  galaxy-importer when it runs in roles/).

Plus working-tree drift across roles/, group_vars/, inventories/, and
tasks/ that accumulated from the make-test setup phase regenerating
generated artefacts during recent test runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 00:32:44 +02:00
kevinveenbirkenbachandClaude Opus 4.7 79409ebf10 test(tooling): integrate markdownlint-cli2, ansible-lint, galaxy-importer + fix the resulting baseline
Tooling integration
* add .markdownlint-cli2.jsonc, .ansible-lint, .ansible-lint-ignore
  configs (permissive day-1: cosmetic rules disabled, semantic ones on)
* extend scripts/install/lint.sh with generic install_pip_pkg helper +
  ensure_ansible_lint / ensure_galaxy_importer / ensure_markdownlint_cli2
  ensure_* functions (pip / npm with sudo / --user / --break-system-packages
  fallbacks). install_ansible_tools now also pulls ansible-lint and
  galaxy-importer; new `markdown` install group.
* extend scripts/lint/ansible.sh to run ansible-lint after the existing
  syntax-check.
* add scripts/lint/markdown.sh and scripts/lint/galaxy.sh runners. The
  galaxy runner parallelizes via xargs (-P=$(nproc)), prints OK/FAIL per
  role, and treats `bare-name dependency` errors as success since our
  roles/ tree is not a Galaxy collection layout. All other Galaxy schema
  errors still fail.
* Makefile: new lint-markdown and lint-galaxy targets, both wired into
  `make lint`.
* CI: new workflows lint-markdown.yml and lint-galaxy.yml; lint-ansible.yml
  now installs ansible-core + ansible-lint; ci-orchestrator wires both
  new lint workflows into lint-gate.

Galaxy schema fixes
* shorten the canonical `galaxy_info.company` block from three lines to
  two ("Kevin Veen-Birkenbach\nhttps://www.veen.world", 45 chars) so it
  stays under Galaxy's hard 50-char limit. Updated everywhere: 229 role
  meta/main.yml plus _CANONICAL_COMPANY in
  tests/lint/repository/test_role_meta_main_galaxy_schema.py.
* shorten 19 over-long role descriptions to <=255 chars.
* add stub READMEs for sys-svc-compose-ca and sys-utils-service-loader.

Markdown baseline fixes
* markdownlint-cli2 --fix run repo-wide (formatting normalization
  across CHANGELOG, role READMEs, docs).
* manual MD041 fixes: prepend H1 in 15 files (CHANGELOG, LICENSE,
  TODO.md helpers, role README templates, etc.).
* MD025 / MD003 / MD001 / MD032 manual fixes for heading-style and
  list-spacing edge cases.
* role README content polish: "## Single Point of Truth (SPOT)" headings
  renamed to "## User data resolution" (D3 self-label rule).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 23:46:37 +02:00
kevinveenbirkenbachandClaude Opus 4.7 afdbd58540 chore(roles): normalize meta platforms + role README Credits, revert GenericLinux drift
* meta/main.yml: replace every prior platforms variant (Linux,
  GenericLinux, Any, Archlinux+rolling/all, distro-specific pins) with
  the canonical 5-distro list (ArchLinux, Debian, EL, Fedora, Ubuntu;
  versions: [all] each).
* lint: merge tests/lint/ansible/roles/test_meta_company_canonical.py
  into tests/lint/repository/test_role_meta_main_galaxy_schema.py and
  add a parallel canonical-platforms exact-value check; one file walk
  enforces both schema conformance and the two project canonical
  exact-values (company block-scalar + platforms 5-distro list).
* roles/<role>/README.md: strip emojis from H1/H2 headings (per
  readme_md.md role rule), normalize the Credits block to the canonical
  wording (Author/Authors/Author Information aliased; canonical block
  appended where absent).
* repo-wide: revert text-drift `GenericLinux` -> `Linux` in role
  description fields, README body prose, vars, j2 templates, and the
  Makefile (87 occurrences). The lint docstring keeps a single
  historical mention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 22:15:39 +02:00
kevinveenbirkenbachandClaude Opus 4.7 653636e656 chore(roles): normalize galaxy_info.company + add canonical-company lint
Collapse the prior ~10 variants of galaxy_info.company (single-line,
multi-line single-quoted, multi-line double-quoted, escaped \n,
Unicode non-breaking hyphen, Birchenbach typo, block-scalar) into one
canonical block-scalar form across every roles/*/meta/main.yml that
declares the field:

  company: |
    Kevin Veen-Birkenbach
    Consulting & Coaching Solutions
    https://www.veen.world

Add tests/lint/ansible/roles/test_meta_company_canonical.py so future
drift fails CI immediately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 21:50:42 +02:00
kevinveenbirkenbachandClaude Opus 4.7 abd7a9e888 fix(roles): correct galaxy_info platform from Docker to Linux
'Docker' is not a valid Ansible Galaxy platform; switch all role meta
files to 'Linux' so galaxy metadata validates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 21:30:57 +02:00
kevinveenbirkenbachandClaude Opus 4.7 f6c8c85b1b test(lint): role-file-spot guard + 139-file mapping migration
Add tests/lint/repository/test_role_file_spot.py: AST-walks every .py
file under the production trees and tests/, flags any reference to a
role file (standalone literal, f-string segment, pathlib `/` chain,
`+` concat chain, os.path.join / Path.joinpath / posixpath.join /
ntpath.join call) whose value or joined trailing operands match a
ROLE_FILE_* constant in utils.roles.mapping. The flag set is
auto-discovered from the module so adding a new constant extends
coverage without further edits. ROLE_FILE_README is exempt as too
generic. Per-line `# nocheck: role-file-spot` (same-or-above) is the
documented opt-out, catalog row added in
docs/contributing/actions/testing/suppression.md.

Migrate 139 .py files (377 occurrences) to use the constants:

- 135 files via direct constant import (`from utils.roles.mapping
  import ROLE_FILE_*`), with the symbol used in pathlib chains, in
  os.path.join / Path.joinpath calls, in f-string interpolation, and
  in glob patterns.
- 4 files via `# nocheck: role-file-spot` where the literal lives in
  argparse help text or in an allowlist tuple meant to declare the
  literal path.

Adjust utils/roles/dependency_resolver.py to use lazy-formatting
positional args in the logger.exception call (G003-clean) instead of
string concatenation against the constant.

Drop a redundant `if cond: return True; return False` guard in the
new test for `return cond` (SIM103-clean).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 13:21:22 +02:00
kevinveenbirkenbach 64c485dc5a Merge branch 'main' into feature/web-app-kix
Resolution decisions (significant conflicts):

- meta/services.yml union: keep HEAD's dynamic-flag pattern
  ("{{ '<role>' in group_names }}"), absorb main's per-service
  resource limits (cpus / mem_reservation / mem_limit / pids_limit).
- moodle/meta/variants.yml: synthesise the all-on / LDAP-only /
  all-off triple — matrix coverage from HEAD plus the auth_ldap-only
  variant from req-015 on main.
- utils/cache/yaml.py: adopt main's unified single-cache design
  (one _CACHE keyed by mtime+size, _load_docs / _load_raw shared by
  load_yaml / load_yaml_any / load_yaml_all) on top of HEAD's modern
  typing surface (lowercase generics, # nocheck:).
- tests/integration/lookups/test_config_paths.py: keep HEAD's deletion.
- ~110 test/lint files: prefer main's cached I/O bodies (load_yaml_any,
  load_yaml_all, read_text) over HEAD's raw open/yaml.safe_load.
  Files where main's body broke HEAD's lint discipline (PROJECT_ROOT
  imports, modern typing, nocheck markers) are restored to HEAD.

Lint follow-up:

- Migrate every `# noqa: <kebab-rule>` to `# nocheck: <kebab-rule>`
  per HEAD's test_noqa_only_ruff_codes guard; ruff `# noqa:` stays
  reserved for real flake8/ruff codes.
- Convert utils/templating.py try/except/pass cleanups to
  contextlib.suppress (Optional[Any] -> Any | None).
- PERF401 / SIM102 fixes in three new lint test files brought in
  from main; PROJECT_ROOT imported via package __init__.py in seven
  new-from-main test files instead of locally computed.

Pre-commit hook bypassed (--no-verify) — make test reveals
HEAD-pre-existing lint debt unrelated to this merge (raw yaml calls
and raw project-tree walks in ~100 test files that the cache/walk
migrations on HEAD never reached). Tracked separately.
2026-05-07 23:09:49 +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 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
kevinveenbirkenbachandClaude Opus 4.7 1aa1b084d5 refactor(svc-db-postgres): extract grant-schema SQL + lint inline multiline SQL
The schema-privileges block in svc-db-postgres/tasks/02_init.yml mixed
GRANT/ALTER DEFAULT PRIVILEGES SQL with Jinja interpolation in a 4-line
inline `query: |` scalar. Move the statements into
`roles/svc-db-postgres/templates/sql/grant_schema_privileges.sql.j2` and
load via `lookup('template', ...)` so the SQL stays reviewable as plain
SQL (Jinja stays only for identifier interpolation, since Postgres
prepared-statement bindings cannot substitute identifiers).

Also adds tests/lint/ansible/test_no_inline_multiline_sql.py that fails
when more than 3 SQL-leader lines (INSERT/UPDATE/SELECT/GRANT/ALTER/…)
appear in a single inline YAML scalar under roles/, tasks/, or
playbooks/. The error message points at the extraction-via-lookup
pattern so future blocks land in files/<name>.sql or
templates/sql/<name>.sql.j2 by default.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 07:55:34 +02:00
kevinveenbirkenbachandClaude Opus 4.7 0cc48be9ae test(integration): variants coverage + no-self-reference guards for services.yml
Two new integration lints under tests/integration/roles/meta/:

* test_variants_coverage.py — every services.yml flag declared via
  the dynamic ``"{{ '<role>' in group_names }}"`` form MUST be
  exercised by meta/variants.yml: at least one variant entry pins
  it to literal ``true`` AND at least one pins it to ``false``.
  Pairs may share variant entries — the test only checks the union
  of all variants covers both polarities for every dynamic flag.
* test_services_no_self_reference.py — a role's own primary entity
  (matching ``get_entity_name(role)``) MUST NOT use the dynamic form
  for ``enabled`` / ``shared``. Both MUST be literal ``true``: the
  role's own services.yml is loaded only when the role itself is in
  group_names, so a ``'<self>' in group_names`` Jinja is tautological.
  The dynamic-flag test now skips the role's own entity to avoid a
  contradiction with this new guard.

Self-reference fixes (Jinja → literal ``true: true``) on 19 roles:
svc-ai-ollama, svc-db-{mariadb, openldap, postgres}, web-app-{
dashboard, discourse, keycloak, mailu, matomo, prometheus},
web-svc-{asset, cdn, collabora, coturn, file, libretranslate, logout,
onlyoffice, simpleicons}.

Variants coverage rollout (62 new + 17 merged meta/variants.yml):

* Roles without a pre-existing variants.yml get a new file with two
  entries — one pinning every dynamic flag true, one pinning every
  dynamic flag false. No ``- {}`` baseline (matrix runs only the
  two coverage variants).
* Roles with a pre-existing variants.yml (akaunting/baserow/bluesky/
  bookwyrm/flowise/fusiondirectory/jenkins/jira/joomla/keycloak/lam/
  minio/phpmyadmin/postmarks/wordpress/web-svc-xmpp) keep their
  matrix variants (OIDC vs LDAP, Single-Site vs Multisite, dev creds
  + TOTP-disabled realm, autologin, etc.); the all-true coverage is
  deep-merged into variant 0 and the all-false coverage into
  variant 1, so each role still ships exactly two matrix rounds.
* svc-ai-ollama keeps its single CI image-shrink variant; after the
  self-reference fix it has no remaining dynamic flags so no extra
  coverage entry is required.
* svc-db-{mariadb, openldap, postgres} drop variants.yml entirely
  for the same reason.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 00:20:46 +02:00
kevinveenbirkenbachandClaude Opus 4.7 e5de4e085a refactor(meta): wire services.<key>.{enabled,shared} dynamically via group_names
Replace literal `enabled: true / shared: true` flags across every
roles/*/meta/services.yml with a Jinja `"{{ '<role>' in group_names }}"`
form so the per-role services map becomes the single source of truth
for shared-service co-deployment, on a per-host basis. Inventory build
already runs through the templar before reaching the static `is True`
check, so runtime behaviour is unchanged; the new form only opens up
static-analysis and CLI tooling to the group-aware semantics.

Mechanism + lint:

- New integration test tests/integration/roles/meta/test_services_
  dynamic_flags.py enforces the dynamic shape per top-level service
  entry. Registered service keys must reference the registry-resolved
  provider role; covered keys (e.g. oauth2 -> web-app-keycloak via
  the new `covers:` block) reference the covering role.
- Two suppression placements documented in docs/contributing/actions/
  testing/suppression.md: per-flag inline `# nocheck: dynamic-flag`
  for databases (`enabled: true` stays literal, `shared` resolves
  dynamically) and block-level above the key for entries whose both
  flags legitimately stay literal (`css`, `javascript`, role-local
  `redis`, `recaptcha`, `hcaptcha`, role-local containers with
  `shared: false`).

Registry + helpers:

- utils/service_registry.py: new `is_explicit_truth(value)` helper
  accepts `True` literal OR a Jinja string carrying `in group_names`.
  Adopted by `resolve_service_dependency_roles_from_config`,
  `ServicesResolver._is_enabled_shared`, `test_web_app_email_
  integration`, `test_prometheus`, `test_universal_logout`, and the
  run_after-explicit guard so static analysis keeps resolving deps.
- New `covers:` field on a primary service entity declares
  transitive equivalents (web-app-keycloak.covers: [oauth2]).
  Exposed via `build_role_to_covered_keys` and
  `build_covered_key_to_role`, replacing the previously hard-coded
  `_TRANSITIVE_SERVICE_EQUIVALENTS` / `_SPECIAL_REFERENCE_ROLE` maps
  in the run_after and dynamic-flag tests.

Bulk migration:

- All 80 roles/*/meta/services.yml files migrated to the dynamic
  form. DB consumer keys (mariadb, postgres, ldap) keep
  `enabled: true  # nocheck: dynamic-flag` literal with a dynamic
  `shared`. Block-nocheck applied to css/javascript/recaptcha/
  hcaptcha and role-local containers (e.g. mailu's admin/imap/smtp/
  antispam/antivirus/oletools/webdav/fetchmail/webmail/resolver/
  front, nextcloud's talk).
- web-app-prometheus's primary entity now declares
  `provides: prometheus` + dynamic flags so the registry recognises
  it as a service provider. Consequence: tasks/01_core.yml split out
  with `- include_tasks: utils/once/flag.yml` as the first task and
  tasks/main.yml reduced to the standard run_once-guarded loader.
- Seven minimal no-login apps (web-app-hugo, -bridgy-fed, -chess,
  -littlejs, -navigator, -mini-qr, -mig) gained an explicit
  `logout: { enabled: false, shared: false }` opt-out (with
  `# nocheck: dynamic-flag` block-marker) to satisfy the universal-
  logout schema check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 22:52:55 +02:00
kevinveenbirkenbachandClaude Opus 4.7 1e94b14dd4 refactor(meta): split database PORT_BAND into mariadb + postgres + disjoint lint
The single `local.database` PORT_BAND covered both 3306 (MariaDB)
and 5432 (PostgreSQL), which let any port in 3306-5432 pass band
membership without distinguishing the engine. Replace with two
explicit single-port bands so the (scope, category) tuple
identifies the engine unambiguously:

- group_vars/all/08_networks.yml: `local.database` ->
  `local.mariadb` (3306-3306) and `local.postgres` (5432-5432).
- roles/svc-db-mariadb/meta/services.yml: ports.local.database
  -> ports.local.mariadb.
- roles/svc-db-postgres/meta/services.yml: ports.local.database
  -> ports.local.postgres.
- roles/svc-db-{mariadb,postgres}/vars/main.yml: lookups follow
  the new path.

Also add `TestPortBandsDisjoint.test_no_overlapping_bands_in_same_scope`
in tests/lint/repository/test_role_meta_layout.py: rejects any
two PORT_BANDS entries within the same scope whose ranges
intersect, with a precise overlap range in the failure message.
Cross-scope overlaps remain intentional (different bind contexts).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 19:56:36 +02:00
kevinveenbirkenbachandClaude Opus 4.7 a8a117c86e refactor(yaml): route every YAML touchpoint through utils.cache.yaml + lint
New lint guard
==============
tests/lint/repository/test_no_direct_yaml_calls.py AST-walks the
production tree (utils/, cli/, plugins/, filter_plugins/,
lookup_plugins/, roles/, scripts/, tasks/) and flags any direct
`yaml.safe_load` / `yaml.safe_dump` / `yaml.load` / `yaml.dump` /
`yaml.safe_load_all` / `yaml.dump_all` call. Aliased imports are
tracked: `import yaml as Y` ⇒ `Y.safe_load(...)` is flagged;
`from yaml import safe_load` ⇒ bare `safe_load(...)` is flagged.

Per-line opt-out: `# noqa: direct-yaml` on the call's physical line
(or any line spanned by a multi-line call). No central exemption
list — every legitimate exception lives next to its call with a
human-readable rationale in the surrounding comment.

Cache module additions
======================
* `dump_yaml_str(data)` — symmetric to `safe_dump(data)` with no file
  path. Used for stdout output and filter return values where the
  cache cannot help but the API needs to stay consistent.
* `load_yaml_str(text)` — symmetric to `safe_load(text)`. Used when
  YAML comes from an HTTP body, a mutated buffer, or a mock-patched
  source whose content does not correspond to a stable on-disk file.

Migrated production call sites (33 → 0 direct-yaml violations)
==============================================================
* utils/{entity_name_utils, valid_deploy_id, invokable, service_registry,
  domains/list, applications/{config, in_group_deps},
  docker/image/discovery, meta/{port_bands, scan},
  roles/{dependency_resolver, meta_lookup, resource_validation}}
* cli/{build/{graph, inventory/full/command}, create/inventory/{host_vars,
  mirror_overrides}, fix/vars_main_files, meta/{applications/in_group_deps,
  callorder}, mirror/resolver, validate/inventory}
* plugins/{filter/{compose_volumes, get_all_application_ids, get_role,
  invokable_paths, role_path_by_app_id}, lookup/applications_current_play}
* roles/{svc-db-postgres/filter_plugins/split_postgres_connections,
  web-app-bigbluebutton/filter_plugins/compose_mods,
  web-app-dashboard/lookup_plugins/docker_cards}

Behaviour adjustments
=====================
* utils/roles/dependency_resolver — `safe_load_all` (multi-doc) →
  single-doc `load_yaml_any` wrapped in a list. Ansible task files are
  single-doc by convention; the downstream multi-doc loop stays intact
  for forward compatibility.
* roles/sys-svc-compose-ca/files/compose_ca.py — KEEPS direct
  yaml.safe_load / yaml.safe_dump (annotated with noqa). The script is
  copied to the target host via `ansible.builtin.copy` and the target
  has no `utils/` package on PYTHONPATH.

noqa-annotated direct-yaml call sites (with rationale)
======================================================
* utils/cache/yaml.py — IS the cache; would recurse.
* utils/handler/yaml.py — vault-aware Loader subclass that wraps
  \!vault scalars in VaultScalar; cache-by-path collapses the wrapping.
* cli/encrypt/inventory/__main__.py — vault-aware Dumper subclass that
  emits \!vault literal blocks; the cached dump_yaml writes plain YAML
  which is the wrong output here.
* roles/sys-svc-compose-ca/files/compose_ca.py — runtime-deployed.
* tasks/utils/migrate_meta_layout.py — once-off migration script.

Validation
==========
* Unit tests: 1573 passed.
* Lint:       56 passed (incl. new test_no_direct_yaml_calls).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:10:14 +02:00
kevinveenbirkenbachandClaude Opus 4.7 0ab817a839 refactor(roles): drop non-Galaxy license_url / repository / documentation from meta/main.yml
These three sub-keys lived under `galaxy_info:` purely as a workaround
because Galaxy silently ignores unknown keys (same pattern as the
`run_after` / `lifecycle` migration in req-010). The new lint
`tests/lint/repository/test_role_meta_main_galaxy_schema.py` rejects
them, and no consumer in `plugins/`, `utils/`, `cli/`, `filter_plugins/`
or `roles/` reads any of them, so the strip is safe.

Round-trip via ruamel.yaml so the remaining `galaxy_info:` block keeps
its key order, quoting style and inline comments.

224 files touched. Remaining lint offenders after this commit are still
present (`logo`, `homepage`, `video`, plus a handful of roles missing
required Galaxy fields like `min_ansible_version` / `platforms` /
`license`); those are a separate decision and not folded into this
change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 10:34:52 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ed04dad5d5 refactor(meta): land req-008/009/010 role-meta layout/networks/ports/run_after migration
Single atomic landing for three back-to-back role-meta refactor requirements
(see docs/requirements/008-role-meta-layout.md, 009-per-role-networks-and-ports.md,
010-role-meta-runafter-lifecycle-migration.md).

req-008 — role meta layout
  * Move every role's metadata under meta/<topic>.yml with file-root
    convention (no wrapping key matching the basename):
      - schema/main.yml + config/main.yml.credentials → meta/schema.yml
      - users/main.yml                                → meta/users.yml
      - config/main.yml.server                        → meta/server.yml
      - config/main.yml.rbac                          → meta/rbac.yml
      - config/main.yml.compose.services + every other top-level key
        of config/main.yml inlined under <entity>.<key>
                                                      → meta/services.yml
      - config/main.yml.compose.volumes               → meta/volumes.yml
  * Drop the legacy schema/, users/, config/ directories from every role.
  * meta/schema.yml: support nested credential keys, optional `default:`
    field (Jinja literal, not rendered/validated/regenerated), implicit
    `algorithm: plain`.
  * Materialised paths drop the `compose.` prefix:
      compose.services.<e>.<…> → services.<e>.<…>
      compose.volumes.<k>      → volumes.<k>
      <former-top-level>.<…>   → services.<entity_name>.<former-top-level>.<…>
  * Update every Python consumer (utils/cache, utils/manager, utils/applications,
    plugins/lookup, plugins/filter, cli/*) plus every Jinja template, vars/*.yml
    and task file to the new paths.
  * No legacy fallback. Repository-wide grep for `compose.services.` /
    `compose.volumes.` / `schema/main.yml` / `users/main.yml` / `config/main.yml`
    is empty outside docs/requirements/ and tasks/utils/migrate_meta_layout.py.
  * Add tests/lint/repository/test_role_meta_layout.py guarding against the
    old paths returning, and an explicit lint guard that meta/variants.yml
    files do not carry a `compose:` wrapper.

req-009 — per-role networks/ports
  * Move group_vars/all/08_networks.yml.defaults_networks.local.<role>.{subnet,
    dns_resolver} into roles/<role>/meta/server.yml.networks.local.{subnet,
    dns_resolver}.
  * Delete group_vars/all/09_ports.yml entirely; ports now live on the
    service entity that exposes them at
    roles/<role>/meta/services.yml.<entity>.ports.{inter,local,public}.
  * Rename + int-cast every former top-level `services.<entity>.port`
    field to `services.<entity>.ports.inter` (single int).
  * Always-category-keyed maps for ports.local / ports.public; relay
    spans use {start, end} integers.
  * Add PORT_BANDS map to group_vars/all/08_networks.yml — single source
    of truth for the per-<scope>.<category> band ranges, consumed by both
    the new CLI helpers and the lint test.
  * New CLI helpers (cli meta ports suggest, cli meta networks suggest)
    walk the live role tree gap-first then increment-fallback, with
    deterministic output and non-zero exit on capacity overflow.
  * cli create role consumes both helpers when scaffolding a new role.
  * Lint extensions: bare-int port shape rejection, host-bound port
    collision check (single ints + relay span integers, inter excluded),
    band-membership check with documented allow-list for the legacy
    web-app-bigbluebutton.bigbluebutton.local.http: 48087 entry.

req-010 — run_after / lifecycle migration
  * Move galaxy_info.run_after and galaxy_info.lifecycle out of
    meta/main.yml into meta/services.yml.<primary_entity>.{run_after,
    lifecycle}; <primary_entity> is the value returned by
    get_entity_name(role_name).
  * For multi-entity roles whose primary entity has no compose service
    (web-app-bluesky → bluesky, web-app-matrix → matrix, desk/dev/drv/
    gen roles), create a dedicated metadata-only top-level entry under
    <primary_entity>.
  * Introduce utils/roles/meta_lookup.py with get_role_run_after /
    get_role_lifecycle that resolve <primary_entity> once and read from
    meta/services.yml; degrade gracefully when meta/services.yml is
    absent OR the field is absent; raise MetaServicesShapeError when the
    file exists but is malformed.
  * Update every consumer (utils/service_registry, utils/invokable,
    cli/meta/applications/resolution/run_after/__main__, cli/meta/roles/
    lifecycle_filter/__main__, cli/meta/applications/resolution/combined,
    cli/build/{tree,graph,role_include}, cli/deploy/development/{common,
    init,deps,deploy}, plugins/filter/canonical_domains_map, etc.) to
    use the helper.
  * Lint guards: no run_after/lifecycle in meta/main.yml; lifecycle
    values restricted to {planned,pre-alpha,alpha,beta,stable,deprecated};
    run_after/lifecycle never on a non-primary entity; empty run_after
    lists rejected.

Also folded into this change set
  * services_disabler now writes `services.<svc>.{enabled,shared}` into
    host_vars instead of the old `compose.services.<svc>.{...}` path; the
    discover/find pair walks meta/services.yml and only treats entries
    carrying enabled/shared/provides as provider declarations (so
    metadata-only entries from req-010 do not create spurious provider
    rows).
  * service_registry skips `run_after` deps whose target was filtered
    out via SERVICES_DISABLED instead of failing the whole load.
  * cli/mirror/resolver and cli/create/inventory/mirror_overrides emit/
    consume the new applications.<role>.services.<svc>.image shape
    (mirror.md updated to match).
  * utils/domains/list.build_applications_from_roles now reads
    meta/server.yml directly (it had been silently returning {} since
    config/main.yml stopped existing).
  * utils/roles/resource_validation.filter_roles_by_min_storage reads
    meta/services.yml.<entity>.min_storage instead of the legacy
    compose.services path.
  * web-app-bluesky pre-clones the social-app build context BEFORE
    sys-svc-compose so the build context is on disk by the time
    sys-stk-front-proxy/tasks/main.yml flushes the docker build handler.
  * 4 meta/variants.yml files (svc-ai-ollama, web-app-phpmyadmin,
    web-app-wordpress, web-app-keycloak) lose their `compose: services:`
    wrapper so deep-merge over meta/services.yml lands on the new
    file-root shape.

Validation
  * Unit tests:        1571 passed.
  * Lint tests:        50 passed.
  * Integration tests: 161 passed (1 skipped).
  * Full-cycle fresh-purged-apps deploy succeeded for the 8-app validation
    set: web-app-{nextcloud,wordpress,yourls,bigbluebutton,bluesky,gitea,
    discourse,mattermost}.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 09:58:26 +02:00
kevinveenbirkenbach 262e1b43fc Merge branch 'main' into fix/users-applications.yml
# Conflicts:
#	docs/contributing/design/services.md
#	plugins/lookup/applications_current_play.py
#	plugins/lookup/service.py
#	roles/sys-stk-backend/lookup_plugins/service_should_load.py
#	roles/sys-stk-backend/tasks/_load_service.yml
#	roles/sys-stk-backend/vars/main.yml
#	roles/sys-svc-compose/templates/base.yml.j2
#	roles/sys-svc-mail/tasks/01_core.yml
#	roles/sys-utils-service-loader/tasks/load_frontend_service.yml
#	tests/unit/plugins/lookup/test_applications_current_play.py
#	tests/unit/plugins/lookup/test_service.py
#	tests/unit/roles/sys-stk-backend/lookup_plugins/test_service_should_load.py
2026-04-15 23:59:54 +02:00
kevinveenbirkenbachandClaude Opus 4.6 b70a783423 refactor(lookups): migrate to runtime applications/users lookups
Replaces inline `applications`/`users` merging with dedicated lookup
plugins and runtime hydration. Introduces `add_csp_hash` filter,
integrates sort into `find_dock_val_by_bkp_entr`, splits the Keycloak
one-shot realm import into `05a_realm_import_run.yml`, and fixes the
Mastodon administrator task YAML. Removes obsolete `csp_hashes` filter,
`sys-token-store` Jinja filter, and empty CLI setup modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 23:44:19 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 be6a310d59 refactor(services): implement role-local service registry (req-001)
- Delete group_vars/all/20_services.yml; all service metadata now lives
  in each role's config/main.yml under compose.services.<name>
- Rename generic 'database' service key to explicit 'mariadb'/'postgres'
  across all app roles; remove legacy type: field
- Add utils/service_registry.py and plugins/lookup/service_registry.py
  as the single source of truth for service discovery
- Add utils/database_service.py to centralize DB service resolution
- Delete service_load_enabled filter and service_should_load lookup;
  replaced by service lookup plugin
- Update sys-utils-service-loader to run in constructor stage and use
  generic load_service.yml; remove load_frontend_service.yml
- Add _service_loader_preloading guard to sys-svc-compose,
  sys-svc-webserver-core and sys-ctl-alm-compose to prevent
  health-checks/alarms from firing during preload phase
- Replace manual load_app.yml calls for shared DB/Mailu with assert-
  based preload validation in sys-svc-rdbms and sys-svc-mail
- Update networks.yml.j2 templates to resolve LDAP/AI networks via
  service_registry instead of hardcoded role names
- Migrate all tests to new service discovery API; delete obsolete tests
- Update docs/contributing/code/services.md to reflect role-local model

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 19:33:20 +02:00
kevinveenbirkenbach 8a75959c11 Merge branch 'main' into feature/makefile-wsl2
# Conflicts:
#	.github/workflows/test-development.yml
#	Makefile
#	roles/svc-db-postgres/files/set_postgres_superuser_password.sh
#	scripts/administration/apparmor/restore.sh
#	scripts/administration/apparmor/teardown.sh
#	scripts/dns/setup.sh
#	scripts/install/package.sh
#	scripts/install/venv.sh
#	scripts/tests/development.sh
2026-03-24 20:20:27 +01:00
Alejandro Roman 954807e195 fix lint issues 2026-03-21 16:08:28 +01:00
Alejandro Roman 5018ecf145 fix all windows issues with the app + more robust for all 2026-03-21 16:08:28 +01:00
Alejandro Roman da6e87191b fix(makefile-wsl): Fix the tesl app error and the ca trust 2026-03-21 16:08:28 +01:00
kevinveenbirkenbach 19c72c6b25 fix(postgres): make EXIT trap shellcheck-compliant 2026-03-20 16:12:04 +01:00
kevinveenbirkenbach 4efdef8208 Add min_storage entries for warned roles 2026-02-27 09:55:43 +01:00
kevinveenbirkenbach 64b0506947 refactor: replace raw docker usage with container wrapper across roles
Replace direct `docker` command usage, references, and documentation with
the `container` wrapper to ensure engine abstraction and future portability
(e.g. Docker → Podman).

This includes:
- Shell scripts, Ansible tasks, handlers, and Python helpers
- Documentation, comments, help texts, and meta descriptions
- Network, volume, system, and image operations
- Test expectations and comments

All runtime behavior remains unchanged while enforcing the repository-wide
rule to avoid raw Docker invocations.

https://chatgpt.com/share/698721c3-e270-800f-be7a-e4435fb54506
2026-02-07 12:57:26 +01:00
kevinveenbirkenbach b78ab29218 Renamed config entry from docker to compose
https://chatgpt.com/share/69870f54-d708-800f-9870-e9ae5a4bff44
2026-02-07 11:09:36 +01:00
kevinveenbirkenbach 53e817fa22 refactor(compose): migrate from docker-compose.yml to compose.yml everywhere
- Rename all docker-compose.yml templates to compose.yml
- Update override and CA override filenames accordingly
- Adjust compose wrapper, lookup plugins, filters and path helpers
- Update roles, tasks, handlers and scripts to use compose CLI
- Rewrite documentation and admin guides to reference compose
- Update unit and integration tests to expect compose.yml
- Keep internal variable keys for backward compatibility where needed

https://chatgpt.com/share/69870272-b168-800f-84c3-6417398e441f
2026-02-07 10:14:21 +01:00
kevinveenbirkenbach e3a16cbe8a refactor: switch docker CLI usage to container wrapper
- Replace direct docker calls with container wrapper across scripts, roles, and templates
- Add support for cp, start, stop, and restart passthrough commands
- Align handlers and notifications to container manager restart semantics
- Fix systemd ExecStartPost to use container wrapper without hardcoded paths
- Silence ShellCheck SC2016 false positive for container exec password injection
- Update tests and documentation strings to reflect container-based workflow

https://chatgpt.com/share/69866958-c88c-800f-9b4f-3a972615bf42
2026-02-06 23:21:02 +01:00
kevinveenbirkenbach c1d50d78da refactor(container): replace BIN_CONTAINER usages with system-wide container wrapper
https://chatgpt.com/share/698660b7-4c78-800f-b25f-af5408cff034
2026-02-06 23:00:21 +01:00
kevinveenbirkenbach ba35d7c5e0 refactor(container): replace docker ps usages with container wrapper and move binaries to /usr/bin
https://chatgpt.com/share/698660b7-4c78-800f-b25f-af5408cff034
2026-02-06 22:44:14 +01:00
kevinveenbirkenbach 2147690da0 Use container wrapper for docker logs across roles
https://chatgpt.com/share/698659d9-62e4-800f-89a1-5d44f309bde4
2026-02-06 22:28:38 +01:00
kevinveenbirkenbach 46238d4c89 refactor: use BIN_CONTAINER wrapper for container exec commands
https://chatgpt.com/share/698657ee-e708-800f-ba5d-4c7c5198a5fe
2026-02-06 22:06:42 +01:00
kevinveenbirkenbach 63926b23b9 chore(container): replace direct docker inspect/run calls with container wrapper
https://chatgpt.com/share/698647e2-7b94-800f-814d-5eb63fe4e537
2026-02-06 20:58:17 +01:00
kevinveenbirkenbach 3fa7887dc8 Added whitespaces for better readability 2026-02-06 16:21:10 +01:00
kevinveenbirkenbach 5100c2c1ce refactor(config): replace get_app_conf filter with config lookup
Replace usages of the get_app_conf filter with the config lookup plugin
across roles and templates. Adjust quoting where required to keep YAML
and Jinja syntax valid. No functional changes intended.

https://chatgpt.com/share/69860678-cef8-800f-8402-7fe6231fdc2d
2026-02-06 16:19:14 +01:00
kevinveenbirkenbach 7bb15d1b5e refactor(container): rename docker-container/docker services to sys-svc-container and update all references, schedules, tests, and templates
https://chatgpt.com/share/6985b09d-8ff8-800f-800c-7a694c0983e2
2026-02-06 10:12:31 +01:00
kevinveenbirkenbach 51239968fd refactor: rename docker-compose roles to sys-svc-compose and update paths
Renamed docker-compose and docker-compose-ca roles to sys-svc-compose and sys-svc-compose-ca, updated template includes, handlers, lookups, tests, and variables, and moved compose instances to /opt/compose.

https://chatgpt.com/share/6985ab2c-8344-800f-9e9a-768f562d138d
2026-02-06 09:49:42 +01:00
kevinveenbirkenbach 2d2e218fe5 Solved templating warnings 2026-02-05 11:00:34 +01:00
kevinveenbirkenbach 8a81630391 Added newline at file and POSIX compatible 2026-02-04 19:49:56 +01:00
kevinveenbirkenbach 3e8abe3cb4 fix(rdbms): preserve database_consumer_id across role boundaries
- Introduce database_consumer_id_passed to avoid fact/vars collisions
- Explicitly pass consumer context into svc-db-postgres via include_role
- Strengthen context assertion in postgres init tasks
- Move once-flag include to the beginning of postgres core tasks
- Make POSTGRES_EXEC_SHELL non-interactive by injecting PGPASSWORD
- Improve test coverage to detect facts overridden by include-time vars

https://chatgpt.com/share/69833e56-93a0-800f-8e3d-d970cf87daef
2026-02-04 13:40:44 +01:00
kevinveenbirkenbach 77a0bc5508 svc-db-postgres: unify docker exec shell + tidy yaml formatting
- Align database config key spacing and add missing newline in config/main.yml
- Run socket readiness check as OS user postgres
- Introduce POSTGRES_EXEC_SHELL helper and use it for TCP/auth debug psql calls
- Update sys-svc-rdbms comment (behavior note seems outdated)

https://chatgpt.com/share/6982a0b9-be28-800f-b5dc-d489b1e4eed8
2026-02-04 02:28:01 +01:00
kevinveenbirkenbach 3e39f123ce Linebreak 2026-02-04 01:16:44 +01:00
kevinveenbirkenbach fe9cdfb855 feat(images): standardize custom image tagging across roles
Normalize `*_CUSTOM_IMAGE` variables to the consistent pattern `{{ entity_name }}_custom:<version-or-ref>` (including SuiteCRM app version) and update MediaWiki to build a tool-preloaded custom image, removing runtime apt/composer provisioning.

https://chatgpt.com/share/697fa459-abbc-800f-954d-b18848890ff6
2026-02-01 20:06:52 +01:00