Skip to content

php-fpm-updated

php-fpm-updated #97

name: Build and Push PHP-FPM+Nginx Multi-Service Images
# IMPORTANT: This workflow depends on php-fpm tier images being available.
# Do NOT add push triggers for php-fpm-nginx/** paths - this would cause race conditions
# where php-fpm-nginx tries to build before php-fpm images are pushed.
# Instead, use repository_dispatch from php-fpm workflow or workflow_dispatch.
on:
# NO push trigger - php-fpm-nginx depends on php-fpm images being built first
# Use repository_dispatch from php-fpm workflow instead
workflow_dispatch:
inputs:
php_version:
description: 'PHP Version to build (8.2, 8.3, 8.4, or all)'
required: false
default: 'all'
os_variant:
description: 'OS variant to build (bookworm or all)'
required: false
default: 'all'
repository_dispatch:
types: [php-fpm-updated]
workflow_run:
workflows: ["Build and Push PHP-FPM Images"]
types: [completed]
branches: [main]
schedule:
# Weekly security rebuild - Monday 04:00 UTC (fallback; workflow_run trigger fires on completion)
- cron: '0 4 * * 1'
jobs:
# =====================================================================
# SLIM TIER - Minimal PHP + essential extensions
# =====================================================================
build-slim-matrix:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
strategy:
fail-fast: false
matrix:
php_version: ['8.2', '8.3', '8.4', '8.5']
os_variant: ['bookworm']
uses: ./.github/workflows/_build-image.yml
with:
image-name: php-fpm-nginx
image-type: php-fpm-nginx
php-version: ${{ matrix.php_version }}
os-variant: ${{ matrix.os_variant }}
tier: slim
target: slim-root
tag-suffix: slim
cache-scope-prefix: php-fpm-nginx
enable-patch-tags: true
enable-latest-tag: true
enable-cosign: true
base-image-type: fpm
test-script: |
docker pull $IMAGE
docker run -d --name test-slim -p 8080:80 $IMAGE
echo "Waiting for container to become healthy..."
for i in {1..60}; do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' test-slim 2>/dev/null || echo "unknown")
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy after ${i}s"
break
elif [ "$STATUS" = "unhealthy" ]; then
echo "Container became unhealthy after ${i}s"
docker logs test-slim
exit 1
fi
sleep 1
done
docker exec test-slim curl -sf http://127.0.0.1/health || { docker logs test-slim && exit 1; }
docker exec test-slim php -v | grep "PHP $PHP_VERSION" || exit 1
docker exec test-slim cbox-init --version || exit 1
docker exec test-slim composer --version || exit 1
# Slim tier should NOT have imagick or node
! docker exec test-slim php -m | grep -q imagick || echo "Warning: imagick found in slim tier"
! docker exec test-slim node --version 2>/dev/null || echo "Warning: node found in slim tier"
docker stop test-slim && docker rm test-slim
secrets: inherit
build-slim-rootless-matrix:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
strategy:
fail-fast: false
matrix:
php_version: ['8.2', '8.3', '8.4', '8.5']
os_variant: ['bookworm']
uses: ./.github/workflows/_build-image.yml
with:
image-name: php-fpm-nginx
image-type: php-fpm-nginx
php-version: ${{ matrix.php_version }}
os-variant: ${{ matrix.os_variant }}
tier: slim
target: slim-rootless
rootless: true
tag-suffix: slim-rootless
cache-scope-prefix: php-fpm-nginx
enable-patch-tags: true
enable-latest-tag: true
enable-cosign: true
base-image-type: fpm
test-script: |
docker pull $IMAGE
docker run -d --name test-slim-rootless -p 8082:8080 $IMAGE
echo "Waiting for container to become healthy..."
for i in {1..60}; do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' test-slim-rootless 2>/dev/null || echo "unknown")
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy after ${i}s"
break
elif [ "$STATUS" = "unhealthy" ]; then
echo "Container became unhealthy after ${i}s"
docker logs test-slim-rootless
exit 1
fi
sleep 1
done
docker exec test-slim-rootless id -u | grep -E "^(33|82)$"
docker exec test-slim-rootless curl -sf http://127.0.0.1:8080/health || { docker logs test-slim-rootless && exit 1; }
docker exec test-slim-rootless php -v | grep "PHP $PHP_VERSION" || exit 1
docker stop test-slim-rootless && docker rm test-slim-rootless
secrets: inherit
# =====================================================================
# STANDARD TIER (DEFAULT) - + ImageMagick, vips, Node.js
# =====================================================================
build-matrix:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
strategy:
fail-fast: false
matrix:
php_version: ['8.2', '8.3', '8.4', '8.5']
os_variant: ['bookworm']
uses: ./.github/workflows/_build-image.yml
with:
image-name: php-fpm-nginx
image-type: php-fpm-nginx
php-version: ${{ matrix.php_version }}
os-variant: ${{ matrix.os_variant }}
tier: standard
target: root
tag-suffix: ''
cache-scope-prefix: php-fpm-nginx
enable-patch-tags: true
enable-latest-tag: true
enable-convenience-tags: true
enable-cosign: true
enable-trivy: true
base-image-type: fpm
build-args: |
BUILD_DATE=${{ github.event.repository.updated_at }}
VCS_REF=${{ github.sha }}
test-script: |
docker pull $IMAGE
docker run -d --name test-multi -p 8080:80 $IMAGE
echo "Waiting for container to become healthy..."
for i in {1..60}; do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' test-multi 2>/dev/null || echo "unknown")
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy after ${i}s"
break
elif [ "$STATUS" = "unhealthy" ]; then
echo "Container became unhealthy after ${i}s"
docker logs test-multi
exit 1
fi
sleep 1
done
echo "=== Container logs ===" && docker logs test-multi || true
echo "=== Container status ===" && docker ps -a --filter name=test-multi || true
docker exec test-multi curl -sf http://127.0.0.1/health || { echo "=== Health check failed, container logs ===" && docker logs test-multi && exit 1; }
docker exec test-multi php -v | grep "PHP $PHP_VERSION" || exit 1
docker exec test-multi cbox-init --version || exit 1
docker exec test-multi composer --version || exit 1
docker exec test-multi node --version || exit 1
docker stop test-multi && docker rm test-multi
secrets: inherit
build-rootless-matrix:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
strategy:
fail-fast: false
matrix:
php_version: ['8.2', '8.3', '8.4', '8.5']
os_variant: ['bookworm']
uses: ./.github/workflows/_build-image.yml
with:
image-name: php-fpm-nginx
image-type: php-fpm-nginx
php-version: ${{ matrix.php_version }}
os-variant: ${{ matrix.os_variant }}
tier: standard
target: rootless
rootless: true
tag-suffix: rootless
cache-scope-prefix: php-fpm-nginx
enable-patch-tags: true
enable-convenience-tags: true
enable-cosign: true
enable-trivy: true
base-image-type: fpm
build-args: |
BUILD_DATE=${{ github.event.repository.updated_at }}
VCS_REF=${{ github.sha }}
test-script: |
docker pull $IMAGE
docker run -d --name test-rootless -p 8082:8080 $IMAGE
echo "Waiting for container to become healthy..."
for i in {1..60}; do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' test-rootless 2>/dev/null || echo "unknown")
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy after ${i}s"
break
elif [ "$STATUS" = "unhealthy" ]; then
echo "Container became unhealthy after ${i}s"
docker logs test-rootless
exit 1
fi
sleep 1
done
echo "=== Container logs ===" && docker logs test-rootless || true
echo "=== Container status ===" && docker ps -a --filter name=test-rootless || true
docker exec test-rootless id -u | grep -E "^(33|82)$"
docker exec test-rootless curl -sf http://127.0.0.1:8080/health || { echo "=== Health check failed, container logs ===" && docker logs test-rootless && exit 1; }
docker exec test-rootless php -v | grep "PHP $PHP_VERSION" || exit 1
docker exec test-rootless cbox-init --version || exit 1
docker exec test-rootless composer --version || exit 1
docker exec test-rootless node --version || exit 1
docker stop test-rootless && docker rm test-rootless
secrets: inherit
# =====================================================================
# CHROMIUM TIER - + Chromium for Browsershot/Dusk/Puppeteer
# =====================================================================
build-chromium-matrix:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
strategy:
fail-fast: false
matrix:
php_version: ['8.2', '8.3', '8.4', '8.5']
os_variant: ['bookworm']
uses: ./.github/workflows/_build-image.yml
with:
image-name: php-fpm-nginx
image-type: php-fpm-nginx
php-version: ${{ matrix.php_version }}
os-variant: ${{ matrix.os_variant }}
tier: chromium
target: chromium-root
tag-suffix: chromium
cache-scope-prefix: php-fpm-nginx
enable-patch-tags: true
enable-latest-tag: true
enable-cosign: true
enable-trivy: true
base-image-type: fpm
test-script: |
docker pull $IMAGE
docker run -d --name test-chromium -p 8080:80 $IMAGE
echo "Waiting for container to become healthy..."
for i in {1..60}; do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' test-chromium 2>/dev/null || echo "unknown")
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy after ${i}s"
break
elif [ "$STATUS" = "unhealthy" ]; then
echo "Container became unhealthy after ${i}s"
docker logs test-chromium
exit 1
fi
sleep 1
done
docker exec test-chromium curl -sf http://127.0.0.1/health || { docker logs test-chromium && exit 1; }
docker exec test-chromium php -v | grep "PHP $PHP_VERSION" || exit 1
docker exec test-chromium cbox-init --version || exit 1
docker exec test-chromium composer --version || exit 1
docker exec test-chromium node --version || exit 1
# Chromium tier should have chromium
docker exec test-chromium which chromium-browser || docker exec test-chromium which chromium
docker stop test-chromium && docker rm test-chromium
secrets: inherit
build-chromium-rootless-matrix:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
strategy:
fail-fast: false
matrix:
php_version: ['8.2', '8.3', '8.4', '8.5']
os_variant: ['bookworm']
uses: ./.github/workflows/_build-image.yml
with:
image-name: php-fpm-nginx
image-type: php-fpm-nginx
php-version: ${{ matrix.php_version }}
os-variant: ${{ matrix.os_variant }}
tier: chromium
target: chromium-rootless
rootless: true
tag-suffix: chromium-rootless
cache-scope-prefix: php-fpm-nginx
enable-patch-tags: true
enable-latest-tag: true
enable-cosign: true
enable-trivy: true
base-image-type: fpm
test-script: |
docker pull $IMAGE
docker run -d --name test-chromium-rootless -p 8082:8080 $IMAGE
echo "Waiting for container to become healthy..."
for i in {1..60}; do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' test-chromium-rootless 2>/dev/null || echo "unknown")
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy after ${i}s"
break
elif [ "$STATUS" = "unhealthy" ]; then
echo "Container became unhealthy after ${i}s"
docker logs test-chromium-rootless
exit 1
fi
sleep 1
done
docker exec test-chromium-rootless id -u | grep -E "^(33|82)$"
docker exec test-chromium-rootless curl -sf http://127.0.0.1:8080/health || { docker logs test-chromium-rootless && exit 1; }
docker exec test-chromium-rootless php -v | grep "PHP $PHP_VERSION" || exit 1
docker exec test-chromium-rootless cbox-init --version || exit 1
docker exec test-chromium-rootless composer --version || exit 1
docker exec test-chromium-rootless node --version || exit 1
# Chromium check
docker exec test-chromium-rootless which chromium-browser || docker exec test-chromium-rootless which chromium
docker stop test-chromium-rootless && docker rm test-chromium-rootless
secrets: inherit
# =====================================================================
# DEV TIER - Standard + Xdebug, PCOV, SPX for development/testing
# =====================================================================
build-dev-matrix:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
strategy:
fail-fast: false
matrix:
php_version: ['8.2', '8.3', '8.4', '8.5']
os_variant: ['bookworm']
uses: ./.github/workflows/_build-image.yml
with:
image-name: php-fpm-nginx
image-type: php-fpm-nginx
php-version: ${{ matrix.php_version }}
os-variant: ${{ matrix.os_variant }}
tier: dev
target: dev-root
tag-suffix: dev
cache-scope-prefix: php-fpm-nginx
enable-patch-tags: true
enable-latest-tag: true
enable-cosign: true
enable-trivy: true
base-image-type: fpm
test-script: |
docker pull $IMAGE
docker run -d --name test-dev -p 8080:80 $IMAGE
echo "Waiting for container to become healthy..."
for i in {1..60}; do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' test-dev 2>/dev/null || echo "unknown")
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy after ${i}s"
break
elif [ "$STATUS" = "unhealthy" ]; then
echo "Container became unhealthy after ${i}s"
docker logs test-dev
exit 1
fi
sleep 1
done
docker exec test-dev curl -sf http://127.0.0.1/health || { docker logs test-dev && exit 1; }
docker exec test-dev php -v | grep "PHP $PHP_VERSION" || exit 1
docker exec test-dev php -m | grep -E "xdebug|pcov|spx" || exit 1
docker exec test-dev cbox-init --version || exit 1
docker exec test-dev composer --version || exit 1
docker exec test-dev node --version || exit 1
# Dev tier should have chromium
docker exec test-dev which chromium-browser || docker exec test-dev which chromium
docker stop test-dev && docker rm test-dev
secrets: inherit
build-dev-rootless-matrix:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
strategy:
fail-fast: false
matrix:
php_version: ['8.2', '8.3', '8.4', '8.5']
os_variant: ['bookworm']
uses: ./.github/workflows/_build-image.yml
with:
image-name: php-fpm-nginx
image-type: php-fpm-nginx
php-version: ${{ matrix.php_version }}
os-variant: ${{ matrix.os_variant }}
tier: dev
target: dev-rootless
rootless: true
tag-suffix: dev-rootless
cache-scope-prefix: php-fpm-nginx
enable-patch-tags: true
enable-latest-tag: true
enable-cosign: true
enable-trivy: true
base-image-type: fpm
test-script: |
docker pull $IMAGE
docker run -d --name test-dev-rootless -p 8082:8080 $IMAGE
echo "Waiting for container to become healthy..."
for i in {1..60}; do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' test-dev-rootless 2>/dev/null || echo "unknown")
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy after ${i}s"
break
elif [ "$STATUS" = "unhealthy" ]; then
echo "Container became unhealthy after ${i}s"
docker logs test-dev-rootless
exit 1
fi
sleep 1
done
docker exec test-dev-rootless id -u | grep -E "^(33|82)$"
docker exec test-dev-rootless curl -sf http://127.0.0.1:8080/health || { docker logs test-dev-rootless && exit 1; }
docker exec test-dev-rootless php -v | grep "PHP $PHP_VERSION" || exit 1
docker exec test-dev-rootless php -m | grep -E "xdebug|pcov|spx" || exit 1
docker exec test-dev-rootless cbox-init --version || exit 1
docker exec test-dev-rootless composer --version || exit 1
docker exec test-dev-rootless node --version || exit 1
# Chromium check
docker exec test-dev-rootless which chromium-browser || docker exec test-dev-rootless which chromium
docker stop test-dev-rootless && docker rm test-dev-rootless
secrets: inherit
# =====================================================================
# Post-build notifications
# =====================================================================
notify-security-updates:
needs: [build-slim-matrix, build-slim-rootless-matrix, build-matrix, build-rootless-matrix, build-chromium-matrix, build-chromium-rootless-matrix, build-dev-matrix, build-dev-rootless-matrix]
runs-on: ubuntu-24.04
if: github.event_name == 'schedule'
steps:
- name: Notify about weekly security rebuild
run: |
echo "Weekly security rebuild completed"
echo "All images rebuilt with latest security patches"
echo "PHP versions: 8.2, 8.3, 8.4"
echo "OS variants: Debian Bookworm"
echo "Rootless variants: Available for all OS variants"
echo "Image tiers: slim, standard, chromium, dev"