Commit Graph
864 Commits
Author SHA1 Message Date
kevinveenbirkenbach 6c20fd4e30 Merge branch 'feature/swarm-nfs' into feature/svc-net-tor
# Conflicts:
#	cli/administration/inventory/provision/cli.py
#	roles/sys-svc-mail-msmtp/templates/msmtprc.conf.j2
2026-07-28 16:19:34 +02:00
kevinveenbirkenbachandClaude Opus 5 8aba198aec feat(worktree): isolated per-branch checkouts sharing one cache stack
Parallel checkouts collided on the compose subnet, host ports and container
names, so a quality run in a worktree died at compose-up. Slot each checkout
via custom.env and layer two overrides on top: the shared cache network in
place of a second cache stack, and the shared .git directory without which
git aborts inside the container.

Both overlays gate on the resource they need, not on the slot number, so a
stray INFINITO_INSTANCE cannot make compose demand a value nobody set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 16:15:59 +02:00
kevinveenbirkenbachandClaude Opus 5 ebd7d2e72b refactor(meta): centralize category/distro SPOTs
categories.yml moves from roles/ to the repository-root meta/, joining packages.yml. utils/roles/categories.py owns its location plus the tree-walking helpers that were stranded in entity/name.py, so no caller spells the path or duplicates the lookup.

meta/distros.yml becomes the SPOT for every distribution: os_family, Galaxy platform name, dev-environment base image, and the pkgmgr/pkgmgr-virgin/environment reference templates. It replaces seven scattered copies (DISTRO_FAMILY, VALID_DISTROS, _DISTROS, _CANONICAL_PLATFORMS, two shell arrays, a Makefile literal). INFINITO_DISTROS is no longer a static default but generated by utils/env/handlers/infinito/distros.py; the shell consumers read it through the env layer.

Drops dead CLI surface found along the way: --categories-file and --roles-dir on the provision CLI plus three unused parameters of generate_dynamic_inventory, and the positional roles_file of the invokable categories command.

tests/lint/repository/test_distros_spot.py pins the copies that cannot call the SPOT (workflow literals, docs table) and forbids composing a pkgmgr reference outside it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 16:15:56 +02:00
kevinveenbirkenbach 71c9f10dfd Merge branch 'feature/swarm-nfs' into feature/svc-net-tor 2026-07-28 11:22:47 +02:00
kevinveenbirkenbachandClaude Opus 5 e409674718 refactor(meta): centralize category/distro SPOTs
categories.yml moves from roles/ to the repository-root meta/, joining packages.yml. utils/roles/categories.py owns its location plus the tree-walking helpers that were stranded in entity/name.py, so no caller spells the path or duplicates the lookup.

meta/distros.yml becomes the SPOT for every distribution: os_family, Galaxy platform name, dev-environment base image, and the pkgmgr/pkgmgr-virgin/environment reference templates. It replaces seven scattered copies (DISTRO_FAMILY, VALID_DISTROS, _DISTROS, _CANONICAL_PLATFORMS, two shell arrays, a Makefile literal). INFINITO_DISTROS is no longer a static default but generated by utils/env/handlers/infinito/distros.py; the shell consumers read it through the env layer.

Drops dead CLI surface found along the way: --categories-file and --roles-dir on the provision CLI plus three unused parameters of generate_dynamic_inventory, and the positional roles_file of the invokable categories command.

tests/lint/repository/test_distros_spot.py pins the copies that cannot call the SPOT (workflow literals, docs table) and forbids composing a pkgmgr reference outside it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 11:19:30 +02:00
kevinveenbirkenbach bb1b4f7f96 Merge branch 'feature/swarm-nfs' into feature/svc-net-tor 2026-07-27 23:55:03 +02:00
kevinveenbirkenbachandClaude Opus 5 2d283ed39a feat(ci): resolve image refs from services.yml SPOT
moby/buildkit was hardcoded in four places across two workflows and catthehacker/ubuntu in two act scripts, none of them reachable by the image mirror: utils/docker/image/discovery.py only scans roles/**/meta/services.yml. So the one image the CI build cannot proceed without was pulled straight from Docker Hub, unmirrored, which is what broke the fedora build in run 30160077828.

Declare both as mirror-only services on svc-runner, following the test-e2e-playwright precedent, so the existing mirror pipeline picks them up unchanged. New scripts/meta/resolve/image/service_ref.sh reads image+version back out of the declaration and is the single source for consumers outside Ansible; its mode argument is mandatory (upstream never touches the network, mirror prefers the GHCR tag and falls back to upstream). The fallback is why the mirror job can keep running in parallel with the build instead of gating it.

ACT_PLATFORM_IMAGE moves to an env handler so act consumers read the declared value via BASH_ENV, and workflow.sh now demands it instead of defaulting to a literal. Operator overrides still win through eb.setdefault.

Verified: service_ref.sh in all four paths (upstream, mirror hit, missing mode, invalid mode); the mirror path matches GHCRProvider.image_base() character for character and resolves against the live registry anonymously; ACT_PLATFORM_IMAGE resolves through the real BASH_ENV mechanism; make quality green on all four targets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 23:53:33 +02:00
kevinveenbirkenbachandClaude Opus 5 21dd54fd4b fix(lint): refresh apt lists before puppeteer provisions chrome
The container image ships with /var/lib/apt/lists emptied, so puppeteer's
--install-deps resolved no package name and chrome-headless-shell then died on
a missing libnspr4. All 262 markdown files with a mermaid block failed to
render, and the wrapper discarded the reason into /dev/null.

Refresh the lists when apt-get is present, and surface the provisioning output
instead of swallowing it, so a failure on a distribution puppeteer cannot serve
names its own cause.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 23:25:49 +02:00
kevinveenbirkenbachandClaude Opus 5 0bde90e47d fix(swarm-test): derive the DR drill backup providers from the closure
The drill configured svc-bkp-remote-2-local with a fixed manager plus NFS provider list for every app, while the manager only runs a backup repository when the app's include closure pulls svc-bkp-volume-2-local or svc-bkp-secrets-2-local in. For an app whose container_backup consumer is statically off, such as svc-dns-unbound, user-backup never ran on the manager, so the pull was rejected with 'Permission denied (publickey,password)' and the fail-if-any wrapper took the whole unit down at drill step 4/9. Four roles reach that step today: svc-dns-unbound, svc-db-redis, svc-registry-cache and svc-registry-docker.

backup_repos.py expresses the placement once and matrix derives the provider list from it per round. derive_includes accepts the round's variant map because INFINITO_APP_VARIANTS only reaches the subprocess callers. The extras file keeps the device paths the drill reads back and loses the provider list nothing read from it, and the drill banners stop claiming backup legs that were never installed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 11:29:47 +02:00
kevinveenbirkenbachandClaude Fable 5 caae36a716 fix(setup): prune stale generated group files
tasks/groups/*-roles.yml are gitignored, so branch switches leave
orphans behind; a stale svc-virt-roles.yml injected a phantom group
into the stage order and referenced a role absent on this branch.
Remove any group file not in the current invokable set before
regenerating, and drop narration comments flagged by the comment lint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 21:46:19 +02:00
kevinveenbirkenbach 0e64865297 Merge branch 'feature/swarm-nfs' into feature/svc-net-tor 2026-07-25 19:12:37 +02:00
kevinveenbirkenbachandClaude Opus 5 5318df2e9d feat(ci): resolve image refs from services.yml SPOT
moby/buildkit was hardcoded in four places across two workflows and catthehacker/ubuntu in two act scripts, none of them reachable by the image mirror: utils/docker/image/discovery.py only scans roles/**/meta/services.yml. So the one image the CI build cannot proceed without was pulled straight from Docker Hub, unmirrored, which is what broke the fedora build in run 30160077828.

Declare both as mirror-only services on svc-runner, following the test-e2e-playwright precedent, so the existing mirror pipeline picks them up unchanged. New scripts/meta/resolve/image/service_ref.sh reads image+version back out of the declaration and is the single source for consumers outside Ansible; its mode argument is mandatory (upstream never touches the network, mirror prefers the GHCR tag and falls back to upstream). The fallback is why the mirror job can keep running in parallel with the build instead of gating it.

ACT_PLATFORM_IMAGE moves to an env handler so act consumers read the declared value via BASH_ENV, and workflow.sh now demands it instead of defaulting to a literal. Operator overrides still win through eb.setdefault.

Verified: service_ref.sh in all four paths (upstream, mirror hit, missing mode, invalid mode); the mirror path matches GHCRProvider.image_base() character for character and resolves against the live registry anonymously; ACT_PLATFORM_IMAGE resolves through the real BASH_ENV mechanism; make quality green on all four targets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:12:37 +02:00
kevinveenbirkenbach 98b6b3db8e Merge branch 'feature/swarm-nfs' into feature/svc-net-tor
# Conflicts:
#	plugins/lookup/application_gid.py
2026-07-25 15:44:55 +02:00
kevinveenbirkenbachandClaude Opus 4.8 4aede4deb9 ci(build): pre-pull syntax frontend with retry
Transient Docker Hub timeouts resolving docker/dockerfile:1 aborted two deploy jobs at '#2 resolve image config'. Pre-pull the frontend from the Dockerfile's syntax line with 3 bounded retries; fail soft so the build still resolves live if Docker Hub stays down. Real build errors remain unretried.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 12:44:02 +02:00
kevinveenbirkenbachandClaude Fable 5 62cf9bd358 style(runner): mark rollback or-true with nocheck reasons
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 09:04:21 +02:00
kevinveenbirkenbachandClaude Fable 5 2475bd4d57 fix(e2e): always source reports base dir from .env
INFINITO_PLAYWRIGHT_REPORTS_BASE_DIR is exported nowhere inside the
container, so make compose-playwright aborted at its env guard; .env
stays the single source of truth, no env-override caching.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 09:02:16 +02:00
kevinveenbirkenbach 0107915009 Merge remote-tracking branch 'fork/feature/swarm-nfs' into feature/svc-net-tor 2026-07-24 20:11:01 +02:00
kevinveenbirkenbachandClaude Opus 4.8 51dbc46876 ci(runner): relocate Docker data-root onto /mnt when roomier
Swarm's nested DinD-node daemons plus the backup DR drill (LUKS loop image,
per-node registry pulls) fill the ~50 GB root fs and trip the <6 GB abort
guard in utils/tests/swarm/matrix.py, while /mnt is a separate ~74 GB
partition sitting idle behind only a swapfile.

Add a runner step that moves the host Docker data-root to /mnt/docker when
/mnt is a separate, roomier partition (detection mirrors enlarge_swap.sh),
before any Docker usage. Best-effort: any failure rolls Docker back onto /
and the step still exits 0, so it can never turn a green deploy red. Wired
into the compose, swarm, and host deploy workflows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 20:05:59 +02:00
kevinveenbirkenbachandClaude Fable 5 4afc9b7702 chore(lint): settle the lint fallout of the swarm-nfs merge
Mark the merged best-effort '|| true' sites (guide EXIT trap, baseline
fetch fallback) with shell-or-true nocheck reasons, parenthesize the
implicitly concatenated message strings ISC004 enforces since the ruff
0.16 pin, and drop the regex narration comments the comment lint flags
on the now-changed test files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 19:13:30 +02:00
kevinveenbirkenbach 1a0af0ce09 Merge remote-tracking branch 'fork/feature/swarm-nfs' into feature/svc-net-tor
# Conflicts:
#	roles/sys-ctl-mtn-cert-deploy/tasks/main.yml
#	roles/sys-svc-compose-ca/tasks/main.yml
#	roles/web-app-dashboard/templates/javascript/oidc.js.j2
#	roles/web-app-mastodon/tasks/01_overrides.yml
#	roles/web-app-peertube/templates/compose.yml.j2
#	scripts/github/guide/compose_deploy.sh
#	scripts/github/guide/host_deploy.sh
#	scripts/make/alias.sh
#	tests/lint/ansible/roles/test_service_core_first_task_run_once.py
2026-07-24 04:30:56 +02:00
kevinveenbirkenbachandClaude Opus 4.8 e9b0d5f76e fix(swarm-drill): give the reschedule convergence poll 240s for slow apps
The failover-drill assert polled service convergence for 60 iterations (120s).
OpenProject web boots in ~140s, so after 06_drain_worker rescheduled it the
replicas turned healthy ~20s after the poll gave up, and 07_assert_state.sh
reported 'Replicas not fully converged: 1/3' and exited 1 even though the stack
did converge moments later. The loop breaks early once converged, so raising the
ceiling to 120 iterations (240s) costs fast apps nothing and only widens the
window for heavy apps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 04:06:39 +02:00
kevinveenbirkenbachandClaude Opus 4.8 4181d2194d fix(env): reload .env when it changed since the last source
load.sh short-circuits on INFINITO_ENV_LOADED, so a shell that already
sourced the env kept a stale copy after 'make dotenv' regenerated .env
(make runs in a subshell and cannot reset the parent's guard), and a
freshly added key was missing until a new shell. Gate the short-circuit
on the .env mtime: re-source when it changed, still a fast no-op when it
did not. A stat failure falls back to the previous idempotent behaviour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:01:32 +02:00
kevinveenbirkenbachandClaude Opus 4.8 6b71d3e8bb feat(ci): deploy branch-new roles first on the push priority line
A push resolves the application roles that exist under roles/ locally but
not at origin/main and passes them as the orchestrator priority line, so
brand-new roles deploy and gate ahead of the regular matrix. The resolver
is stdlib-only (git ls-tree plus an application_id line scan) so it runs
on a bare runner without installing the resolver stack, and yields an
empty set when the baseline ref cannot be resolved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:01:32 +02:00
kevinveenbirkenbachandClaude Opus 4.8 6b1dd1c1d3 refactor(guide): reuse the rescue-diagnostics collector and extract the compose replay
host_deploy.sh drops its bespoke dump: the EXIT trap now runs
utils/diagnostics/container.py on failure before removing the boot
container, because the workflow's failure() collect step only fires
after this script has already torn the container down. That yields the
full snapshot (logs, inspect, in-container systemctl and journal, DiD
recursion) instead of three ad-hoc files, and successful runs skip the
capture entirely.

The compose-mode README replay moves out of test-guide.yml into
scripts/github/guide/compose_deploy.sh unchanged; the workflow step
becomes a one-liner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:01:31 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0d71c041e7 fix(guide): capture host-mode boot diagnostics before teardown
The host-deploy EXIT trap now dumps failed units, the tail of the
journal, and the container log from the pkgmgr boot container into the
rescue-diagnostics base before removing the container, so a failed guide
run leaves a readable artifact instead of tearing down silently. The
base directory is hard-required from the workflow environment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:01:31 +02:00
kevinveenbirkenbachandClaude Opus 4.8 ceb6e65e49 refactor(ci): single-source the rescue-diagnostics base dir from default.env
INFINITO_RESCUE_DIAGNOSTICS_BASE joins default.env as the only literal
of the runner-side collection root and is wired through the passthrough
handler into the generated .env. The four deploy workflows load it with
one grep bootstrap step after checkout instead of hardcoding the path in
their env blocks; upload paths and per-app DIR derivations reference the
variable. dedicated.sh sources it from .env alongside the existing
diagnostics keys and hard-requires it, no script-level default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:01:31 +02:00
kevinveenbirkenbachandClaude Opus 4.8 07d31d9001 fix(ci): retry-harden every egress path (curl filter, shell downloads, package managers)
Transient CDN/mirror TLS resets are a recurring hard-fail class (docker.com, PyPI). Harden all three egress paths at their SPOT: the curl filter now emits --retry-all-errors (retries=0 opts a call out), so every ansible curl retries; a new lint requires --retry-all-errors on file-downloading curl and --tries on wget in verbatim files/*.sh (which cannot use the filter), fixing the moodle/alias downloads it flagged; and the update role configures native retries for every package manager - apt Acquire::Retries, dnf retries=, pacman XferCommand over retrying curl - covering all five CI distros (ubuntu shares apt, centos shares dnf).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:01:31 +02:00
kevinveenbirkenbachandClaude Fable 5 2584de09ef Merge branch 'feature/swarm-nfs' into feature/svc-net-tor
Conflicts: keep both new passthrough env vars; adopt the swarm-nfs
ollama manager-ops split while porting our model-pull retry hardening
into it. Strip three narration comments the stricter comment lint
rejects.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 02:09:38 +02:00
kevinveenbirkenbachandClaude Opus 4.8 22fde6d285 fix(swarm-test): pin cluster node IPs and hostnames deterministically
Every DinD node is attached to a second network by the bootstrap cache connect, so the embedded DNS returns two A records per node name in undefined order; workers that resolved the manager onto the cache network could not reach the registry bound to the lab IP and rejected locally built images with 'No such image', pushing all replicas onto the manager. Pin a fixed lab ipv4_address per node and every node name via extra_hosts (files-first resolution wins over DNS); the subnet and IPs live in default.env as the INFINITO_SWARM_* SPOT consumed by compose interpolation and the topology exports. Proven by A/B: the identical friendica variant-2 swarm job went from worker rejects to fully green including the drain test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 20:50:16 +02:00
kevinveenbirkenbachandClaude Fable 5 ea54eb609c refactor(guide): extract the compose README deploy into a script
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:36:12 +02:00
kevinveenbirkenbachandClaude Fable 5 62a46e472f Merge branch 'feature/swarm-nfs' into feature/svc-net-tor
Resolves the runner Dockerfile to the lint-standard Acquire::Retries=3
and keeps the collabora TODO wording; hardens the merged tree for the
stricter lints (apt retries in the tor Dockerfile, shell-or-true
markers, comment cleanup).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:27:25 +02:00
kevinveenbirkenbachandClaude Fable 5 b2440a7180 fix(swarm-test): deterministic lab networking and lifecycle
Pin node hostnames to their lab-net IPs (dual-homed nodes let Docker
DNS hand workers the cache-net IP while the registry binds only the
lab IP), quiesce the CI compose tor before bootstrap (it publishes the
same onion identity as the cluster), and reap wedged nodes with a
retried SIGKILL so leftovers cannot starve later phases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 15:35:47 +02:00
kevinveenbirkenbachandClaude Opus 4.8 fb78b17901 fix(guide): resolve *.localhost inside the host-mode boot container
The hlth-webserver health service HEAD-requests the deploy's *.infinito.localhost vhosts, but the guide boot container has no resolver mapping for them (no CoreDNS sidecar, no dev-flow dns setup), so the service failed every start and burned 25x60s retries. Install libnss-myhostname on the apt branch and ensure the myhostname NSS module is listed in nsswitch.conf on every distro; it resolves any *.localhost name to loopback where the openresty vhosts answer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 23:22:48 +02:00
kevinveenbirkenbachandClaude Opus 4.8 ff463b7b6a fix(ci): pass GHA repo owner into the DNS DinD env generation
Removing the baked .env forces cli.meta.env to regenerate inside the
container; with GITHUB_ACTIONS=true its on-GHA guard aborts (exit 2) unless
GITHUB_REPOSITORY_OWNER / GITHUB_REPOSITORY is set, so .env is never written
and compose-up dies with KeyError: INFINITO_CONTAINER. Forward the owner and
repository (already resolved on the runner) into each per-distro container.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 12:05:39 +02:00
kevinveenbirkenbachandClaude Opus 4.8 43d23ef696 refactor(agents): reorganize operator workflows into i8- skills
Move the operator cheatsheet's copy-paste prompts into per-workflow i8- skills
that each route to their authoritative procedure doc, and reduce the cheatsheet
to a table index plus a usage paragraph, enforced by a new lint. Add skills for
develop, test, quality, refactor, the debug and requirement flows, and
commit / pull-request / push-trigger-pull; rename the iteration skills to
i8-iterate-* and rename the index to i8-help. Wire the active-listening,
dialectic, and robot skills into the flows for clarification, uncertain root
causes, and autonomous execution. Drop the Infinito-specific agent aliases
(alias.md, INFINITO_ALIAS_MD, the make alias agent section, and the two alias
tests); terminal aliases stay.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 11:14:44 +02:00
kevinveenbirkenbachandClaude Opus 4.8 04f424bcb3 fix(ci): regenerate .env per distro inside the DNS DinD
The prebuilt CI image ships a .env generated at build time with the debian
default, and load.sh only regenerates when it is missing, so every distro's
DinD ran with debian's INFINITO_CONTAINER and config regardless of the image.
Remove the baked .env before make compose-up so it is regenerated from the
container's INFINITO_DISTRO, restoring the per-distro behaviour the old
matrix flow had on a fresh checkout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 11:14:44 +02:00
kevinveenbirkenbachandClaude Opus 4.8 34669658ef fix(guide): install systemd in the initless pkgmgr image before booting it
The host-mode guide booted the pkgmgr distro image with --entrypoint
/sbin/init, but that image ships no init at all (it is a package-manager CLI
tool), so runc rejected the container with exit 127. Install systemd in a
throwaway container, commit an image that carries /sbin/init, and boot that
instead. Also read the post-loop readiness state into a variable so a
degraded systemd is not masked by pipefail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 11:14:44 +02:00
kevinveenbirkenbachandClaude Opus 4.8 36e4d67df2 fix(dns-test): accept a degraded systemd instead of failing under pipefail
The readiness check piped systemctl is-system-running into grep. Because
is-system-running exits non-zero on a degraded system (only running returns
0) and the script runs under set -o pipefail, the pipeline propagated that
non-zero even when grep matched, so the loop never broke and centos, which
settles at degraded with one failed unit, always exited 1 regardless of the
timeout. Read the state into a variable and match it, so a degraded system
is accepted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 11:14:44 +02:00
kevinveenbirkenbachandClaude Opus 4.8 138d40ac92 fix(ci): use fuse-overlayfs for the DNS DinD inner daemon
The per-distro DNS Docker-in-Docker container runs on an overlay rootfs, so
the inner daemon's default overlayfs snapshotter fails to mount overlay on
overlay ("invalid argument") and docker compose up never brings coredns and
infinito up. Install fuse-overlayfs and write /etc/docker/daemon.json with
storage-driver fuse-overlayfs plus containerd-snapshotter disabled before
starting docker, mirroring the swarm node daemon.json that already runs this
way in CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 03:59:51 +02:00
kevinveenbirkenbachandClaude Opus 4.8 6768591afc refactor(dns-test): drop the redundant INFINITO_PULL_POLICY shell default
parallel.sh already passes INFINITO_PULL_POLICY=always into the DinD
container, so the shell-side "${INFINITO_PULL_POLICY:-always}" default is
redundant and a second source of truth that contradicts default.env's
INFINITO_PULL_POLICY=never. Read the value from the environment; default.env
stays the single default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 03:01:35 +02:00
kevinveenbirkenbachandClaude Opus 4.8 fc1b35e9a7 fix(guide): unmask systemd-logind before enabling root linger
The host-deploy base image ships systemd-logind masked, so loginctl
enable-linger cannot activate org.freedesktop.login1 over D-Bus and the
guide fails after the image pull with "Could not activate remote peer".
Unmask and start logind before enabling linger; it is a no-op where logind
is already active.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 03:01:35 +02:00
kevinveenbirkenbachandClaude Opus 4.8 fb731041f4 refactor(ci): extract fork-prereq gate to a script; drop gate jobs
Move the inline fork-prerequisites result assertion out of the
fork-prereqs-ready job into scripts/github/gate/fork_prereqs.sh (result
passed via the RESULT env var; the job now checks out the repo).

The staged diff also drops the code-quality-gate and test-install-gate
aggregator jobs and wires their members (lint, test, security-codeql,
test-install-make, test-install-pkgmgr) directly into the done job's needs,
which still hard-fails on any upstream failure or cancellation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 03:01:35 +02:00
kevinveenbirkenbachandClaude Opus 4.8 1bbe25f0d0 feat(ci): run DNS tests one job with per-distro Docker-in-Docker
Collapse the per-distro DNS test matrix into a single job that runs each
triggered distro in its own systemd Docker-in-Docker container, built from
that distro's prebuilt CI image, all in parallel. Each container installs
and starts a local docker daemon, brings the dev stack up and runs the
existing dns.sh suite unchanged. The local daemon makes the dev compose
bind-mounts resolve against the baked repo, and the per-container network
namespace keeps the static CoreDNS IP and subnet from colliding across
distros. GITHUB_ACTIONS=true and INFINITO_BUILD=0 pin the deploy to the
pull-and-run path (no cache stack, no local image build); dig is installed
unconditionally so arch keeps the host-query assertions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 03:01:35 +02:00
kevinveenbirkenbachandClaude Opus 4.8 9deac861e6 refactor(ci): run install tests in one job per mode, distros from SPOT
Collapse the 5-distro matrix in the make and pkgmgr install-test workflows
into a single job that runs every distro in parallel inside extracted
scripts, cutting 5 runners down to 1 per workflow. Each distro installs
against its own copy of the checkout so the concurrent in-container builds
never race on the shared tree. The distro list is read from the
INFINITO_DISTROS SPOT in default.env instead of being hardcoded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 21:23:38 +02:00
kevinveenbirkenbachandClaude Opus 4.8 04414a0ec5 revert(swarm-nfs): drop backup-drill rework, openproject bypass, xwiki replicas:1
Back to the pre-swarm-nfs-fix baseline:
- 5c2c8f531 + 985ec7eab: the swarm writer-quiesce DR drill. Deep-dive
  attribution of CI run 29715516546 found it the dominant failure source
  (17 of 23 jobs): [9b/9] handed-off generation mismatch, [6/9] quiesce
  stack-rm network race, [4/9] remote-2-local pull stall, compose discourse
  restore duplicate key.
- d6f980574: openproject swarm ingress to web:8080. Carried a leaking
  proxy_internal_port lookup (no per-app default) that broke the seaweedfs
  dependency front-proxy ('Key web not found'); the bypass-the-Apache-proxy
  approach was never verified against downloads/X-Sendfile.
- 1dd153bad: xwiki replicas:1 + mem 3g. Did not resolve the xwiki superadmin
  failure (still red in run 29715516546), so it is unverified.

Re-approach each from a clean baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:11:18 +02:00
kevinveenbirkenbachandClaude Opus 4.8 5c2c8f531f fix(backup): pass quality-high for the swarm writer-quiesce drill
Follow-up corrections to b449f8157 (swarm DR drill):
- export test.env in 06/07 via set -a so the child db restore/probe
  scripts inherit BKP_TEST_DATABASES_CSV and BKP_TEST_RESTORE_BIN
  instead of aborting at their :? guards before any restore runs
- move db_probe.sh/db_restore.sh into a db/ subfolder (probe.sh,
  restore.sh) and update all callers, so they stop tripping the
  prefix-grouped-files structure lint
- define TEST_E2E_SWARM_DRILL in group_vars/all and drop the now
  redundant default() from test.env.j2 (satisfies the undefined-var
  and spot-default lints together)
- fix a mermaid parse error in the drill README: a ; in a
  sequenceDiagram note was read as a statement separator

make quality-high green (test + lint phases, 15 targets, 0 failures).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 05:45:26 +02:00
kevinveenbirkenbach 985ec7eab9 fix(backup): quiesce swarm writers for restore
Defer Swarm SQL replay to the disposable DR drill so shared writers such as Discourse are stopped before the destructive restore. Verify the selected generation with before/after database probes while preserving the Compose restore path.
2026-07-20 05:45:26 +02:00
kevinveenbirkenbachandClaude Opus 4.8 972596a758 fix(guide): document the administrator SSH key step and seed it in CI via --vars
The README Production block went provision -> deploy with no admin-key step, so user-administrator failed early (authorized_keys empty) for both the Guide CI jobs and any real user copying the README. Add a visible 'provision --vars' step that sets users.administrator.authorized_keys (merged into host_vars) in both Production variants of the role README template, regenerate all role READMEs, and replace the invisible CI payload splice with a visible substitution of the documented <your-ssh-public-key> placeholder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 05:45:26 +02:00
kevinveenbirkenbachandClaude Opus 4.8 9563948782 feat(make): add swarm-app-exec, group act exec scripts under exec/
swarm-exec reaches a DinD node; reaching a swarm service container (moodle,
openldap) needed a nested docker exec. Add swarm-app-exec, which resolves one
running replica on the node and runs a base64-encoded command inside it, so
live in-container inspection stays on a sanctioned make helper. Adding a
second exec_* script tripped the prefix-group lint, so exec_node.sh moves to
exec/node.sh and the new one lands at exec/service.sh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 05:45:25 +02:00
kevinveenbirkenbachandClaude Fable 5 d88f272f9f Merge branch 'feature/swarm-nfs' into feature/svc-net-tor
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 15:31:08 +02:00