Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b1035da
Initial plan
Copilot Oct 22, 2026
b043bce
Investigate persistent Debian mirror 404
Copilot Oct 22, 2026
2b17b48
Use archived Bullseye repositories in PHP builds
Copilot Oct 22, 2026
58edab8
Mark validation complete
Copilot Oct 22, 2026
f9b566c
Use a pinned Bullseye security snapshot
Copilot Oct 22, 2026
71ae6c8
Fix failing Test workflow builds
Copilot Oct 22, 2026
07fcd65
Track pending CI approval
Copilot Oct 22, 2026
3727206
Fix failing Test workflow matrix builds
Copilot Oct 22, 2026
5e89fd9
Record pending CI approval
Copilot Oct 22, 2026
84396aa
Fix PHP extension matrix build failures
Copilot Oct 22, 2026
1287120
Fix PECL metadata transport and remove separate Copilot Chat installa…
Copilot Oct 22, 2026
1288915
Record validation results and pending CI approval
Copilot Oct 22, 2026
db29472
Upgrade legacy PEAR transport and handle built-in PHP 8.5 OPcache
Copilot Oct 22, 2026
012daca
Make offline PEAR bootstrap safe on fresh images
Copilot Oct 22, 2026
e1dd7ae
Remove obsolete Copilot Chat README instructions
Copilot Oct 22, 2026
0db0f2a
Address review feedback on PEAR workaround and build tests
Copilot Oct 22, 2026
3c6552d
Select the latest PHP-compatible PEAR release for legacy upgrades
Copilot Oct 22, 2026
5514e4b
Stop suppressing dependency-marking failures in all PHP images
Copilot Oct 22, 2026
32e6eec
Reflow remaining paragraphs affected by Copilot Chat removal
Copilot Oct 22, 2026
6a90c5b
Build ARM64 images on native ARM runners
Copilot Oct 22, 2026
eecfb4b
Record native ARM workflow validation
Copilot Oct 22, 2026
3bfdd6d
Update Ubuntu version for ARM64 platform in workflow
darrenoh Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/actions/build-php-images/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ inputs:
bake_overrides_file:
description: >
Path to a pre-generated bake overrides JSON file that maps each
phpX_Y-downloader target to the correct code-server / Copilot Chat
phpX_Y-downloader target to the correct code-server
versions and SHA256 digests for its Debian tier. Pass the path to
the preseed-bake-overrides artifact downloaded from the detect job.
required: true
Expand Down Expand Up @@ -114,6 +114,7 @@ runs:
password: ${{ inputs.ghcr_user && inputs.ghcr_token || inputs.github_token }}

- name: Set up QEMU
if: ${{ !((runner.arch == 'ARM64' && startsWith(inputs.platform, 'linux/arm64')) || (runner.arch == 'X64' && startsWith(inputs.platform, 'linux/amd64'))) }}
uses: docker/setup-qemu-action@v4

- name: Set up Docker Buildx
Expand Down
213 changes: 20 additions & 193 deletions .github/actions/preseed-downloads/action.yml

Large diffs are not rendered by default.

27 changes: 13 additions & 14 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ on:
no_cache:
description: >
Disable Docker layer cache and force a fresh download of all build
artefacts (code-server, Copilot Chat).
When false, the cache is used as normal.
artefacts (code-server). When false, the cache is used as normal.
type: boolean
default: true

Expand Down Expand Up @@ -188,12 +187,12 @@ jobs:
path: ${{ steps.preseed.outputs.bake_overrides_file }}
retention-days: 1

# Upload the flat pre-downloaded/ directory (code-server .deb files and
# Copilot Chat VSIX) as a workflow artifact so build jobs can download it
# directly. Using an artifact (rather than the GHA preseed cache) is
# more reliable because artifacts are guaranteed to persist for the
# lifetime of the workflow run; the GHA cache can be evicted mid-run
# and a cache miss would silently produce an empty pre-downloaded/ dir.
# Upload the flat pre-downloaded/ directory (code-server .deb files)
# as a workflow artifact so build jobs can download it directly. Using
# an artifact (rather than the GHA preseed cache) is more reliable because
# artifacts are guaranteed to persist for the lifetime of the workflow
# run; the GHA cache can be evicted mid-run and a cache miss would silently
# produce an empty pre-downloaded/ dir.
- name: Upload preseed pre-downloaded artifacts
if: steps.detect.outputs.versions != ''
uses: actions/upload-artifact@v7
Expand Down Expand Up @@ -222,7 +221,7 @@ jobs:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.detect.outputs.build_matrix) }}
runs-on: ubuntu-latest
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }}
permissions:
contents: read
packages: write
Expand All @@ -231,11 +230,11 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6

# Download the pre-seeded code-server .deb and Copilot Chat VSIX files
# that the detect job assembled and uploaded. Using a workflow artifact
# (not the GHA preseed cache) guarantees these files are present for the
# lifetime of the run; a cache miss would silently produce an empty
# pre-downloaded/ directory and fail the Docker build.
# Download the pre-seeded code-server .deb files that the detect job
# assembled and uploaded. Using a workflow artifact (not the GHA preseed
# cache) guarantees these files are present for the lifetime of the run;
# a cache miss would silently produce an empty pre-downloaded/ directory
# and fail the Docker build.
- name: Download preseed pre-downloaded artifacts
uses: actions/download-artifact@v8
with:
Expand Down
79 changes: 15 additions & 64 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,12 @@ jobs:
path: ${{ steps.preseed.outputs.bake_overrides_file }}
retention-days: 1

# Upload the flat pre-downloaded/ directory (code-server .deb files and
# Copilot Chat VSIX) as a workflow artifact so build jobs can download it
# directly. Using an artifact (rather than the GHA preseed cache) is
# more reliable because artifacts are guaranteed to persist for the
# lifetime of the workflow run; the GHA cache can be evicted mid-run
# and a cache miss would silently produce an empty pre-downloaded/ dir.
# Upload the flat pre-downloaded/ directory (code-server .deb files)
# as a workflow artifact so build jobs can download it directly. Using
# an artifact (rather than the GHA preseed cache) is more reliable because
# artifacts are guaranteed to persist for the lifetime of the workflow
# run; the GHA cache can be evicted mid-run and a cache miss would silently
# produce an empty pre-downloaded/ dir.
- name: Upload preseed pre-downloaded artifacts
if: steps.versions.outputs.has_versions == 'true'
uses: actions/upload-artifact@v7
Expand All @@ -198,7 +198,7 @@ jobs:
build:
name: Build and test PHP ${{ matrix.version }}
runs-on: ubuntu-latest
needs: [lint-yaml, lint-shell, lint-dockerfile, verify-extension, verify-code-server, detect]
needs: [lint-yaml, lint-shell, lint-dockerfile, verify-code-server, detect]
if: needs.detect.outputs.has_versions == 'true'
strategy:
fail-fast: false
Expand All @@ -221,21 +221,21 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Download the pre-seeded code-server .deb and Copilot Chat VSIX files
# that the detect job assembled and uploaded. Using a workflow artifact
# (not the GHA preseed cache) guarantees these files are present for the
# lifetime of the run; a cache miss would silently produce an empty
# pre-downloaded/ directory and fail the Docker build.
# Download the pre-seeded code-server .deb files that the detect job
# assembled and uploaded. Using a workflow artifact (not the GHA preseed
# cache) guarantees these files are present for the lifetime of the run;
# a cache miss would silently produce an empty pre-downloaded/ directory
# and fail the Docker build.
- name: Download preseed pre-downloaded artifacts
uses: actions/download-artifact@v8
with:
name: preseed-pre-downloaded
path: ${{ runner.temp }}/build-downloads/pre-downloaded

# Download the bake-overrides JSON generated by the detect job's preseed
# run. It contains the correct code-server / Copilot Chat versions and
# SHA256 digests for every detected Debian codename, including the
# downgraded versions needed for older codenames (Buster / Bullseye).
# run. It contains the correct code-server versions and SHA256 digests
# for every detected Debian codename, including the downgraded versions
# needed for older codenames (Buster / Bullseye).
- name: Download preseed bake overrides
uses: actions/download-artifact@v8
with:
Expand All @@ -254,54 +254,6 @@ jobs:
- name: Run PHP functional tests
run: bash tests/run-dockerfile.sh --version ${{ matrix.version }}

verify-extension:
name: Verify Copilot Chat VSIX
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6

- name: Get pinned Copilot Chat version
id: copilot-version
run: |
echo "version=$(grep -m1 '^ARG COPILOT_CHAT_VERSION=' base/Dockerfile | cut -d= -f2)" >> "$GITHUB_OUTPUT"

- name: Cache Copilot Chat VSIX
id: cache-vsix
uses: actions/cache@v5
with:
path: ${{ runner.temp }}/copilot-chat.vsix
key: copilot-chat-vsix-${{ steps.copilot-version.outputs.version }}

- name: Download Copilot Chat VSIX (cache miss only)
if: steps.cache-vsix.outputs.cache-hit != 'true'
run: |
VERSION="${{ steps.copilot-version.outputs.version }}"
VSIX_URL="https://marketplace.visualstudio.com/_apis/public/gallery/publishers/GitHub/vsextensions/copilot-chat/${VERSION}/vspackage"
echo "Downloading GitHub.copilot-chat ${VERSION} from VS Marketplace"
curl -fsSL --compressed --retry 5 --retry-all-errors --connect-timeout 30 \
"${VSIX_URL}" -o "$RUNNER_TEMP/copilot-chat.vsix"

- name: Verify Copilot Chat VSIX
run: |
EXPECTED_SHA="$(grep -m1 '^ARG COPILOT_CHAT_VSIX_SHA256=' base/Dockerfile | cut -d= -f2)"
if ! od -An -tx1 -N4 "$RUNNER_TEMP/copilot-chat.vsix" | tr -d ' \n' | grep -qi '^504b0304'; then
ACTUAL_MAGIC="$(od -An -tx1 -N8 "$RUNNER_TEMP/copilot-chat.vsix" | tr -d ' \n')"
echo "Downloaded file is not a valid ZIP (expected magic 504b0304, got: ${ACTUAL_MAGIC})"
echo "The Marketplace may have returned a rate-limit/error page or gzip-encoded content."
exit 1
fi
ACTUAL_SHA="$(sha256sum "$RUNNER_TEMP/copilot-chat.vsix" | cut -d' ' -f1)"
echo "Computed SHA256: ${ACTUAL_SHA}"
if [ -n "${EXPECTED_SHA}" ]; then
echo "${EXPECTED_SHA} $RUNNER_TEMP/copilot-chat.vsix" | sha256sum -c -
echo "SHA256 verified."
else
echo "::warning::COPILOT_CHAT_VSIX_SHA256 is not set in base/Dockerfile."
echo "::warning::Set ARG COPILOT_CHAT_VSIX_SHA256=${ACTUAL_SHA} in base/Dockerfile to enable integrity verification."
fi

verify-code-server:
name: Verify code-server .deb hashes
runs-on: ubuntu-latest
Expand Down Expand Up @@ -371,7 +323,6 @@ jobs:
- lint-yaml
- lint-shell
- lint-dockerfile
- verify-extension
- verify-code-server
- prepare
- detect
Expand Down
8 changes: 4 additions & 4 deletions 7.4/base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
&& docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j$(nproc) gd pspell \
&& pecl install -o -f \
igbinary-3.2.12 \
imagick-3.8.1 \
igbinary \
imagick \
oauth-2.0.7 \
&& docker-php-ext-enable gd igbinary imagick oauth pspell \
&& apt-mark auto '.*' > /dev/null \
Expand All @@ -57,7 +57,7 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
| while IFS= read -r lib; do dpkg-query --search "$(realpath "$lib" 2>/dev/null || echo "$lib")" 2>/dev/null; done \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual; } || true \
| xargs -r apt-mark manual; } \
&& apt-get purge -y --auto-remove \
-o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/* \
Expand All @@ -76,7 +76,7 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
done \
&& wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc \
| gpg --dearmor -o /usr/share/keyrings/postgresql.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/postgresql.gpg] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
&& echo "deb [signed-by=/usr/share/keyrings/postgresql.gpg] https://apt-archive.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg-archive main" \
> /etc/apt/sources.list.d/pgdg.list \
&& apt-get update \
&& apt-get install -y postgresql-client-18 \
Expand Down
8 changes: 4 additions & 4 deletions 8.0/base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
&& docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j$(nproc) gd pspell \
&& pecl install -o -f \
igbinary-3.2.12 \
imagick-3.8.1 \
igbinary \
imagick \
&& docker-php-ext-enable gd igbinary imagick pspell \
&& apt-mark auto '.*' > /dev/null \
&& echo "$savedAptMark" | xargs -r apt-mark manual \
Expand All @@ -51,7 +51,7 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
| while IFS= read -r lib; do dpkg-query --search "$(realpath "$lib" 2>/dev/null || echo "$lib")" 2>/dev/null; done \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual; } || true \
| xargs -r apt-mark manual; } \
&& apt-get purge -y --auto-remove \
-o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/* \
Expand All @@ -70,7 +70,7 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
done \
&& wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc \
| gpg --dearmor -o /usr/share/keyrings/postgresql.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/postgresql.gpg] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
&& echo "deb [signed-by=/usr/share/keyrings/postgresql.gpg] https://apt-archive.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg-archive main" \
> /etc/apt/sources.list.d/pgdg.list \
&& apt-get update \
&& apt-get install -y postgresql-client-18 \
Expand Down
7 changes: 3 additions & 4 deletions 8.1/base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
&& docker-php-ext-configure gd --with-avif --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j$(nproc) gd pspell \
&& pecl install -o -f \
igbinary-3.2.12 \
imagick-3.8.1 \
igbinary \
imagick \
&& docker-php-ext-enable gd igbinary imagick pspell \
&& apt-mark auto '.*' > /dev/null \
&& echo "$savedAptMark" | xargs -r apt-mark manual \
Expand All @@ -52,7 +52,7 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
| while IFS= read -r lib; do dpkg-query --search "$(realpath "$lib" 2>/dev/null || echo "$lib")" 2>/dev/null; done \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual; } || true \
| xargs -r apt-mark manual; } \
&& apt-get purge -y --auto-remove \
-o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/* \
Expand All @@ -77,4 +77,3 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
&& rm -f /etc/apt/apt.conf.d/01dpkg-no-pty

USER ${USER}

7 changes: 3 additions & 4 deletions 8.2/base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
&& docker-php-ext-configure gd --with-avif --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j$(nproc) gd pspell \
&& pecl install -o -f \
igbinary-3.2.12 \
imagick-3.8.1 \
igbinary \
imagick \
&& docker-php-ext-enable gd igbinary imagick pspell \
&& apt-mark auto '.*' > /dev/null \
&& echo "$savedAptMark" | xargs -r apt-mark manual \
Expand All @@ -52,7 +52,7 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
| while IFS= read -r lib; do dpkg-query --search "$(realpath "$lib" 2>/dev/null || echo "$lib")" 2>/dev/null; done \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual; } || true \
| xargs -r apt-mark manual; } \
&& apt-get purge -y --auto-remove \
-o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/* \
Expand All @@ -77,4 +77,3 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
&& rm -f /etc/apt/apt.conf.d/01dpkg-no-pty

USER ${USER}

3 changes: 1 addition & 2 deletions 8.3/base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
| while IFS= read -r lib; do dpkg-query --search "$(realpath "$lib" 2>/dev/null || echo "$lib")" 2>/dev/null; done \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual; } || true \
| xargs -r apt-mark manual; } \
&& apt-get purge -y --auto-remove \
-o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/* \
Expand Down Expand Up @@ -82,4 +82,3 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
RUN composer self-update 2.7.7

USER ${USER}

3 changes: 1 addition & 2 deletions 8.4/base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
| while IFS= read -r lib; do dpkg-query --search "$(realpath "$lib" 2>/dev/null || echo "$lib")" 2>/dev/null; done \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual; } || true \
| xargs -r apt-mark manual; } \
&& apt-get purge -y --auto-remove \
-o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/* \
Expand Down Expand Up @@ -81,4 +81,3 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
RUN composer self-update

USER ${USER}

3 changes: 1 addition & 2 deletions 8.5/base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
| while IFS= read -r lib; do dpkg-query --search "$(realpath "$lib" 2>/dev/null || echo "$lib")" 2>/dev/null; done \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual; } || true \
| xargs -r apt-mark manual; } \
&& apt-get purge -y --auto-remove \
-o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/* \
Expand Down Expand Up @@ -81,4 +81,3 @@ RUN echo 'Dpkg::Use-Pty "0";' > /etc/apt/apt.conf.d/01dpkg-no-pty \
RUN composer self-update

USER ${USER}

2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Baseline JSON files in `tests/baselines/` track pre-existing lint violations per

#### Composite Actions
- **`.github/actions/build-php-images`** — detects changed PHP versions and runs `docker buildx bake` using `docker-bake.hcl`. Called directly by the trigger workflows below.
- **`.github/actions/preseed-downloads`** — resolves code-server and Copilot Chat versions, restores/downloads artifacts into `$RUNNER_TEMP/build-downloads/pre-downloaded/`, and exposes `DOWNLOADS_DIR` + SHA256 outputs for Docker builds.
- **`.github/actions/preseed-downloads`** — resolves code-server versions, restores/downloads artifacts into `$RUNNER_TEMP/build-downloads/pre-downloaded/`, and exposes `DOWNLOADS_DIR` + SHA256 outputs for Docker builds.

#### Workflows
- **`docker-build.yml`** — triggered on pushes to `main` or `develop` and by manual `workflow_dispatch`; detects changed versions, builds images per-platform in parallel, and merges platform-specific digests into multi-arch manifests.
Expand Down
Loading
Loading