Extend the 'meta roles applications complexity' report:
- --sort takes comma-separated multi-key clauses with per-key direction (e.g. 'desc embeds, asc weight'); replaces --order.
- --variant lists one row per meta/variants.yml variant; --format adds yaml.
- New columns: weight (was total), random, id, row, variants, bundles, jobs, lifecycle, covered_by, compose, swarm.
- covered_by marks the first green (1-based id) different-role predecessor that embeds the row; variant>0 never covered.
- bundles is the compose bundle/job count (whole-role) or 1 (per variant); jobs is the running sum.
- compose/swarm reflect the CI test-deploy skip logic (invokable + tested lifecycle + per-mode skip + swarm deployable variant).
- --filter is a boolean expression DSL (%% == \!= < > <= >=, and/or/xor/not, parens, set literals) over row fields.
Add shared SPOTs: variant_bundles.compose_bundle_counts and variant_status.deployable_variant_indices, reused by the swarm/compose matrix. Update roundtrip.sh/md to --sort "desc weight".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chown the config dir to www-data before the swarm installer run so the PHP writability checks pass, and raise the healthcheck start_period to 180s to cover the slower swarm/NFS first boot.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NFSv4 without a matching idmap Domain squashes non-root owners (www-data uid 33) to nobody (65534) on the client, breaking PHP ownership checks. Pin Domain=localdomain on the ganesha server flavor and the dev/act client, and drop the stale idmap cache. Scoped to dev/act so the production kernel flavor stays untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
failed_roles for the swarm/docker scope only flags a role whose job in that mode actually ran and was not green; total_state is green when every mode that ran is green. A role with no job in a mode (a host driver that never deploys in swarm) is no longer counted as failed for that mode, so 'ci trigger --failed swarm' / 'ci status --failed swarm' stop listing drv-* and other roles that simply do not run in swarm.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the scripts/tests/environment deploy_* steps (07/08/09) to compose_*; add 12_swarm.sh (svc-db-mariadb via the swarm-* targets) and 13_roundtrip.sh (svc-db-postgres via make roundtrip), with MARIADB_APP/POSTGRES_APP in common.sh. Rename every make act-swarm-* target to make swarm-* (drop the act- prefix) across the Makefile (moved to the alphabetical slot), the deploy/act, deploy/swarm and e2e helper scripts, and the agent iteration docs. Strip mid-code narration comments from the touched scripts per the comment policy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 'Zip generated inventory' step reads $INFINITO_INVENTORY_DIR on the
runner host, but the compose deploy writes inventory inside the DiD
container and the cleanup trap removed the container before the always()
zip step ran, so it failed with 'No inventory found under
/home/runner/inventories/github*'. Copy the inventory dir from the
container to the host parent in cleanup() before 'down'/rm; container and
host paths match, so the zip step then finds it. Non-fatal (|| true).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 2daygeek 'check website availability' link returned HTTP 404 and tripped the external-URL reachability test; it was tangential to Mastodon anyway.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In swarm mode (INFINITO_DEPLOY_MODE=swarm) variant_bundles emits one matrix entry per variant (bundle size 1) and drops variants whose meta/variants.yml override disables every service; the all-enabled variant already exercises the role so a bare all-off deploy adds no coverage. Absolute variant indices are preserved so the emitted index still maps to the deploy planner's round. Compose mode is unchanged. Adds the variant_disables_all_services predicate (utils.roles.applications.services.variant_status) plus unit coverage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove OLLAMA_CONTAINER_ADDRESS, TAIGA_MANAGER_CONTAINER_ADDRESS and
FIDER_CONTAINER_PORT (no remaining references after the cross-node exec
migration and the fider wget removal), and switch the fider swarm tenant
bootstrap from uri to uri_retry for module-lint compliance.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The fider image ships only the fider binary (its own healthcheck runs
'./fider ping'), so the swarm in-container wget readiness probe failed
with exec: wget: executable file not found in $PATH. Use './fider ping'
for readiness and post the tenant bootstrap from the hosting node against
the ingress-published routing-mesh port instead of in-container wget.
Compose paths are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
container exec/inspect ran on the swarm manager while the task may run on
a worker, yielding No such container or an empty container id. Use the
existing resolve_host_cid helper (HOST_NODE + LOCAL_CID + delegate_to)
for the ollama model pre-pull, the taiga admin migrations and the
peertube post-backfill health inspect, matching the fider/matomo/decidim
pattern. The helper resolves to the local host + compose cid in compose
mode, so that path is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
slugify only stripped whitespace, so a card title carrying a non-ASCII
character (e.g. U+2011 non-breaking hyphen) produced a non-ASCII slug
that crashed the Simple Icons uri probe with 'ascii' codec can't encode
character. NFKD-normalize then drop non-ASCII; identity for ASCII titles
so existing slugs are unchanged. Adds a regression test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The healthz, media, upload and ws location templates called proxy_pass
without internal_port, so in swarm they fell back to
services.<entity>.ports.internal.http. Apps whose entity service has no
internal http port (e.g. bluesky) hard-failed the nginx config render
with resolve_upstream: swarm upstream needs
services.<entity>.ports.internal.http. Forward internal_port (default '')
like html.conf.j2 already does; no-op where it is not passed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enable ENABLE_PROMETHEUS_EXPORTER and _OPTIMIZATION via a {% if services.prometheus.enabled %} guard so the bbb-exporter only runs when web-app-prometheus is present, instead of staying hard-commented. Add a head-level '# nocheck: comments-valid' opt-out so the vendored upstream sample.env comments stay valid.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_context.sh derived NFS_VOLUMES by filtering meta/volumes.yml entries on a per-volume
nfs: key that no role declares, so the inter-round purge cleared no volume data and
round-0 state leaked into round-1. Filter on type==volume instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The gate grepped a stale step-name marker ("across variant rounds") that never appears
in the act log, so every swarm leg false-failed even on a passing deploy. Match the
actual workflow step name instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
matomo_site_domain used a folded YAML scalar where the regex_replace backslashes are
not unescaped, so base-domain extraction silently returned the input unchanged. In
root tracking scope this registered one Matomo site per consumer subdomain instead of
a single shared registrable-domain site, failing the SitesManager coverage spec.
Use a double-quoted scalar (repo convention) so the regex sees an escaped dot.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop trap/warning from the exception markers; lint .j2 by stripping the suffix to the underlying language plus Jinja {# #} comments; add a head-level '# nocheck: comments-valid' opt-out via is_suppressed_in_head, documented in suppression.md; strip narration comments from friendica/nextcloud templates and opt the vendored discourse app.yml out with the new marker.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 🧩 Subset path in pr_affected_roles.sh exec'd a bare `python`, the setup-python interpreter without PyYAML, so subset_roles.py failed with `ModuleNotFoundError: No module named 'yaml'`. Source scripts/meta/env/load.sh so ${PYTHON} resolves to the venv (where PyYAML is installed), matching the diff resolver path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On redeploy 'semaphore user add --admin --external' panics with a Postgres unique-constraint violation (duplicate key ... user_username_key), whose message lacks the word 'exist' the failed_when guard looked for, so the task failed. Skip the add when the login already appears in 'semaphore user list', and tolerate 'duplicate key' in failed_when as a safety net.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lookup('tls', ..., 'url.base') always ends with '/', so the OIDC redirect_url and SEMAPHORE_WEB_ROOT contained a double slash (...example//api/...). The '//api/...' callback path did not match the oauthstate cookie's default path '/api/auth/oidc/keycloak', so Semaphore reported 'http: named cookie not present' and bounced OIDC logins back to /auth/login. Apply regex_replace('/+$', '') (same idiom as web-app-keycloak) to both values; Keycloak's redirect URIs use a '/*' wildcard so the single-slash callback still matches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v2.18.13 is not published on Docker Hub, so 'compose pull' failed with 'manifest unknown' and aborted the deploy. Pin v2.18.12 (latest stable 2.x, verified pullable in the nested daemon) and align the README bump-policy note and the 028 requirement spec.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
odoo: sale_management.spec.js asserted `breadcrumb.or(appTitle)` without
`.first()`; when the Sales module installs correctly BOTH the menu brand
and the breadcrumb render, so `.or()` resolved to 2 elements and
`toBeVisible()` raised a strict-mode violation — failing the success case.
Add `.first()` (matching the same file's createQuotation locator).
nextcloud: the GitLab OAuth provisioning hard-`raise`d when no Organization
existed yet (default org is seeded asynchronously). Resolve/create it
defensively and add `retries/until` so a transient not-yet-seeded window no
longer fails the whole deploy.
jenkins: the deploy-lockdown service stop had no retry; under load systemd
intermittently refused with "Transaction is destructive". Add `retries/until`
so the transient contention is ridden out.
moodle: the shared `compose up` handler had no retry; a transient missing
external network (central DB stack still creating it) failed the deploy. Add
`retries/until`, matching the handler's existing compose-pull pattern.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the 80 open CodeQL/code-scanning alerts on this branch.
Security-sensitive fixes:
- cli/administration/deploy/development: break the unsafe import cycle by
extracting the env/arg helpers into a leaf module `env.py`, giving the
load-time DAG compose -> common -> deps -> env.
- web-app-bluesky login-broker server.js: prototype-pollution guard on
cookie parsing, CR/LF + control-char log sanitisation, HTML-escaped
exception text, and a ReDoS-bounded handle regex.
- web-app-baserow SSO: harden the open-redirect `next` handling.
- network diagnose probes: require TLS >= 1.2.
- actionlint installer: validate tar members against path traversal.
- decidim/apt-purge lint regexes: rewrite to avoid catastrophic backtracking.
- 6x Playwright URL regexes anchored; crs-k/stale-branches pinned to a commit SHA.
Mechanical cleanups: unused imports/globals/locals, empty-except comments,
import-and-import-from, implicit-string-concat, multiple-definition,
unreachable/useless-assignment.
False positives are intentionally left in place (Django settings patches,
Odoo __manifest__, side-effect imports, cross-module symbols).
Recurrence guards:
- import-linter contract (pyproject + tests/lint) locks the dev-deploy
package acyclic; a new cycle now fails CI instead of only CodeQL.
- new lint test requires every bandit (S###) `noqa` to carry a justification.
- BLE001 (blind-except) added to ruff as a ratchet (tests/roles exempt,
current production offenders grandfathered).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Untrusted fork PRs mirrored anonymously after the credential hardening (empty Docker Hub creds -> Login to Docker Hub skipped -> anonymous pulls), which got rate-limited and failed images-mirror-missing-fork. Restore secrets: inherit on the fork mirror job and the GHCR login token so the mirror authenticates again. The build keeps its hardening (it pulls its parent from GHCR and needs no Docker Hub creds).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
crs-k/stale-branches@v9.0.1 rejects days-before-stale >= days-before-delete (the v8 'delete immediately' behaviour with days-before-delete: 0 is gone), so the daily cleanup failed with 'Invalid inputs'. Set days-before-stale: 350 and days-before-delete: 360 - deletion still triggers at 360 days of inactivity, with a 10-day stale warning before it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
odoo: sale_management.spec.js asserted `breadcrumb.or(appTitle)` without
`.first()`; when the Sales module installs correctly BOTH the menu brand
and the breadcrumb render, so `.or()` resolved to 2 elements and
`toBeVisible()` raised a strict-mode violation — failing the success case.
Add `.first()` (matching the same file's createQuotation locator).
nextcloud: the GitLab OAuth provisioning hard-`raise`d when no Organization
existed yet (default org is seeded asynchronously). Resolve/create it
defensively and add `retries/until` so a transient not-yet-seeded window no
longer fails the whole deploy.
jenkins: the deploy-lockdown service stop had no retry; under load systemd
intermittently refused with "Transaction is destructive". Add `retries/until`
so the transient contention is ridden out.
moodle: the shared `compose up` handler had no retry; a transient missing
external network (central DB stack still creating it) failed the deploy. Add
`retries/until`, matching the handler's existing compose-pull pattern.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Untrusted fork PRs mirrored anonymously after the credential hardening (empty Docker Hub creds -> Login to Docker Hub skipped -> anonymous pulls), which got rate-limited and failed images-mirror-missing-fork. Restore secrets: inherit on the fork mirror job and the GHCR login token so the mirror authenticates again. The build keeps its hardening (it pulls its parent from GHCR and needs no Docker Hub creds).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>