SEMAPHORE_ADMIN_PASSWORD resolves to the web-app-semaphore-breakglass user password, and the deploy hands it to Semaphore only when the account is first created. The swarm matrix rotates credentials between the sync and the async pass, and --except administrator exempts the literal user key 'administrator', not this one, so the async pass typed a password the app had never been told about. In run 33923190220 semaphore#1 failed with 'Incorrect login or password' after 15.6 minutes of onion-scaled retries.
The realign runs ungated by SSO: the break-glass account exists precisely for the case where the OIDC path is unavailable. Both account operations now share one stack-host block instead of repeating the condition.
Verified end to end against semaphore v2.19.12 on postgres: the CLI accepts --password as a non-interactive flag, change-by-login reports the user changed, the stored hash moves, and it then verifies against the rotated password and no longer against the seeded one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add tests/lint/repository/test_task_file_numbering.py: numbered task
files use exactly two leading digits, a numbered *_core.yml must be
00_core.yml, per directory the numbers are unique/consecutive and start
at 01 (00 with a core file), numbered includes appear in ascending
order, and shared helpers live in tasks/utils/ instead of the numbered
chain.
Apply it across every role: rename NN_core.yml to 00_core.yml with a
consecutive down-shift of the siblings, move letter-suffix children into
a subdirectory named after their parent task, relocate helpers included
from more than one place into tasks/utils/, and update every reference
(include_tasks, path_absolute, .ansible-lint-ignore, requirement docs).
Legacy narration comments in the touched files are deleted; the upstream
xwiki.properties reference block opts out via nocheck. The unused
MAILU_IP6_PUBLIC var (only ever named in now-removed comments) is dropped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The OIDC admin provisioning ran `container exec {{ SEMAPHORE_NAME }}` with the
bare compose name, which does not exist under docker stack deploy. Resolve the
hosting node + local container id and delegate the execs there. The three
tasks that shared the IS_STACK_HOST + SEMAPHORE_OIDC_ENABLED guard move into
02_oidc.yml, included once under that condition.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sys-svc-compose already flushes compose handlers (and runs post-deploy) when the including role passes compose_handlers_flush: true. Roles that set it false and then immediately hand-roll their own meta: flush_handlers just reimplement that flush. Add an adjacency-scoped lint that catches the back-to-back form (a deferred flush with real work between the include and the flush stays legitimate and keeps false), and collapse the five offenders (checkmk, chess, jellyfin, pihole, semaphore) to compose_handlers_flush: true.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On redeploy 'semaphore user add --admin --external' panics with a Postgres unique-constraint violation (duplicate key ... user_username_key), whose message lacks the word 'exist' the failed_when guard looked for, so the task failed. Skip the add when the login already appears in 'semaphore user list', and tolerate 'duplicate key' in failed_when as a safety net.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements requirement 028: web-app-semaphore deploying semaphoreui/semaphore
v2.18.13 behind sys-svc-proxy with central PostgreSQL, native Keycloak OIDC
SSO, and LDAP login, plus guest/biber/administrator Playwright personas.
Auth/admin model verified against the upstream v2.18.13 source (config.go,
api/login.go, api/router.go, web/src/views/Auth.vue, config.schema.yaml,
cli/cmd/user_add.go): the OIDC callback matches by email and rejects local
users, the login form is LDAP-only when ldap_enable=true, and OIDC users are
never admin from a claim. The role therefore seeds a distinct break-glass
local admin and creates the Keycloak administrator as an external admin at
deploy time (semaphore user add --admin --external). Correct v2.x env names
(SEMAPHORE_LDAP_ENABLE/_SERVER/_BIND_DN/_BIND_PASSWORD/_SEARCH_DN, OIDC
provider JSON) confirmed against the source.
Validation: make autoformat clean; make test green (lint, test-lint,
test-unit, test-integration 190, test-external). Live full-matrix deploy +
Playwright still pending (host container runtime fault, environment-only).
Refs: docs/requirements/028-web-app-semaphore.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>