Commit Graph
652 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 e8e94e89ef style(tests): reflow the onlyoffice deadline assertions the way autoformat writes them
The second post-merge quality-high pass reported '1 file reformatted' and wrapped two long assertIn calls. Parsing both revisions yields an identical AST, so only the line breaks moved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-15 23:08:45 +02:00
kevinveenbirkenbach ac36bc30e0 Merge branch 'feature/designer' into feature/store-i18n 2026-10-15 22:59:05 +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
kevinveenbirkenbachandClaude Opus 5 5b69b5c7c6 Merge branch 'feature/designer' into feature/store-i18n
Six conflicts, all from git pairing store-i18n's renamed roles with their designer originals.

web-app-meta/meta/services.yml and web-app-sphinx/vars/main.yml: store-i18n renamed web-app-sphinx to web-app-meta and dropped its custom build, so the role keeps the prebuilt ghcr.io/infinito-nexus/meta image and the old sphinx role stays deleted.

svc-ai-tika/templates/compose.yml.j2: a bogus rename pairing dropped sphinx build lines into the tika service. Rejected; the template is byte-identical to its pre-merge state again.

web-app-docs/templates/Dockerfile.j2: the designer file renders sphinx under the design title and reads SPHINX_IMAGE, SPHINX_OUTPUT_DIR and SPHINX_EXEC_DIR, none of which exist in web-app-docs, which builds from files/Dockerfile. Rejected.

web-app-docs/templates/playwright.env.j2 and files/playwright/test-design.js: both sides add independently, so both are kept. Of the designer's three new env reads only designTitle survives; iconUrl and faviconUrl are shadowed inside the test by stricter requireDotenvValue reads and were dead at module scope.

plugins/lookup/email.py merges clean toward designer, which is intended rather than a loss: 08ef66d8de (2026-10-08) replaces the submission_via_relay port-25 rule of 233f15f3e0 (2026-07-01) with per-bot app passwords under SSO, and carries the matching 04_manage_user/app_password tasks.

Checked after resolving: no duplicate YAML keys across the 17 merged YAML files, and no nocheck line dropped above a renamed key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-15 22:21:50 +02:00
kevinveenbirkenbachandClaude Opus 5 1e61debad2 fix(objstore): sign funkwhale's media URLs against the host the browser dials
funkwhale has two S3 surfaces sharing one private bucket. Audio goes out as X-Accel-Redirect and the front nginx fetches it server-side, but attachments (instance logo, avatars, covers) land in the API JSON as absolute URLs the BROWSER dials. AWS_S3_ENDPOINT_URL drives the boto3 client, so with the internal endpoint django-storages signed URLs for http://seaweedfs:8333, a host no browser can reach.

Point the client at the new bucket-less public_endpoint key. Measured against the live gateway: a URL signed for seaweedfs:8333 and sent to the public host answers 403 SignatureDoesNotMatch, while one signed for the public host reaches bucket lookup — query-string SigV4 binds the Host, so the signing host and the fetch host must be the same string. AWS_S3_CUSTOM_DOMAIN cannot serve this: storages/backends/s3.py:689 returns UNSIGNED without a CloudFront signer, and the bucket also holds the music library that music/views.py exists to gate.

AWS_CA_BUNDLE follows, because the public endpoint is TLS on the local CA and botocore reads its trust store only from that variable (configprovider.py:59) — sys-svc-compose-ca injects SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE and NODE_EXTRA_CA_CERTS, never this one. It takes the bundle rather than the bare root cert, since AWS_CA_BUNDLE replaces the default store.

AWS_QUERYSTRING_AUTH=True fixes a pre-existing break: upstream defaults it to `not PROXY_MEDIA`, which produced unsigned URLs against a private bucket. The new front nginx.conf.j2 serves /_protected/media, which the stock template resolves with `alias ${MEDIA_ROOT}/$1;` and therefore 404s for every S3-backed listen.

web-svc-seaweedfs keeps no onion vhost. Its tor-contract reason now states the mechanical cause instead of the old claim that the role is never a Tor-browser surface, which decidim and funkwhale have made false: the node hidden service publishes http only while sys-front-tls keys listen/ssl on application_id, so an onion vhost would listen on 443 against a port-80 hidden service. Attachment fetches therefore still fail on an onion pick, and the spec is left to fail there rather than record the miss and pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-15 22:14:38 +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
kevinveenbirkenbachandClaude Opus 5 f2804e6662 fix(svc-ai-agent-broker): read the caller's groups on every completion
allows() is called once per /v1/chat/completions and nowhere else, so the 60s cache saved two Keycloak admin GETs on a request that then runs an LLM inference. On an authorization decision that is not a trade worth making.

It produced the CI failure directly. The spec walks hermes then openclaw: it grants biber only hermes, the hermes completion re-reads and stores {hermes}, then it swaps the grant to openclaw. Open WebUI sees the new group on the fresh sign-in, which is why the models assertion passes, but about eight seconds later the openclaw completion hits a fresh cache entry, reads {hermes} and refuses. A hit returned the value without re-stamping the timestamp, so all three attempts - 13.9s, 15.2s, 15.2s - stayed inside one 60s window and failed identically.

The same cache was a security defect in the other direction: a revoked caller stayed entitled for up to a minute, contradicting requirement 037's own sentence that the broker refuses the request and stops the agent once a caller has lost the group. Step 3 of that document now says the groups are resolved on every completion, which is what the code does. The admin-token cache is untouched - that is a credential lifetime bounded by expires_in, not an entitlement answer.

Verified with a negative control: the CI scenario run against the real access.py through a transport fake, alongside a transcription of the pre-fix groups_of over the same transport. Pre-fix, the swapped-group check returns False, which is the CI failure; post-fix it returns True, and a revocation between two checks is seen by the second.

The repository had already worked around this twice: files/test/test.sh slept 65 seconds after granting a membership and membership.py constructed the client with the cache disabled. Both are removed.

The group-to-agent mapping was not at fault - broker and spec derive the path from the identical rbac_group_path expression, and the sibling spec that holds both groups passed.

Each completion now costs two Keycloak admin round trips, unmeasured on an onion deployment where KEYCLOAK_URL is a .onion against a 15s per-call timeout. The unit test that asserted the cached answer is replaced by two that encode the CI failure. Requirement 037 still has 47 translated catalogs carrying the removed access_cache_seconds row; the i18n tests pass, so it is obsolete rather than broken.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-15 15:29:04 +02:00
kevinveenbirkenbach 844544a74b Merge branch 'main' into feature/designer 2026-10-15 15:00:31 +02:00
kevinveenbirkenbachandClaude Opus 5 1e9936a9b1 fix(tests): point the distro loop test at the function that composes the parent image
efaed0dbb9 dropped pkgmgr_image from utils/distros.py and left this test importing it at line 23 and calling it at line 91, so the module raises ImportError at collection. pytest reports that as '7581 passed, 1 error' while test-unit-python exits 1, which is why the count looks healthy and the target is red.

The test's intent outlived the refactor. INFINITO_PARENT_IMAGE still exists and default.env documents it as composed by utils/env/handlers/infinito/parent_image.py; only the oracle the assertion compared against went away.

base_image(distro, owner, tag) at utils/distros.py:116 is the successor with a compatible signature, and it is what that handler itself calls - parent_image.py:20 is 'return base_image(distro, owner=owner, tag=tag)'. The assertion now checks the value against the function that produces it instead of against a parallel copy that could drift.

Collection goes from ImportError to 11 tests collected. The body shells out through distros.sh for every distro, so it gets its real exercise in the next full gate rather than here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-15 14:57:02 +02:00
kevinveenbirkenbach 6d68498b96 Merge branch 'feature/designer' into feature/store-i18n 2026-10-15 13:29:03 +02:00
kevinveenbirkenbachandClaude Opus 5 9a5965b87c fix(web-app-moodle): gate the ownership walks on a settled bootstrap
01_manager_ops.yml waits for the moodle container to report healthy and then runs through to the ownership walks, but compose.yml.j2 gives a healthcheck to moodle alone. cron runs the same entrypoint against the same shared code volume and is not covered by that wait, so a bootstrap could still be filling and tearing down .bootstrap.stage while the walks traversed it. That is the 5089 'cannot access' lines and the closing 'Stale file handle' of job 116229890655.

5c3749c437 and 7bc5822421 serialized bootstraps against each other, append-only lock and a sentinel recheck when flock returns EIO across NFS clients. Neither serializes the deploy against a bootstrap, and no condition expressed 'the tree is final' for the deploy to wait on. The walks now wait for the state a finished bootstrap leaves behind: the stage gone and the sentinel holding the expected release. That observes the outcome rather than the lock, so it holds whether or not flock serializes on the mount.

The sentinel path moves to vars/main.yml and reaches the image as an ARG and ENV, like the stage, so the deploy and the entrypoint read one definition instead of two.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-15 11:49:15 +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
kevinveenbirkenbachandClaude Opus 5 db1a27b3f2 fix(web-app-moodle): let a second bootstrap stand down instead of swapping again
Job 116229890655 died in 03_ownership.yml after 69 seconds with 5089 chmod errors, every one of them under .bootstrap.stage, and the walk ended on 'find: failed to read file names from file system at or below /var/www/html: Stale file handle'. A second moodle-entrypoint.sh bootstrap completed inside that window and tore the staging tree away under the running find. The doubled phpspreadsheet path segment in the output is incidental.

0c748f74ed added the staging directory so a failed copy leaves the old tree in place, and guarded two instances on different releases through the version check. Two instances on the SAME release still race, because the lock lives on the code volume and does not serialize across NFS clients. moodle_bootstrap_code_dir now re-reads the sentinel after its copy and stands down when another container has finished the same bootstrap.

The three code-dir walks prune the staging directory, so a teardown mid-walk no longer fails them, and they batch through -exec + instead of one process per entry. MOODLE_BOOTSTRAP_STAGE moves into vars/main.yml and reaches the image as an ARG and ENV, so every service built from it, cron included, resolves the same path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-15 11:17:14 +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
kevinveenbirkenbach 26a7a273ff Merge branch 'main' into feature/store-i18n 2026-10-15 04:49:45 +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 cb47c44e0b Merge branch 'main' into feature/store-i18n
# Conflicts:
#	roles/web-app-seaweedfs/meta/services.yml
2026-10-15 00:41:04 +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 ac28c0b378 fix(ci): keep the backfill floor on the path the architecture gate takes
The main merge joined two correct rules into one broken script. The
architecture gate republishes the highest version when :latest misses an
architecture, because the published install instructions pull the image
untagged and only a release of that version rewrites latest. The backfill
floor refuses any tag below v9.0.2, which cannot build from its own tree.

The gate returned before the floor was ever consulted, so an incomplete
:latest would have asked the backfill for a tag it cannot build. Both
paths now ask below_floor, which also replaces the comparison the loop
carried inline.

The picker's own test could not have caught it: its docker stub answers
tags without emitting a manifest, and architectures.sh reads an empty
manifest as every architecture missing, so the gate fired on every case
and printed the newest tag. The stub now serves the whole pool, which is
the state the floor cases assume anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-14 14:33:45 +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
kevinveenbirkenbach 5e2a5fcc31 Merge remote-tracking branch 'origin/main' into feature/store-i18n
# Conflicts:
#	scripts/lint/mermaid.sh
#	tests/unit/python/utils/docker/test_image_discovery_lifecycle.py
#	utils/docker/image/discovery.py
2026-10-14 13:44:56 +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
kevinveenbirkenbachandClaude Opus 5.5 fd9286b24f test(lint): mark the stub call log as written by the test
test_no_uncached_file_reads rejects Path.read_text in tests unless the file is
written in the test itself. The mermaid script test reads the call log its
stubs append to during the run, so that read carries `# nocheck: cache-read`.
The mermaid cli test is formatted with ruff.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 01:32:35 +02:00
kevinveenbirkenbachandClaude Opus 5.5 ee8d2bd983 test(env): check each distro's parent image against its own reference
The loop test only required three different parent images for three distros,
which a swap between two of them would still pass. Each iteration now also
records the owner and tag it sees, and its image must equal
pkgmgr_image(distro, owner, tag). With the loader of main the test fails on
arch with pkgmgr-debian:stable.

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