From 3fd6f1c89269e1ba9e34189f7ea8bbd904b88433 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:50:07 +0200 Subject: [PATCH 01/14] feat(ci): add shared PHP+Node image source ci/images/Dockerfile --- ci/images/Dockerfile | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 ci/images/Dockerfile diff --git a/ci/images/Dockerfile b/ci/images/Dockerfile new file mode 100644 index 0000000..18157b2 --- /dev/null +++ b/ci/images/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:1 +# Reusable PHP + Node 22 toolchain, never install PHP in a CI job. +ARG PHP_VERSION=8.3 +FROM node:22-bookworm-slim AS node +FROM composer:2 AS composer +FROM php:${PHP_VERSION}-cli-bookworm +LABEL org.opencontainers.image.source="https://github.com/CodeCornTech/.github" +ENV DEBIAN_FRONTEND=noninteractive COMPOSER_ALLOW_SUPERUSER=1 NODE_PATH=/usr/local/lib/node_modules +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends bash git curl ca-certificates unzip zip jq python3 libzip-dev libicu-dev libonig-dev libpq-dev libsqlite3-dev; \ + docker-php-ext-install -j 2 bcmath intl mbstring pdo_mysql pdo_pgsql pdo_sqlite zip; \ + apt-get clean; rm -rf /var/lib/apt/lists/* +COPY --from=node /usr/local/bin/node /usr/local/bin/node +COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/ +COPY --from=composer /usr/bin/composer /usr/local/bin/composer +RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ + && ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \ + && php -v && node -v && npm -v && composer --version +WORKDIR /workspace From 53218fe9bffad33f6c73637f9b70d6dc97be0a29 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:50:10 +0200 Subject: [PATCH 02/14] feat(ci): add shared PHP+Node image source ci/images/Dockerfile.playwright --- ci/images/Dockerfile.playwright | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 ci/images/Dockerfile.playwright diff --git a/ci/images/Dockerfile.playwright b/ci/images/Dockerfile.playwright new file mode 100644 index 0000000..a77259e --- /dev/null +++ b/ci/images/Dockerfile.playwright @@ -0,0 +1,10 @@ +# syntax=docker/dockerfile:1 +ARG BASE_IMAGE=ghcr.io/codecorntech/ci-php:8.1-node22-v1 +FROM ${BASE_IMAGE} +ARG PLAYWRIGHT_VERSION=1.62.1 +ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \ + PLAYWRIGHT_MODULE=/usr/local/lib/node_modules/playwright +RUN npm install --global "playwright@${PLAYWRIGHT_VERSION}" \ + && npx playwright install --with-deps chromium \ + && node -e 'require(process.env.PLAYWRIGHT_MODULE).chromium.executablePath()' +WORKDIR /workspace From 9cb1cb1703e613a34a1133b11509894c568f4103 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:50:13 +0200 Subject: [PATCH 03/14] feat(ci): add shared PHP+Node image source ci/images/README.md --- ci/images/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 ci/images/README.md diff --git a/ci/images/README.md b/ci/images/README.md new file mode 100644 index 0000000..cf8a708 --- /dev/null +++ b/ci/images/README.md @@ -0,0 +1,17 @@ +# Shared PHP/Node CI containers on DKR-01 + +Use five versioned images in GHCR: + +- `ghcr.io/codecorntech/ci-php:8.1-node22-v1` +- `ghcr.io/codecorntech/ci-php:8.2-node22-v1` +- `ghcr.io/codecorntech/ci-php:8.3-node22-v1` +- `ghcr.io/codecorntech/ci-php:8.4-node22-v1` +- `ghcr.io/codecorntech/ci-php:8.1-node22-playwright1.62.1-v1` + +These are proposed tags **not yet published**. Image inputs are official `php:-cli-bookworm`, Node 22, Composer 2; Playwright installs Chromium once at image build time. + +On DKR-01 run `bash ci/images/build.sh --dry-run`, then `--build`, then `--check`. Publish only when the check passes, with an authorized `docker login ghcr.io` using a `write:packages` token and `--publish`. + +Private GHCR packages need explicit GitHub Actions access for *both* `CodeCornTech/barbagia-media-manager` and `fgirolami29/wp-life-helper`, or make the image public. Workflow `jobs.container` attempts to pull images; a locally built tag is not a published GHCR package. **Do not merge consumer workflows before image access is confirmed.** + +The build command requires >=6 GiB free on /var/lib/docker and does not remove volumes, images or caches. With only 32 GiB, inspect `docker system df` after builds. Jobs must be trusted: membership in the Docker group is root-equivalent and different Unix accounts sharing the same Docker daemon do **not** isolate runner credentials. Do not add untrusted fork PRs. Do not mount Docker socket into PHP job containers. From 62f222c1c362b3b83f0ffbc099bee79f060aa8c7 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:50:37 +0200 Subject: [PATCH 04/14] feat(ci): add explicit image build check and publish tool --- ci/images/build.sh | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 ci/images/build.sh diff --git a/ci/images/build.sh b/ci/images/build.sh new file mode 100644 index 0000000..7672e50 --- /dev/null +++ b/ci/images/build.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# DKR-01: prepare four PHP/Node toolchains and one Playwright image. +# --dry-run (default), --build, --check, --publish (explicit). +set -Eeuo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")" +mode="${1:---dry-run}" +[[ "$#" -le 1 ]] || exit 2 +case "$mode" in --dry-run|--build|--check|--publish) ;; *) echo 'Usage: build.sh --dry-run|--build|--check|--publish' >&2; exit 2 ;; esac +base='ghcr.io/codecorntech/ci-php' +versions=(8.1 8.2 8.3 8.4) +browser_tag="${base}:8.1-node22-playwright1.62.1-v1" +if [[ "$mode" == --dry-run ]]; then + echo 'NO CHANGES: would build/verify four PHP images + one Playwright image' + for v in "${versions[@]}"; do echo "${base}:${v}-node22-v1"; done + echo "$browser_tag" + echo 'No Docker prune. Publish separate and explicit, requires GHCR login.' + df -h /var/lib/docker 2>/dev/null || df -h / + exit 0 +fi +docker info >/dev/null || { echo 'ERROR: cannot access Docker daemon' >&2; exit 1; } +if [[ "$mode" == --build ]]; then + free_kb="$(df -Pk /var/lib/docker | awk 'NR==2 {print $4}')" + [[ "$free_kb" =~ ^[0-9]+$ && "$free_kb" -ge 6291456 ]] || { echo 'ERROR: less than 6GiB disk free' >&2; exit 1; } + for v in "${versions[@]}"; do + tag="${base}:${v}-node22-v1" + docker buildx build --load --pull --progress=plain -f Dockerfile --build-arg "PHP_VERSION=$v" -t "$tag" . + done + docker buildx build --load --progress=plain -f Dockerfile.playwright \ + --build-arg "BASE_IMAGE=${base}:8.1-node22-v1" \ + --build-arg 'PLAYWRIGHT_VERSION=1.62.1' -t "$browser_tag" . + echo 'Build finished; run --check before --publish.' +elif [[ "$mode" == --check ]]; then + for v in "${versions[@]}"; do + tag="${base}:${v}-node22-v1" + docker image inspect "$tag" >/dev/null || { echo "MISSING: $tag"; exit 1; } + major="${v%%.*}"; minor="${v#*.}" + docker run --rm --pull=never --entrypoint sh "$tag" -ec \ + "php -r 'exit(PHP_MAJOR_VERSION===$major && PHP_MINOR_VERSION===$minor ? 0 : 1);' && php -m | grep -iq zip && node -e 'process.exit(process.versions.node.startsWith(\\\"22.\\\")?0:1)' && composer --version" + echo "OK: $tag" + done + docker image inspect "$browser_tag" >/dev/null || { echo "MISSING: $browser_tag"; exit 1; } + docker run --rm --pull=never --entrypoint sh "$browser_tag" -ec \ + 'node -e '\''const p=require(process.env.PLAYWRIGHT_MODULE); if(!require("fs").existsSync(p.chromium.executablePath())) process.exit(1)'\'' + echo "OK: $browser_tag" + docker system df + echo 'CHECK PASSED' +else + echo 'Requires existing docker login ghcr.io (write:packages).' + read -r -p 'Type PUBLISH to upload all five images: ' confirm + [[ "$confirm" == PUBLISH ]] || { echo 'Aborted'; exit 1; } + for v in "${versions[@]}"; do + tag="${base}:${v}-node22-v1" + docker image inspect "$tag" >/dev/null + docker push "$tag" + done + docker image inspect "$browser_tag" >/dev/null + docker push "$browser_tag" + echo 'Verify GHCR visibility/access from BOTH GitHub owners before CI migration.' +fi From e9075a5036dbc99ca035ab8582869c4d2f06401b Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:50:55 +0200 Subject: [PATCH 05/14] fix(ci): simplify Node 22 shell version assertion --- ci/images/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/images/build.sh b/ci/images/build.sh index 7672e50..1fd3193 100644 --- a/ci/images/build.sh +++ b/ci/images/build.sh @@ -35,7 +35,7 @@ elif [[ "$mode" == --check ]]; then docker image inspect "$tag" >/dev/null || { echo "MISSING: $tag"; exit 1; } major="${v%%.*}"; minor="${v#*.}" docker run --rm --pull=never --entrypoint sh "$tag" -ec \ - "php -r 'exit(PHP_MAJOR_VERSION===$major && PHP_MINOR_VERSION===$minor ? 0 : 1);' && php -m | grep -iq zip && node -e 'process.exit(process.versions.node.startsWith(\\\"22.\\\")?0:1)' && composer --version" + "php -r 'exit(PHP_MAJOR_VERSION===$major && PHP_MINOR_VERSION===$minor ? 0 : 1);' && php -m | grep -iq zip && node --version | grep -q '^v22[.]' && composer --version" echo "OK: $tag" done docker image inspect "$browser_tag" >/dev/null || { echo "MISSING: $browser_tag"; exit 1; } From 2e716473fcf246103ac19517a8685e9548fc6a71 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:53:23 +0200 Subject: [PATCH 06/14] ci: add explicit manual GHCR publication via dedicated Linux runner --- .github/workflows/publish-ci-php-images.yml | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/publish-ci-php-images.yml diff --git a/.github/workflows/publish-ci-php-images.yml b/.github/workflows/publish-ci-php-images.yml new file mode 100644 index 0000000..c5eac5a --- /dev/null +++ b/.github/workflows/publish-ci-php-images.yml @@ -0,0 +1,50 @@ +name: Publish shared CI PHP images + +on: + workflow_dispatch: + +permissions: + contents: read + packages: write + +concurrency: + group: codecorn-ci-image-publisher + cancel-in-progress: false + +jobs: + publish: + name: Build, verify and publish GHCR PHP/Playwright toolchains + runs-on: [self-hosted, Linux, X64, codecorn-org] + timeout-minutes: 120 + if: github.repository == 'CodeCornTech/.github' + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Verify DKR-01 disk and build prerequisites + shell: bash + run: | + set -euo pipefail + test "$(hostname -s | tr '[:upper:]' '[:lower:]')" = dkr-01 + docker info >/dev/null + bash ci/images/build.sh --dry-run + + - name: Build container images + shell: bash + run: bash ci/images/build.sh --build + + - name: Smoke-test PHP, Composer, Node and Playwright + shell: bash + run: bash ci/images/build.sh --check + + - name: Authenticate only for GHCR publication + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish the five versioned images + shell: bash + run: printf 'PUBLISH\n' | bash ci/images/build.sh --publish From d6cef572035b162c9cbd85d7884f793b64076067 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:54:54 +0200 Subject: [PATCH 07/14] fix(ci): use multi-target Dockerfile to share local build cache without GHCR bootstrapping dependency --- ci/images/Dockerfile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ci/images/Dockerfile b/ci/images/Dockerfile index 18157b2..c2846ba 100644 --- a/ci/images/Dockerfile +++ b/ci/images/Dockerfile @@ -3,7 +3,7 @@ ARG PHP_VERSION=8.3 FROM node:22-bookworm-slim AS node FROM composer:2 AS composer -FROM php:${PHP_VERSION}-cli-bookworm +FROM php:${PHP_VERSION}-cli-bookworm AS base LABEL org.opencontainers.image.source="https://github.com/CodeCornTech/.github" ENV DEBIAN_FRONTEND=noninteractive COMPOSER_ALLOW_SUPERUSER=1 NODE_PATH=/usr/local/lib/node_modules RUN set -eux; \ @@ -18,3 +18,13 @@ RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ && ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \ && php -v && node -v && npm -v && composer --version WORKDIR /workspace + +FROM base AS ci + +FROM base AS playwright +ARG PLAYWRIGHT_VERSION=1.62.1 +ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \ + PLAYWRIGHT_MODULE=/usr/local/lib/node_modules/playwright +RUN npm install --global "playwright@${PLAYWRIGHT_VERSION}" \ + && npx playwright install --with-deps chromium \ + && node -e 'require(process.env.PLAYWRIGHT_MODULE).chromium.executablePath()' From fa8fbec49f5295bb8b237d266c4ba355c2507e9b Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:55:04 +0200 Subject: [PATCH 08/14] fix(ci): build Chromium image as Dockerfile target from official PHP base --- ci/images/build.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/images/build.sh b/ci/images/build.sh index 1fd3193..85329b8 100644 --- a/ci/images/build.sh +++ b/ci/images/build.sh @@ -23,10 +23,10 @@ if [[ "$mode" == --build ]]; then [[ "$free_kb" =~ ^[0-9]+$ && "$free_kb" -ge 6291456 ]] || { echo 'ERROR: less than 6GiB disk free' >&2; exit 1; } for v in "${versions[@]}"; do tag="${base}:${v}-node22-v1" - docker buildx build --load --pull --progress=plain -f Dockerfile --build-arg "PHP_VERSION=$v" -t "$tag" . + docker buildx build --load --pull --progress=plain --target ci -f Dockerfile --build-arg "PHP_VERSION=$v" -t "$tag" . done - docker buildx build --load --progress=plain -f Dockerfile.playwright \ - --build-arg "BASE_IMAGE=${base}:8.1-node22-v1" \ + docker buildx build --load --progress=plain --target playwright -f Dockerfile \ + --build-arg 'PHP_VERSION=8.1' \ --build-arg 'PLAYWRIGHT_VERSION=1.62.1' -t "$browser_tag" . echo 'Build finished; run --check before --publish.' elif [[ "$mode" == --check ]]; then From c47e9cc6052e4544a379f4d4c00bcace48f117b6 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:55:11 +0200 Subject: [PATCH 09/14] refactor(ci): remove redundant Playwright Dockerfile now built as Dockerfile target --- ci/images/Dockerfile.playwright | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 ci/images/Dockerfile.playwright diff --git a/ci/images/Dockerfile.playwright b/ci/images/Dockerfile.playwright deleted file mode 100644 index a77259e..0000000 --- a/ci/images/Dockerfile.playwright +++ /dev/null @@ -1,10 +0,0 @@ -# syntax=docker/dockerfile:1 -ARG BASE_IMAGE=ghcr.io/codecorntech/ci-php:8.1-node22-v1 -FROM ${BASE_IMAGE} -ARG PLAYWRIGHT_VERSION=1.62.1 -ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \ - PLAYWRIGHT_MODULE=/usr/local/lib/node_modules/playwright -RUN npm install --global "playwright@${PLAYWRIGHT_VERSION}" \ - && npx playwright install --with-deps chromium \ - && node -e 'require(process.env.PLAYWRIGHT_MODULE).chromium.executablePath()' -WORKDIR /workspace From 35fef8f53d89cd741a37a97e0388110d1f8f23ab Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:56:04 +0200 Subject: [PATCH 10/14] security(ci): build all PHP minors from updated Ubuntu 24.04 PPA packages instead of EOL upstream image --- ci/images/Dockerfile | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/ci/images/Dockerfile b/ci/images/Dockerfile index c2846ba..d46f55c 100644 --- a/ci/images/Dockerfile +++ b/ci/images/Dockerfile @@ -1,16 +1,29 @@ # syntax=docker/dockerfile:1 -# Reusable PHP + Node 22 toolchain, never install PHP in a CI job. +# Ubuntu Noble + maintained PHP packages (including legacy 8.1), Node 22 and Composer. +# Build once per PHP minor; never install PHP from inside a CI job. ARG PHP_VERSION=8.3 FROM node:22-bookworm-slim AS node FROM composer:2 AS composer -FROM php:${PHP_VERSION}-cli-bookworm AS base +FROM ubuntu:24.04 AS base +ARG PHP_VERSION LABEL org.opencontainers.image.source="https://github.com/CodeCornTech/.github" ENV DEBIAN_FRONTEND=noninteractive COMPOSER_ALLOW_SUPERUSER=1 NODE_PATH=/usr/local/lib/node_modules RUN set -eux; \ apt-get update; \ - apt-get install -y --no-install-recommends bash git curl ca-certificates unzip zip jq python3 libzip-dev libicu-dev libonig-dev libpq-dev libsqlite3-dev; \ - docker-php-ext-install -j 2 bcmath intl mbstring pdo_mysql pdo_pgsql pdo_sqlite zip; \ - apt-get clean; rm -rf /var/lib/apt/lists/* + apt-get install -y --no-install-recommends \ + ca-certificates curl gnupg software-properties-common \ + bash git zip unzip jq python3; \ + add-apt-repository -y ppa:ondrej/php; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + "php${PHP_VERSION}-cli" "php${PHP_VERSION}-zip" "php${PHP_VERSION}-bcmath" \ + "php${PHP_VERSION}-intl" "php${PHP_VERSION}-mbstring" \ + "php${PHP_VERSION}-sqlite3" "php${PHP_VERSION}-pgsql" \ + "php${PHP_VERSION}-mysql" "php${PHP_VERSION}-xml" \ + "php${PHP_VERSION}-curl"; \ + ln -s "/usr/bin/php${PHP_VERSION}" /usr/local/bin/php; \ + apt-get clean; \ + rm -rf /var/lib/apt/lists/* COPY --from=node /usr/local/bin/node /usr/local/bin/node COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/ COPY --from=composer /usr/bin/composer /usr/local/bin/composer From 5ef1c5d4d65a638f91356c5d29f7b72a5f49b638 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:56:54 +0200 Subject: [PATCH 11/14] docs(ci): document updated PHP build source and tokenless manual GHCR publisher --- ci/images/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ci/images/README.md b/ci/images/README.md index cf8a708..b8e48c9 100644 --- a/ci/images/README.md +++ b/ci/images/README.md @@ -8,10 +8,14 @@ Use five versioned images in GHCR: - `ghcr.io/codecorntech/ci-php:8.4-node22-v1` - `ghcr.io/codecorntech/ci-php:8.1-node22-playwright1.62.1-v1` -These are proposed tags **not yet published**. Image inputs are official `php:-cli-bookworm`, Node 22, Composer 2; Playwright installs Chromium once at image build time. +These are proposed tags **not yet published**. Images use Ubuntu 24.04 and the same updated Ondrej PHP APT packages as DKR-01 (including the PHP 8.1 compatibility branch), Node 22 from official Node images and Composer 2. This avoids building CI on a long-unpatched official PHP 8.1 image. Playwright installs Chromium once at image build time. -On DKR-01 run `bash ci/images/build.sh --dry-run`, then `--build`, then `--check`. Publish only when the check passes, with an authorized `docker login ghcr.io` using a `write:packages` token and `--publish`. +On DKR-01 (the 32 GB VM) run `bash ci/images/build.sh --dry-run`, then `--build`, then `--check`. Publish only when the check passes, with an authorized `docker login ghcr.io` using a `write:packages` token and `--publish`. Private GHCR packages need explicit GitHub Actions access for *both* `CodeCornTech/barbagia-media-manager` and `fgirolami29/wp-life-helper`, or make the image public. Workflow `jobs.container` attempts to pull images; a locally built tag is not a published GHCR package. **Do not merge consumer workflows before image access is confirmed.** The build command requires >=6 GiB free on /var/lib/docker and does not remove volumes, images or caches. With only 32 GiB, inspect `docker system df` after builds. Jobs must be trusted: membership in the Docker group is root-equivalent and different Unix accounts sharing the same Docker daemon do **not** isolate runner credentials. Do not add untrusted fork PRs. Do not mount Docker socket into PHP job containers. + +## Manual publisher (no PAT stored on VM) + +After a successful local `--check`, the source PR has been approved and merged to the default branch, and the organization runner is online, manually dispatch `.github/workflows/publish-ci-php-images.yml`. It authenticates using the short-lived workflow `GITHUB_TOKEN` with `packages: write` and publishes the five versioned tags. Do not dispatch until there is sufficient disk free and no other heavy build running. From 3733a8e914f0815e28736c603882659a3a3f28d9 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:59:29 +0200 Subject: [PATCH 12/14] fix(ci): repair broken Playwright smoke-test quoting in image builder --- ci/images/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/images/build.sh b/ci/images/build.sh index 85329b8..2ef5626 100644 --- a/ci/images/build.sh +++ b/ci/images/build.sh @@ -39,8 +39,8 @@ elif [[ "$mode" == --check ]]; then echo "OK: $tag" done docker image inspect "$browser_tag" >/dev/null || { echo "MISSING: $browser_tag"; exit 1; } - docker run --rm --pull=never --entrypoint sh "$browser_tag" -ec \ - 'node -e '\''const p=require(process.env.PLAYWRIGHT_MODULE); if(!require("fs").existsSync(p.chromium.executablePath())) process.exit(1)'\'' + docker run --rm --pull=never --entrypoint node "$browser_tag" -e \ + 'const p = require(process.env.PLAYWRIGHT_MODULE); const fs = require("node:fs"); if (!fs.existsSync(p.chromium.executablePath())) process.exit(1);' echo "OK: $browser_tag" docker system df echo 'CHECK PASSED' From fee863b4dbd9c781174cfd2a26bf92036323dea8 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 02:17:50 +0200 Subject: [PATCH 13/14] fix(ci): repair GitHub runner asset digest check in existing registration one-shot --- ci/runners/patch-register-v2.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 ci/runners/patch-register-v2.py diff --git a/ci/runners/patch-register-v2.py b/ci/runners/patch-register-v2.py new file mode 100644 index 0000000..c31d3ae --- /dev/null +++ b/ci/runners/patch-register-v2.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +"""Fix runner v2's incorrect GitHub SHA-256 release note parser, safely.""" +from pathlib import Path +import subprocess +import sys + +if len(sys.argv) != 2: + raise SystemExit("Usage: python3 patch-register-v2.py /path/to/cc-dkr01-register-runners-v2.sh") +p = Path(sys.argv[1]).resolve() +source = p.read_text() +start_marker = 'read -r download_url expected_sha archive_name < <(python3 -c' +end_marker = '[[ "$expected_sha" =~ ^[a-f0-9]{64}$ ]]' +if start_marker not in source or end_marker not in source: + raise SystemExit("STOP: expected v2 checksum parser not found; no changes") +start = source.index(start_marker) +end = source.index(end_marker, start) +replacement = '''resolved="$(python3 -c '\nimport json, re, sys\ntry:\n meta = json.load(sys.stdin)\nexcept ValueError as exc:\n raise SystemExit(f"Invalid GitHub release metadata: {exc}")\npattern = re.compile(r"actions-runner-linux-x64-[0-9]+(?:\\.[0-9]+)+\\.tar\\.gz")\nassets = [a for a in meta.get("assets", []) if pattern.fullmatch(a.get("name", ""))]\nif len(assets) != 1:\n raise SystemExit("Expected exactly one GitHub runner Linux x64 archive")\na = assets[0]\nname = a["name"]\nurl = a.get("browser_download_url", "")\nif not url.startswith("https://github.com/actions/runner/releases/download/"):\n raise SystemExit("Unexpected GitHub runner download URL")\ndig = re.fullmatch(r"sha256:([a-fA-F0-9]{64})", a.get("digest") or "")\nbody = meta.get("body") or ""\nnotes = re.search(re.escape(name)+r"[\\x60\\s:|–-]+(?:SHA-?256[\\s:=]+)?([a-fA-F0-9]{64})(?![a-fA-F0-9])", body, re.I)\nif dig and notes and dig.group(1).lower() != notes.group(1).lower():\n raise SystemExit("Checksum disagreement between API digest and release notes")\nsha = dig or notes\nif sha is None:\n raise SystemExit("No official SHA256 for Linux runner; refusing to proceed")\nprint(f"{url}\\t{sha.group(1).lower()}\\t{name}")\n' <<< "$metadata")" || fail 'Release information/checksum incomplete'\nIFS=$'\\t' read -r download_url expected_sha archive_name <<< "$resolved"\n''' +updated = source[:start] + replacement + source[end:] +backup = p.with_name(p.name + ".before-digest-fix") +if backup.exists(): + raise SystemExit("STOP: backup already exists; refusing to overwrite") +subprocess.run(["bash", "-n"], input=updated, text=True, check=True) +backup.write_text(source) +p.write_text(updated) +print(f"PATCHED: {p}\nBACKUP: {backup}\nVALIDATED: bash -n passed") From 08993059f7e853469ceea80e4dc0e54e383d2334 Mon Sep 17 00:00:00 2001 From: Federico Girolami <68548715+fgirolami29@users.noreply.github.com> Date: Sun, 25 Oct 2026 06:10:22 +0100 Subject: [PATCH 14/14] ci: validate shared images on trusted pull requests --- .github/workflows/publish-ci-php-images.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-ci-php-images.yml b/.github/workflows/publish-ci-php-images.yml index c5eac5a..1ed239c 100644 --- a/.github/workflows/publish-ci-php-images.yml +++ b/.github/workflows/publish-ci-php-images.yml @@ -1,6 +1,7 @@ name: Publish shared CI PHP images on: + pull_request: workflow_dispatch: permissions: @@ -16,7 +17,7 @@ jobs: name: Build, verify and publish GHCR PHP/Playwright toolchains runs-on: [self-hosted, Linux, X64, codecorn-org] timeout-minutes: 120 - if: github.repository == 'CodeCornTech/.github' + if: github.repository == 'CodeCornTech/.github' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) steps: - uses: actions/checkout@v4 with: @@ -39,6 +40,7 @@ jobs: run: bash ci/images/build.sh --check - name: Authenticate only for GHCR publication + if: github.event_name == 'workflow_dispatch' uses: docker/login-action@v3 with: registry: ghcr.io @@ -46,5 +48,6 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Publish the five versioned images + if: github.event_name == 'workflow_dispatch' shell: bash run: printf 'PUBLISH\n' | bash ci/images/build.sh --publish