Commit Graph
75 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 76adaeb9e8 fix(dev-python): take the supported Python range from requires-python
bf9c5be173 capped the bootstrap at 3.14 with constants inside install.sh, a
second copy of the range pyproject.toml already declares. requires-python is
now the single source (>=3.11,<3.15): install.sh derives its floor and cap
from it, in the repository or, on a remote host where the script runs alone,
from INFINITO_PYTHON_REQUIRES that the dev-python role reads from the same
file; any other form fails loudly. The Dockerfile copies pyproject.toml
before the bootstrap, and pip now refuses 3.15 for the package as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:42:34 +01:00
kevinveenbirkenbachandClaude Opus 5.5 4e060767f1 fix(update): resolve EPEL packages from dl.fedoraproject.org on CentOS
CentOS jobs also die on "Failed to download metadata for repo 'epel':
Cannot download repomd.xml ... All mirrors were tried" (feature/store-i18n
run 39658756830, Baserow#0 and Nextcloud#8 aarch64): the failing host's
librepo log shows mirrors that answered and then failed the checksum the
metalink expects, the stale-metalink signature already pinned away for
the CentOS Stream repos, plus TLS errors from two mirrors.

centos_mirror.sh now also rewrites the EPEL repo files: each section's
own commented `#baseurl=https://download.example/pub/epel/...` template
becomes the baseurl on dl.fedoraproject.org and the Fedora metalink line
goes. Using the file's template keeps the path variables of whichever
epel-release is installed (`$releasever${stream:+s}` on CentOS Stream,
`releasever_minor` in newer releases). A file whose metalinks lack that
template stops the script with an error instead of losing its source.

The Dockerfile runs the script again right after
scripts/install/filesystem.sh. Its first run precedes the EPEL install
(filesystem.sh adds epel-release for btrfs-progs), so the image shipped an
unpinned epel.repo, and the failing call, sys-svc-ssh's OpenSSH install,
runs before roles/update/tasks/centos.yml pins anything.

On CentOS Stream the template resolves to epel/10s/, which on
dl.fedoraproject.org serves the same aarch64 repomd.xml as epel/10.4/, the
only path the epel-10s metalink lists.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 20:36:48 +02:00
kevinveenbirkenbachandClaude Opus 5.5 88d76aeea0 fix(update): resolve CentOS Stream packages from mirror.stream.centos.org
Since 2026-10-21 18:45Z part of the metalinks mirrors.centos.org hands
to CI runners still expects the previous BaseOS repomd.xml (sha512
bed4f4…) while every mirror and the master already serve the current one
(2f4550…). dnf then rejects every mirror ("checksum doesn't match",
"All mirrors were tried", Nextcloud#2, Nextcloud#10, Mattermost#1 in run
39684179395) or resolves BaseOS from a mirror still on the previous push
while AppStream is current ("nothing provides rpm = 4.19.1.1-27.el10
needed by rpm-build-4.19.1.1-27.el10", Baserow#0, Akaunting#0).

roles/update/files/centos_mirror.sh replaces every metalink of the
CentOS Stream repo files with the matching baseurl on
mirror.stream.centos.org and prints what it pinned. The Dockerfile runs
it before its first dnf call, so the dev and swarm-node images never
consult a metalink; roles/update reaches fedora.yml through a new
centos.yml that runs it first, so production hosts resolve against the
same source. The script is idempotent and prints nothing once every
section is pinned.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 10:30:38 +02:00
kevinveenbirkenbachandClaude Opus 5 ef45d69dc3 perf(image): build the collections into a layer the source cannot invalidate
COPY . preceded every expensive RUN, so each commit rebuilt the whole
chain and ansible-galaxy ran on every build of every distro. The
collection install now sits in its own layer fed only by default.env,
scripts/install, the dev-python shell scripts, requirements/ and the
three files the version check imports. Everything that needs the full
tree, the project's own distro package included, stays behind COPY ..

Measured on a role-only change: the collection layer stays CACHED and
the build drops to 3m10s against a CI baseline of 855-899s. An
unchanged rebuild is 15 cached layers in 1.5s.

The gha cache imported and exported nothing in a plain run step, so the
export moves to the registry the job already authenticates against.
ignore-error keeps a cache write failure from taking the build with it,
which it otherwise does: proven against an unreachable registry, where
the build fails hard without it and exits 0 with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 20:54:49 +02:00
kevinveenbirkenbachandClaude Opus 5 8d8701d9b3 feat(apt): one Ubuntu mirror list for the package cache and image builds, with failover, a short timeout and a health test
The ubuntu CI image build (job 103277644636) hung on Canonical's
archive: apt fetched 26 MB at 417 kB/s, then 61.6 kB in 29 s, then sat
on the package download until the job was cancelled after 23 minutes,
while download.docker.com delivered 42.8 MB in under a second on the
same runner. Locally archive.ubuntu.com and security.ubuntu.com took
20-26 s to the first byte against 2 s for a university mirror.

INFINITO_APT_UBUNTU_MIRRORS in default.env is now the one list of
Ubuntu mirrors, in failover order: de.archive.ubuntu.com, ftp.fau.de,
ftp.halifax.rwth-aachen.de, archive.ubuntu.com.

- Image builds: the Dockerfile sources the list alongside the install
  scripts and runs scripts/install/apt-mirrors.sh first. In an image
  with ubuntu.sources it points both stanzas at apt's mirror+file
  method, priorities in list order, and sets Acquire::http::Timeout to
  10 s, so apt moves on from a mirror that fails or goes silent; other
  distros are untouched. In ubuntu:latest apt-get update took 8 s
  against 110 s on the stock sources, a dead first mirror was left
  behind within 25 s against 62 s on apt's defaults, and the swarm node
  packages (80 debs) came in 17 s. The Dockerfile's own RUN lines ran
  clean on ubuntu, debian, fedora and arch.
- Package cache: package.sh proxies the first entry as the primary
  Ubuntu repos and the second as their fallback. Nexus revalidates
  metadata on every request here (metadataMaxAge 0), which costs a
  full download from a mirror without an ETag; both de.archive and
  ftp.fau.de send one, and the 15 MB universe index now comes back
  from the cache in 0.02 s.
- tests/external/repository/test_apt_ubuntu_mirrors.py keeps the list
  honest: the first mirror must answer InRelease within 5 s (median of
  three) or the test names a faster later one, no mirror's -updates may
  lag the freshest by more than 24 h, and every entry must be the
  primary archive, a Canonical country alias or an enabled official
  mirror in Launchpad's registry. It caught the first draft of the
  list: mirrors.edge.kernel.org is not an official Ubuntu mirror.

After pulling, an existing checkout needs `make dotenv-force`: plain
`make dotenv` re-reads the old .env through BASH_ENV and keeps its
values.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 17:13:54 +02:00
kevinveenbirkenbach 2e3f257134 feat(image): bake the php and ruby interpreters into the image
The unit suites had to install their toolchain on every run. The image
now carries the interpreters, so the make prerequisite finds them present.

The Composer vendor tree deliberately stays out of the image build: the
package cache is reached through the compose extra_hosts mapping, which
only exists for a running container, so a build-time composer install
would bypass the cache and talk to packagist directly. Fetching it from
the test target keeps it on the cached path.
2026-08-22 20:56:55 +02:00
kevinveenbirkenbachandClaude Opus 5 0e8c535830 feat(ci): draw the data-root filesystem per distro
The pick was made once per matrix entry, in a workflow step ahead of the drill,
and every distro of that entry then ran on it. Coverage followed: one entry
exercised one filesystem, and the swarm never reached zfs at all, because
resolve.sh's userland_for() kept it out of any entry containing arch, centos or
fedora - which is every automatic sweep. The draw now happens inside the loop in
distros.sh, once per distro, so a five-distro entry covers zfs with 87% and all
three kinds with 62% probability instead of one kind with certainty.

The workflow input changes meaning with it. It was a single kind, offered as a
choice in the manual entry; it is now a space-separated allow-list. Empty draws
from all three, and naming kinds narrows the draw to them and marks the pick
required, so a host that cannot deliver one of them fails rather than quietly
running on something else. A name that is not a filesystem this project puts a
data root on is rejected outright instead of reaching the applying step.

For the draw to mean anything the images have to carry the userlands, so
scripts/install/zfs.sh becomes scripts/install/filesystem.sh and installs ext4
and btrfs alongside zfs. That is not symmetric work: btrfs-progs does not exist
in the EL family at all, because RHEL dropped btrfs, and a plain
`dnf install btrfs-progs` there fails the whole transaction and takes e2fsprogs
with it. EPEL carries it, so the EL family goes through EPEL from the start -
split by distro rather than by a failed first attempt, since Fedora has no
epel-release to fall back to and routing a transient failure there would turn it
into a fatal one. verify() now demands all four binaries, so a packaging
regression fails the build instead of leaving a hole the drill discovers later.
Measured on all five bases: debian trixie, ubuntu 26.04, arch, fedora 44 and
centos 10 each end with mkfs.ext4, mkfs.btrfs, zfs and zpool present and ldd
fully resolved.

Two defects only became reachable once a single job could ask for different
filesystems in turn:

  * docker_dataroot.sh answered "already prepared" and returned for any mount it
    found, so from the second distro on, every draw would have been silently
    ignored on a host that survives the sweep - the runner in compose and host
    mode. It now compares what is mounted against what was asked for and
    dismantles the old root when they differ. Everything docker cached there
    goes with it; that is what covering more than one filesystem per entry
    costs.
  * reclaim_stale_pool ran only when the new pick was zfs, so a zfs to btrfs
    transition left the previous distro's pool imported on the shared kernel,
    pinning a loop device over an image its dead container had already unlinked.
    It now runs on every pick, which needed the /dev/zfs handling lifted out of
    the zfs branch into zfs_device_ready().

The restore_docker trap is armed before anything can stop docker, rather than
after, so a failure while dismantling a data root cannot leave a host without a
daemon. resolve.sh splits its allow-list into an array before testing it, so a
whitespace-only input is the empty list it looks like rather than a narrowed one
that yields an empty pick marked required. pick.sh clears both keys when it
declines to draw: they are declared in default.env, so a generated .env carries
whatever was exported when it was made, and a caller gating its apply on the
value would otherwise repoint the developer's own docker in the very case the
skip exists to prevent.

Verified by execution rather than by reading: a privileged container ran
docker_dataroot.sh with real loop devices and mkfs through ext4 -> ext4 -> btrfs
-> ext4, confirming the recognition, the replacement in both directions and that
no loop leaks across it; a second harness with a recording zpool stub confirmed
that a btrfs pick does reclaim a stale pool, that no pool means no destroy, and
that a host without /dev/zfs still completes an ext4 pick without touching zpool.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 13:27:35 +02:00
kevinveenbirkenbachandClaude Opus 5 ed1121053b feat(images): carry the zfs userland on all distros
dockerd's zfs graphdriver resolves `zfs` on PATH and opens /dev/zfs before it
accepts a data root (moby daemon/graphdriver/zfs/zfs.go:54,59), so the userland
has to sit in the image, not on the host. Only the apt images carried it, which
is why resolve.sh's userland_for() kept zfs out of every entry containing arch,
centos or fedora - that is every automatic swarm sweep, so the zfs data root and
the zfs branch of the backup launcher were never once exercised. Run
30781723102 shows it: "zfs left out: not every distro of this entry carries the
userland".

scripts/install/zfs.sh joins the three existing install scripts in the image
build and takes a different route per distro, each one run in a real container
of the matching base image before landing here:

  debian trixie 2.3.2   zfsutils-linux, contrib enabled first
  ubuntu 26.04 2.4.1    zfsutils-linux
  arch 2.4.3            the AUR PKGBUILD, which configures --with-config=user
  fedora 44 2.4.3       zfsonlinux repo plus a dummy zfs-kmod provider
  centos 10 2.2.10      the same, from the epel path

The dummy is what keeps the RPM route usable: the `zfs` package carries an
ungated `Requires: %{name}-kmod`, so a plain `dnf install zfs` resolves through
zfs-dkms and installs 389 extra packages and 1.44 GB of kernel and toolchain to
build a module against a kernel the container never boots. Providing zfs-kmod
from an empty package keeps the same install at 60 packages with no kernel
package at all, and the transaction is checked afterwards so a regression fails
the build instead of bloating the image. The repository is removed again once
the userland is in, because the dummy pins one version and an upgrade would
otherwise re-resolve the kmod at deploy time.

Version drift against the host module is not a concern: zpool create enables a
feature only when the loaded module reports it through /sys/module/zfs/features
.pool (cmd/zpool/zpool_main.c, module/zcommon/zfeature_common.c), and libzfs
compares no versions at all (lib/libzfs/os/linux/libzfs_util_os.c:123-134), so a
newer userland degrades rather than breaking.

With every image carrying it, userland_for() and every_distro_carries() have
nothing left to decide and go. Two defects that only became reachable once zfs
could be drawn are fixed in the same move:

  * /dev/zfs is a misc device, so the old lookup in /proc/devices could never
    find its major and the node was never created. A node container's /dev is
    populated at creation and udevd is masked, so nothing else fills the gap
    either: the first bring-up of every job would have declined, blaming a
    module that modprobe had just loaded. The minor now comes from /proc/misc.
  * Pools live in the kernel the lab nodes share with the runner, while the
    teardown only removes containers, and the node hostnames repeat across the
    distro sweep. From the second distro on, zpool create hit the previous
    iteration's pool. It is destroyed and its loop detached first.

The node image drops its own apt-only zfs branch and instead masks the six boot
units the userland enables by preset, which on a node can only fail: the pool is
created after boot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 13:27:34 +02:00
kevinveenbirkenbachandClaude Opus 5 384410814a fix(docker): keep hadolint on bash for the root Dockerfile
hadolint 2.15.0 drops the SHELL binding across an intervening ARG/ENV block and lints the RUN instructions as POSIX sh, so the pinned bash shell went unnoticed and lint-dockerfile failed on SC3040/SC3001 without the Dockerfile having changed. Declaring SHELL below that block restores the binding; nothing runs before it, so the build is unaffected.

The last RUN sits behind ENV container=docker and STOPSIGNAL, where the same reset applies. Moving those would change the environment the entry.sh compile step runs in, so that one instruction carries an ignore instead. Give HEALTHCHECK its command in JSON notation, which DL3025 asks for and which drops the needless shell.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 17:22:28 +02:00
kevinveenbirkenbachandClaude Opus 5 41754343b6 ci(dockerfile): ban syntax parser directive
Drop the `# syntax=docker/dockerfile:1` line from the root Dockerfile and
add a lint test that keeps it out of every Dockerfile in the repo.

The directive makes BuildKit pull an external frontend image from Docker
Hub at build time. That tag floats to the newest 1.x release, Podman and
Buildah have no frontend dispatch and ignore it entirely, and the pull
turns every build into a network dependency that fails whenever Hub is
unreachable or rate-limits the runner.

The root Dockerfile used no frontend-only feature, so the build behaviour
is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 23:25:50 +02:00
kevinveenbirkenbachandClaude Opus 4.8 867ab9e17d fix(image): source install-script paths from default.env, harden apt key download
The pull_request_target image build ran main's push.sh (which does not pass
the install-script build args) against the PR Dockerfile, so all five distro
builds died with 'INFINITO_PYTHON_INSTALL_SCRIPT: parameter null or not set'.
Drop those three ARGs and source the paths straight from default.env inside
the install RUN (default.env is already in the build context), keeping one
SPOT and removing the cross-branch build-arg coupling.

apt_repo.sh no longer swallows a failed key download: download_file returns
curl's status and retries transient failures, and install_key aborts when the
key download fails or lands empty, instead of installing an empty keyring that
later makes apt reject the repo as unsigned (the suitecrm node's DNS blip).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 03:04:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 9f8aa6e9b6 refactor(env): SPOT the install scripts and derive the nfs export base from group_vars
INFINITO_PYTHON_INSTALL_SCRIPT and INFINITO_DOCKER_CLI_INSTALL_SCRIPT join the default.env SPOT: the Dockerfile consumes them as required args (:? in the RUN as last defense), all three image builders (compose.yml, build.sh, push.sh) pass them hard-required, the shell consumers read the SPOT via default.env grep and the Makefile via BASH_ENV. INFINITO_SWARM_NFS_EXPORT_BASE is no longer a static default: a new env handler derives it from the deploy SPOT group_vars/all/15_storage.yml, 00_topology greps that file (and the STATE_SUBDIR constant) directly, and the swarm test extras stop restating the group_vars value. Stale hadolint directives are restored and recognised; leftover narration comments in the touched files are dropped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 23:04:40 +02:00
kevinveenbirkenbachandClaude Opus 4.8 bcd6e82f14 refactor(install): SPOT the package install script path
INFINITO_PACKAGE_INSTALL_SCRIPT in default.env replaces the scripts/install/package.sh literal in the Dockerfile (new required build arg), the environment install step, and both swarm bootstrap call sites. Shell consumers hard-require the variable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 17:46:34 +02:00
kevinveenbirkenbachandClaude Opus 4.7 00c4246c3a refactor(env): introduce INFINITO_VENV_DIR SPOT, fix dev image build
Adds INFINITO_VENV_DIR=/opt/venvs/infinito to default.env (registered
in passthrough's STATIC_KEYS so make dotenv propagates it to .env) and
threads it through the build chain: compose.yml forwards it as a build
arg; scripts/image/push.sh asserts and --build-args it; the Dockerfile
declares the matching ARG/ENV and derives PYTHON/PIP/PATH from the
variable. scripts/meta/env/python.sh now prefers the value when set
before its fallback discovery so the SPOT is effective in env
resolution.

Fixes the dev image build regression where 'infinito --version' in
scripts/docker/entry.sh exited 127. Root cause: the Dockerfile RUN
SHELL uses bash -lc, which sources /etc/profile and resets PATH to
the Debian default, dropping ${INFINITO_VENV_DIR}/bin from it. The
fix invokes the script via its absolute path "${INFINITO_VENV_DIR}/bin/infinito"
so the post-install version check no longer depends on a PATH that
the login-shell init has just clobbered.

scripts/lint/wrapper.sh's existing PATH-reset note now references the
variable instead of the literal path. test-install-make.yml and
test-install-pkgmgr.yml read INFINITO_VENV_DIR from default.env up
front, pass it into their pkgmgr containers, and source the venv
activate script via the variable.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 22:12:19 +02:00
kevinveenbirkenbachandClaude Opus 4.7 848fd08308 fix(docker): drop pkgmgr from dev container, install in-tree via make
`pkgmgr install infinito` started failing in CI after the floating
`pkgmgr-<distro>:stable` base image was rebuilt with kpmx 1.15.x, which
no longer registers the `infinito` identifier in its default config:

    Identifier 'infinito' did not match any repository in config.

The error string then flowed through `pkgmgr path infinito` in
entry.sh and was treated as the install directory, leading to
`make install` in a bogus cwd. This broke test-development,
test-install-pkgmgr, and build-ci-images for every distro.

The pkgmgr step in the dev Dockerfile is redundant — `make install`
runs `pip install -e .` which already produces the `infinito` CLI
shim via pyproject.toml's [project.scripts] entry. Drop the pkgmgr
calls, the rsync-into-INFINITO_PATH dance, and the `INFINITO_PATH`
variable; everything operates on `INFINITO_SRC_DIR` directly.

Production `pkgmgr install` paths (roles/sys-cli, roles/web-app-navigator,
.github/workflows/test-install-pkgmgr.yml) are intentionally untouched —
the test-install-pkgmgr job is what surfaces the upstream kpmx breakage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 10:50:35 +02:00
kevinveenbirkenbachandClaude Opus 4.7 183f41845b refactor(env): SPOT pkgmgr base image via handler, drop slim variant
default.env decomposed into INFINITO_PARENT_IMAGE_{OWNER,TAG,VARIANT}
atoms. New utils/env/handlers/infinito_parent_image.py owns the URL
composition `ghcr.io/{owner}/pkgmgr-{distro}:{tag}` as the single
definition site; scripts/image/{push,build}.sh consume the handler-
emitted INFINITO_PARENT_IMAGE from .env instead of inlining their own
format string.

Side effect: `compose up --build` now picks the parent image that
matches INFINITO_DISTRO. Previously default.env's hardcoded
pkgmgr-arch:stable won regardless, so debian-named containers were
silently built FROM the arch base. scripts/image/build.sh already
overrode per-distro, so its behavior is unchanged.

Other cleanups landed in the same pass:
- Slim variant removed end-to-end (Dockerfile stage, --variant CLI
  flag, slim suffix logic). Zero in-repo callers used it.
- compose.yml build-args INFINITO_PARENT_IMAGE / INFINITO_SRC_DIR now
  carry the same `:?Run 'make dotenv'…` guard as the runtime entries,
  so a missing .env fails fast with a clear message instead of with
  buildkit's `base name should not be blank`.

Note: fork-CI (pull_request_target trusted-helpers path) will stay red
until main's scripts/image/push.sh and scripts/meta/wait/images.sh
adopt the INFINITO_* names too — that is the deliberate cost of
keeping a single SPOT instead of carrying a bridge alias.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 10:44:50 +02:00
kevinveenbirkenbachandClaude Opus 4.7 12a56e449d refactor(env): move env/default.env to repo-root default.env and drop env/
With env.development and env/ci.env both gone, env/default.env was the
last inhabitant of the directory. Hoisting it to the repo root removes
the empty-shell indirection: the SPOT is now [default.env](default.env),
sitting next to .env (its generated companion) and other repo-root
configuration artefacts like compose.yml, Dockerfile, and Makefile.

Path updates land in:
- generator: cli/meta/env/__main__.py (REPO_ROOT / "default.env")
- workflow: .github/workflows/test-install-make.yml (awk reads from
  default.env, error message updated in lockstep)
- lint tests: test_used_keys_declared.py and test_infinito_prefix.py
  switched from globbing env/*.env to targeting default.env directly;
  test_default_env_keys_handled.py points at the new path
- drift test: test_inventory_vars_file_spot.py whitelist + STATIC_ENV
- smoke test: tests/integration/meta/env/test_dotenv_generator.py
  STATIC_ENV + every docstring/error message

Doc-string and prose references in utils/env/*, scripts/*,
cli/administration/deploy/development/{common,coredns}.py,
Makefile, Dockerfile, and every doc page that mentioned env/default.env
are rewritten in the same pass so future readers see one path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 16:22:33 +02:00
kevinveenbirkenbachandClaude Opus 4.7 a77a570409 refactor(env): consolidate INFINITO_* namespace and centralize path/image SPOTs
Closes a stack of env-pipeline regressions left over from the recent
INFINITO_* namespace refactor by:

* DOMAIN_PRIMARY now reads INFINITO_DOMAIN strictly (was: legacy DOMAIN
  env var → defaulted to infinito.localhost, which never resolved
  because CoreDNS serves the INFINITO_DOMAIN value, infinito.example).
* group_vars/all/08_networks.yml IPs (ip4/ip6/dns) now read
  INFINITO_IP4 / INFINITO_IP6 / INFINITO_DNS_IP.
* PLAYWRIGHT_KEEP_ALL renamed to INFINITO_PLAYWRIGHT_KEEP end-to-end
  (env-builder, role default, run_one.yml docker exec, playwright.config.js,
  lint allowlist, docs anchor, CI workflow vars).
* PKGMGR_IMAGE renamed to INFINITO_PARENT_IMAGE; Dockerfile ARG drops
  the inline default so env/default.env is the only SPOT.
* INFINITO_SRC_DIR centralized: Dockerfile ARG (no default) + ENV;
  compose forwards it as build-arg, volume mount path, and container env;
  every Python CLI + shell helper + CI workflow now reads it strictly
  (os.environ[...] / "${INFINITO_SRC_DIR:?...}") instead of the literal
  /opt/src/infinito.
* New entries in env/default.env: INFINITO_IP6=::1, INFINITO_PARENT_IMAGE,
  INFINITO_PLAYWRIGHT_KEEP=false. All three propagated through
  utils/env/handlers/passthrough.STATIC_KEYS and (where consumed in the
  container) through compose.yml's environment block.

Validated end-to-end with `make test` (2360 tests across lint, lint-*,
test-lint, test-external, test-unit, test-integration).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 02:30:34 +02:00
kevinveenbirkenbachandClaude Opus 4.7 b278cd8ccd refactor(entry.sh): switch INFINITO_COMPILE{,_SILENCE} env-vars to flags
scripts/docker/entry.sh's two compile-control reads (INFINITO_COMPILE
and INFINITO_COMPILE_SILENCE) crashed during Dockerfile RUN because
that context has no env-pipeline: env/default.env exists in the repo
but no make/BASH_ENV/load.sh chain materialises it into the running
shell. The bare reads under `set -u` triggered unbound-variable exit
even though the keys were registered in default.env + passthrough.

Move build/silence control from env to script flags so entry.sh is
self-contained at bootstrap time. Each flag now triggers its action
directly inside the parse loop -- no COMPILE/SILENCE state vars:

    --compile         -> run_local_build (streaming)
    --compile-silent  -> run_local_build >/dev/null 2>&1
    --                -> end flag parsing, rest is the exec target

Drop INFINITO_COMPILE + INFINITO_COMPILE_SILENCE from the whole
registry surface: env/default.env, passthrough.STATIC_KEYS, the
infinito_compile handler (gone entirely), compose.yml's env
injection block, the dotenv-generator baseline test, the
build_env unit-test fixture, and the now-no-op `-e INFINITO_COMPILE=0`
in test-install-{make,pkgmgr} workflows.

Dockerfile RUN switches to `entry.sh --compile -- true`; runtime
ENTRYPOINT receives no flags so the default (no-compile) holds and
exec hands off to systemd via the CMD.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 23:25:39 +02:00
kevinveenbirkenbach 623fa60c64 fix(docker): increase healthcheck timeout 2026-03-30 22:34:51 +02:00
kevinveenbirkenbach 66bbffe963 Fix docker image reference scan hints 2026-03-25 19:24:53 +01:00
kevinveenbirkenbach 2e37a3689f Merge branch 'main' into feature/lint
# Conflicts:
#	Dockerfile
#	scripts/administration/ghcr/cleanup_ci_images.sh
#	scripts/meta/wait/image.sh
2026-03-22 21:51:23 +01:00
kevinveenbirkenbach fb6c85783f Fix hadolint warning in slim Docker stage 2026-03-21 12:52:03 +01:00
kevinveenbirkenbach 0cfd8414d2 fix: resolve hadolint DL3059 warning 2026-03-21 10:36:35 +01:00
kevinveenbirkenbach 3b11d7f469 feat(packaging): add distro packages and install via package metadata 2026-03-19 02:53:15 +01:00
kevinveenbirkenbach 9766f8e8b3 feat(dev-python): install Python 3.11+ by default
- add cross-distro dev-python installer script and wire it into role + Dockerfile
- regenerate dockerignore from gitignore on make build and ignore logs/
- harden sys-pip-install break-system-packages support probe
- raise project requires-python to >=3.11
2026-02-21 23:45:41 +01:00
kevinveenbirkenbach 9bc6d3f431 docker: install ssh client in CI image for Ansible controller
Extend the systemd/dbus installation step to include the SSH client on all
supported distros, fixing Ansible controller failures caused by a missing
`ssh` binary.

https://chatgpt.com/share/698a3356-f018-800f-80e2-f327f00ce65f
2026-02-09 20:19:34 +01:00
kevinveenbirkenbach 7bb15d1b5e refactor(container): rename docker-container/docker services to sys-svc-container and update all references, schedules, tests, and templates
https://chatgpt.com/share/6985b09d-8ff8-800f-800c-7a694c0983e2
2026-02-06 10:12:31 +01:00
kevinveenbirkenbach 18acafa9dc ci(images): build & push full+slim CI images
- Build matrix now includes variants: full + slim
- Tag slim images with -slim suffix and use Dockerfile target selection
- Use dedicated gha cache scopes per distro+variant
- Update release checks/retag/promote scripts to handle slim images too

https://chatgpt.com/share/69701a4e-b000-800f-be7e-162dcb93b1d2
2026-01-21 01:46:52 +01:00
kevinveenbirkenbach e23ffdc2aa feat(nix): make flake config non-interactive via nix.conf and simplify NIX_CONFIG handling
Enable accept-flake-config globally in the image to avoid interactive prompts.
Remove automatic NIX_CONFIG injection from Makefile and only export it when explicitly set.

https://chatgpt.com/share/696fb4fc-8cf0-800f-af72-f5d6428be14c
2026-01-20 18:01:23 +01:00
kevinveenbirkenbach fda49ca30e fix(ci,nix): make flake config non-interactive and add safe pkgmgr debug
- Stop baking NIX_CONFIG into Docker image layers
- Build effective NIX_CONFIG via Makefile using real multiline config
- Always allow flake-provided nixConfig non-interactively
- Add sanitized rescue/debug block for pkgmgr update (no credential leaks)
- Improve CI diagnostics for GitHub token / nix access issues

https://chatgpt.com/share/696f8687-4444-800f-ab86-021bcb35a7a6
2026-01-20 14:43:09 +01:00
kevinveenbirkenbach 659b45f7a2 build(docker): enforce and run base image slim.sh cleanup during build
- ensure slim.sh exists in pkgmgr base image
- execute safe verbose cleanup before entrypoint
- reduce final Docker image size without touching runtime files

https://chatgpt.com/share/696f4ab6-fae8-800f-9a46-e73eb8317791
2026-01-20 10:54:55 +01:00
kevinveenbirkenbach a323f2199d optimized code tests 2026-01-15 02:07:31 +01:00
kevinveenbirkenbach 8ea56548d1 Optimized INFINITO_BUILD_LOCAL 2026-01-15 01:53:32 +01:00
kevinveenbirkenbach 6b7b67681e fix(ci): unblock systemd startup and stabilize Docker healthcheck
Ensure systemd is started immediately as PID 1 in the infinito container
to avoid blocking on pkgmgr during boot. Increase compose runner timeout
to match healthcheck window and make healthcheck script executable in the
image.

This fixes containers getting stuck in "starting" state and makes CI
startup deterministic.

https://chatgpt.com/share/696683a2-9350-800f-a0cf-587d4013cb29
2026-01-13 19:02:43 +01:00
kevinveenbirkenbach 8c82db3556 feat(ci): add Docker healthcheck and wait for healthy container in compose runner
Add a systemd-based Docker HEALTHCHECK that verifies:
- systemd readiness
- infinito CLI availability and executability
- infinito CLI smoke test (--help)

Update CI compose runner to wait for Docker health status instead of
manual systemd polling. The runner now:
- resolves the infinito container via docker compose
- waits for healthy state
- dumps journalctl and docker logs on timeout

This removes boot race conditions and makes container readiness deterministic.

https://chatgpt.com/share/696683a2-9350-800f-a0cf-587d4013cb29
2026-01-13 18:40:33 +01:00
kevinveenbirkenbach 3a5912845f fix(docker): remove implicit cgroup volume from base image
Drop VOLUME /sys/fs/cgroup from Dockerfile and rely on docker-compose
to mount cgroups explicitly for systemd-based CI containers.
Prevents creation of anonymous volumes in derived images.

https://chatgpt.com/share/695f94d9-d4a0-800f-a9eb-19e67445a0fe
2026-01-08 12:28:07 +01:00
kevinveenbirkenbach 6f9e887f35 fix(docker,systemd): disable systemd-firstboot in container + harden docker restart diagnostics
- mask systemd-firstboot and first-boot-complete in Dockerfile to prevent systemd boot hang in CI/compose
- ensure machine-id is initialized during image build
- add early assertions for systemd availability and PID 1 correctness in docker role
- replace blocking docker restart with non-blocking restart + 60s wait and detailed debug dump on failure
- normalize task numbering (fuse-overlayfs, reset) for clarity

https://chatgpt.com/share/6953fbef-c508-800f-ac06-39f64f92f008
2025-12-30 17:20:59 +01:00
kevinveenbirkenbach d65749157f chore(docker): silence hadolint version pinning warnings for multi-distro CI install
Ignore DL3008, DL3033 and DL3041 for the systemd/dbus installation step.
This image is used for multi-distro CI testing, where package version
pinning is neither portable nor desirable.

https://chatgpt.com/share/6950ffb3-1970-800f-9533-a115905ef6b7
2025-12-28 10:59:54 +01:00
kevinveenbirkenbach 789ba5a247 - Make Nix non-interactive by default via NIX_CONFIG (accept-flake-config)
- Restore docker service handling outside containers, validate inside containers
- Simplify entry.sh: keep original rebuild/copy logic, remove systemd branching
- Switch CI deploy runner:
  - start only CoreDNS via docker compose
  - start infinito via docker run with systemd, cgroup host access and bind mounts
- Avoid compose limitations (no cgroupns=host support)
- Ensure deterministic container naming for exec-based CI operations

https://chatgpt.com/share/6950110b-111c-800f-bbcd-83a551d5a3b8
2025-12-27 20:47:50 +01:00
kevinveenbirkenbach b207134472 feat(ci): enable systemd-in-docker and use explicit container detection
- Install systemd and dbus in Docker image for CI service tests
- Run systemd as PID 1 with proper container conventions
- Mark container environment explicitly via ENV container=docker
- Simplify container detection to rely on explicit env flag
- Skip host-only tasks (timezone, systemctl handlers) inside containers
- Adjust docker-compose runner for privileged systemd execution
- Reduce CI parallelism to avoid OOM kills

This makes systemd-based Ansible roles reliable in Docker/CI
and avoids fragile virtualization heuristics.

https://chatgpt.com/share/6950110b-111c-800f-bbcd-83a551d5a3b8
2025-12-27 18:14:28 +01:00
kevinveenbirkenbach 71604e199e refactor(sys-svc-docker): split docker CLI (container) from engine (host)
- move distro-aware docker CLI installation into shared install-cli.sh
- use install-cli.sh only in Dockerfile (container context)
- install docker engine and compose only on hosts via Ansible
- guard engine installation with DOCKER_IN_CONTAINER
- avoid mixing Docker Inc. repo with Debian docker.io packages

https://chatgpt.com/share/694c3985-f228-800f-ac5c-b0ae41558596
2025-12-24 20:05:24 +01:00
kevinveenbirkenbach b48c26ef80 refactor(user-root): move OpenSSH client install to role and modularize root SSH key setup
- Remove openssh-client from Dockerfile to avoid hard dependency
- Add user-root SSH variable definitions for private/public key paths
- Split root SSH key generation into dedicated task file
- Ensure idempotent key creation and output handling
- Improve maintainability by decoupling container build from user provisioning

https://chatgpt.com/share/6948403b-7440-800f-bb45-72456827a6c5
2025-12-21 19:45:11 +01:00
kevinveenbirkenbach 70497bd404 fix(docker): install openssh-client on Debian/Ubuntu images
https://chatgpt.com/share/6947ca03-4b00-800f-b9a3-6a9dc148a739
2025-12-21 11:20:39 +01:00
kevinveenbirkenbach cc9c2dceb8 fix(ci,nix): forward NIX_CONFIG into docker build and runtime to avoid GitHub rate limits
- Export NIX_CONFIG in CI jobs and Makefile
- Pass NIX_CONFIG as docker build-arg
- Use NIX_CONFIG only in relevant Dockerfile RUN steps (no secret persistence)
- Forward NIX_CONFIG to test and deploy containers

https://chatgpt.com/share/6947c681-abc8-800f-ba12-de2ff3a7abad
2025-12-21 11:05:31 +01:00
kevinveenbirkenbach fcfb37beba fix(dockerfile): prevent CentOS Stream from matching Fedora install path
- Match Fedora Docker CLI install only on ID=fedora
- Remove fedora from RHEL/CentOS ID_LIKE matching
- Ensure CentOS Stream 9 is handled by the RHEL/CentOS branch
- Avoid invalid docker-cli/moby-engine installs on CentOS

https://chatgpt.com/share/69472a4d-ac6c-800f-b5e1-e2fcd815ded0
2025-12-21 00:05:34 +01:00
kevinveenbirkenbach e3dcd4b2a6 fix(dockerfile): avoid curl conflicts on CentOS Stream and reduce redundant installs
- Drop explicit ca-certificates/curl installs on Fedora and RHEL/CentOS paths
- Rely on pkgmgr base images for common tooling
- Prevent curl vs curl-minimal conflicts on CentOS Stream 9
- Keep Docker CLI installation logic unchanged

https://chatgpt.com/share/69472a4d-ac6c-800f-b5e1-e2fcd815ded0
2025-12-20 23:59:13 +01:00
kevinveenbirkenbach 8c6d694c66 Solved Dockerfile bug 2025-12-20 23:47:51 +01:00
kevinveenbirkenbach 103659a534 refactor(deploy): modularize dedicated deploy CLI and fix make cwd issue
- Split dedicated deploy logic into focused modules (apps, modes, paths, proc, runner)
- Ensure all make targets run from the repository root via explicit cwd handling
- Fix "make: No rule to make target" errors caused by incorrect working directory
- Slim down __main__ to a thin CLI entrypoint
- Rework unit tests into one file per module and expand coverage

https://chatgpt.com/share/69472632-9740-800f-962c-bcb4474963d6
2025-12-20 23:41:41 +01:00
kevinveenbirkenbach fdcc910ce9 fix(tests, docker): pin Python venv inside image and stop leaking host PYTHON
- Define PYTHON and PIP explicitly in Dockerfile (/opt/venvs/infinito)
- Remove PYTHON env passthrough from test container runs
- Prevent host virtualenv paths from breaking container tests
- Align .dockerignore build path handling

https://chatgpt.com/share/6946f090-52b8-800f-919d-d534ae4ea163
2025-12-20 20:16:54 +01:00