Commit Graph
23 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 93cbd2045e fix(web-app-penpot): pass the administrator password on stdin
manage.py create-profile and update-profile parse their options with
argparse, and `-p <value>` reads a value that starts with `-` as the next
option. A generated password with a leading dash therefore fails both
commands with exit 2 ("argument -p/--password: expected one argument")
before they reach the backend, and the bootstrap task retries until it
gives up. On main this broke the guide run of Penpot#2 in run
39668792538: the Docker events of the rescue snapshot show eight
create-profile execs ending with exit 2 while the backend was healthy
and its prepl received no call.

Without -p, manage.py reads the password with getpass, which falls back
to stdin when the exec has no TTY. Both tasks now feed the password to
`container exec -i` on stdin. This also keeps the password out of the
exec command line, which Docker writes into its event stream and
therefore into the rescue diagnostics artifacts.

The password-quote lint masked only block `stdin: |` / `stdin: >`
values and flagged the inline `stdin: "{{ PENPOT_ADMIN_PASSWORD }}"` as
an unquoted password in a shell command. stdin never reaches a shell, so
the lint now masks every `stdin:` form; an unquoted password in `cmd`
is still reported.

Fixes #1139

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 17:41:43 +02:00
kevinveenbirkenbachandClaude Opus 5 ceb7e9e628 fix(penpot,matrix,n8n,swarm-drill): repair four CI root causes
From triage of run 33923190220. Each fix is independently complete.

penpot: ansible templates delegate_to before the task's merged when, and AnsibleUndefinedVariable escapes get_delegated_vars_and_hostname. PENPOT_BACKEND_HOST_NODE is set only by the resolve include, so the bootstrap task hard-failed on every host that include skipped instead of skipping with it. A block-level when cannot prevent this; move the tasks into a dynamic include.

matrix: the MDAD reconfigure looked for volume matrix_matrix_mdad_state (real name matrix_mdad_state) and container matrix-mdad (real name matrix). Both if-bodies were skipped while the task still reported changed, so a rotated S3 secret never reached synapse and every media write got SignatureDoesNotMatch. Move the shell into its own script and pass the real names through the environment. Also add no_log to the vars.yml render, whose diff printed 16 secrets in cleartext.

n8n: when the settings file and N8N_ENCRYPTION_KEY disagree, oclif's findCommand throws for every command, so 'n8n export:credentials' is unreachable in exactly the state that would need it. The re-key is one-way; pin it rotatable: false.

swarm-drill: distros.sh sees only rc=124 and cannot tell a wedged deploy from one the sweep deadline cut short. Say so where the governor caps it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:46:34 +02:00
kevinveenbirkenbachandClaude Opus 5 c750247e33 fix(joomla,yourls,penpot): exec on the node that holds the container
container exec is node-local: sys-svc-container/files/python/container.py dispatches docker exec with no swarm routing. These roles passed *_CONTAINER_ADDRESS, which names the service cluster-wide, and ran from the stack host, so a call landed on the manager and answered "No such container" whenever the task sat on a worker.

Each branch now resolves the hosting node and its node-local id once through sys-svc-compose/tasks/utils/swarm/resolve_host_cid.yml, the way web-app-baserow already does, and every exec uses *_LOCAL_CID with delegate_to on *_HOST_NODE. Compose is untouched: resolve_host_cid sets *_HOST_NODE to inventory_hostname there, so the delegation is to self and resolve_compose_cid passes the old address through.

penpot resolves only when native login is enabled, because the resolver waits up to 600x2s for convergence and both of its exec tasks are already gated on that flag.

The *_LOCAL_CID names come from a runtime set_fact, which the resolver lint cannot approve, so each scanned call site carries the nocheck the migrated mastodon sites already use. Measured with the lint parser itself: 19 exec lines carry a target, 19 would be flagged, 19 are suppressed. No nocheck reaches a rendered command or a task name, and the one comment that does land inside a bash body passes bash -n.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 13:47:35 +02:00
kevinveenbirkenbachandClaude Opus 5 575a5a87ac refactor(layout): sort shipped scripts by language and split the unit suite
Every script a role ships now lives under `files/<language>/`, and the unit
suite is split the same way: `tests/unit/python/` beside new sibling trees for
the other languages. `tests/lint/filesystem/test_role_files_layout.py` keeps it
that way; directories whose shape is dictated from outside carry a `.nocheck`
naming the constraint (Docker build contexts, WordPress mu-plugins, the Joomla
extension, Odoo addons, Playwright suites, the `files/test/test.sh` harness
that `test-e2e-cli` resolves generically).

This cannot be applied in halves. Moving a file and repointing what names it
are the same change: 14 references resolve only at deploy time, so the tree is
green either before or after, never between.

Those 14 were found by static resolution over every reference class, not by the
test suite -- a full `make quality-high` was green while all of them were
broken:

  * 7 `src:` values built from a role variable holding a bare filename
    (svc-bkp-remote-2-local, decidim, espocrm, friendica, joomla, pixelfed x2).
    The variable doubles as the destination name inside the container, so the
    language segment belongs at the call site, not in the variable.
  * `sys-service/tasks/04_base.yml` resolved every consumer's systemd script
    through `first_found(..., errors='strict')` over `files/script.{sh,py}`.
    18 roles ship exactly that and have no template fallback: the play would
    have died on each.
  * `sys-front-inj-javascript` fell back to `<role>/files/javascript.js`; the
    interface now reads `files/javascript/javascript.js`.
  * `sys-front-inj-logout/templates/head_sub.j2` built the cache-busting mtime
    path through `path_absolute(... 'files', ...)`.
  * 4 `ansible.builtin.script:` invocations naming a bare command
    (pull.py, prune.sh, set_postgres_superuser_password.sh, ganesha-serve.sh).
  * `dev-python/files/shell/install.sh` pointed at a sibling role's script.

Depth-dependent paths inside the moved trees were bumped accordingly: 59
`parents[N]` computations, 12 relative climbs, and the per-role recover CLI,
whose path `cli/administration/recover/recoverers.py` still assembled from the
pre-move location.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 01:31:09 +02:00
kevinveenbirkenbachandClaude Opus 5 a46348b6cf refactor(handlers): bind notify to a listen topic, not to a name
A handler name was doing two jobs at once: the line an operator reads in the
play output, and the identifier a `notify:` binds to. The two pull in opposite
directions -- the log wants "restart the SSH service, with diagnostics", the
binding wants a token that survives quoting, YAML flow style and a shell
round-trip -- and a name that changes for readability silently changes which
handler runs.

Every handler that a notify reached by name now carries a `listen:` with that
exact wording, so the binding is explicit before anything is renamed. The
topics themselves become identifiers: `compose up` reads `compose-up`,
`Restart NFS ganesha` reads `restart-nfs-ganesha`, 22 of them across both
sides of every notify. Where the topic is assembled at runtime, both branches
moved together: the conditional in sys-service carries two literals, and the
LDAP renderer interpolates `import-{{ folder }}-ldif-files`, which is what
makes those handlers reachable at all.

Freed from the binding, the names take the emoji prefix every other task line
already has.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 03:38:42 +02:00
kevinveenbirkenbachandClaude Opus 4.8 11efe2cfdf feat(sys-ca-selfsigned): shared java-with-ca-trust wrapper for JVM roles
One entrypoint wrapper imports the project CA into the JVM truststore for penpot and jenkins: env-driven (CA_TRUST_CERT from sys-svc-compose-ca, set in both modes), fail-open without a cert (production), fail-loud with one; writable cacerts are patched in place, read-only layouts fall back to /tmp + JAVA_TOOL_OPTIONS. Replaces penpot's inline compose command and jenkins' role-local entrypoint, which silently skipped the import when cacerts were not writable. ca_cert_container becomes a plain constant since its only Jinja consumer moved into the wrapper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 22:18:21 +02:00
kevinveenbirkenbachandClaude Opus 4.8 8130c7fa49 fix(penpot): drop redundant | quote from container exec target
PENPOT_BACKEND_CONTAINER_ADDRESS resolves to a bare container id/name; | quote is redundant and inconsistent with the rest of the swarm container-exec pattern. Also remove a cosmetic comment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 01:56:37 +02:00
kevinveenbirkenbachandClaude Opus 4.8 cddb0dc59b feat(lint): require emoji-prefixed task/step/job names + convert existing
Add tests/lint/repository/test_name_emoji_and_length.py: every Ansible task
name and GitHub Actions workflow/job/step name must start with an emoji (first
char not 0-9/A-Za-z) and be <= 80 chars.

- emit emoji names at the source: cli/build/include generator now writes
  "🔧 setup <app>" / "🚿 flush handlers after <app>" so generated
  tasks/groups/*-roles.yml satisfy the convention too
- convert existing task/step/job/workflow names to emoji-prefixed, choosing a
  task-fitting icon per action (📦 install, ✅ ensure, 🔍 probe, 🥾 bootstrap,
  🚀 deploy, 🧩 service-module load, ⬇️ checkout, 📂 include-routines, ...)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 22:45:06 +02:00
kevinveenbirkenbach 29519603ef Merge remote-tracking branch 'origin/main' into feature/swarm-nfs 2026-06-14 20:32:59 +02:00
evangelostsak e7a799f542 feat(web-app-penpot): disable native login under OIDC (Kevin review)
- meta/services.yml: new `penpot.native_login` toggle, default off when
  web-app-keycloak is present (forces SSO), on otherwise.
- vars: PENPOT_FLAGS renders enable-/disable-login-with-password from it.
- tasks: administrator local-password bootstrap gated on native_login.
- playwright: test-login-native.js skips when `sso` is enabled.
- meta/main.yml: drop the Figma parenthetical from the description.
- README + req-023 updated.
2026-06-10 15:35:03 +02:00
evangelostsak e88a102354 fix(web-app-penpot): drop dead default() flagged by lint
- tasks/main.yml: remove `| default('')` from changed_when on command
  results (stdout always set when the conditional fires) per
  test_no_redundant_default_on_module_io.
- env.j2: drop `| default('')` on LDAP.SERVER.SECURITY (defined
  unconditionally in group_vars/all/12_ldap.yml) per
  test_no_jinja_default_on_spot_path.
2026-06-09 23:39:38 +02:00
evangelostsakandClaude Opus 4.8 532c2893d6 feat(web-app-penpot): native local-password login + docs
Bootstrap a local password for the administrator profile via the backend
PREPL (enable-prepl-server) so native email/password login works alongside
OIDC/LDAP. Adds enable-login-with-password and a registration toggle driven
by services.penpot.registration_enabled (off once OIDC is the login path).

- vars: PENPOT_FLAGS gains password login + prepl + conditional registration;
  PENPOT_REGISTRATION_ENABLED and PENPOT_ADMIN_{EMAIL,PASSWORD,FULLNAME}.
- tasks: idempotent create-profile / update-profile bootstrap (no_log).
- playwright: test-login-native.js drives the local password Login button.
- README + requirement 023: native login, registration toggle, canonical
  domain penpot.design, CA-trust gating, spec split; tick project/asset
  coverage; fix header 235 -> 023.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:21:20 +02:00
evangelostsak edef635fbb feat(web-app-penpot): add Penpot design platform role (req 235)
Add a new `web-app-penpot` role deploying Penpot's frontend + backend +
exporter containers behind the standard reverse proxy, with shared
svc-db-postgres and a svc-db-redis sidecar. Wires native OIDC (Keycloak)
and LDAP (OpenLDAP) login via PENPOT_FLAGS, CSP/vhost via sys-stk-full,
backup-ready asset volume, marketplace tags, matrix variants
(all-on/all-off/LDAP-only), and a service-gated Playwright spec with
guest/biber/administrator personas plus separate OIDC and LDAP scenarios.

Implements docs/requirements/235-setup-penpot.md. Live-deploy DoD items
(fresh-host deploy, domain reachability, full in-container `make test`)
remain unverified in the sandbox and are left unchecked.
2026-06-06 08:22:13 +02:00
evangelostsak 4757a639b0 revert: clean branch to upstream main state
Remove the entire web-app-penpot role, all associated docs, and all other
changes accumulated on this branch. Working tree now matches origin/main
exactly. Commit history preserved for reference.
2026-06-06 08:22:13 +02:00
evangelostsak ba28a31e55 Remove Penpot pre-test health gate; rely on Playwright in-test OIDC retries 2026-06-06 08:22:12 +02:00
evangelostsak c2ffe56a68 Add health check for Penpot API before Playwright tests to ensure backend readiness 2026-06-06 08:22:12 +02:00
evangelostsak d2906a9f63 fix(web-app-penpot): clear Playwright failures on role success 2026-06-06 08:22:12 +02:00
evangelostsak 3a9c4efb68 fix(web-app-penpot): remove uri_retry waits - let Playwright handle timing
Removed both frontend and backend uri_retry waits from main.yml.
Penpot has no post-deploy Ansible configuration tasks, so these waits
serve no purpose and cause CI failures (502 Bad Gateway after 15min).

Pattern analysis of 10+ other roles shows uri_retry is only used when
post-deploy Ansible tasks need to configure the application (e.g., Gitea,
Odoo). Roles without such tasks (Taiga, Discourse, Matrix, Mattermost, etc.)
have no waits and rely on Playwright's generous built-in timeouts (60-120s).

Local test results: 4/4 Playwright tests passed in 1.3m
- CSP test: 3.7s
- Admin OIDC login/logout: 29.4s
- Biber OIDC login/logout: 18.8s
- Project creation: 24.0s

Fixes CI deployment timeout that occurred in runs 25045210187,
25053926236, and 25060875762 where backend API check failed with
502 after 181 retries (15 minutes).
2026-06-06 08:22:11 +02:00
evangelostsak d073a82a21 Replace fixed pause with active backend API polling
After analyzing CI runs 25045210187 and 25053926236:
- Even with 180s pause, SPA still showing loading screen (3 img tags)
- Frontend HTTP 200 doesn't guarantee backend is ready
- CI artifacts show backend takes longer to initialize

Solution: Actively poll backend API endpoint until it responds.
Using /api/main/methods/get-profile with uri_retry, accepting
200/401/403 status codes (auth errors mean API is up).

Max wait: 180 retries × 5s = 15 minutes
2026-06-06 08:22:11 +02:00
evangelostsak daf1f443bd Increase SPA initialization pause from 90s to 180s for CI
After analyzing CI artifacts from run 25045210187:
- Admin test: SPA still showing loading screen (3 img tags)
- Biber test: SPA loaded but backend returned Bad Gateway

90s was insufficient for both SPA initialization and backend
readiness in CI's resource-constrained environment. Increasing to
180s to provide adequate buffer for full stack initialization.
2026-06-06 08:22:11 +02:00
evangelostsak 4912aa79e7 fix(web-app-penpot): add 90s pause for SPA initialization
Root cause: Frontend HTTP 200 != JavaScript app ready
- Nginx responds immediately but Penpot SPA needs time to:
  * Load JavaScript bundles
  * Initialize ClojureScript/React app
  * Render OIDC login button
- CI error: 'No OIDC login button found' - button not rendered yet
- Tests were starting before SPA fully initialized

Solution: Add 90s pause after frontend HTTP check passes
- Gives CI environment time for resource-constrained initialization
- Addresses actual failure: button missing, not container readiness
2026-06-06 08:22:11 +02:00
evangelostsak 66ad32b835 fix(web-app-penpot): use docker exec to check backend health (port not published to host) 2026-06-06 08:22:11 +02:00
evangelostsak 7ef199a89c feat(penpot): add main.yml for web-app-penpot tasks and deployment 2026-06-06 08:22:10 +02:00