Commit Graph
35 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 4.8 339af1ce20 fix(swarm-test): do not fail pre-clean on a raced act container removal
down_act_outer.sh is a best-effort pre-clean (set +e), but its last statement (docker rm -f) can race act's own --rm finalize and return non-zero, so the script exited non-zero and aborted swarm-zombie before the deploy. Force exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 16:47:34 +02:00
kevinveenbirkenbachandClaude Opus 4.8 001d880a72 fix(test/swarm): pin roundtrip swarm to one variant per app
A multi-variant app expanded into N swarm matrix entries, and
swarm-zombie ran them all on one host under identical SWARM_NAME
cluster names, so every deploy after the first aborted with
'network ... already exists'. Pin the act matrix to variant 0
(default, overridable via variant=) so a multi-variant app validates
a single cluster, in parity with the compose step's variant=0.
workflow.sh now splits ACT_MATRIX on ';' into multiple --matrix
filters so apps and variant can be pinned together.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:28:42 +02:00
kevinveenbirkenbachandClaude Opus 4.8 c1fb96584b refactor(tests): rename swarm make targets to swarm-* and reorganize env steps
Rename the scripts/tests/environment deploy_* steps (07/08/09) to compose_*; add 12_swarm.sh (svc-db-mariadb via the swarm-* targets) and 13_roundtrip.sh (svc-db-postgres via make roundtrip), with MARIADB_APP/POSTGRES_APP in common.sh. Rename every make act-swarm-* target to make swarm-* (drop the act- prefix) across the Makefile (moved to the alphabetical slot), the deploy/act, deploy/swarm and e2e helper scripts, and the agent iteration docs. Strip mid-code narration comments from the touched scripts per the comment policy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 12:24:19 +02:00
kevinveenbirkenbachandClaude Opus 4.8 8547a800ed refactor(ci): unify deploy tests to compose+swarm, retire INFINITO_DEPLOY_TYPE
Delete the per-deploy-type workflows (test-deploy-local/universal/
workstation) and the redundant act-all/act-app runner scripts (replaced
by make act-workflow). Only compose and swarm deploy tests remain.

Both now discover the full application matrix (every deployment type,
lifecycle-filtered to alpha/beta/rc/stable) instead of just the server/
web-* scope, via apps.sh resolving applications.type without --type.

Retire INFINITO_DEPLOY_TYPE entirely: discovery is now unconditional and
the deploy step never read it. Removed from apps.sh, ci/all.sh, the local
deploy scripts, the two workflows, default.env, the env passthrough list,
the dotenv generator test, the Makefile compose-deploy target, and docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 18:31:35 +02:00
kevinveenbirkenbachandClaude Opus 4.8 3a55877a39 refactor(swarm-test): SPOT topology naming in default.env + add act-swarm-clean
Hoist the swarm-test node, network and NFS-export-base names out of the shell
harness (00_topology.sh) into default.env (INFINITO_SWARM_*), so the shell
harness and the Python harness (extend_inventory, write_extras) read one source
instead of each hardcoding the suffixes. 00_topology.sh, extend_inventory.py,
write_extras.py and 01/09/11 now read the SPOT; passthrough.py exports the keys
into the generated .env. 00_topology.sh reads default.env directly (not .env)
because the workflow sources it before make dotenv runs.

Rename topology.sh -> 00_topology.sh (sorts ahead of 01_/02_, the order it is
sourced in) and update all 10 references.

Add scripts/tests/deploy/swarm/clean_all.sh + make act-swarm-clean to reclaim
wedged act-swarm state between runs, label-scoped via INFINITO_SWARM_TEST_LABEL
so it never touches unrelated containers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:31:34 +02:00
kevinveenbirkenbachandClaude Opus 4.8 f414c8df27 fix(test): harden act-swarm cleanup
down_act_outer: match the act outer runner by name prefix instead of a hardcoded image ancestor, so it works regardless of ACT_PLATFORM_IMAGE. 13_cleanup: warn loudly when a node survives 'docker rm -f' (kernel D-state / stuck NFS, reboot needed) instead of swallowing the error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 19:29:39 +02:00
kevinveenbirkenbachandClaude Opus 4.8 761450363b feat(make): add act-runner-image target for Docker-28/29 act runs
Recent Docker tightened docker cp tar extraction, so act aborts at job setup when the stock catthehacker runner image's /var/run symlink is unpacked into. Add a make act-runner-image target that builds local/act-runner-fixed (stock image with /var/run removed); pass it via ACT_PLATFORM_IMAGE. Document the symptom + fix in iteration/workflow.md and cross-reference from playwright.md and README.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 12:16:49 +02:00
kevinveenbirkenbachandClaude Opus 4.8 821ff4c386 feat(act-swarm): add act-swarm-playwright helper to rerun specs on the live cluster
Mirror compose-playwright for the act-swarm DinD cluster: copy the working-tree's modified files into the node's frozen bootstrap copy (the nodes are not bind-mounted), then rerun via rerun-spec.sh. rerun-spec.sh gains an opt-in TEST_E2E_PLAYWRIGHT_NETWORK_HOST that switches it to --network host (mirroring the deploy-time swarm branch in 02_run_one.yml) so cross-app canonical domains are reachable on the node; the compose path is unchanged. Document the all-green-before-redeploy gate in playwright.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 16:05:10 +02:00
kevinveenbirkenbachandClaude Opus 4.7 9cee64430a feat(act-swarm-zombie): disable= plumbing via ACT_ENV semicolon
* Makefile: `make act-swarm-zombie app=<id> disable=<keys>` now
  forwards the `disable=` value into the act run via ACT_ENV. The
  swarm provision CLI inside the workflow reads it through
  `apply_services_disabled_from_env` and removes the named provider
  roles from the test inventory + sets their `enabled: false /
  shared: false` overrides in host_vars. Matches the iteration spec
  in docs/agents/action/iteration/role.md.
* scripts/tests/deploy/act/workflow.sh: switch the ACT_ENV pair
  separator from comma to semicolon so values can themselves carry
  commas (the disable= list is comma-separated). Only one in-tree
  caller (the Makefile target above) currently uses ACT_ENV with
  multiple pairs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 13:34:06 +02:00
kevinveenbirkenbachandClaude Opus 4.7 b0ce32d6c3 ops(act/workflow): preflight on stale devtmpfs binds in actcache
Sandboxed sessions leave behind devtmpfs bind-mounts under
${ACT_ACTION_CACHE_PATH}/<action>@<ver>/.claude/{hooks,settings.json,
settings.local.json,skills}/ and .mcp.json. Act tries to refresh the
cached action repo on the next run, can't `os.RemoveAll` the mounts
(EBUSY), and dies in `Set up job` with no actionable error.

Add a preflight that detects stale `devtmpfs` mounts under the active
cache path and aborts with exit 3 and a copy-pasteable
`sudo umount … && sudo rm -rf …` remediation BEFORE invoking act. Stays
silent when no stale mounts are present.

Confirmed firing in iter 2 of a sandboxed act-swarm-zombie run; silent
in every subsequent t-series run after the operator cleaned the cache.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 00:17:11 +02:00
kevinveenbirkenbachandClaude Opus 4.7 d22f05353e feat(make/act-swarm): zombie target + topology SPOT
Adds `act-swarm-zombie app=<id>` which runs the swarm matrix-app test and leaves
the DinD cluster + lab network alive afterwards (INFINITO_KEEP_SWARM_NODES=true
forwarded via the new ACT_ENV passthrough in workflow.sh) so failures can be
post-mortem inspected with `make act-swarm-exec node=… cmd=…`,
`make act-swarm-shell node=…`, and finally released with `make act-swarm-down`.

Consolidates the swarm-test topology (manager / worker / nfs-server names, lab
network, NFS export base) into scripts/tests/deploy/swarm/topology.sh as the
single source of truth: 13_cleanup.sh and shell_node.sh source it directly,
the GitHub Actions workflow ingests it into $GITHUB_ENV via a setup step,
replacing the duplicate env: block entries.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-14 19:08:46 +02:00
kevinveenbirkenbachandClaude Opus 4.7 2cdf1f1a31 chore: swarm-mode template lint, act tweaks, formatting
Add tests/lint/ansible/jinja/test_no_ansible_facts_in_templates.py: under
swarm-mode the rendered template lands once on the primary manager and is
slurp-distributed to every other node, so any per-host fact reference
(ansible_hostname, ansible_default_ipv4, ansible_fqdn, ansible_nodename,
ansible_machine_id, ansible_eth0, ansible_all_ipv*_addresses) would
silently produce wrong output on the other nodes. The lint blocks them
at PR time.

scripts/tests/deploy/act/workflow.sh forwards --action-offline-mode +
--action-cache-path so repeated local act runs do not refetch the actions
catalog.

roles/sys-ctl-cln-anon-volumes/tasks/01_core.yml quoting cleanup applied
by autoformat (single to double quotes, no semantic change).

.claude/settings.json adds Workflow to permissions.allow so the
multi-agent Workflow tool no longer prompts per invocation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 17:11:07 +02:00
kevinveenbirkenbachandClaude Opus 4.7 35cb7a6d89 feat(scripts/tests/act): pass workflow inputs via ACT_INPUTS
ACT_INPUTS="key=value [key2=value2]" expands to repeated --input flags so
make act-workflow can drive workflow_dispatch-style inputs without falling
back to a manual act invocation. Needed for the swarm pilot, where the
discover job's `whitelist` input gates the per-app matrix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 22:24:45 +02:00
kevinveenbirkenbachandClaude Opus 4.7 3c07721303 feat(swarm-nfs): bootstrap Docker Swarm + NFS-backed shared volumes (#023)
Adds the svc-docker-swarm, svc-docker-swarm-manager, svc-storage-nfs-server
and svc-storage-nfs-client roles, the compose_volumes filter NFS support,
the test-deploy-swarm-nfs CI workflow with its swarm-nfs-pilot script set,
and the deploy_mode-aware compose/swarm wiring across mediawiki, mariadb
and sys-svc-* helpers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 18:57:34 +02:00
kevinveenbirkenbachandClaude Opus 4.7 8e293bd921 fix(compose-deploy): stop persisting runtime routing knobs into .env
Root cause of step-08 deploy failures across all 4 test-development
distros in run 26572559126: `make compose-deploy mode=reinstall
apps=web-app-dashboard INFINITO_SERVICES_DISABLED=matomo` (step 07)
made make export the cmdline var into the recipe's shell env, which
reached `runner.py` → `make setup` → `make dotenv`. The dotenv
generator's `setdefault` then baked the runtime-only value into the
on-disk `.env`. Step 08 sourced that .env, filtered `web-app-matomo`
out of its primary apps, and exited with "All primary apps disabled".

Fix:

- Drop the 7 routing knobs (apps, mode, purge, bundles, disable,
  full_cycle, variant) from `passthrough.STATIC_KEYS` and from
  `default.env`. They are per-invocation; .env is the persistent SPOT
  and must not capture them.
- Switch every consumer from `os.environ["KEY"]` strict access to
  `os.environ.get("KEY", "")` so the missing-key path stops at "no
  filter" instead of a KeyError.
- Rename the env-var surface to match the make-alias surface 1:1
  (lowercase): INFINITO_APPS → apps, INFINITO_SERVICES_DISABLED →
  disable, INFINITO_BUNDLES → bundles, INFINITO_FULL_CYCLE →
  full_cycle, INFINITO_VARIANT → variant, INFINITO_DEPLOY_MODE → mode,
  INFINITO_PURGE_ENTITIES → purge. The make alias was the user-facing
  name in every doc; routing-knob env vars now use the same name so
  the cmdline form, the recipe env-prefix, and the consumer reads
  all line up.
- Collapse the Makefile `compose-deploy` recipe to just the two
  knobs whose alias differs from the env name (`type` →
  INFINITO_DEPLOY_TYPE, `debug` → INFINITO_DEBUG). GNU Make
  auto-exports cmdline variables into the recipe's shell env, so the
  identical-name `$(if $(apps),apps="$(apps)")` lines were pure
  noise.
- Drop `disable: ${disable}` from compose.yml. The dev CLI builds
  `extra_env` per `docker exec` call and forwards `disable` via
  `-e`, so the container env never needs the value frozen at
  compose-up time.
- Update the 3 actual leak call sites
  (07/08/09_deploy_*.sh) to the make-alias form
  (`disable=matomo`, `full_cycle=true`, `variant=1`).
- Add `SIM112` to ruff's ignore set: the lowercase env names are
  the deliberate make/env parity, not a typo.

Repro before fix: `INFINITO_SERVICES_DISABLED=matomo make dotenv`
wrote `INFINITO_SERVICES_DISABLED=matomo` to `.env`. After fix:
`disable=matomo make dotenv` leaves `.env` untouched.

Tests: `make test` and `make autoformat` green (2711 tests, 8 lint
sub-targets).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 17:47:03 +02:00
kevinveenbirkenbachandClaude Opus 4.7 356e3366be feat(make): consolidate local deploy into single make deploy router
Replace the seven separate deploy-* make targets with a single `make
deploy` recipe that maps short Make variables to the canonical
INFINITO_* environment variables and dispatches via the new
scripts/tests/deploy/local/deploy/main.sh router. main.sh and all
sub-scripts continue to read the long-form names, so Ansible passthrough
and container forwarding stay unchanged.

Short Make CLI now exposes apps, mode, purge, type, bundles, disabled,
and full_cycle as aliases. The router picks the verb folder
(initialize, reinstall, update) and scope (all, selection) based on the
chosen mode plus the presence of INFINITO_APPS or INFINITO_BUNDLES.

Also folds in the global INFINITO_TEST_DEPLOY_TYPE -> INFINITO_DEPLOY_TYPE
rename across default.env, passthrough handler, dotenv test, CI deploy
workflows, discovery scripts, and the server stage task.

Active contributor and agent-iteration docs, role-level Administration
and README files, the bundles README, and the deploy reset helper are
all retargeted to the new short CLI. docs/requirements/ entries stay
untouched as historical audit logs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:22:28 +02:00
kevinveenbirkenbachandClaude Opus 4.7 9a578952c3 feat(env): SPOT env-pipeline + INFINITO_* namespace
Replace 8 bash env modules with a single Python generator
(cli.meta.env) that materialises env/static.env + runtime context
into a gitignored .env. Each variable lives in its own handler
under utils/env/handlers/; defaults carry inline one-line `# ...`
comments.

Rename to the INFINITO_* schema:
- workflow inputs: APPS, SERVICES_DISABLED, CMD, DEBUG, WHITELIST,
  BUNDLES, FULL_CYCLE, VARIANT, RUN_FLAGS, LIMIT_HOST, PARALLEL
- inventory: INVENTORY_DIR / INVENTORY_FILE / HOST_VARS_FILE,
  PW_FILE -> INVENTORY_PASSWORD_FILE
- venv: VENV_BASE / VENV_FALLBACK / VENV_NAME
- new SPOT defaults: COMPILE_SILENCE, CONTAINER_NO_CA,
  WAIT_HEALTH_TIMEOUT_S, PACKAGE_CACHE_FRONTEND_INIT_IMAGE,
  promoted IMAGE_TAG from GHA-only to general passthrough

compose.yml hardening:
- inline \${VAR:-default} removed; strict \${VAR:?...} everywhere
- new integration lint blocks regressions

Tooling:
- scripts/install/all.sh owns the stamp-file install logic
  (Makefile target delegates)
- scripts/meta/env/load.sh is the single shell entry point;
  8 bash modules deleted
- new shell lint: no INFINITO_* defaults outside env/static.env

Tests:
- 93 new unit tests for utils/env/{builder,parser,runtime,writer}
- restructure tests/lint into repository/ (file-content rules) vs
  filesystem/{shell,python,env}/ (filesystem-shape rules)
- drift + smoke integration tests against the generator

Docs:
- variables.md is now a pointer; per-variable docs live next to
  the key in env/static.env / env/ci.env
- compose.yml.md drops the Default column

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 20:40:34 +02:00
kevinveenbirkenbachandClaude Opus 4.7 16bdbf03d7 docs: fix repo-wide drift against documentation.md style rules
Three classes of drift cleaned up across 85 .md files:

- Em-dashes: replaced ` — ` with the contextually correct separator
  (colon for `term: explanation`, period to split clauses across
  sentences/source lines, parentheses for asides, semicolon for
  tight-linked continuations). Two intentional exceptions remain
  in `docs/contributing/documentation.md` (the rule literal) and
  inside a fenced ```sh block in `docs/contributing/actions/debugging/ci.md`.
- SPOT self-labels: rewrote "This is the SPOT for X" to describe
  the subject directly ("This directory holds X" / "This file
  describes X"). Cross-references to other artefacts' canonical
  role remain unchanged.
- Heading-emoji position: moved leading-emoji headings (`# 🧩 Title`)
  to trailing position (`# Title 🧩`) per the headline rule that
  emojis MUST come after the title text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 20:12:07 +02:00
Environment TestandClaude Sonnet 4.6 0b8e16a45d test(lint): add markdown link checker and fix all broken links
Add tests/lint/repository/test_markdown_links.py — a hard-fail lint
test that scans all git-tracked markdown files for broken relative and
repo-root-absolute links. Skips external URLs, anchor-only links, fenced
code blocks, and placeholder targets.

Apply documentation.md rules to all staged files: add emojis after
headlines in docs files, remove emojis from role README headings,
standardize Credits sections to the canonical format, remove empty
sections and orphaned sentences referring to missing documentation files,
fix raw URLs and incorrect link text across role READMEs and docs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 22:00:46 +02:00
Environment TestandClaude Sonnet 4.6 5c1a963d30 docs(tools): restructure security and framework docs, add cleanup workflow
- Move flow/security/ghcr.md to tools/ghcr/authentication.md
- Move flow/security/mirror-cleanup.md to tools/ghcr/cleanup.md
- Move flow/security/branch.md to tools/github/branch.md
- Move flow/security/repository.md to tools/github/repository.md
- Move code/frameworks/ansible.md to tools/ansible.md
- Merge code/frameworks/Makefile.md into tools/make.md and extract file rules to artefact/files/makefile.md
- Rename tools/makefile.md to tools/make.md
- Add .github/workflows/images-mirror-cleanup.yml for manual GHCR cleanup
- Apply documentation.md standards: emojis on headlines, no em dashes, filename link text
- Fix all cross-references broken by directory restructuring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 16:31:22 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 15ac4f062a refactor(ci): rename test-development.yml to test-environment.yml
Aligns the workflow filename with its job name and purpose.
Updates all references in ci-orchestrator.yml and act/workflow.sh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 12:39:15 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 06a688c59a refactor(deploy): rename APP to APPS throughout the project
Makes clear that multiple space- or comma-separated app ids are
supported. Renames the env var, CLI flag (--app → --apps), and all
references in scripts, workflows, and documentation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 11:41:31 +02:00
kevinveenbirkenbach 0200b22298 Normalize scripts layout and Make targets 2026-03-28 09:00:38 +01:00
kevinveenbirkenbach e0f9ef1dd4 refactor: rename deploy ci and act scripts 2026-03-28 06:27:48 +01:00
kevinveenbirkenbach 5570be2b84 test(act): force single-job execution for local workflow runs 2026-03-20 21:38:44 +01:00
kevinveenbirkenbach 009e4e053f test(act): allow configurable concurrent jobs (default 5) 2026-03-20 21:08:10 +01:00
kevinveenbirkenbach 662c789d29 test(act): simplify workflow script and set platform image 2026-03-20 19:43:39 +01:00
kevinveenbirkenbach 6f2dd46637 test(act): clean ignored files and repair ownership in fresh mode 2026-03-20 16:22:45 +01:00
kevinveenbirkenbach b31ffbd2c1 test(act): add reusable workflow runner target 2026-03-20 16:09:23 +01:00
kevinveenbirkenbach a715970506 Refactor lint tooling and shell scripts 2026-03-18 12:37:31 +01:00
kevinveenbirkenbach 1bb67164e0 ci: add whitelist filtering for deploy test matrix
* Replace ONLY_APP with space-separated WHITELIST across CI and local/act deploy tests
* Rename build-test-matrix.sh to scripts/meta/resolve/apps.sh and apply optional whitelist filter
* Add manual CI entry workflow to run orchestrator with distros/whitelist inputs

https://chatgpt.com/share/698c406c-0968-800f-ae84-1bcca023363c
2026-02-11 09:40:00 +01:00
kevinveenbirkenbach 249103bb56 feat(ci): unify local act workflow with CI discovery logic and strict input resolver
- Use docker-up + ci-discover-output for local discover job (same as CI)
- Add strict input resolver (scripts/meta/resolve/input.sh) with enforced env contract
- Ensure ONLY_APP is always defined (may be empty, but never unset)
- Expose resolved inputs as workflow outputs for linear matrix deploy
- Enforce linear matrix execution via max-parallel: 1
- Align act helper scripts with linear matrix semantics

https://chatgpt.com/share/6970ba64-8c2c-800f-bb90-7edbe9168582
2026-01-21 12:37:03 +01:00
kevinveenbirkenbach 6c84807778 feat(test-local): add all-app inventory init and run targets
Add two local test helpers to run full-stack deployments for all discovered apps:

- make test-local-inventory-init-all: bring up dev stack and create inventory
  (/etc/inventories/local-full-<type>/servers.yml) including all apps
- make test-local-run-all: run deploy using the pre-initialized inventory

Also mark various scripts as executable (chmod +x) to ensure consistent
behavior across environments.

https://chatgpt.com/share/6970018e-1244-800f-9345-ca1596061729
2026-01-20 23:43:10 +01:00
kevinveenbirkenbach d5801f35b0 refactor(ci): make deploy env vars strict and export pythonpath
- Make TEST_DEPLOY_TYPE strictly required in build-test-matrix
- Enforce required env vars in all act/ci/local deploy scripts
- Add deploy type validation everywhere
- Restore and export PYTHONPATH in Makefile
- Remove unused test directory variables

https://chatgpt.com/share/69700042-3fcc-800f-8bf9-b429d5bb7e82
2026-01-20 23:22:32 +01:00
kevinveenbirkenbach 12704e9fa3 refactor(test-act): extract act deploy logic into dedicated scripts
- Move test-act target logic into scripts/tests/deploy/act/all.sh
- Move test-act-app target logic into scripts/tests/deploy/act/app.sh
- Keep Makefile targets as thin delegators
- Preserve existing flags and behavior

This keeps the Makefile minimal and makes act workflows reusable.

https://chatgpt.com/share/696ff4de-3abc-800f-a206-2f3dd5dec170
2026-01-20 22:34:14 +01:00