logparse.parse_log does a one-shot read of a single deploy log with no
reuse, so the cached-read requirement does not apply; annotate the read
with a cache-read nocheck. The pytest_summary docstring shows the script
path as a usage example, not self-referential logic; annotate it with a
self-path-reference nocheck.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
run.sh reads INFINITO_TEST_TYPE for the per-target junit report filename
but the key was undeclared, failing the used-keys, dotenv-generator and
alphabetical-sort lints. Add the empty default to default.env (after
INFINITO_TEST_RUNNER) and wire it through the static passthrough handler
so make dotenv materialises it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The inline run: block for the host-mode deploy exceeded the 11-line
inline-script-block cap. Move the body to scripts/github/guide/host_deploy.sh
and reduce the workflow step to a single bash invocation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The compose-flavor front-proxy wiring gave the element and synapse vhosts only their local port, so in swarm mode resolve_upstream fell back to services.matrix.ports.internal.http (the ansible-flavor MDAD service has no ports) and aborted. It now passes each vhost its own service key and internal port, mirroring the ansible flavor, so the proxy resolves matrix_element and matrix_synapse on the overlay; MATRIX_ELEMENT_CONTAINER_PORT is added as its source.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Guide test no longer hardcodes svc-db-postgres: guide_select picks an explicit role, else a random one from priority, then whitelist, then every invokable role, and resolves the role's deploy mode plus a random distro's pkgmgr image. A host role runs its README procedure verbatim inside that pkgmgr distro booted with systemd (enable-linger for the user manager) against localhost; a stack role boots the systemd compose server and runs the docker-run payload against it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A host role installs onto the machine itself, so its README Production block now shows the direct local-connection procedure (clone, scripts/install/package.sh, make install, source the env, then provision + deploy against localhost) instead of the container docker-run aimed at a remote server; stack roles keep the docker-run block. The mode is derived from role_has_stack. make docs now runs readme-generate quick_setup=true so the block stays in sync, and every host role README is regenerated accordingly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Angular build cache and the Yarn dependency cache land under the app dir, which the first-run seed copies into the persistent suitecrm_data volume; baudolo backs up every docker volume with no per-volume exclude, so the remote-backup pull choked on the thousands of tiny .angular/cache files (4.2G peak, exit 1). Both caches are regenerable and unused at runtime once the frontend is built and node_modules is gone, so they are removed right after yarn build.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
07_assert_state.sh sources _context.sh under set -o pipefail, and the printf | head-n1 that picked the first NFS volume raised a broken-pipe write error the moment head closed the pipe after line one. Any role with two or more NFS volumes (erpnext ships erpnext_sites and erpnext_logs) crashed the reschedule assertion before it could check the surviving marker. Plain parameter expansion returns the same first line with no pipe.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parallel.sh mirrors its per-target wall-clock table into
GITHUB_STEP_SUMMARY, so make lint and make test render a target/status/
duration overview on the run page. The pytest runner writes junit XML
per suite into build/test-reports and test.yml renders them via
utils/github/pytest_summary.py: headline counts with total time,
failures opened with durations, every test collapsed slowest-first.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The swarm deploy job now renders the same summary page as compose:
role runtimes, a swarm structure section (topology roles plus live node
list and service placement from the manager) and the Playwright table.
The matrix deploy tees its ansible output and prints the compose
matrix-deploy round/PASS markers, so the runtime parser segments swarm
runs per variant round too.
The runtime table unifies tasks and roles with a Type column (tasks
first), appends the recap total row and flags a mismatch between the
parsed sum and the recap total; rows are read from ROLES RECAP sections
only, which removes the double-counted TASKS RECAP task rows. Each role
row carries one column per host: executed, skipped or failed, with
ignored failures downgraded. The Playwright variant column drops the
redundant variant- prefix. Verified against the logs of runs
29656268771 (swarm) and 29620158441 (compose): every segment's parsed
sum matches the recap total within 0.05s.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The alias lint requires an i8 shorthand for every make target and the filesystem lint requires the execute bit on every script under scripts/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Atlassian's CDN served the CI runner a 404 for a live page because the probe announced itself as a bot; the reachability question is whether a link works in a browser, so the probe now sends a browser user agent like standard link checkers do. Touching the file pulled it into the changed-scope comment lint, so its narration blocks are gone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swarm cannot bind published host-mode ports to a single address, so the service moves onto the host network and the registry itself binds REGISTRY_HTTP_ADDR to whatever the manager hostname resolves to on the stack host, which is the only address workers and the local pusher ever use. The lookup fails loudly when resolution is missing or lands on loopback. The orphaned internal-port var is gone and the README security note now describes the real mechanism.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The separate CI job is gone; hadolint now runs as lint-dockerfile inside the parallel make lint fan-out through the standard wrapper, with the binary installed by the lint toolchain installer like every other tool. The SARIF gate script had no consumer left and is removed. Scope stays the root Dockerfile at the warning threshold, matching the old job.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docker manifest inspect is a pure client command and ignores the daemon's insecure-registries list, so against the swarm-local HTTP registry it reported every existing manifest as missing. The push skip therefore never triggered: each deploy re-pulled every external image over the slow lab NAT, re-pushed it, and the final rmi dropped the engine copy again. With the flag the second and every later sync recognizes cached manifests and completes in seconds, proven live on the kept matrix cluster. The test fake_run learns the timeout keyword the pull sites now pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The act NAT path delivers large upstream images at roughly 1.4 MB/s, so an 836 MB pull legitimately exceeds the shared 600s subprocess timeout; killing the client discards the in-flight layer and every retry restarts from zero, wedging the cache-externals handler in an endless loop. Pulls now get a 1800s budget, three times the measured cold duration of the largest offender, while tag and push keep the tight limit because they only ever talk to the node-local registry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On a fresh swarm cluster the bridges wrote registration.yaml as uid 1337, synapse (991) crash-looped on PermissionError, and the stack converge gate sits before the ansible chmod tasks, so the deploy could never turn green; swarm also ignores the service_healthy depends_on that serializes this in compose mode. The mautrix entrypoint chowns /data to $UID and drops privileges via su-exec, so pinning the bridge UID/GID to the synapse uid makes every registration owner-readable the moment it exists. The synapse uid moves into vars as the single source for the compose env and the volume chown.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Six replicas race the bootstrap lock across three NFS clients; the truncating open invalidated the holder's lock state, so flock died with EIO and the task crash-looped with exit 65 until the winner finished the ~6min code copy. The lock file is now opened append-only and flock failures fall back to a sentinel recheck loop instead of killing PID 1. The converge budget gets the same 600-retry override the other slow-bootstrap roles already carry; the default 150 missed this run's recovery by six seconds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The container shellcheck flags the render_one body as unreachable
because the function is only invoked through xargs + bash -c; extend
the existing SC2329 directive.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the ten lint-*.yml workflows and the four test-code-*.yml
workflows with lint.yml (make lint fanning out every check, plus the
hadolint SARIF job) and test.yml (make test running unit, integration,
lint and external suites in parallel on the runner host). Drop the
lint-gate and test-code-gate jobs; code-quality-gate now needs lint,
test and security-codeql directly. Dependency diagram, workflows table
and pipeline docs follow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The mermaid lint rendered one file at a time, booting a fresh
chrome-headless-shell per diagram. Fan the renders out with xargs -P
capped by INFINITO_WORKER_CPU (the shared parallel-make constant) and
print collected failures serially afterwards.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The guide job runs the README command in a plain docker-run container
where systemd is not PID 1, so stopping apt-daily died with "Service is
in unknown state". Extract the neutralize block into
tasks/utils/apt/neutralize.yml behind the sd_booted check
(/run/systemd/system); ready.yml keeps the container gate on the
include and always waits for the dpkg lock. A README documents the gate
chain with a mermaid schema.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The matrix and plan aliases called the query CLI without the .env
loaded, so the strict INFINITO_* lookups died with a KeyError. A new
i8source alias sources scripts/meta/env/load.sh (venv python + .env)
and the six inspection aliases chain it. Relax the alias lint from no
adjacent consonants to at most two so the name passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Plan job renders under a map emoji with plan wording across the CLI,
docs and aliases; the guide workflow, its orchestrator wiring, the
dependency diagrams and the workflows doc follow the test-guide name.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The plan job called the bare setup-python interpreter, so the import
chain died at PyYAML (No module named 'yaml') and the .env-backed
INFINITO_* lookups had no values. Source scripts/meta/env/load.sh and
call "${PYTHON}" like the discovery scripts do. Rename test-readme.yml
to test-guide.yml.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The swarm pre-deploy build renders only the Dockerfile and env into the build context before any role task runs, so the file COPY of boot-entrypoint.sh failed with not-found. The script is now embedded at template render time through a quoted BuildKit heredoc, keeping the role file as the single source and making the build context-independent; the copy task is gone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The discovery query read every INFINITO_* knob with a silent .get default, which the env lint forbids: defaults live in default.env, and an unset key must fail loudly instead of masking a broken environment. All five accesses go through os.environ[...] now; the documented empty-value branches (sort spec falling back to default.env, auto job slots) stay explicit logic. The complexity model's random tie-breaker suppression carries its required justification.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The clamp check fails with Permission denied in an unprivileged container, which made the append task fire and hard-fail the README production run that follows the documented docker run without extra capabilities. A permission failure now skips the clamp instead: capability-less environments cannot and need not clamp, while the privileged DiD runs keep clamping. The narration block above the check moves to history.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The context derived PRIMARY_NFS_VOLUME from the first declared volume, but gitlab's first volume is repositories with nfs: false (gitaly cannot run on NFS), so the seed step wrote its pre-drain marker into a state directory that never exists on the export and killed a job whose deploy had already converged. Volumes declaring nfs: false are now skipped, making the drain check target a volume that actually lives on the export (gitlab_shared).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The bridges write their registration.yaml into directories they create with a restrictive mode, and the role only chmodded the file; synapse, running as its unprivileged user with the directory bind-mounted read-only, crash-looped on PermissionError and the stack never converged. The directories now get mode 0755 before the file chmod and the synapse restart.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A SIGKILL (the observed swarm task exit 137) can leave a zero-byte postmaster.pid on the NFS-backed data volume; the stock entrypoint then refuses every restart with 'lock file postmaster.pid is empty', looping forever and taking keycloak, synapse and every other central-DB consumer down with it. A boot entrypoint wrapper removes the pid file only when it exists and is empty, which in a PID-1 container is deterministically stale, then execs the stock entrypoint. The script ships as a role file copied into the build context; PGDATA is asserted rather than defaulted since the upstream image always sets it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The discovery SPOT (cli.meta.ci.query) now runs one query shape per mode:
compose and host stay whole-role with variants packed into bundles, swarm
ranks and budget-cuts on per-variant rows and emits role#variant tokens
through apps.sh into variant_bundles, so the cut can pick a subset of a
role's variants and the --matrix view is the selection priority.
Replace the --unique flag with visible dna/clone columns (heaviest role
per dna cluster stays ahead of the cut), add the integrated column and
sort, count host bundles like compose, and give the deploy plan an id
counter, per-variant weights and display-width-aware alignment matching
the matrix renderer. Add i8ma*/i8pa* inspection aliases per mode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The orchestrator renders the per-mode deploy plan into the run summary.
test-run becomes test-readme (book emoji), depends on build-ci-images,
grants NET_ADMIN (the in-container MSS clamping needs it) and seeds the
run's freshly built CI image instead of the published default. The
manual run-name stars the priority roles.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cli.meta.ci.query is the single source of truth for the app-discovery
query (filter, coverage-first sort, lifecycle envelope, compose
--unique, INFINITO_MAX_JOBS cap with 'auto'); apps.sh shrinks to one
call through it. cli.meta.ci.plan renders one section per deploy mode
in the query's own order (priority line starred, capped regular line,
then the candidates behind the cut), one row per variant, glossary
symbols in the header, with --cli for fixed-width terminal tables.
New glossary symbols: skip, priority, distros, unavailable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
elastic.co answers the probe's Accept/UA with 406 while the page is
alive; classify it with 401/403/405/415 as server-alive instead of a
dead link.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>