Commit Graph
14682 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 4.8 bd2759b81e fix(web-app-mastodon): declare sidekiq/migrate service names so container_address resolves
04_wait.yml resolves the sidekiq container via container_address, which requires services.sidekiq.name; it was unset (unlike mastodon/streaming), so the wait aborted with "services.sidekiq.name not set". Add name: mastodon-sidekiq (and mastodon-migrate) matching the already-built container names.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 13:57:30 +02:00
kevinveenbirkenbachandClaude Opus 4.8 9e7f28db20 test(lint): enforce swarm config objects are world-readable + widen 18 roles to 0444
Swarm config objects mount root:root; a 0440 mode is unreadable by a non-root container app user and crashloops the service (jenkins JCasC AccessDeniedException, exit 5). docker compose never hits this since there the file is a host bind-mount inheriting host perms.

New lint test_config_object_world_readable flags type:config modes lacking o+r (type:secret exempt, per-line # nocheck escape). Widen the 22 offending 0440 configs across 18 roles to 0444: adds read only, cannot break a reader, and fixes latent non-root cases (e.g. synapse, pixelfed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 13:57:30 +02:00
kevinveenbirkenbachandClaude Opus 4.8 c1cc7e35a7 fix(web-app-erpnext): escape $@ as $$@ so docker stack deploy accepts the backend entrypoint
docker stack deploy interpolates $ in the compose file and rejects a bare $@ ("invalid interpolation format"); docker compose tolerates it. $$@ normalises to $@ in both, so the entrypoint is compose- and swarm-compatible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 13:57:30 +02:00
kevinveenbirkenbachandClaude Opus 4.8 96aa7784b6 feat(sys-svc-compose): scale all task timeouts via TIMEOUT_FACTOR lookup
Add a 'timeout' lookup plugin that returns round(base * TIMEOUT_FACTOR)
(group_vars default 1, so value-identical to the old literals). One knob
in group_vars stretches every wait in the stack for a slow uplink:
TIMEOUT_FACTOR: 2 doubles them all.

- plugins/lookup/timeout.py + unit tests (identity, factor scaling,
  string coercion, error paths).
- group_vars/all/17_resource.yml: TIMEOUT_FACTOR: 1.
- Convert all 34 literal 'timeout:' task keywords / module args across 25
  task+handler files to "{{ lookup('timeout', <base>) | int }}".
- tests/lint/repository/yaml/test_timeout_uses_lookup.py enforces the
  lookup in every task/handler timeout (opt-out: # nocheck: timeout-lookup).
- swarm.yml pre-deploy build handler: add the missing timeout (same
  network-build hang vector as compose.yml's Build compose).
- Drop 13 pre-existing narration comments in the touched files (comment
  lint enforces whole-file on any edited file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 13:57:30 +02:00
kevinveenbirkenbachandClaude Opus 4.8 db1002b743 fix(sys-svc-compose): 60min timeout on compose pull + build handlers
A network pull with no time bound can hang indefinitely: run10 sat 6h
in 'docker compose build --pull' for bbb with no new output, and the
streaming monitor stayed silent because a hang emits no log lines.

Add timeout: 3600 to both network-pull handlers (compose pull, Build
compose) so a stuck pull self-aborts at 60min and the deploy fails
loudly instead of hanging forever.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 13:57:30 +02:00
kevinveenbirkenbachandClaude Opus 4.8 9697c9e2f7 fix(ci-swarm): collect playwright reports via docker exec not docker cp
/var/lib/infinito on the swarm manager is an NFS mount inside the container's own mount namespace. docker cp reads the daemon-side rootfs and sees the empty mountpoint, so it copied zero files and the uploaded Playwright artifact was always empty. Stream the reports out through docker exec (in-namespace), the same access pattern the swarm diagnostics already use.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 13:57:29 +02:00
infinito-nexus-updater[bot] 1e1813c382 update(images): update Docker image versions (web-app-mediawiki) 2026-07-03 11:15:31 +00:00
kevinveenbirkenbach d23ba93408 Merge pull request #374 from infinito-nexus/update/skills-20260703-219b046
update(skills): update agent skills to latest versions
2026-07-03 13:12:15 +02:00
kevinveenbirkenbach 8bf6aa4e7d Merge pull request #369 from infinito-nexus/update/docker-image-versions-web-app-erpnext-20260703-3382b1e
update(images): update Docker image versions (web-app-erpnext)
2026-07-03 13:11:17 +02:00
infinito-nexus-updater[bot] 55f5b5dfdc update(skills): update skills-lock.json to latest versions 2026-07-03 11:11:04 +00:00
kevinveenbirkenbach 7fed5ea8a1 Merge pull request #371 from infinito-nexus/update/docker-image-versions-web-app-opentalk-20260703-596632f
update(images): update Docker image versions (web-app-opentalk)
2026-07-03 13:10:31 +02:00
kevinveenbirkenbach dd02cdb958 Merge pull request #372 from infinito-nexus/update/docker-image-versions-web-app-peertube-20260703-fb51a1b
update(images): update Docker image versions (web-app-peertube)
2026-07-03 13:10:08 +02:00
kevinveenbirkenbach 33e4215e3e Merge pull request #373 from infinito-nexus/update/docker-image-versions-web-app-prometheus-20260703-9ae9357
update(images): update Docker image versions (web-app-prometheus)
2026-07-03 13:09:45 +02:00
infinito-nexus-updater[bot] a15062260d update(images): update Docker image versions (web-app-prometheus) 2026-07-03 11:08:24 +00:00
infinito-nexus-updater[bot] d69652bb84 update(images): update Docker image versions (web-app-peertube) 2026-07-03 11:08:20 +00:00
infinito-nexus-updater[bot] b7b77ecd5c update(images): update Docker image versions (web-app-opentalk) 2026-07-03 11:08:16 +00:00
infinito-nexus-updater[bot] bcc3f740c9 update(images): update Docker image versions (web-app-erpnext) 2026-07-03 11:08:06 +00:00
kevinveenbirkenbach ab0517efea Merge pull request #367 from infinito-nexus/dependabot/github_actions/docker/login-action-4.3.0
chore(deps): bump docker/login-action from 4.1.0 to 4.3.0
2026-07-03 11:53:00 +02:00
kevinveenbirkenbach 61283b14ef Merge pull request #368 from infinito-nexus/dependabot/github_actions/docker/setup-buildx-action-4.2.0
chore(deps): bump docker/setup-buildx-action from 4.0.0 to 4.2.0
2026-07-03 11:52:35 +02:00
Alejandro Roman 48c49b8c7c Merge remote-tracking branch 'upstream/main' into feature/role-stalwart-mail 2026-07-03 07:26:30 +02:00
Alejandro Roman 948b5f2f84 fix error in deploy 2026-07-03 06:58:17 +02:00
dependabot[bot] c15ac75044 chore(deps): bump docker/setup-buildx-action from 4.0.0 to 4.2.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 4.0.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd...bb05f3f5519dd87d3ba754cc423b652a5edd6d2c)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-03 01:15:41 +00:00
dependabot[bot] 063b653011 chore(deps): bump docker/login-action from 4.1.0 to 4.3.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.1.0 to 4.3.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/4907a6ddec9925e35a0a9e82d7399ccc52663121...c99871dec2022cc055c062a10cc1a1310835ceb4)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-03 01:15:37 +00:00
kevinveenbirkenbachandClaude Opus 4.8 0d84f30ed5 fix(web-app-erpnext): satisfy make quality after the swarm fix
Follow-up to the swarm CID/redis fix that make quality flagged:
- Extract the site-bootstrap sequence into 02b_create_site.yml (renamed from
  02b_restart_frappe.yml; now new-site + restart + re-resolve + wait) and include
  it with when: rc \!= 0, so the oversized when-block disappears and the sub-file
  is not parsed at all when the site already exists.
- The backend entrypoint now execs the image's real /usr/local/bin/entrypoint.sh
  after the config-ready gate, so the sites/assets symlink that entrypoint creates
  is preserved instead of relying on a sibling replica to recreate it.
- Drop the now-dead ERPNEXT_BACKEND_CONTAINER_ADDRESS var, shorten two over-long
  OIDC restart task names, and remove stale narration comments.

Also suppress the INFINITO_MAIN_REMOTE/BRANCH env lints in the git pre-push helper:
they are maintainer tooling knobs, not deployment keys.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 02:13:51 +02:00
kevinveenbirkenbachandClaude Opus 4.8 38a3e4a9e2 fix(web-app-erpnext): reach the backend on its real node and gate workers on redis config in swarm
The manager-op tasks ran 'container exec {{ ERPNEXT_BACKEND_CONTAINER_ADDRESS }}'
where the address resolves a LOCAL container id. Under swarm the backend task can
run on another node (and 'service update --force' reschedules it), so the id came
back empty, collapsing the command and failing 'apply API-bot' with a bogus 'No
such file'. Resolve the backend's hosting node + local id via resolve_host_cid,
delegate_to that node, and re-resolve after each forced restart. The four apply_*.py
tasks can no longer use a host '< file' redirect once delegated, so feed the script
through 'stdin:' instead; the OIDC on-change restart splits into compose vs swarm.

docker stack deploy strips depends_on conditions, so queue/websocket/scheduler
booted before the one-shot configurator wrote redis_* into common_site_config.json
and wedged on frappe's 127.0.0.1 defaults. Gate each worker (and the backend
entrypoint) to block until redis_socketio appears, closing the race in swarm and
staying a no-op in compose where the config already exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:47:23 +02:00
kevinveenbirkenbachandClaude Opus 4.8 e070bb2d8d ci: fail when upstream main is not merged into the branch
Adds scripts/git/assert_main_merged.sh, which fetches the upstream main
tip and fails unless it is fully merged into HEAD. Wired in two places:

- pre-push: a new make-test-main-merged pre-commit hook (pre-push stage)
  runs 'make test-main-merged' before every push.
- pull request: a new assert-main-merged job checks out the real PR head
  (not the merge ref) and asserts the base branch is merged into it, so a
  PR whose branch lags main goes red.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:47:22 +02:00
kevinveenbirkenbach 4f70eb12e8 Merge branch 'main' into feature/web-app-jellyfin 2026-07-03 01:30:25 +02:00
kevinveenbirkenbachandClaude Opus 4.8 e0559a10d8 feat(ci): configurable lifecycle envelope + mode selection
Lifecycle envelope for test-deploy discovery is now driven by
INFINITO_LIFECYCLES (default: alpha beta rc stable) instead of a
hardcoded constant. apps.sh forwards it to the complexity CLI via a
new --lifecycles flag (comma- or whitespace-separated); the model
falls back to the built-in default when unset.

Manual CI (entry-manual) gains two override fields: 'lifecycles' to
override the envelope per run, and 'modes' (default 'swarm compose')
to pick which deploy jobs run. ci-orchestrator gates test-deploy-compose
and test-deploy-swarm on the selected modes and threads lifecycles into
both. The new key is registered in the passthrough handler so it lands
in the generated .env.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:11:20 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0ec8f41772 chore(make): add quality + autoformat-restage targets
quality runs autoformat then the full test suite in one shot as a
pre-commit gate.

autoformat-restage runs autoformat and re-stages files it rewrote that
were already staged, but only when the working tree held no unstaged
tracked changes beforehand -- so a partially-staged hunk is never swept
into the index. Logic lives in scripts/git/autoformat_restage.sh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:11:20 +02:00
kevinveenbirkenbachandClaude Opus 4.8 82f4baa42f fix(web-app-funkwhale): pass the Typesense API key on the command line for swarm
The embedded typesense sidecar crashed under 'docker stack deploy' with 'API key
is not specified': its command set no --api-key and relied on an env_file value,
but stack deploy ignores env_file and funkwhale's env.j2 never set
TYPESENSE_API_KEY anyway. Declare a funkwhale-owned api_key credential and pass it
as --api-key on the command line (honored by both compose and stack deploy). In
compose the sidecar stays profiles-gated off, so this only changes the swarm path.

Apply the same fix to the standalone svc-db-typesense engine, which has the
identical latent gap (its key already lives in vars, so --api-key just mirrors the
env value and CLI precedence makes it a no-op in compose).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:11:20 +02:00
kevinveenbirkenbachandClaude Opus 4.8 9bf35d672a fix(xwiki): rotate the swarm config name so the final deploy converges
In the swarm-DinD test the ansible controller (ansible_connection: docker) cannot read the STACK_HOST-rendered xwiki.properties, so compose_volumes' content hash falls back to the source path and the config name is constant. xwiki deploys twice (bootstrap with the OIDC block off, final with it on after the extension installs), so the config content changes but the name does not, and the final docker stack deploy fails to update the immutable config with 'only updates to Labels are allowed'.

Honor an explicit name: on config/secret entries in compose_volumes (volumes already did), and give xwiki's properties config a name that rotates with the OIDC phase, so the final deploy creates a new config instead of updating in place. Backward-compatible: no other role sets name: on a config. make test green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:11:19 +02:00
kevinveenbirkenbach 5ca10de67d Merge branch 'main' into feature/web-app-checkmk 2026-07-03 01:03:37 +02:00
kevinveenbirkenbachandClaude Fable 5 1fd7e21b3a fix(web-app-checkmk): inject a reachable logout control for the SSO variant
Checkmk's oauth2-proxy header-auth GUI exposes no logout control the shared
Playwright personas can reach: the classic GUI is iframe-based, its top-frame
toolbar is bare <div>/<a> (no <header>/<nav> anchor), and the native logout
hides behind the 'User' menu. So the biber and administrator personas failed at
'app -> universal logout' with 'no in-app logout control reachable'.

Add a 'javascript' service (gated on services.sso.enabled) so the platform
inlines templates/javascript.js.j2 into every Checkmk response. The script marks
a logout control with data-injected-logout (which the persona helper checks
first): it appends a themed icon to a header/nav group when one exists, else
falls back to a fixed-position top-right button on the top-frame <body>. The
control points at OIDC.CLIENT.LOGOUT_URL so a click clears both the Checkmk and
the Keycloak/oauth2-proxy session. Inline injection is admitted by Checkmk's own
CSP ('unsafe-inline'). Verified in-container before redeploy, then full-matrix
reinstall: V1 6 passed (incl. biber+admin logout), V2/V3 2 passed/4 skipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 00:53:15 +02:00
kevinveenbirkenbachandClaude Opus 4.8 84296b2451 chore(claude): allow WebFetch to kix + listmonk release domains
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:06 +02:00
kevinveenbirkenbachandClaude Opus 4.8 7fce11867c fix(compose_volumes): drop orphan top-level config/secret when all mounts are gated off
compose_volumes emitted a top-level configs:/secrets: entry for every declared
config/secret, but container_volumes already gates each per-service reference on
its mount's when:. So a config whose only mount was gated off (e.g. LDAP
disabled) became an orphan: a top-level object no service references, whose file:
source points at a file the role never renders (same flag). Under docker stack
deploy that source is read at deploy time and the deploy fails; under compose it
was a harmless unused entry.

Gate the top-level emission on the same mount_when_passes container_volumes uses,
so a top-level config/secret exists iff a service actually references it. To share
the predicate, promote the private _mount_when_passes from container_volumes into
utils/roles/applications/mounts and import it in both filters; the bare except
moves with it (inline noqa), so container_volumes no longer needs its BLE001
per-file-ignore.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:06 +02:00
kevinveenbirkenbachandClaude Opus 4.8 aa8e3a9120 fix(web-app-opentalk): make embedded object store and kcadm provisioning swarm-safe
The embedded minio/seaweedfs object store emitted only compose-native resource
keys (cpus/mem_limit) and no replica pin, so under 'docker stack deploy' it got
the default replica count and raced its own state across nodes. Gate the
compose-native resource block to compose and add a swarm deploy: block pinned to
replicas=1 (deploy.yml.j2 self-gates to swarm, so compose emits nothing new).
Give the embedded seaweedfs an explicit name to avoid colliding with a
standalone web-app-seaweedfs sharing the cluster.

The kcadm provisioning tasks ran 'container exec' against a local container id
that only exists on the node hosting Keycloak; in swarm that node is not
necessarily the one running the play. Resolve the Keycloak host node and its
local container id, delegate_to that node, and split the controller restart into
'container restart' (compose) vs 'container service update --force' (swarm).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:06 +02:00
kevinveenbirkenbachandClaude Opus 4.8 bd65cb3823 fix(web-app-moodle): deliver mail through Mailu on every swarm host
moodle mail failed two ways in a swarm cluster. (1) moodle's build_context
pulled the FULL sys-svc-mail-msmtp role early, which rendered /root/.msmtprc on
the manager before Mailu had created the no-reply token and tripped the role's
run-once flag, so the manager kept an empty-password msmtprc and the health
check failed 'authentication method PLAIN needs a password'. Pull only
tasks_from: ca_bundle so the manager re-renders on the normal post-token pass.
(2) msmtprc forced host=127.0.0.1 for any in-container host, but the NFS server
sits outside the swarm mesh and has no local Mailu, so it hit 'connect to
127.0.0.1:465 refused'. Narrow the 127.0.0.1 branch to compose or a real
svc-swarm-node AND to when Mailu is actually deployed (email.external); else use
email.host, and map the NFS node's test DNS to the manager IP. Also import the
shared self-signed Root CA for the relay's TLS trust.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 e2f3685ed7 fix(web-app-mastodon): migrate via a one-shot service and DRY the compose template
The role flushed handlers to start web/streaming/sidekiq and only then exec'd
'rails db:migrate' into the running web container, so on a fresh DB the app
booted against an unmigrated schema (swarm converge gate timed out) and the
compose migration was OOM-killed sharing the 2g web cgroup with the live puma
server. Add a dedicated one-shot migrate service (own 2g container, db:prepare,
replicas 1) that web/streaming/sidekiq depend on (service_completed_successfully
in compose; swarm converges via crash-restart + the run-once gate). Refactor the
four near-identical service blocks into a mastodon_service macro.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 cc5f13b7a7 fix(web-app-listmonk): let the container self-install and pin to v6.2.0
The stock image CMD exits on an empty DB ("Run --install."), so the server
crash-looped on first deploy while the role exec'd 'yes | ./listmonk --install'
into that same crashing container, which was SIGKILLed (rc 137) and never
converged. Adopt listmonk's own recommended container command (idempotent
install, upgrade, then serve; the super admin is seeded from LISTMONK_ADMIN_USER
/ _PASSWORD), and replace the exec dance with a DB-reachability probe plus a
wait for the users table to be seeded. Pin latest to the current stable v6.2.0
so the CLI/schema contract cannot drift. Same flow in compose and swarm.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 9789f22583 fix(web-app-taiga): resolve gateway upstreams by swarm-compatible service name
The gateway nginx hardcoded compose container_names (taiga-front/back/...) that
do not exist as DNS names in swarm, so it crash-looped on [emerg] host not found
in upstream. Point the upstreams at the service keys (which resolve in both
compose and swarm), render the role gateway config unconditionally (gating only
the OIDC location block), pin the protected service name to its SPOT var, and
add the back service to the async depends_on so it waits for the shared image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 b9665f1f58 fix(web-app-penpot): pin real image versions and allowlist the OIDC host for SSRF
The version pin sat on the app-level penpot entry, which container_image never
reads, so the frontend/backend/exporter deployed :latest. Since Penpot 2.15 the
backend SSRF-blocks outbound HTTP to hosts resolving to private IPs, so the OIDC
token exchange to the privately-resolving Keycloak failed with unable-to-auth
and login never completed. Pin the three image-bearing services to 2.16.2 (the
release CI actually exercised) and set PENPOT_SSRF_ALLOWED_HOSTS to the Keycloak
host (https? strips either scheme). env.j2 stays shared by compose and swarm.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 45a7d500c8 fix(web-app-mailu): only pin the central redis VIP when redis is shared
mailu_admin uniquely runs on public DNSSEC resolvers, so internal service names
are pinned to overlay VIPs via extra_hosts. That pinning assumed a central redis
stack; with a local redis sidecar MAILU_SWARM_REDIS_HOST is the bare name
'redis', and pinning it to an absent central VIP shadowed stack DNS, so admin
blackholed its first redis dial and was SIGKILLed unhealthy in a loop (blocking
chess and mini-qr). Gate the redis VIP inspect, the fact, and the extra_hosts
line on the redis engine not being local. The shared-redis path is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 b25e34ca5e fix(web-app-bluesky): stage pdsadmin before deploy and keep it a bind mount
The pdsadmin binary is mounted from the volumes dir, but 02_pds.yml ran only
after the sys-stk-front-proxy include whose flush already deploys the stack, so
the deploy aborted on the missing source. Move the PDS staging ahead of the
front-proxy block. Also revert pdsadmin from a docker config back to a bind
mount: it is a ~9MB Go binary and swarm configs are capped at ~1MB, so type:
config fails stack deploy. The pds service is manager-pinned, so the
manager-staged bind source is always present on the node that runs it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0b3ab77183 fix(web-app-kix): allow a longer swarm converge budget for the backend
The KIX backend runs a FRESH_INSTALL schema bootstrap that outlives the default
150x2s converge budget on loaded CI runners (db reaches 1/1 while the backend
stays 0/1 Starting), so the swarm converge gate timed out. Make the gate's
retries overridable (default unchanged at 150) and raise it to 600 for kix.
Swarm-only gate; compose is unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 1ca9cf5b07 fix(web-app-roulette-wheel): install from the committed lockfile
The build copied only package.json and ran npm install, discarding the
upstream package-lock.json (present at the repo root on master); npm then
fresh-resolved @types/node to a major that the app's pinned TypeScript cannot
parse, failing 'npm run build' with TS1110. Copy the lockfile and use npm ci
so the pinned dependency tree is honoured. Same build path in compose and swarm.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 37fc95adf5 fix(web-svc-libretranslate): use the image's own healthcheck script
The shared curl healthcheck probes with curl, which the upstream
libretranslate image (python slim) does not ship, so every replica was killed
unhealthy (exit 137) even after gunicorn was listening and the stack never
converged. Use the image's bundled scripts/healthcheck.py, which passes during
the two-phase argos-model cold boot and probes /health afterwards. Renders
identically for compose and swarm.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 f5d4f474f9 fix(svc-prx-openresty): retry a forced swarm redeploy on stale update status
When the forced OpenResty rollout handler fires twice in one play, the second
'service update --force' can submit a stale object version and the daemon
rejects it with 'update out of sequence', failing the deploy. Retry until the
CLI reports success; any other stderr fails on the first attempt. Swarm-only
and stack-host gated, so compose is byte-identical.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 7c6c56df70 fix(web-app-matomo): gate web-svc re-injection on the rendered body_filter Lua
The re-injection loop stat-checked each web-svc consumer's compose instance dir,
which proxy-only vhosts (web-svc-html) never have, so they were skipped and kept
a placeholder matomo token, failing the Playwright tracker assertion. Stat the
per-consumer body_filter Lua that sys-front-inj-all renders (the identical
lookup used to write it), so every deployed+injected consumer is re-rendered
with the real token. compose-dir-exists implies lua-exists for all six
candidates, so no consumer that was re-injected before is now skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 1d70a71259 fix(web-app-discourse): dedent Jinja block tags in config.yml.j2
With trim_blocks on, the two-space indented block tags leaked their leading
whitespace into the rendered output, prepending spaces to the next line and
producing a mis-indented key inside the env: mapping that broke Psych YAML
parsing on a fresh wordpress+discourse deploy. Move the block tags to column 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 6c87cbbf2f fix(web-app-yourls): render env as dotenv KEY=value + lint the format
The env file was written in YAML colon syntax; docker compose's lenient parser
tolerated it but docker stack deploy's kvfile reader rejects the colon lines and
aborts the swarm deploy. Convert to canonical KEY=value (dotenv_quote on the two
passwords) and add a repo-wide lint (env-file-dotenv-syntax) so no env.j2
regresses to YAML mapping syntax.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:26:05 +02:00