Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .github/workflows/build-83.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ on:
description: "Stable php-src tag matching installed apk version (e.g. 8.3.31). Leave empty to skip."
required: false

permissions:
contents: read
packages: write

jobs:
build:
uses: ./.github/workflows/build-php.yml
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/build-84.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ on:
description: "Stable php-src tag matching installed apk version (e.g. 8.4.19). Leave empty to skip."
required: false

permissions:
contents: read
packages: write

jobs:
build:
uses: ./.github/workflows/build-php.yml
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/build-85.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ on:
description: "Stable php-src tag matching installed apk version (e.g. 8.5.4). Leave empty to skip."
required: false

permissions:
contents: read
packages: write

jobs:
build:
uses: ./.github/workflows/build-php.yml
Expand Down
39 changes: 32 additions & 7 deletions .github/workflows/build-php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,14 @@ on:

env:
REGISTRY: skilldlabs/php
GHCR_REGISTRY: ghcr.io/skilld-labs/php
COMPOSER_HASH: "c8b085408188070d5f52bcfe4ecfbee5f727afa458b2573b8eaaf77b3419b0bf2768dc67c86944da1544f06fa544fd47"
DRUSH_VERSION: "8.5.0"

permissions:
contents: read
packages: write

jobs:
build-base:
runs-on: ubuntu-latest
Expand All @@ -45,13 +50,18 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate tags
id: tags
run: |
TAGS="${{ env.REGISTRY }}:${{ inputs.php }}"
TAGS="${{ env.REGISTRY }}:${{ inputs.php }},${{ env.GHCR_REGISTRY }}:${{ inputs.php }}"
if [ -n "${{ inputs.version }}" ]; then
TAGS="${TAGS},${{ env.REGISTRY }}:${{ inputs.version }}"
TAGS="${TAGS},${{ env.REGISTRY }}:${{ inputs.version }},${{ env.GHCR_REGISTRY }}:${{ inputs.version }}"
fi
echo "tags=${TAGS}" >> "$GITHUB_OUTPUT"
echo "base_tag=${{ env.REGISTRY }}:${{ inputs.php }}" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -103,13 +113,18 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate tags
id: tags
run: |
TAGS="${{ env.REGISTRY }}:${{ inputs.php }}-fpm"
TAGS="${{ env.REGISTRY }}:${{ inputs.php }}-fpm,${{ env.GHCR_REGISTRY }}:${{ inputs.php }}-fpm"
if [ -n "${{ inputs.version }}" ]; then
TAGS="${TAGS},${{ env.REGISTRY }}:${{ inputs.php }}-fpm-${{ inputs.version }}"
TAGS="${TAGS},${{ env.REGISTRY }}:${{ inputs.php }}-fpm-${{ inputs.version }},${{ env.GHCR_REGISTRY }}:${{ inputs.php }}-fpm-${{ inputs.version }}"
fi
echo "tags=${TAGS}" >> "$GITHUB_OUTPUT"
echo "base_tag=${{ env.REGISTRY }}:${{ inputs.php }}-fpm" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -157,13 +172,18 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate tags
id: tags
run: |
TAGS="${{ env.REGISTRY }}:${{ inputs.php }}-unit"
TAGS="${{ env.REGISTRY }}:${{ inputs.php }}-unit,${{ env.GHCR_REGISTRY }}:${{ inputs.php }}-unit"
if [ -n "${{ inputs.version }}" ]; then
TAGS="${TAGS},${{ env.REGISTRY }}:${{ inputs.php }}-unit-${{ inputs.version }}"
TAGS="${TAGS},${{ env.REGISTRY }}:${{ inputs.php }}-unit-${{ inputs.version }},${{ env.GHCR_REGISTRY }}:${{ inputs.php }}-unit-${{ inputs.version }}"
fi
echo "tags=${TAGS}" >> "$GITHUB_OUTPUT"
echo "base_tag=${{ env.REGISTRY }}:${{ inputs.php }}-unit" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -211,11 +231,16 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate tags
id: tags
run: |
echo "tags=${{ env.REGISTRY }}:${{ inputs.php }}-unit-dev" >> "$GITHUB_OUTPUT"
echo "tags=${{ env.REGISTRY }}:${{ inputs.php }}-unit-dev,${{ env.GHCR_REGISTRY }}:${{ inputs.php }}-unit-dev" >> "$GITHUB_OUTPUT"

- uses: docker/build-push-action@v7
with:
Expand Down
19 changes: 17 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

Docker image collection for PHP (7.4 through 8.5) optimized for Drupal, published as `skilldlabs/php` on Docker Hub. All images are multi-platform (linux/amd64, linux/arm64) and Alpine-based (except FrankenPHP which uses Ubuntu).
Docker image collection for PHP (7.4 through 8.5) optimized for Drupal, published to two registries from a single build: Docker Hub (`skilldlabs/php`) and GitHub Container Registry (`ghcr.io/skilld-labs/php`). All images are multi-platform (linux/amd64, linux/arm64) and Alpine-based (except FrankenPHP which uses Ubuntu).

Read `RELEASE_RUNBOOK.md` before doing a monthly PHP patch release. It contains the release checklist and the March 11, 2026 postmortem for the CI failure that was fixed in commit `7531dc0`.

Expand Down Expand Up @@ -192,7 +192,22 @@ docker-php/

3. **Push All Platforms** (if tests pass)
- Builds for all platforms using cache
- Pushes to Docker Hub with version tags
- Pushes the same digest to **both** Docker Hub and ghcr.io with version tags (see Container Registries below)

### Container Registries

CI publishes every image to two registries in a single `docker/build-push-action` push (same digest, no rebuild or re-pull, multi-arch preserved):

| Registry | Image | Auth |
|----------|-------|------|
| Docker Hub | `skilldlabs/php` (`env.REGISTRY`) | `DOCKERHUB_USERNAME` / `DOCKERHUB_TOKEN` secrets |
| GitHub Container Registry | `ghcr.io/skilld-labs/php` (`env.GHCR_REGISTRY`) | built-in `GITHUB_TOKEN` (`github.actor`) |

- The ghcr namespace is the **GitHub org** `skilld-labs` (hyphen), not the Docker Hub org `skilldlabs` (no hyphen). They are deliberately different and cannot be unified.
- ghcr push needs `packages: write`. The permission is declared at the workflow level in `build-php.yml` **and** in each caller (`build-{VER}.yml`) — for `workflow_call`, the caller's `permissions` block governs the `GITHUB_TOKEN` scope handed to the reusable workflow. No extra secret is required because the package lives under the same org as the repo.
- New ghcr packages are **private** by default. After the first push, set the `php` package visibility to Public and link it to the repo on the org Packages page.
- Both `:NN` rolling tags and optional `:VERSION` tags are mirrored. The amd64 test image (`base_tag`) is Docker Hub-only since it is loaded locally and never pushed.
- FrankenPHP workflows push to Docker Hub only.

### Cache Strategy

Expand Down
Loading