Commit Graph
405 Commits
Author SHA1 Message Date
kevinveenbirkenbach bb4646cfc0 Merge branch 'main' into feature/designer 2026-10-16 22:23:40 +02:00
kevinveenbirkenbach 748189e890 Merge pull request #974 from pragepani/fix/sys-ctl-hlth-journalctl/skip-container-logs
fix(sys-ctl-hlth-journalctl): skip container logs in the host journal…
2026-10-16 22:15:20 +02:00
kevinveenbirkenbachandClaude Opus 5.5 3855b75c5f fix(sys-ctl-hlth-journalctl): read long and repeated MESSAGE fields
Without `--all`, `journalctl --output=json` encodes a field over 4096
bytes as `null`, so a long host error matched nothing and the check
passed. A repeated `MESSAGE` field arrives as a JSON array of its
values, and `bytes()` on that array raised `TypeError`, which aborted
the whole check.

Pass `--all`, and tell a byte array (all integers) from an array of
field values, decoding each value on its own.

Reproduced against the host journal: a 5000-byte error line read back
as `"MESSAGE":null` and produced no match; an entry with two `MESSAGE`
fields, sent over the native journal socket, read back as
`["error first","error second"]` and crashed `host_errors` with
`TypeError`. With the fix both are reported.

Also clear the two test-lint findings of this PR:
`test_comments_valid` rejects the narrative comment above the
`journalctl` Popen, so it goes; the stderr-to-file choice stays, because
an unread stderr pipe that fills up would block `journalctl` while
stdout is still being read. `test_no_uncached_file_reads` flags the
read of the stub's argument file, which lives in the test's tempdir and
is written per run, so the path-keyed `utils.cache.files.read_text`
could serve a stale body; the read opts out with
`# nocheck: cache-read tempdir fixture`, as the other stub-driven
script tests do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 22:11:09 +02:00
kevinveenbirkenbachandClaude Opus 5.5 d36e203171 fix(cache): render the applications carrier without the caller's application_id
`lookup('tls', ...)` aligns a cross-app domain to the consumer named by
`application_id` (utils/tls_common.py `align_domain_to_consumer`): a
clearnet-primary consumer gets the clearnet sibling of an onion-primary
target. The carrier key does not cover `application_id`, so a park that
renders inside a role hands that role's alignment to every later role
whose key matches.

The constructor and `tasks/utils/setup/role.yml` park at play scope,
where `application_id` is undefined. The re-park in
`roles/sys-token-store/tasks/write.yml` (67f6c8d7e8) and the nested-load
re-park render inside the caller: `svc-prx-openresty` at the preload
loader, `web-app-stalwart` or a provider role on the NFS host, the web
app itself under `sys-stk-front-base`. In a tor deploy that turned onion
targets into clearnet domains for onion-primary roles served from such a
park, and baked the caller's group into Nextcloud's sociallogin
`groupMapping`.

Drop `application_id` from the render's variables and from the
templar's for the duration of the render, so every park carries the
play-scope result the constructor parks have always carried. Rename the
nested re-park task so its log lines do not read as a role boundary.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:25:43 +02:00
kevinveenbirkenbach 1ea0390347 Merge branch 'main' into feature/designer 2026-10-16 18:52:09 +02:00
Prageeth Panicker dc3d30acaa fix(sys-ctl-hlth-journalctl): skip container logs in the host journal check
Problem:
Every compose service logs to journald through
sys-svc-container/templates/base.yml.j2. The daily journal health
check grepped the whole journal for "error", so routine application
errors from containers triggered the host alert (email/Telegram) and
buried real host problems.

Change:
- Read the last day of the journal as JSON (MESSAGE, CONTAINER_ID,
  SYSLOG_IDENTIFIER, _SYSTEMD_UNIT) and skip every entry that carries
  a CONTAINER_ID field. Docker daemon and system errors are still
  reported.
- Report each match as one readable "source: message" line instead
  of raw journal output.
- Stream the journalctl output line by line instead of buffering a
  whole day of logs in memory.
- Exit 2 with journalctl's stderr if journalctl itself fails, instead
  of silently reporting "no errors".
- Rewrite the check in Python (files/python/script.py, deployed by
  sys-service like the other sys-ctl-hlth roles) and decode non-UTF-8
  messages.
- Fix the success message, which claimed "All docker containers are
  healthy".

Impact:
Container log errors no longer raise this host alert on any
installation. Container health stays covered by sys-ctl-hlth-container;
application log analysis belongs in a log platform such as Wazuh.
system_service_suppress_flush is unchanged until a deploy confirms
that no host errors remain.

Tests:
Unit tests run the script against a fake journalctl (8 cases):
container error ignored, daemon error reported, identifier fallback,
non-UTF-8 message, journalctl failure, many container lines streamed
past, clean journal passes. The core cases fail against the previous
script.
2026-10-16 14:41:05 +00:00
kevinveenbirkenbachandClaude Opus 5.5 891d46d589 fix(ci): time the DNS53 sampler probes from /proc/uptime
The 🐳 Workspace (ubuntu:latest) job on main (run 38879556215, 1b36ef6120)
failed test-unit-python in the hook's second make test:

    AssertionError: Regex didn't match: '^ localhost=ok/\d+ms$'
    not found in ' localhost=ok/-1612931482679767779ms'

ubuntu:latest is Ubuntu 26.04, whose date is uutils coreutils. uutils
0.7.0 ignores the field width in %3N and prints all nine nanosecond
digits, so `date +%s%3N` yields a 19-digit value instead of epoch
milliseconds. Against main's sampler, uutils 0.7.0 date reports
localhost=ok/7081859ms, and an unpadded nanosecond value turns the
difference negative.

answer_once now takes both timestamps from /proc/uptime, which the
kernel prints with two decimals whatever date the image ships. The
sampler already reads /proc/net/udp, so it was Linux-only before. The
resolution is 10 ms; the same probe reports 10 to 20 ms with uutils
date on PATH.

The new test puts a date on PATH that does not understand %3N; main's
sampler dies on it, the fixed one does not call date for the latency.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 14:15:25 +02:00
kevinveenbirkenbach 78bdb6e515 Merge branch 'main' into feature/designer
# Conflicts:
#	docs/contributing/artefact/files/plugins/lookup/applications.md
2026-10-16 11:44:29 +02:00
kevinveenbirkenbachandClaude Opus 5.5 67f6c8d7e8 fix(cache): keep the applications carrier valid across token store writes
Since 778c18fb23 the carrier key holds the tokens file's mtime and size.
sys-token-store changed that stat on every inclusion (00_core.yml
touched the file) and on every persisted token, and nothing re-parked a
carrier afterwards. Every later task's fresh worker then rendered the
applications SPOT cold, 30 to 80 s each. On main fea6f31d9f (run
38812964308) seven heavy jobs logged 194 to 287 such tasks and ran out
of the 20400 s budget; the same jobs before 778c18fb23 logged 65 to 83,
all of them real waits.

- 00_core.yml touches the store with modification_time and access_time
  preserved, so it only creates a missing file. In swarm the controller
  sees the store over NFS with attribute caching, and the touch alone
  made the second pass go cold without any write.
- write.yml re-parks the carrier when the persist or the controller
  mirror changed the store. Both run once, so every host that ran the
  include gets the same verdict.
- tasks/utils/setup/role.yml re-parks after every role on every host,
  so hosts that did not run the writing role recover too. Where the
  key still matches this serves the carried payload; measured 0.1 s per
  role in a local compose deploy.
- The tokens file enters the key as the SHA-256 of its content,
  memoised per inode, mtime and size, so a touch no longer changes the
  key. A stat that still matches serves the memo (1.67 us against
  1.41 us for the old stat signature), and a stale stat keeps key and
  YAML cache on the same old content instead of pairing new content
  with an old key.
- applications_carrier opens the tokens file before it computes the
  key, so on the NFS-mounted controller the parked key reflects the
  revalidated attributes rather than a cached stat that changes a
  minute later.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 11:11:47 +02:00
kevinveenbirkenbachandClaude Opus 5.5 130789d9b9 fix(litellm): give the admin UI the corporate logo through LiteLLM's own ui_theme_config
The web-app-litellm stylesheet swapped the navbar image with content: url(), so LiteLLM's own logo setting stayed empty and its UI theme page showed nothing. svc-ai-litellm now renders litellm_settings.ui_theme_config.logo_url from LITELLM_UI_LOGO_URL, the design lockup of web-app-litellm, only when that role is deployed with design and a logo; the stylesheet override is removed. The design spec asserts the served setting, the navbar src, the absence of a CSS swap, the loaded wide lockup and the value on the UI theme page; the config render test covers both the logo and the no-logo case.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:45:04 +02:00
kevinveenbirkenbachandClaude Opus 5.5 1c35cdef91 style: apply the autoformat output main drifted from
`make quality-high` on main (fea6f31d9f) rewrote two files in its
autoformat step:

- roles/sys-service/handlers/refresh_systemctl_service.yml: the
  handler's `when` was wrapped by hand; the formatter wraps it at line
  width. Both forms parse to the same YAML.
- tests/unit/python/roles/web-app-nextcloud/test_onlyoffice_check_deadline.py:
  two over-long assertIn calls that ruff format splits.

A second autoformat run leaves the tree unchanged, so main is stable
under the formatter again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:35:21 +02:00
kevinveenbirkenbachandClaude Opus 5.5 fad0c60e52 fix(lint): provision mmdc's browser libraries off apt and fail hard
On the Fedora, CentOS Stream and Manjaro workspace jobs install-lint
ran `puppeteer browsers install chrome-headless-shell --install-deps`
as root, and puppeteer rejected it with "apt-get does not seem to be
available". The installer only warned ("optional lint tool"), so
install-lint passed while mmdc had no browser.

--install-deps is now passed only where apt-get exists. Everywhere else
the libraries `ldd` reports missing are installed by soname: dnf and
yum through the RPM provide `<soname>()(64bit)`, pacman through the
first package `pacman -F usr/lib/<soname>` names (libgbm.so.1 is owned
by both mesa and mesa-amber, which conflict). browser_installed() also
requires every library to resolve, so the install-lint stamp stays
stale until the browser can actually load.

A failed npm install, a missing mmdc, a failed or timed-out browser
download and unresolved libraries now raise instead of warning.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 23:45:54 +02:00
kevinveenbirkenbachandClaude Opus 5.5 e6f552ca0a fix(install): provision minitest next to ruby on Arch and the RedHat family
The Manjaro workspace job failed test-unit-ruby with "cannot load such
file -- minitest". Debian vendors minitest in libruby, but Arch ships it
as ruby-minitest and Fedora and CentOS Stream as rubygem-minitest, none
of which ruby pulls in. ensure_ruby_present now probes
`require "minitest"` and installs the distro package when the probe
fails, then fails loudly if minitest is still missing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 23:45:54 +02:00
kevinveenbirkenbachandClaude Opus 5.5 2756fd56f1 fix(cache): install CA anchors where every update-ca-trust family reads them
The Fedora and CentOS Stream workspace jobs failed TLS to pypi.org,
registry.npmjs.org and repo.packagist.org (CERTIFICATE_VERIFY_FAILED,
curl error 7). package-frontend/ca.sh put the frontend CA into the Arch
anchor dir /etc/ca-certificates/trust-source/anchors, which p11-kit on
the RedHat family never reads.

Probing for the anchor dir does not fix it: with-ca-trust.sh creates
both /etc/pki/ca-trust/source/anchors and
/etc/ca-certificates/trust-source/anchors on every update-ca-trust host,
so the probe in registry-ca.sh already picks the RedHat dir on a reused
Arch runner. Both scripts now install into both dirs, rebuild only when
one of them changed, and take CA_ROOT so unit tests run them against a
fake root.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 23:45:53 +02:00
kevinveenbirkenbachandClaude Opus 5.5 5359b99ed4 style(tests): reflow the onlyoffice deadline assertions the way autoformat writes them
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 23:33:58 +02:00
kevinveenbirkenbach a575b86469 Merge branch 'main' into feature/designer 2026-10-15 22:46:09 +02:00
kevinveenbirkenbach ec2a4b96f0 Merge pull request #930 from infinito-nexus/fix/web-app-nextcloud/onlyoffice-settings-race
fix(web-app-nextcloud): write onlyoffice's server URL last and recheck it
2026-10-15 22:36:45 +02:00
kevinveenbirkenbach 63eede9137 Merge branch 'main' into feature/designer 2026-10-15 20:25:34 +02:00
kevinveenbirkenbach c38f95457b Merge pull request #955 from infinito-nexus/fix/constructor/bootstrap-carrier/925
perf(constructor): carry the applications SPOT into the bootstrap roles
2026-10-15 17:54:46 +02:00
kevinveenbirkenbach f126b24693 Merge branch 'main' into feature/designer 2026-10-15 16:23:42 +02:00
kevinveenbirkenbach 06a74e7658 Merge pull request #869 from infinito-nexus/fix/web-app-stalwart/relay-rule/793
fix(web-app-stalwart): authenticate bots with an app password under SSO
2026-10-15 15:42:19 +02:00
kevinveenbirkenbach 844544a74b Merge branch 'main' into feature/designer 2026-10-15 15:00:31 +02:00
kevinveenbirkenbach 932c1fc70a Merge pull request #932 from infinito-nexus/fix/plugins/onion-apex-redirect/924
fix(lookup): keep the onion apex from redirecting to a clearnet homepage
2026-10-15 11:48:14 +02:00
kevinveenbirkenbach fb125e163d Merge branch 'main' into feature/designer 2026-10-15 11:32:54 +02:00
kevinveenbirkenbach 84f6e86d8e Merge remote-tracking branch 'origin/main' into fix/lint/mermaid-browser-revision 2026-10-15 11:23:26 +02:00
kevinveenbirkenbachandClaude Opus 5.5 33bb7e99e9 fix(lint): resolve puppeteer-core like node and keep other browser revisions
- browser_revision() reads revisions.js from the puppeteer-core that
  node resolves for mmdc's puppeteer: puppeteer/node_modules first, then
  the node_modules directories above it. When mermaid-cli pins another
  puppeteer-core, npm nests puppeteer's own copy, and the sibling held a
  different revision.
- A failed browser install removes only the directories of the pinned
  revision before the retry. Removing the whole chrome-headless-shell
  cache also deleted browsers that other puppeteer installs share.

Addresses the CodeRabbit review on #958.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:23:22 +02:00
kevinveenbirkenbach b1d38f49d6 Merge pull request #934 from infinito-nexus/fix/act/scope-outer-teardown
fix(act): remove only the own worktree's act containers
2026-10-15 10:51:48 +02:00
kevinveenbirkenbach 51d75fb804 Merge pull request #908 from infinito-nexus/fix/env/distro-derived-keys/794
fix(env): derive the image and container names from the distro under test
2026-10-15 10:50:59 +02:00
kevinveenbirkenbach 706745162b Merge remote-tracking branch 'origin/main' into fix/lint/mermaid-browser-revision 2026-10-15 10:36:09 +02:00
kevinveenbirkenbachandClaude Opus 5.5 cce4373abd fix(web-app-nextcloud): bound the onlyoffice check by a wall-clock deadline
`retries: 30` with `delay: 10` only bounded the wait while each
`occ onlyoffice:documentserver --check` failed fast. onlyoffice v10.2.1
gives the health check and the version command 60 s each and the
conversion 120 s, so a document server that accepts connections but
does not answer held the deploy for up to two hours before it failed.

The retries now also stop once a deadline of
`lookup('timeout', 300)` seconds, set right before the check, has
passed. A per-attempt `timeout:` would not bound the total: ansible-core
2.21 leaves the `until` loop on the first timed-out attempt, so only a
limit below onlyoffice's 60 s would end a hanging server's loop.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 04:04:33 +02:00
kevinveenbirkenbachandClaude Opus 5.5 c74a31e83e fix(lint): install the browser revision mmdc's own puppeteer pins
install-lint provisioned chrome-headless-shell with `npx --yes
puppeteer browsers install`, which runs the latest puppeteer from the
registry. mmdc runs with the puppeteer npm installed next to it as a
peer dependency when mmdc itself was installed, and looks for exactly
the revision that puppeteer-core pins. Once puppeteer 25.14.0 was out,
npx installed 155.0.8059.79 while mmdc with puppeteer-core 25.13.0
wanted 155.0.8059.39, and lint-mermaid failed on every file. Because
browser_installed() accepted any chrome-headless-shell, a stack with
the wrong revision never repaired itself.

The browser is now installed through the puppeteer CLI next to mmdc,
which installs its puppeteer-core's revision, and only that revision,
read from puppeteer-core's revisions.js, counts as installed.

Closes #957

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 03:21:30 +02:00
kevinveenbirkenbachandClaude Opus 5.5 778c18fb23 perf(constructor): carry the applications SPOT into the bootstrap roles
The constructor parked no applications carrier between the merge block
and the token store, so every lookup in the svc-swarm-node, svc-storage,
svc-registry and svc-virt bootstrap rendered the SPOT cold, 25 to 30 s
each on a loaded host.

A carrier there was unsafe: in swarm, svc-storage-nfs-server mounts the
NFS export over the controller's DIR_VAR_LIB, so the tokens file the
render reads can appear in the middle of that section, and the cache key
did not notice. merged_applications_cache_key now includes the tokens
file's path, mtime and size, as the users cache key already did, so a
carrier rejects itself once the tokens change. A carrier parked after
the add_host tasks then serves the bootstrap roles. It is parked only in
swarm mode: in compose the section runs in about a second, so a carrier
there would add a render without saving one.

In a swarm drill of web-app-prometheus up to the known NFS probe-write
failure, "Resolve the NFS server backend flavor" went from 29.6 s to
0.07 s, "Probe-mount the NFS export" from 26.4 s to 0.4 s, "Mount
cluster-shared infinito state on controller" from 23.7 s to 0.5 s and
"Ensure Docker-In-Docker DNS works" from 30.0 s to 3.6 s; the run to
that point took 768 s instead of 1735 s.

Closes #925

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 03:21:17 +02:00
kevinveenbirkenbach 2b43cd666f Merge remote-tracking branch 'origin/main' into feature/designer 2026-10-15 02:39:36 +02:00
kevinveenbirkenbach 1967a218d0 Merge remote-tracking branch 'origin/main' into dependabot/pip/ruff-0.17.0 2026-10-15 01:24:32 +02:00
kevinveenbirkenbachandClaude Opus 5.5 47565781ea style(python): import datetime as dt for ruff 0.17
ruff 0.17.0 adds `datetime` -> `dt` to the default aliases of
flake8-import-conventions, and the project selects ICN, so lint-python
failed with ICN001 in seven files.

The files now import `datetime as dt`. ruff's unsafe fix also renamed
two local names in utils/github/playwright_summary.py into the alias:
the `_to_iso_z` parameter and the parsed value in
`_parse_iso_timestamp`. Both are now called `moment`; with the bare fix,
nine tests of test_playwright_summary.py failed with UnboundLocalError
and AttributeError.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 01:24:31 +02:00
kevinveenbirkenbach e8ffe2536e Merge remote-tracking branch 'origin/main' into HEAD 2026-10-15 01:07:59 +02:00
kevinveenbirkenbach b637742f96 Merge remote-tracking branch 'origin/main' into fix/env/distro-derived-keys/794 2026-10-15 01:07:53 +02:00
kevinveenbirkenbachandClaude Opus 5.5 87c9a5f41b test(act): mark the docker stub log read as outside the file cache
The teardown test reads the call log its docker stub writes into a
temporary directory. test_no_uncached_file_reads flagged that read in
CI; the path-keyed read cache does not fit a file written per run, so
the read keeps Path.read_text with the cache-read opt-out, as the other
stub-log tests do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 01:07:50 +02:00
kevinveenbirkenbach 06084d560a Merge remote-tracking branch 'origin/main' into fix/act/scope-outer-teardown 2026-10-15 01:07:50 +02:00
kevinveenbirkenbach 565e15f2a5 Merge remote-tracking branch 'origin/main' into fix/plugins/onion-apex-redirect/924 2026-10-15 01:07:47 +02:00
kevinveenbirkenbach 7344565df1 Merge remote-tracking branch 'origin/main' into fix/web-app-nextcloud/onlyoffice-settings-race 2026-10-15 01:07:41 +02:00
kevinveenbirkenbach 365ea9e764 Merge pull request #911 from infinito-nexus/fix/web-app-prometheus/blackbox-verify/851
fix(web-app-prometheus): verify publicly trusted probe targets while the private CA is missing
2026-10-15 00:17:47 +02:00
kevinveenbirkenbachandClaude Opus 5.5 46373c1ba3 fix(act): remove only the own worktree's act containers
down_act_outer.sh removed every container named act-*, so starting or
releasing a swarm drill in one worktree killed the act container of a
drill another worktree had running.

workflow.sh now labels act's job containers with
infinito.worktree=${INFINITO_CONTAINER} through the default
ACT_CONTAINER_OPTIONS, and down_act_outer.sh removes only containers
carrying the own label. Both scripts refuse to run without
INFINITO_CONTAINER, so the filter never widens to every act container;
make exports BASH_ENV, which loads it from .env.

Worktrees without a custom.env share the default container name and
therefore still share one label among themselves.

Closes #933

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:50:07 +02:00
kevinveenbirkenbachandClaude Opus 5.5 45732329b6 fix(lookup): keep the onion apex from redirecting to a clearnet homepage
On a Tor node current_play_redirect_domains schedules the bare node
onion as a redirect source targeting DOMAIN_HOMEPAGE. Without
web-app-dashboard, DOMAIN_HOMEPAGE falls back to SOFTWARE_DOMAIN
(infinito.nexus), so web-opt-rdr-domains rendered a 301 from
http://<node>.onion/ to http://infinito.nexus/: an onion visitor sent
to a clearnet host over plain HTTP.

The onion apex entry is now added only when DOMAIN_HOMEPAGE is an
onion host. Otherwise the onion apex gets no redirect and the default
server answers.

Closes #924

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:35:29 +02:00
kevinveenbirkenbachandClaude Opus 5.5 bfaa834f29 fix(web-app-nextcloud): write onlyoffice's server URL last and recheck it
The role writes onlyoffice's settings with one occ call per key,
DocumentServerUrl first and jwt_secret fourth. Nextcloud loads app
config once per process, so a cron.php that starts inside that batch
runs onlyoffice's EditorsCheck job, registered when the app was
enabled, with the document server URL but without the secret. The
document server answers "Invalid token", EditorsCheck stores it in
settings_error, and onlyoffice stops loading its Files integration;
EditorsCheck itself skips every later run while that error is set.

Seen in run 38455782184 (Nextcloud#4, swarm): cron.php started at
06:50:00 inside the batch that ran from 06:49:59 to 06:50:04, logged
"Invalid token" at 06:50:22, and both Playwright passes found .docx
files offered for download only.

DocumentServerUrl is now written last, so a process that cached the
config before the batch ended sees no URL and EditorsCheck returns
early. After the batch, `occ onlyoffice:documentserver --check` sets
settings_error from a fresh check: a redeploy clears an error latched
earlier, and a document server that stays unreachable fails the
deploy. A unit test keeps the URL last.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:34:52 +02:00
kevinveenbirkenbach a3c8f31d07 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	roles/web-app-stalwart/README.md
2026-10-14 13:34:01 +02:00
kevinveenbirkenbach 67b508cc5d Merge pull request #918 from infinito-nexus/fix/images/retired-role-discovery
fix(images): leave eol roles out of image discovery
2026-10-14 10:04:03 +02:00
kevinveenbirkenbach e6b88662be Merge pull request #919 from infinito-nexus/fix/ci/release-backfill-floor
fix(ci): stop the release backfill below the oldest buildable tag
2026-10-14 10:02:04 +02:00
kevinveenbirkenbach 7b80a807ab Merge pull request #909 from infinito-nexus/fix/lint/mermaid-browser-install/795
fix(lint): provision the mermaid browser in install-lint, not in the parallel lint run
2026-10-14 08:59:04 +02:00
kevinveenbirkenbachandClaude Opus 5.5 078d0a0682 fix(lint): provision mmdc's browser only when it is missing
install-lint provisioned chrome-headless-shell on every run, and `make lint`
runs install-lint once per lint target in parallel whenever its stamp is not
fresh. On CI the stamp never is (eslint lives in node_modules, not on PATH),
so one lint job provisioned 16 times (run 38455552981). As root, puppeteer's
--install-deps runs `apt-get satisfy` even for an installed browser, and two
of those at once fail on the dpkg lock; a failed provisioning then deleted the
shared browser cache while lint-mermaid could be rendering with it.

The browser is now provisioned only when it is not installed, and --force
drops its cache so `make install-lint-force` still reinstalls it with its
libraries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 08:56:59 +02:00