fix(ci): resolve date-buckets timezone + Testcontainers Docker failures (#476) #494
Reference in New Issue
Block a user
Delete Branch "fix/issue-476-ci-failures"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #476
What changed
Failure 1 —
date-buckets.spec.tstimezone (2 tests)Added
TZ: Europe/Berlinto the "Run unit and component tests" step env.startOfDay()usessetHours(0, 0, 0, 0)which applies the local timezone. The CI runner runs in UTC; Berlin midnight dates (+02:00) are 22:00 UTC the previous day, causingbucketByDayto return the wrong bucket.Verified:
TZ=UTC npx vitest run date-buckets.spec.ts→ 2 failures.TZ=Europe/Berlin→ 7/7 green.Failure 2 — Testcontainers Docker daemon not found (234 errors)
Added two env vars to the
backend-unit-testsjob:DOCKER_HOSTunix:///var/run/docker.sockTESTCONTAINERS_RYUK_DISABLEDtruerunner-config.yaml
Committed the existing (untracked) act_runner config to the repo. Documents the NAS runner configuration required for Docker socket availability. Must be deployed to the runner host alongside the act_runner binary.
Tests
TZ=UTC date-buckets.spec.tswas red before, is green after CI change ✅ab7fe81b2ato5512790d5a👨💻 Felix Brandt — Senior Fullstack Developer
Verdict: ✅ Approved
No application code changed. This is a pure CI config fix — exactly the right scope for a "two unrelated CI failures" bug.
What I checked
TDD discipline: The "red" phase for the TZ fix is documented in the PR description with exact verification steps (
TZ=UTC → 2 failures,TZ=Europe/Berlin → 7/7 green). Since the tests already exist indate-buckets.spec.ts, this is the correct TDD pattern for a CI environment fix — you can't write a new failing test when the failing test is already there. ✅Atomicity: Three commits, three distinct concerns (TZ fix, Docker env vars, runner config tracking). No bundling. ✅
No new production code:
ci.ymlandrunner-config.yamlonly. Zero risk of accidental behavior change. ✅One note (non-blocking): The
TZenv is scoped to the "Run unit and component tests" step. If a future test is added in a different step (e.g. a separate--runstep) that also uses timezone-sensitive code, it won't pick up the TZ. ApplyingTZ: Europe/Berlinat the job level instead of the step level would be belt-and-suspenders — but the current scoping matches exactly what's failing, so I won't flag it as a blocker.🏛️ Markus Keller — Application Architect
Verdict: ✅ Approved
Two targeted env var additions and committing the runner config. No architectural concerns.
What I checked
Infrastructure-as-code principle: Committing
runner-config.yamlto the repository is the right move. A self-hosted runner configuration that lives only on the NAS filesystem is invisible to the development process and gets lost on server reinstall. Tracking it here documents the requirement and makes it reproducible. ✅Scope: The CI config changes are minimal and surgical.
TZ: Europe/Berlinon the test step is the correct fix at the correct layer — the tests encode Berlin-time semantics, CI should match. ✅No documentation updates required: This PR adds no new services, no new packages, no schema changes, no API routes. The documentation update matrix produces zero required updates. ✅
Note for deployment: The PR description correctly notes that
runner-config.yaml"must be deployed to the runner host alongside the act_runner binary." This manual step should be logged somewhere (Gitea wiki, ops runbook) so it isn't forgotten after a future NAS rebuild.🔐 Nora "NullX" Steiner — Security Engineer
Verdict: ⚠️ Approved with concerns
The fixes are correct and the concern is about the Docker socket mount — this is a known trade-off, not a blocker, but it should be documented.
What I checked
DOCKER_HOST: unix:///var/run/docker.sock— This env var tells the JVM process to connect to the host Docker daemon via the Unix socket. It only matters if the socket is actually mounted (whichrunner-config.yamlensures). No issue here in isolation. ✅TESTCONTAINERS_RYUK_DISABLED: "true"— Ryuk is the Testcontainers watchdog that cleans up containers after test runs. Disabling it means orphaned containers can survive if the test process crashes. In CI (ephemeral environments), this is safe — the job container dies and takes its children with it. In a persistent dev environment it would be a concern. ✅ for CI.Docker socket mount in
runner-config.yaml(CWE-269 — Improper Privilege Management):Mounting the Docker socket into a container is effectively granting root access to the host, because any process inside the container can spawn privileged containers that mount host paths. For a self-hosted NAS running a private family archive, this risk is acceptable — the runner doesn't accept untrusted code.
Suggestion (non-blocking)
Add a comment to
runner-config.yamldocumenting the security trade-off:This is especially important if the NAS runner is ever opened to fork PRs.
🧪 Sara Holt — QA Engineer
Verdict: ✅ Approved
Both fixes are well-specified and the verification approach is correct given the constraints.
What I checked
TZ fix — verified locally: PR description shows exact red/green evidence:
TZ=UTC vitest run date-buckets.spec.ts→ 2 failures ✅ (confirmed in implementation)TZ=Europe/Berlin vitest run date-buckets.spec.ts→ 7/7 green ✅This is the appropriate verification method. You cannot write a new failing unit test for "CI runs in UTC" — the existing tests already encode the expected behavior. The fix is environmental. ✅
Testcontainers fix — CI-only: Correct assessment that this cannot be verified locally. The acceptance criterion ("Backend integration tests pass in CI") requires an actual CI run. The approach (
DOCKER_HOST+TESTCONTAINERS_RYUK_DISABLED) is the documented standard for Testcontainers in nested container environments. ✅No test regressions introduced: The changes are additive env vars. No test logic touched. ✅
One process note (non-blocking): After this PR merges, it would be worth tagging the first green CI run in a comment on the issue as verification that the Testcontainers fix works. Issue #476 acceptance criterion 2 can't be fully signed off until a CI run confirms it.
🎨 Leonie Voss — UX Designer & Accessibility Strategist
Verdict: ✅ Approved
No UI, frontend component, or accessibility changes in this diff. This is a CI infrastructure-only fix.
What I checked
This PR unblocks CI, which means PRs with accessibility improvements (like #493) can now be verified end-to-end in the pipeline. LGTM from a UX perspective.
🚀 Tobias Wendt — DevOps & Platform Engineer
Verdict: ✅ Approved
These are exactly the right fixes. Let me walk through each one.
TZ: Europe/Berlinon the test stepCorrect and minimal. Scoped to the one step that needs it, not broadcast to all steps. The CI runner image (
mcr.microsoft.com/playwright:v1.58.2-noble) is based on Ubuntu Noble which defaults to UTC — setting TZ explicitly at the step level is the clean solution. ✅DOCKER_HOST+TESTCONTAINERS_RYUK_DISABLEDBoth are the documented standard fixes for Testcontainers in a Docker-in-Docker or socket-forwarding environment:
DOCKER_HOST=unix:///var/run/docker.sock— tells Testcontainers where to connect. Without this, Testcontainers walks a detection chain that may fail in the runner container environment. ✅TESTCONTAINERS_RYUK_DISABLED=true— Ryuk tries to run as a privileged container; in environments where Docker permissions are limited, this fails and blocks all tests. Disabling it is safe in ephemeral CI environments. ✅runner-config.yamlGood to have this tracked. The config is correct:
docker_hostsetsDOCKER_HOSTinside job containers (belt-and-suspenders alongside the workflow-level env var) ✅valid_volumeswhitelists the socket path so the runner allows the bind mount ✅optionswith-v /var/run/docker.sock:/var/run/docker.sockensures every job container gets the socket ✅One practical note: The NAS runner needs to be restarted with
--config runner-config.yaml(or the equivalent systemd unit updated) after deploying this file. Worth adding to the ops runbook or a README note near the file.📋 Elicit — Requirements Engineer
Verdict: ✅ Approved
Two acceptance criteria, two targeted fixes. Clean traceability.
Requirements traceability
date-buckets.spec.tsboth failing tests green in CITZ: Europe/Berlinon test stepDOCKER_HOST+TESTCONTAINERS_RYUK_DISABLEDon backend jobScope assessment
The PR is correctly scoped to the issue. The
runner-config.yamladdition is infrastructure documentation that directly supports acceptance criterion 2 — it provides the Docker socket availability that the env vars depend on. Appropriate scope expansion. ✅One open item
Acceptance criterion 2 ("Backend integration tests pass in CI") cannot be fully verified without a CI run. This is expected and documented in the PR. The issue should be left open until the first green CI run confirms it, then closed.
No requirements gaps or ambiguities identified.
👨💻 Felix Brandt — Senior Fullstack Developer
Verdict: ✅ Approved
Clean, minimal CI fix. Nothing to flag from a code quality or TDD perspective.
What I checked
TZ: Europe/Berlinscoped to the test step. Correct and precise. ✅LGTM.
🏛️ Markus Keller — Application Architect
Verdict: ✅ Approved
No change from my previous review. The PR remains correctly scoped to CI infrastructure — zero application architecture impact.
runner-config.yamlis now tracked in the repo with appropriate comments explaining both the security trade-off and the networking behavior. Infrastructure-as-code done right. ✅No documentation update requirements triggered. Ready to merge.
🔐 Nora "NullX" Steiner — Security Engineer
Verdict: ✅ Approved
My previous concern has been addressed. The Docker socket security trade-off is now explicitly documented in
runner-config.yaml:This is exactly what I asked for — the threat model is stated, the risk acceptance is justified, and the guard condition against misuse is explicit. A future operator reading this config will understand why it's configured this way before copying it elsewhere. ✅
No remaining security concerns.
🧪 Sara Holt — QA Engineer
Verdict: ✅ Approved
No change from my previous assessment. Both fixes are correct, verification evidence is documented in the PR description, and the security note addition is a non-functional improvement that adds no test risk.
My process note from the last round still stands: after merge, confirm the first green CI run on this branch and post it as a comment closing issue #476 acceptance criterion 2. ✅
🎨 Leonie Voss — UX Designer & Accessibility Strategist
Verdict: ✅ Approved
CI infrastructure only. No UI, accessibility, or brand changes. LGTM.
🚀 Tobias Wendt — DevOps & Platform Engineer
Verdict: ✅ Approved
The security comment is a welcome addition. The full
runner-config.yamlis now self-documenting:docker_host— explains the DOCKER_HOST propagation mechanism ✅valid_volumes— explains the whitelist purpose ✅options— now has the security trade-off documented ✅force_pull: false— explains the networking decision ✅My deployment note from last round stands: the NAS runner needs
--config runner-config.yamlin its systemd unit. That's an ops task outside the PR scope — not a blocker.Ready to merge.
📋 Elicit — Requirements Engineer
Verdict: ✅ Approved
All acceptance criteria covered, Nora's concern resolved. The security documentation addition is a net positive with no requirements implications.
date-buckets.spec.tsboth failing tests green in CINo remaining open requirements questions.