From a10c238d09e0bb98c4f238a14ae52deae2e31b48 Mon Sep 17 00:00:00 2001 From: Aron Gates Date: Wed, 16 Sep 2026 14:57:58 -0700 Subject: [PATCH 1/2] fix(ci): pin semantic-release preset and repair python/php image builds after base drift Three independent CI breakages, each root-caused and reproduced before fixing: - release.yml installed semantic-release and the conventionalcommits preset unpinned. Preset 10.0.0 (published 2026-06-26, after our last successful release) requires conventional-changelog-writer@9 while @semantic-release/release-notes-generator still bundles writer@8, so generateNotes fails and no release is cut. Pinned to semantic-release@25.0.9 + conventional-changelog-conventionalcommits@9.3.1; verified the exact pair renders notes and that 10.4.0 reproduces the CI error in a local dry run. - python image: the dhi.io/python:3.14 packaging moved from /opt/python to /usr (interpreter /usr/bin/python, site-packages /usr/lib/python3.14/site-packages) and is now apt-managed. Installing Debian's python3-dev (and the libpulse-dev/portaudio19-dev chains) makes apt REMOVE python-3.14 entirely, which is the 'python: command not found' CI failure. Dropped those packages (headers ship as python-3.14-dev; nothing in requirements/ compiles against pulse or portaudio) and updated the final-stage copy paths and ENTRYPOINT PATH to the new layout. - php image: shared libs were copied from the hard-coded /usr/lib/x86_64-linux-gnu dir, which does not exist in the arm64 php-source stage (lstat error). Now globbed per-arch into /usr/local/lib, which is on the default ld.so path for both arches. Co-Authored-By: Claude Fable 5 --- .github/workflows/release.yml | 5 ++++- docker/php.Dockerfile | 11 ++++++----- docker/python.Dockerfile | 22 ++++++++++++++-------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca13f0f..2eafaf2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,8 +31,11 @@ jobs: node-version: "lts/*" - name: Install semantic-release + # Pinned: conventional-changelog-conventionalcommits@10 requires + # conventional-changelog-writer@9, but @semantic-release/release-notes-generator + # still bundles writer@8 - the unpinned combo broke generateNotes on 2026-09-16. run: | - npm install -g semantic-release conventional-changelog-conventionalcommits + npm install -g semantic-release@25.0.9 conventional-changelog-conventionalcommits@9.3.1 - name: Get latest release before id: before diff --git a/docker/php.Dockerfile b/docker/php.Dockerfile index 5e8fb21..a88e0ef 100644 --- a/docker/php.Dockerfile +++ b/docker/php.Dockerfile @@ -28,11 +28,12 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Copy PHP installation from DHI PHP image COPY --from=php-source /opt/php-8.5 /opt/php-8.5 -# Copy shared libraries PHP depends on (avoids chasing individual packages) -COPY --from=php-source /usr/lib/x86_64-linux-gnu/libargon2.so* /usr/lib/x86_64-linux-gnu/ -COPY --from=php-source /usr/lib/x86_64-linux-gnu/libsodium.so* /usr/lib/x86_64-linux-gnu/ -COPY --from=php-source /usr/lib/x86_64-linux-gnu/libicu*.so* /usr/lib/x86_64-linux-gnu/ -COPY --from=php-source /usr/lib/x86_64-linux-gnu/libonig.so* /usr/lib/x86_64-linux-gnu/ +# Copy shared libraries PHP depends on (avoids chasing individual packages). +# The multiarch dir differs per platform (x86_64-linux-gnu vs +# aarch64-linux-gnu), so glob it and land the libs in /usr/local/lib, which +# is on the default ld.so search path for either arch. The compile steps +# below run ldconfig before first invoking php. +COPY --from=php-source /usr/lib/*-linux-gnu/libargon2.so* /usr/lib/*-linux-gnu/libsodium.so* /usr/lib/*-linux-gnu/libicu*.so* /usr/lib/*-linux-gnu/libonig.so* /usr/local/lib/ # Put PHP in PATH for build steps ENV PATH="/opt/php-8.5/bin:${PATH}" diff --git a/docker/python.Dockerfile b/docker/python.Dockerfile index 6ba27a8..f58464c 100644 --- a/docker/python.Dockerfile +++ b/docker/python.Dockerfile @@ -25,7 +25,12 @@ RUN apt-get update && \ g++ \ make \ pkg-config \ - python3-dev \ + # NOTE: no python3-dev here - Debian's python3 (3.13) chain CONFLICTS with + # the DHI python-3.14 packages and apt resolves it by REMOVING python 3.14 + # entirely ("python: command not found"). The image already ships + # python-3.14-dev / libpython-3.14-dev. Same reason portaudio19-dev and + # libpulse-dev were dropped below: their chains pull Debian python3 and + # nothing in requirements/ compiles against them. # Development libraries (runtime libs installed in final stage) libxml2-dev \ libxslt-dev \ @@ -38,8 +43,6 @@ RUN apt-get update && \ libfreetype6-dev \ liblcms2-dev \ libwebp-dev \ - portaudio19-dev \ - libpulse-dev \ && apt-get autoremove -y \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -135,10 +138,13 @@ COPY --from=runtime-deps /usr/lib/aarch64-linux-gnu /usr/lib/aarch64-linux-gnu COPY --from=runtime-deps /usr/bin/pdftotext /usr/bin/pdftoppm /usr/bin/pdfinfo /usr/bin/ COPY --from=runtime-deps /usr/bin/ffmpeg /usr/bin/ffprobe /usr/bin/ -# Copy installed Python packages from builder -# DHI Python is installed in /opt/python, not /usr/local -COPY --from=builder /opt/python/lib/python3.14/site-packages /opt/python/lib/python3.14/site-packages -COPY --from=builder /opt/python/bin /opt/python/bin +# Copy installed Python packages from builder. +# DHI python >=3.14.7 is packaged under /usr (interpreter at /usr/bin/python, +# site-packages at /usr/lib/python3.14/site-packages); the old /opt/python +# layout no longer exists. Console-script shims are not carried over - the +# runner invokes `python {file}` and library use is import-based (`python -m +# pip` still works since the upgraded pip lands in site-packages). +COPY --from=builder /usr/lib/python3.14/site-packages /usr/lib/python3.14/site-packages # Copy /usr/bin/env for ENTRYPOINT COPY --from=runtime-deps /usr/bin/env /usr/bin/ @@ -150,7 +156,7 @@ WORKDIR /mnt/data # Sanitized environment via env -i ENTRYPOINT ["/usr/bin/env", "-i", \ - "PATH=/opt/python/bin:/usr/bin:/bin", \ + "PATH=/usr/bin:/bin", \ "HOME=/tmp", \ "TMPDIR=/tmp", \ "PYTHONUNBUFFERED=1", \ From b246cd10037729d395060f1e121a77445ce10259 Mon Sep 17 00:00:00 2001 From: Aron Gates Date: Wed, 16 Sep 2026 15:07:19 -0700 Subject: [PATCH 2/2] fix(ci): fall back to runner:latest when the runner image is not built in-run Language images do FROM runner_image, which was always pointed at the current run's dev- tag - but that tag only exists when docker/runner/** changed (runner build job) or on releases (retag-runner). A PR touching a language Dockerfile without touching the runner made every language build fail with 'kubecoderun-runner:dev-: not found'. Use :latest (the last released runner) in that case; release and force_all behavior unchanged. Co-Authored-By: Claude Fable 5 --- .github/workflows/docker-publish.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 532a63f..23f648c 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -124,7 +124,19 @@ jobs: echo "is_release=false" >> $GITHUB_OUTPUT fi echo "image_tag=${TAG}" >> $GITHUB_OUTPUT - echo "runner_image=ghcr.io/${OWNER_LC}/kubecoderun-runner:${TAG}" >> $GITHUB_OUTPUT + # Language images do `FROM runner_image`. Point them at ${TAG} only + # when that tag will actually exist in this run: the runner build + # job creates it when docker/runner/** changed (or force_all), and + # retag-runner creates it on releases. Otherwise (a PR or main push + # touching a language Dockerfile but not the runner) fall back to + # :latest - the last released runner - since nothing pushes a + # dev-${SHORT_SHA} runner tag in that run. + if [[ "${{ steps.filter.outputs.runner }}" == "true" || "${{ github.event.inputs.force_all }}" == "true" || "${{ github.ref }}" == refs/tags/* ]]; then + RUNNER_TAG="${TAG}" + else + RUNNER_TAG="latest" + fi + echo "runner_image=ghcr.io/${OWNER_LC}/kubecoderun-runner:${RUNNER_TAG}" >> $GITHUB_OUTPUT # ==================== Build Jobs ==================== # Note: All build jobs skip when is_cross_repo_pr == 'true' (cross-repo PRs can't push to upstream registry)