From 76e6298f7cbda9500cd56aad383dace49a4d110b Mon Sep 17 00:00:00 2001 From: Headgent Development Date: Sun, 14 Jun 2026 11:01:37 +0200 Subject: [PATCH] ci(image-compliance): smoke tests, hadolint, Trivy, date tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring the phpfpm image repo to the same compliance bar as phpcli: - Smoke tests (support/makefiles/test.mk): build per-arch test images and verify php-fpm actually boots & is healthy, all extensions load, OPcache+JIT are active, and the nginx vhost renders to a valid config (nginx -t). - Push guard: run the smoke tests before the push steps and skip pushing on pull_request events, so PRs validate without publishing. - hadolint job + .hadolint.yaml (DL3018, DL4006 — both deliberate base-image decisions). Removed a `|| true` in src/php/Dockerfile that masked the entire build RUN stage. - entrypoint.sh: run the php-fpm master and workers as appuser (chown stdio pipes once as root, then drop) — no root service, dev bind-mount UID remap preserved. - Trivy report job (continue-on-error) writing a vuln summary to the run. - Immutable date tags (:8.x-YYYYMMDD, :1.28-YYYYMMDD) alongside the moving tags. - README: moving vs. immutable tag guidance; test targets documented. --- .github/workflows/phpfpm.yml | 78 ++++++++++- .hadolint.yaml | 12 ++ Makefile | 1 + README.md | 19 ++- src/php/Dockerfile | 2 +- src/php/entrypoint.sh | 14 +- support/makefiles/docker.build.push.mk | 10 +- support/makefiles/docker.helper.mk | 8 ++ support/makefiles/test.mk | 172 +++++++++++++++++++++++++ 9 files changed, 306 insertions(+), 10 deletions(-) create mode 100644 .hadolint.yaml create mode 100644 support/makefiles/test.mk diff --git a/.github/workflows/phpfpm.yml b/.github/workflows/phpfpm.yml index 31cea1e..e3b3bdf 100644 --- a/.github/workflows/phpfpm.yml +++ b/.github/workflows/phpfpm.yml @@ -1,4 +1,4 @@ -name: Build and Push Docker Images +name: Build, Test and Push Docker Images on: workflow_dispatch: @@ -22,6 +22,7 @@ on: - '.env' - 'src/**' - 'support/**' + - '.hadolint.yaml' - '.github/workflows/phpfpm.yml' - '!**/*.md' pull_request: @@ -32,6 +33,7 @@ on: - '.env' - 'src/**' - 'support/**' + - '.hadolint.yaml' - '.github/workflows/phpfpm.yml' - '!**/*.md' @@ -53,6 +55,29 @@ jobs: GH_TOKEN: ${{ github.token }} run: gh api -X PUT repos/${{ github.repository }}/actions/workflows/phpfpm.yml/enable + # ============================================================================= + # Lint — both Dockerfiles, single required context "hadolint" + # ============================================================================= + hadolint: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Check out the repository + uses: actions/checkout@v6 + + - name: Lint php Dockerfile (hadolint) + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: src/php/Dockerfile + config: .hadolint.yaml + + - name: Lint nginx Dockerfile (hadolint) + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: src/nginx/Dockerfile + config: .hadolint.yaml + # ============================================================================= # PHP-FPM Images (Matrix Build) # ============================================================================= @@ -87,7 +112,13 @@ jobs: - name: Set up QEMU uses: docker/setup-qemu-action@v4 + - name: Run smoke tests (amd64/arm64 on test images) + run: make test-all PHP_VERSION=${{ matrix.php_version }} + + # Push-Guard: PRs build & smoke-test above but never publish; only + # merge/schedule/dispatch push the moving + immutable date tags. - name: Build and push PHP-FPM ${{ matrix.php_version }} + if: github.event_name != 'pull_request' env: DOCKER_HUB: ${{ secrets.DOCKER_USER_NAME }} run: | @@ -124,8 +155,51 @@ jobs: - name: Set up QEMU uses: docker/setup-qemu-action@v4 + - name: Run smoke test (nginx config, amd64/arm64) + run: make nginx-test + + # Push-Guard: PRs build & smoke-test above but never publish. - name: Build and push Nginx + if: github.event_name != 'pull_request' env: DOCKER_HUB: ${{ secrets.DOCKER_USER_NAME }} + run: make nginx-push DOCKER_HUB=${DOCKER_HUB} + + # ============================================================================= + # Trivy — non-blocking CVE visibility on the freshly published images + # (base-image CVEs are not self-fixable, so this reports instead of failing). + # continue-on-error keeps the run green even if Trivy itself errors. + # ============================================================================= + trivy-report: + if: github.event_name != 'pull_request' + needs: [build-phpfpm, build-nginx] + runs-on: ubuntu-latest + continue-on-error: true + permissions: + contents: read + strategy: + matrix: + image: + - headgent/phpfpm:8.2 + - headgent/phpfpm:8.3 + - headgent/phpfpm:8.4 + - headgent/nginx:1.28 + fail-fast: false + steps: + - name: Scan image for HIGH/CRITICAL CVEs + uses: aquasecurity/trivy-action@v0.36.0 + with: + image-ref: ${{ matrix.image }} + format: table + severity: HIGH,CRITICAL + exit-code: '0' + output: trivy-report.txt + + - name: Publish report to job summary run: | - make nginx-push DOCKER_HUB=${DOCKER_HUB} + { + echo "## Trivy CVE report — ${{ matrix.image }} (HIGH/CRITICAL)"; + echo '```'; + cat trivy-report.txt; + echo '```'; + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..41757c2 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,12 @@ +# hadolint configuration for the phpfpm base image (php-fpm + nginx). +# Every ignore below is a deliberate base-image design decision, not an oversight. +ignored: + # DL3018 — "Pin versions in apk add". The apk packages are Alpine system libraries + # that track the pinned ALPINE_VERSION / nginx-alpine build-arg. Pinning each library + # version individually would be brittle (versions change with every Alpine point + # release) and high-maintenance for a base image whose job is to stay current with + # security patches. Applies to both src/php/Dockerfile and src/nginx/Dockerfile. + - DL3018 + # DL4006 — "Set pipefail before RUN with a pipe". The only pipe is `yes "" | pecl install`; + # the left side (`yes`) cannot meaningfully fail, so pipefail adds no safety here. + - DL4006 diff --git a/Makefile b/Makefile index 3546679..48bc9ed 100755 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ export include ./support/makefiles/docker.helper.mk include ./support/makefiles/docker.build.local.mk include ./support/makefiles/docker.build.push.mk +include ./support/makefiles/test.mk include ./support/makefiles/ssh.mk # --------------------------------------------------------------------------- diff --git a/README.md b/README.md index ad7ddc4..5485f5d 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,15 @@ Modern PHP applications demand more than basic runtime environments. **Jardis PH |-----|---------------|--------| | `1.28`, `latest` | Nginx 1.28 | Current | +### Moving vs. Immutable Tags + +Each publish produces two kinds of tags: + +- **Moving tags** — `8.4`, `latest`, `1.28` — always point to the newest rebuild of that line. They pick up Alpine and base-image security patches automatically, so the content behind a moving tag changes over time. +- **Immutable date tags** — `8.4-YYYYMMDD`, `1.28-YYYYMMDD` (e.g. `8.4-20260614`) — are added on every publish and are never moved to a different build. They let you pin a reproducible image and roll back to a known-good state. + +**Recommendation:** Pin an immutable date tag in production deployments for reproducible builds and deterministic rollback; use the moving tags where you want patches applied automatically (e.g. local development). + **Published under the `headgent/` Docker Hub namespace:** - https://hub.docker.com/r/headgent/phpfpm - https://hub.docker.com/r/headgent/nginx @@ -313,9 +322,17 @@ make build-and-push-all | `make phpfpm-build-all` | Build PHP-FPM (all versions) | | `make nginx-build` | Build Nginx image | | `make build-all` | Build all images locally | -| `make phpfpm-push` | Push PHP-FPM (current version) | +| `make phpfpm-push` | Push PHP-FPM (current version, + immutable date tag) | | `make build-and-push-all` | Build and push all images | | `make build-cache-delete` | Clear buildx cache | +| `make test-all` | Run php-fpm smoke tests (FPM boot, extensions, OPcache/JIT; amd64+arm64) | +| `make nginx-test` | Validate the rendered nginx config (`nginx -t`; amd64+arm64) | +| `make test-fpm-start` | Smoke test: php-fpm boots and answers `/ping` | +| `make test-extensions` | Smoke test: all expected PHP extensions loaded | +| `make test-opcache` | Smoke test: OPcache active and JIT enabled | + +> Smoke tests build per-arch test images and run them on `amd64` + `arm64`. For a fast +> native-only run, override the platform list, e.g. `make test-all TEST_PLATFORMS=arm64`. --- diff --git a/src/php/Dockerfile b/src/php/Dockerfile index 259709a..9f2bc1a 100644 --- a/src/php/Dockerfile +++ b/src/php/Dockerfile @@ -43,7 +43,7 @@ RUN apk add --no-cache \ xdebug-${XDEBUG_VERSION} \ pcov-${PCOV_VERSION} \ && docker-php-ext-enable apcu redis amqp rdkafka xdebug pcov \ - && docker-php-ext-enable opcache || true + && docker-php-ext-enable opcache # -------------------- # Runtime stage diff --git a/src/php/entrypoint.sh b/src/php/entrypoint.sh index ef08457..60eed0d 100644 --- a/src/php/entrypoint.sh +++ b/src/php/entrypoint.sh @@ -86,6 +86,16 @@ EOF echo "info: Runtime configuration generated" -# Privilege drop and execute -[ "$(id -u)" = "0" ] && exec su-exec "$APP_USER" "$@" +# Privilege drop and execute. +# The container starts as root only for one-time init: align APP_USER's UID/GID +# with a bind-mounted /app (dev) and hand the container's stdout/stderr to +# APP_USER. The latter matters because php-fpm reopens the global error_log +# (/proc/self/fd/2, initially root-owned) AFTER the drop — without chowning the +# stdio pipes, FPM init fails with "failed to open error_log: Permission denied". +# With them chowned, the php-fpm master AND its workers run as APP_USER; nothing +# stays root. (Brief root-for-init is the standard Docker pattern, like gosu.) +if [ "$(id -u)" = "0" ]; then + chown "$APP_USER:$APP_USER" /proc/self/fd/1 /proc/self/fd/2 || true + exec su-exec "$APP_USER" "$@" +fi exec "$@" diff --git a/support/makefiles/docker.build.push.mk b/support/makefiles/docker.build.push.mk index 9967424..65db142 100644 --- a/support/makefiles/docker.build.push.mk +++ b/support/makefiles/docker.build.push.mk @@ -50,11 +50,12 @@ phpfpm-push: buildx-builder-create .check-docker-login ## Build and push PHP-FPM --build-arg FPM_PM_MAX_SPARE_SERVERS=$(FPM_PM_MAX_SPARE_SERVERS) \ --build-arg FPM_PM_MAX_REQUESTS=$(FPM_PM_MAX_REQUESTS) \ -t $(PHPFPM_IMAGE):$(PHP_VERSION) \ + -t $(PHPFPM_IMAGE):$(PHP_VERSION)-$(IMAGE_DATE) \ $(if $(filter $(PHP_VERSION),$(PHP_LATEST)),-t $(PHPFPM_IMAGE):latest) \ -f ./src/php/Dockerfile \ ./src/php \ $(BUILD_EXTRA_FLAGS) - @echo "✅ Pushed $(PHPFPM_IMAGE):$(PHP_VERSION)" + @echo "✅ Pushed $(PHPFPM_IMAGE):$(PHP_VERSION) (+ :$(PHP_VERSION)-$(IMAGE_DATE))" .PHONY: phpfpm-push phpfpm-push-all: buildx-builder-create .check-docker-login ## Build and push PHP-FPM images for all PHP versions (multi-arch) @@ -103,11 +104,11 @@ phpfpm-push-all: buildx-builder-create .check-docker-login ## Build and push PHP --build-arg FPM_PM_MIN_SPARE_SERVERS=$(FPM_PM_MIN_SPARE_SERVERS) \ --build-arg FPM_PM_MAX_SPARE_SERVERS=$(FPM_PM_MAX_SPARE_SERVERS) \ --build-arg FPM_PM_MAX_REQUESTS=$(FPM_PM_MAX_REQUESTS) \ - -t $(PHPFPM_IMAGE):$$v $$LATEST_TAG \ + -t $(PHPFPM_IMAGE):$$v -t $(PHPFPM_IMAGE):$$v-$(IMAGE_DATE) $$LATEST_TAG \ -f ./src/php/Dockerfile \ ./src/php \ $(BUILD_EXTRA_FLAGS); \ - echo "✅ Pushed $(PHPFPM_IMAGE):$$v"; \ + echo "✅ Pushed $(PHPFPM_IMAGE):$$v (+ :$$v-$(IMAGE_DATE))"; \ done .PHONY: phpfpm-push-all @@ -124,11 +125,12 @@ nginx-push: buildx-builder-create .check-docker-login ## Build and push Nginx im --build-arg PUID=$(PUID) \ --build-arg PGID=$(PGID) \ -t $(NGINX_IMAGE):$(WEBSERVER_VERSION) \ + -t $(NGINX_IMAGE):$(WEBSERVER_VERSION)-$(IMAGE_DATE) \ -t $(NGINX_IMAGE):latest \ -f ./src/nginx/Dockerfile \ ./src/nginx \ $(BUILD_EXTRA_FLAGS) - @echo "✅ Pushed $(NGINX_IMAGE):$(WEBSERVER_VERSION)" + @echo "✅ Pushed $(NGINX_IMAGE):$(WEBSERVER_VERSION) (+ :$(WEBSERVER_VERSION)-$(IMAGE_DATE))" .PHONY: nginx-push # --------------------------------------------------------------------------- diff --git a/support/makefiles/docker.helper.mk b/support/makefiles/docker.helper.mk index 2252d39..b3b3b28 100644 --- a/support/makefiles/docker.helper.mk +++ b/support/makefiles/docker.helper.mk @@ -15,6 +15,14 @@ PHP_LATEST ?= 8.4 PHPFPM_IMAGE = $(DOCKER_HUB)/phpfpm NGINX_IMAGE = $(DOCKER_HUB)/nginx +# --------------------------------------------------------------------------- +# Immutable date tag (UTC), added alongside the moving :/:latest tags in +# the push targets only. Lets consumers pin a reproducible image for rollback +# (e.g. headgent/phpfpm:8.4-20260614, headgent/nginx:1.28-20260614) while the +# moving tags stay current for patch hygiene. Override for reproducible re-tags. +# --------------------------------------------------------------------------- +IMAGE_DATE ?= $(shell date -u +%Y%m%d) + # --------------------------------------------------------------------------- # Build Flags (can be overridden via command line) # --------------------------------------------------------------------------- diff --git a/support/makefiles/test.mk b/support/makefiles/test.mk new file mode 100644 index 0000000..289b2d3 --- /dev/null +++ b/support/makefiles/test.mk @@ -0,0 +1,172 @@ +# --------------------------------------------------------------------------- +# Smoke tests (Multi-Arch: amd64 & arm64) for the php-fpm + nginx base images. +# +# phpfpm has no application test suite — these are base-image smoke tests that +# prove the freshly built images actually boot and expose what consumers rely on: +# - php-fpm starts and answers /ping with "pong" (uses the baked HEALTHCHECK) +# - every expected PHP extension is loaded +# - OPcache is active (CLI) and JIT is on +# - the nginx vhost template renders to a syntactically valid config +# +# Test images are built per-arch with --load and tagged :-test, mirroring +# the phpcli test flow. Override TEST_PLATFORMS=arm64 for a fast native-only run. +# --------------------------------------------------------------------------- +##@ Test + +PHPFPM_TEST_IMAGE ?= headgent-phpfpm-test +NGINX_TEST_IMAGE ?= headgent-nginx-test +TEST_PLATFORMS ?= amd64 arm64 + +# Extensions installed by src/php/Dockerfile (docker-php-ext-install + pecl). +# xdebug is omitted on purpose: it is loaded but disabled (XDEBUG_MODE=off). +PHP_EXTENSIONS := apcu redis gd intl mbstring bcmath soap exif sockets dom zip mysqli curl pdo_mysql pdo_pgsql pcov amqp rdkafka + +# All build-args required by src/php/Dockerfile (runtime ARGs have no defaults). +FPM_TEST_BUILD_ARGS = \ + --build-arg COMPOSER_VERSION=$(COMPOSER_VERSION) \ + --build-arg PHP_VERSION=$(PHP_VERSION) \ + --build-arg ALPINE_VERSION=$(ALPINE_VERSION) \ + --build-arg APCU_VERSION=$(APCU_VERSION) \ + --build-arg REDIS_PECL_VERSION=$(REDIS_PECL_VERSION) \ + --build-arg XDEBUG_VERSION=$(XDEBUG_VERSION) \ + --build-arg PCOV_VERSION=$(PCOV_VERSION) \ + --build-arg AMQP_VERSION=$(AMQP_VERSION) \ + --build-arg RDKAFKA_VERSION=$(RDKAFKA_VERSION) \ + --build-arg PUID=$(PUID) \ + --build-arg PGID=$(PGID) \ + --build-arg PHP_MEMORY_LIMIT=$(PHP_MEMORY_LIMIT) \ + --build-arg PHP_MAX_EXECUTION_TIME=$(PHP_MAX_EXECUTION_TIME) \ + --build-arg PHP_TIMEZONE=$(PHP_TIMEZONE) \ + --build-arg PHP_ERROR_REPORTING=$(PHP_ERROR_REPORTING) \ + --build-arg PHP_DISPLAY_ERRORS=$(PHP_DISPLAY_ERRORS) \ + --build-arg PHP_LOG_ERRORS=$(PHP_LOG_ERRORS) \ + --build-arg APCU_SHM_SIZE=$(APCU_SHM_SIZE) \ + --build-arg OPCACHE_MEMORY_CONSUMPTION=$(OPCACHE_MEMORY_CONSUMPTION) \ + --build-arg OPCACHE_MAX_ACCELERATED_FILES=$(OPCACHE_MAX_ACCELERATED_FILES) \ + --build-arg OPCACHE_VALIDATE_TIMESTAMPS=$(OPCACHE_VALIDATE_TIMESTAMPS) \ + --build-arg OPCACHE_REVALIDATE_FREQ=$(OPCACHE_REVALIDATE_FREQ) \ + --build-arg OPCACHE_JIT=$(OPCACHE_JIT) \ + --build-arg OPCACHE_JIT_BUFFER_SIZE=$(OPCACHE_JIT_BUFFER_SIZE) \ + --build-arg XDEBUG_MODE=$(XDEBUG_MODE) \ + --build-arg XDEBUG_START_WITH_REQUEST=$(XDEBUG_START_WITH_REQUEST) \ + --build-arg XDEBUG_CLIENT_HOST=$(XDEBUG_CLIENT_HOST) \ + --build-arg XDEBUG_CLIENT_PORT=$(XDEBUG_CLIENT_PORT) \ + --build-arg XDEBUG_LOG_LEVEL=$(XDEBUG_LOG_LEVEL) \ + --build-arg XDEBUG_IDEKEY=$(XDEBUG_IDEKEY) \ + --build-arg PCOV_ENABLED=$(PCOV_ENABLED) \ + --build-arg FPM_PM=$(FPM_PM) \ + --build-arg FPM_PM_MAX_CHILDREN=$(FPM_PM_MAX_CHILDREN) \ + --build-arg FPM_PM_START_SERVERS=$(FPM_PM_START_SERVERS) \ + --build-arg FPM_PM_MIN_SPARE_SERVERS=$(FPM_PM_MIN_SPARE_SERVERS) \ + --build-arg FPM_PM_MAX_SPARE_SERVERS=$(FPM_PM_MAX_SPARE_SERVERS) \ + --build-arg FPM_PM_MAX_REQUESTS=$(FPM_PM_MAX_REQUESTS) + +# --------------------------------------------------------------------------- +# Build per-arch test images (--load into the local docker engine) +# --------------------------------------------------------------------------- +build-test-images: buildx-builder-create ## Build per-arch php-fpm test images (:amd64-test/:arm64-test) for PHP_VERSION + @set -e; \ + for arch in $(TEST_PLATFORMS); do \ + echo "🔧 Building php-fpm test image for $$arch (PHP $(PHP_VERSION))..."; \ + docker buildx build \ + --load \ + --platform=linux/$$arch \ + $(FPM_TEST_BUILD_ARGS) \ + -t $(PHPFPM_TEST_IMAGE):$$arch-test \ + -f ./src/php/Dockerfile \ + ./src/php; \ + done +.PHONY: build-test-images + +build-nginx-test-images: buildx-builder-create ## Build per-arch nginx test images (:amd64-test/:arm64-test) + @set -e; \ + for arch in $(TEST_PLATFORMS); do \ + echo "🔧 Building nginx test image for $$arch..."; \ + docker buildx build \ + --load \ + --platform=linux/$$arch \ + --build-arg WEBSERVER_VERSION=$(WEBSERVER_VERSION) \ + --build-arg PUID=$(PUID) \ + --build-arg PGID=$(PGID) \ + -t $(NGINX_TEST_IMAGE):$$arch-test \ + -f ./src/nginx/Dockerfile \ + ./src/nginx; \ + done +.PHONY: build-nginx-test-images + +# --------------------------------------------------------------------------- +# php-fpm smoke tests +# --------------------------------------------------------------------------- +test-fpm-start: build-test-images ## php-fpm boots and answers /ping with pong (baked HEALTHCHECK) on both archs + @set -e; \ + for arch in $(TEST_PLATFORMS); do \ + echo ">>> php-fpm start on $$arch"; \ + cid=$$(docker run -d --platform=linux/$$arch -e XDEBUG_MODE=off $(PHPFPM_TEST_IMAGE):$$arch-test); \ + status=starting; \ + for i in $$(seq 1 30); do \ + if [ "$$(docker inspect -f '{{.State.Running}}' $$cid 2>/dev/null)" != "true" ]; then status=exited; break; fi; \ + status=$$(docker inspect -f '{{.State.Health.Status}}' $$cid 2>/dev/null || echo nohealth); \ + [ "$$status" = "healthy" ] && break; \ + sleep 2; \ + done; \ + docker logs $$cid > /tmp/fpm-$$arch.log 2>&1 || true; \ + docker rm -f $$cid >/dev/null 2>&1 || true; \ + if [ "$$status" != "healthy" ]; then \ + echo "❌ php-fpm not healthy on $$arch (status=$$status)"; echo "--- container log ---"; cat /tmp/fpm-$$arch.log; exit 1; \ + fi; \ + echo "✅ php-fpm started & healthy (/ping → pong) on $$arch"; \ + done +.PHONY: test-fpm-start + +test-extensions: build-test-images ## All expected PHP extensions loaded on both archs + @set -e; \ + for arch in $(TEST_PLATFORMS); do \ + echo ">>> PHP extensions on $$arch"; \ + for ext in $(PHP_EXTENSIONS); do \ + docker run --rm --platform=linux/$$arch -e XDEBUG_MODE=off $(PHPFPM_TEST_IMAGE):$$arch-test php -r "\ +if (!extension_loaded('$$ext')) {fwrite(STDERR,'Missing: $$ext'); exit(1);} \ +"; \ + done; \ + echo "✅ All extensions loaded on $$arch"; \ + done +.PHONY: test-extensions + +test-opcache: build-test-images ## OPcache loaded + active (enable & enable_cli) and JIT on on both archs + @set -e; \ + for arch in $(TEST_PLATFORMS); do \ + echo ">>> OPcache & JIT on $$arch"; \ + docker run --rm --platform=linux/$$arch -e XDEBUG_MODE=off $(PHPFPM_TEST_IMAGE):$$arch-test php -r "\ +if (!(extension_loaded('Zend OPcache') || extension_loaded('opcache'))) {fwrite(STDERR,'OPcache not loaded'); exit(1);} \ +if (ini_get('opcache.enable')!=='1') {fwrite(STDERR,'opcache.enable != '.ini_get('opcache.enable')); exit(1);} \ +if (ini_get('opcache.enable_cli')!=='1') {fwrite(STDERR,'opcache.enable_cli != '.ini_get('opcache.enable_cli')); exit(1);} \ +\$$jit = ini_get('opcache.jit'); \ +if (in_array(strtolower((string)\$$jit), ['', '0','off','disable','disabled'], true)) {fwrite(STDERR,'opcache.jit disabled: '.\$$jit); exit(1);} \ +\$$jit_buffer = ini_get('opcache.jit_buffer_size'); \ +if (\$$jit_buffer === '0' || \$$jit_buffer === '' || (int)\$$jit_buffer === 0) {fwrite(STDERR,'opcache.jit_buffer_size is 0'); exit(1);} \ +"; \ + echo "✅ OPcache active & JIT on on $$arch"; \ + done +.PHONY: test-opcache + +# --------------------------------------------------------------------------- +# nginx smoke test +# --------------------------------------------------------------------------- +nginx-test: build-nginx-test-images ## nginx vhost template renders to a valid config (nginx -t) on both archs + @set -e; \ + for arch in $(TEST_PLATFORMS); do \ + echo ">>> nginx -t on $$arch"; \ + docker run --rm --platform=linux/$$arch \ + --add-host app:127.0.0.1 \ + -e APP_ROOT=/app -e DOCUMENT_ROOT=/public -e INDEX_FILE=index.php -e HOST=localhost -e PHP_PORT=9000 \ + --entrypoint sh $(NGINX_TEST_IMAGE):$$arch-test \ + -c "envsubst '\$$APP_ROOT \$$DOCUMENT_ROOT \$$INDEX_FILE \$$HOST \$$PHP_PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -t"; \ + echo "✅ nginx config valid on $$arch"; \ + done +.PHONY: nginx-test + +# --------------------------------------------------------------------------- +# Bundle +# --------------------------------------------------------------------------- +test-all: test-fpm-start test-extensions test-opcache ## Run all php-fpm smoke tests (both archs) + @echo "✅ All php-fpm smoke tests passed for $(TEST_PLATFORMS)!" +.PHONY: test-all