From 775f319ab0e87cb80bab07729573a02c6f842fe5 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 5 Mar 2025 14:43:59 -0600 Subject: [PATCH 01/63] Bump PHP Extension Installer to version 2.7.27 --- .../local/bin/docker-php-serversideup-install-php-ext-installer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer index 5685cd921..6f071246a 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer +++ b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer @@ -11,7 +11,7 @@ script_name="docker-php-serversideup-install-php-ext-installer" ############ # Environment variables ############ -PHP_EXT_INSTALLER_VERSION="2.7.0" +PHP_EXT_INSTALLER_VERSION="2.7.27" ############ # Main From 39ee9b8f608a6da727ae56f5cc34ae4ea698dc2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=BCske?= Date: Wed, 5 Mar 2025 22:16:44 +0100 Subject: [PATCH 02/63] Add PHP_FPM_PM_MAX_REQUESTS as environment variable (#513) Co-authored-by: Jay Rogers --- .../docs/7.reference/1.environment-variable-specification.md | 1 + .../usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf | 4 ++++ src/variations/fpm-apache/Dockerfile | 1 + src/variations/fpm-nginx/Dockerfile | 1 + src/variations/fpm/Dockerfile | 1 + 5 files changed, 8 insertions(+) diff --git a/docs/content/docs/7.reference/1.environment-variable-specification.md b/docs/content/docs/7.reference/1.environment-variable-specification.md index 597464177..510ad729d 100644 --- a/docs/content/docs/7.reference/1.environment-variable-specification.md +++ b/docs/content/docs/7.reference/1.environment-variable-specification.md @@ -49,6 +49,7 @@ We like to customize our images on a per app basis using environment variables. `PHP_FPM_PM_MAX_SPARE_SERVERS`
*Default: "3"*|The desired maximum number of idle server processes. Used only when pm is set to dynamic. (Official docs)|fpm* `PHP_FPM_PM_MIN_SPARE_SERVERS`
*Default: "1"*|The desired minimum number of idle server processes. Used only when pm is set to dynamic. (Official docs)|fpm* `PHP_FPM_PM_START_SERVERS`
*Default: "2"*|The number of child processes created on startup. Used only when pm is set to dynamic. (Official docs)|fpm* +`PHP_FPM_PM_MAX_REQUESTS`
*Default: "0"*|The number of requests each child process should execute before respawning. This can be useful to work around memory leaks in 3rd party libraries. (Official docs)|fpm* `PHP_FPM_POOL_NAME`
*Default: "www"*|Set the name of your PHP-FPM pool (helpful when running multiple sites on a single server).|fpm* `PHP_FPM_PROCESS_CONTROL_TIMEOUT`
*Default: "10s"*|Set the timeout for the process control commands. (Official docs)|fpm* `PHP_MAX_EXECUTION_TIME`
*Default: "99"*|Set the maximum time in seconds a script is allowed to run before it is terminated by the parser. (Official docs)|all diff --git a/src/php-fpm.d/usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf b/src/php-fpm.d/usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf index e8d3e0cff..10e74a3e2 100644 --- a/src/php-fpm.d/usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf +++ b/src/php-fpm.d/usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf @@ -141,6 +141,10 @@ pm.min_spare_servers = ${PHP_FPM_PM_MIN_SPARE_SERVERS} ; Note: Mandatory when pm is set to 'dynamic' pm.max_spare_servers = ${PHP_FPM_PM_MAX_SPARE_SERVERS} +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +pm.max_requests = ${PHP_FPM_PM_MAX_REQUESTS} ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Healthcheck settings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/variations/fpm-apache/Dockerfile b/src/variations/fpm-apache/Dockerfile index e6c4cdcf3..5a07f5c35 100644 --- a/src/variations/fpm-apache/Dockerfile +++ b/src/variations/fpm-apache/Dockerfile @@ -63,6 +63,7 @@ ENV APACHE_DOCUMENT_ROOT=/var/www/html/public \ PHP_FPM_PM_MAX_SPARE_SERVERS="3" \ PHP_FPM_PM_MIN_SPARE_SERVERS="1" \ PHP_FPM_PM_START_SERVERS="2" \ + PHP_FPM_PM_MAX_REQUESTS="0" \ PHP_FPM_POOL_NAME="www" \ PHP_FPM_PROCESS_CONTROL_TIMEOUT="10s" \ PHP_MAX_EXECUTION_TIME="99" \ diff --git a/src/variations/fpm-nginx/Dockerfile b/src/variations/fpm-nginx/Dockerfile index a8197b6df..034b008d7 100644 --- a/src/variations/fpm-nginx/Dockerfile +++ b/src/variations/fpm-nginx/Dockerfile @@ -87,6 +87,7 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_FPM_PM_MAX_SPARE_SERVERS="3" \ PHP_FPM_PM_MIN_SPARE_SERVERS="1" \ PHP_FPM_PM_START_SERVERS="2" \ + PHP_FPM_PM_MAX_REQUESTS="0" \ PHP_FPM_POOL_NAME="www" \ PHP_FPM_PROCESS_CONTROL_TIMEOUT="10s" \ PHP_MAX_EXECUTION_TIME="99" \ diff --git a/src/variations/fpm/Dockerfile b/src/variations/fpm/Dockerfile index e97261604..f6771af72 100644 --- a/src/variations/fpm/Dockerfile +++ b/src/variations/fpm/Dockerfile @@ -38,6 +38,7 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_FPM_PM_MAX_SPARE_SERVERS="3" \ PHP_FPM_PM_MIN_SPARE_SERVERS="1" \ PHP_FPM_PM_START_SERVERS="2" \ + PHP_FPM_PM_MAX_REQUESTS="0" \ PHP_FPM_POOL_NAME="www" \ PHP_FPM_PROCESS_CONTROL_TIMEOUT="10s" \ PHP_MAX_EXECUTION_TIME="99" \ From dd8698d8b1a11f807779f924faef262a6cabbf06 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 5 Mar 2025 15:21:38 -0600 Subject: [PATCH 03/63] Alphabatized variables --- .../docs/7.reference/1.environment-variable-specification.md | 2 +- .../usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf | 1 + src/variations/fpm-apache/Dockerfile | 2 +- src/variations/fpm-nginx/Dockerfile | 2 +- src/variations/fpm/Dockerfile | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/content/docs/7.reference/1.environment-variable-specification.md b/docs/content/docs/7.reference/1.environment-variable-specification.md index 510ad729d..0e2144190 100644 --- a/docs/content/docs/7.reference/1.environment-variable-specification.md +++ b/docs/content/docs/7.reference/1.environment-variable-specification.md @@ -46,10 +46,10 @@ We like to customize our images on a per app basis using environment variables. `PHP_ERROR_REPORTING`
*Default: "22527"*|Set PHP error reporting level. Must be a number. Use this tool for help. (Official docs)|all `PHP_FPM_PM_CONTROL`
*Defaults:
fpm: dynamic
fpm-apache: ondemand
fpm-nginx: ondemand*|Choose how the process manager will control the number of child processes. (Official docs)|fpm* `PHP_FPM_PM_MAX_CHILDREN`
*Default: "20"*|The number of child processes to be created when pm is set to static and the maximum number of child processes to be created when pm is set to dynamic. (Official docs)|fpm* +`PHP_FPM_PM_MAX_REQUESTS`
*Default: "0"*|The number of requests each child process should execute before respawning. This can be useful to work around memory leaks in 3rd party libraries. (Official docs)|fpm* `PHP_FPM_PM_MAX_SPARE_SERVERS`
*Default: "3"*|The desired maximum number of idle server processes. Used only when pm is set to dynamic. (Official docs)|fpm* `PHP_FPM_PM_MIN_SPARE_SERVERS`
*Default: "1"*|The desired minimum number of idle server processes. Used only when pm is set to dynamic. (Official docs)|fpm* `PHP_FPM_PM_START_SERVERS`
*Default: "2"*|The number of child processes created on startup. Used only when pm is set to dynamic. (Official docs)|fpm* -`PHP_FPM_PM_MAX_REQUESTS`
*Default: "0"*|The number of requests each child process should execute before respawning. This can be useful to work around memory leaks in 3rd party libraries. (Official docs)|fpm* `PHP_FPM_POOL_NAME`
*Default: "www"*|Set the name of your PHP-FPM pool (helpful when running multiple sites on a single server).|fpm* `PHP_FPM_PROCESS_CONTROL_TIMEOUT`
*Default: "10s"*|Set the timeout for the process control commands. (Official docs)|fpm* `PHP_MAX_EXECUTION_TIME`
*Default: "99"*|Set the maximum time in seconds a script is allowed to run before it is terminated by the parser. (Official docs)|all diff --git a/src/php-fpm.d/usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf b/src/php-fpm.d/usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf index 10e74a3e2..500808839 100644 --- a/src/php-fpm.d/usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf +++ b/src/php-fpm.d/usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf @@ -145,6 +145,7 @@ pm.max_spare_servers = ${PHP_FPM_PM_MAX_SPARE_SERVERS} ; This can be useful to work around memory leaks in 3rd party libraries. For ; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. pm.max_requests = ${PHP_FPM_PM_MAX_REQUESTS} + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Healthcheck settings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/variations/fpm-apache/Dockerfile b/src/variations/fpm-apache/Dockerfile index 5a07f5c35..8d1a31bd0 100644 --- a/src/variations/fpm-apache/Dockerfile +++ b/src/variations/fpm-apache/Dockerfile @@ -60,10 +60,10 @@ ENV APACHE_DOCUMENT_ROOT=/var/www/html/public \ PHP_ERROR_REPORTING="22527" \ PHP_FPM_PM_CONTROL=dynamic \ PHP_FPM_PM_MAX_CHILDREN="20" \ + PHP_FPM_PM_MAX_REQUESTS="0" \ PHP_FPM_PM_MAX_SPARE_SERVERS="3" \ PHP_FPM_PM_MIN_SPARE_SERVERS="1" \ PHP_FPM_PM_START_SERVERS="2" \ - PHP_FPM_PM_MAX_REQUESTS="0" \ PHP_FPM_POOL_NAME="www" \ PHP_FPM_PROCESS_CONTROL_TIMEOUT="10s" \ PHP_MAX_EXECUTION_TIME="99" \ diff --git a/src/variations/fpm-nginx/Dockerfile b/src/variations/fpm-nginx/Dockerfile index 034b008d7..5ce225211 100644 --- a/src/variations/fpm-nginx/Dockerfile +++ b/src/variations/fpm-nginx/Dockerfile @@ -84,10 +84,10 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_ERROR_REPORTING="22527" \ PHP_FPM_PM_CONTROL=dynamic \ PHP_FPM_PM_MAX_CHILDREN="20" \ + PHP_FPM_PM_MAX_REQUESTS="0" \ PHP_FPM_PM_MAX_SPARE_SERVERS="3" \ PHP_FPM_PM_MIN_SPARE_SERVERS="1" \ PHP_FPM_PM_START_SERVERS="2" \ - PHP_FPM_PM_MAX_REQUESTS="0" \ PHP_FPM_POOL_NAME="www" \ PHP_FPM_PROCESS_CONTROL_TIMEOUT="10s" \ PHP_MAX_EXECUTION_TIME="99" \ diff --git a/src/variations/fpm/Dockerfile b/src/variations/fpm/Dockerfile index f6771af72..d54352ff9 100644 --- a/src/variations/fpm/Dockerfile +++ b/src/variations/fpm/Dockerfile @@ -35,10 +35,10 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_ERROR_REPORTING="22527" \ PHP_FPM_PM_CONTROL=dynamic \ PHP_FPM_PM_MAX_CHILDREN="20" \ + PHP_FPM_PM_MAX_REQUESTS="0" \ PHP_FPM_PM_MAX_SPARE_SERVERS="3" \ PHP_FPM_PM_MIN_SPARE_SERVERS="1" \ PHP_FPM_PM_START_SERVERS="2" \ - PHP_FPM_PM_MAX_REQUESTS="0" \ PHP_FPM_POOL_NAME="www" \ PHP_FPM_PROCESS_CONTROL_TIMEOUT="10s" \ PHP_MAX_EXECUTION_TIME="99" \ From 4612d3da3c698bad9d46c8df0bac0e3a39aada6e Mon Sep 17 00:00:00 2001 From: RadeJR <33696623+RadeJR@users.noreply.github.com> Date: Wed, 5 Mar 2025 22:29:32 +0100 Subject: [PATCH 04/63] Add environment variable: PHP_MAX_INPUT_VARS (#500) * Added configurability for max_input_vars and default value * A-z variables --------- Co-authored-by: Ilija Radojkovic Co-authored-by: Jay Rogers --- .../docs/7.reference/1.environment-variable-specification.md | 3 ++- .../usr/local/etc/php/conf.d/serversideup-docker-php.ini | 4 ++-- src/variations/cli/Dockerfile | 1 + src/variations/fpm-apache/Dockerfile | 1 + src/variations/fpm-nginx/Dockerfile | 1 + src/variations/fpm/Dockerfile | 1 + src/variations/unit/Dockerfile | 1 + 7 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/content/docs/7.reference/1.environment-variable-specification.md b/docs/content/docs/7.reference/1.environment-variable-specification.md index 510ad729d..02b936331 100644 --- a/docs/content/docs/7.reference/1.environment-variable-specification.md +++ b/docs/content/docs/7.reference/1.environment-variable-specification.md @@ -54,6 +54,7 @@ We like to customize our images on a per app basis using environment variables. `PHP_FPM_PROCESS_CONTROL_TIMEOUT`
*Default: "10s"*|Set the timeout for the process control commands. (Official docs)|fpm* `PHP_MAX_EXECUTION_TIME`
*Default: "99"*|Set the maximum time in seconds a script is allowed to run before it is terminated by the parser. (Official docs)|all `PHP_MAX_INPUT_TIME`
*Default: "-1"*|This sets the maximum time in seconds a script is allowed to parse input data, like POST and GET. Timing begins at the moment PHP is invoked at the server and ends when execution begins. The default setting is -1, which means that max_execution_time is used instead. Set to 0 to allow unlimited time. This directive is hardcoded to -1 for the CLI SAPI by PHP. (Official docs)|all +`PHP_MAX_INPUT_VARS`
*Default: "1000"*|Set the limits for number of input variables (e.g., POST, GET, or COOKIE variables) that PHP will process in a single request. (Official docs)|all `PHP_MEMORY_LIMIT`
*Default: "256M"*|Set the maximum amount of memory in bytes that a script is allowed to allocate. (Official docs)|all `PHP_OPCACHE_ENABLE`
*Default: "0" (to keep developers sane)*|Enable or disable OPcache. ⚠️ This will set **both values** for `opcache.enable` and `opcache.enable_cli`. (Official docs)|all `PHP_OPCACHE_INTERNED_STRINGS_BUFFER`
*Default: "8"*|The amount of memory used to store interned strings, in megabytes. (Official docs)|all @@ -78,4 +79,4 @@ We like to customize our images on a per app basis using environment variables. `UNIT_PROCESSES_MAX`
*Default: "20"*|The maximum number of application processes that can be started. (Official Docs)| unit `UNIT_PROCESSES_SPARE`
*Default: "2"*|Minimum number of idle processes that Unit tries to maintain for an app. (Official Docs)| unit `UNIT_WEBROOT`
*Default: "/var/www/html/public"*|Base directory of the app’s file structure. All URI paths are relative to it. (Official Docs)| unit -`UNIT_MAX_BODY_SIZE`
*Default: "104857600"* (100MB) | Sets maximum number of bytes in the body of a client’s request. (Official docs) | unit \ No newline at end of file +`UNIT_MAX_BODY_SIZE`
*Default: "104857600"* (100MB) | Sets maximum number of bytes in the body of a client’s request. (Official docs) | unit diff --git a/src/common/usr/local/etc/php/conf.d/serversideup-docker-php.ini b/src/common/usr/local/etc/php/conf.d/serversideup-docker-php.ini index 1a99a587b..a5da09e1e 100644 --- a/src/common/usr/local/etc/php/conf.d/serversideup-docker-php.ini +++ b/src/common/usr/local/etc/php/conf.d/serversideup-docker-php.ini @@ -423,7 +423,7 @@ max_input_time = ${PHP_MAX_INPUT_TIME} ;max_input_nesting_level = 64 ; How many GET/POST/COOKIE input variables may be accepted -;max_input_vars = 1000 +max_input_vars = ${PHP_MAX_INPUT_VARS} ; How many multipart body parts (combined input variable and file uploads) may ; be accepted. @@ -1972,4 +1972,4 @@ opcache.revalidate_freq=${PHP_OPCACHE_REVALIDATE_FREQ} ;ffi.enable=preload ; List of headers files to preload, wildcard patterns allowed. -;ffi.preload= \ No newline at end of file +;ffi.preload= diff --git a/src/variations/cli/Dockerfile b/src/variations/cli/Dockerfile index 3896b3fde..63a926b3a 100644 --- a/src/variations/cli/Dockerfile +++ b/src/variations/cli/Dockerfile @@ -35,6 +35,7 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_ERROR_REPORTING="22527" \ PHP_MAX_EXECUTION_TIME="99" \ PHP_MAX_INPUT_TIME="-1" \ + PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ diff --git a/src/variations/fpm-apache/Dockerfile b/src/variations/fpm-apache/Dockerfile index 5a07f5c35..13750f2c7 100644 --- a/src/variations/fpm-apache/Dockerfile +++ b/src/variations/fpm-apache/Dockerfile @@ -68,6 +68,7 @@ ENV APACHE_DOCUMENT_ROOT=/var/www/html/public \ PHP_FPM_PROCESS_CONTROL_TIMEOUT="10s" \ PHP_MAX_EXECUTION_TIME="99" \ PHP_MAX_INPUT_TIME="-1" \ + PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ diff --git a/src/variations/fpm-nginx/Dockerfile b/src/variations/fpm-nginx/Dockerfile index 034b008d7..e38a9cf63 100644 --- a/src/variations/fpm-nginx/Dockerfile +++ b/src/variations/fpm-nginx/Dockerfile @@ -92,6 +92,7 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_FPM_PROCESS_CONTROL_TIMEOUT="10s" \ PHP_MAX_EXECUTION_TIME="99" \ PHP_MAX_INPUT_TIME="-1" \ + PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ diff --git a/src/variations/fpm/Dockerfile b/src/variations/fpm/Dockerfile index f6771af72..9bb89a734 100644 --- a/src/variations/fpm/Dockerfile +++ b/src/variations/fpm/Dockerfile @@ -43,6 +43,7 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_FPM_PROCESS_CONTROL_TIMEOUT="10s" \ PHP_MAX_EXECUTION_TIME="99" \ PHP_MAX_INPUT_TIME="-1" \ + PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ diff --git a/src/variations/unit/Dockerfile b/src/variations/unit/Dockerfile index 88aa98f08..d1e2723d4 100644 --- a/src/variations/unit/Dockerfile +++ b/src/variations/unit/Dockerfile @@ -91,6 +91,7 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_ERROR_REPORTING="22527" \ PHP_MAX_EXECUTION_TIME="99" \ PHP_MAX_INPUT_TIME="-1" \ + PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ From 7625ec643921f327a384eebdf0ca0a8c73d9ab82 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 5 Mar 2025 16:42:32 -0600 Subject: [PATCH 05/63] Refactored "set-file-permissions" and "set-id" scripts. Fixes #502 --- ...cker-php-serversideup-set-file-permissions | 99 ++++++++++++++++--- .../local/bin/docker-php-serversideup-set-id | 6 ++ 2 files changed, 94 insertions(+), 11 deletions(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions index 4139a9f9e..63afb9cde 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions +++ b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions @@ -1,5 +1,5 @@ #!/bin/sh -set -e +set -eu ################################################### # Usage: docker-php-serversideup-set-file-permissions --owner USER:GROUP --service SERVICE @@ -44,6 +44,12 @@ if [ -z "$OWNER" ] || [ -z "$SERVICE" ]; then usage fi +# Check for root privileges +if [ "$(id -u)" -ne 0 ]; then + echo "${script_name}: This script must be run as root within the container. Be sure to set \"USER root\" in your Dockerfile before running this script." + exit 1 +fi + # Detect the operating system using /etc/os-release if [ -f "/etc/os-release" ]; then . /etc/os-release @@ -70,19 +76,54 @@ case "$OS" in debian) case "$SERVICE" in cli) - DIRS="/var/www/ $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; fpm) - DIRS="/var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /var/www + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; apache) - DIRS="/run /etc/apache2 /etc/ssl/private /var/log/apache2 /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /run + /var/www + /var/log/apache2 + /etc/apache2 + /etc/ssl/private + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; nginx) - DIRS="/run /etc/nginx/ /var/log/nginx /etc/ssl/private /var/cache/nginx/ /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /run + /var/www + /var/log/nginx + /var/cache/nginx + /etc/nginx + /etc/ssl/private + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; unit) - DIRS="/var/log/unit /var/run/unit /etc/unit /etc/ssl/private/ /var/lib/unit/ /var/www/ $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /var/www + /var/log/unit + /var/run/unit + /var/lib/unit + /etc/unit + /etc/ssl/private + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; *) echo "$script_name: Unsupported service: $SERVICE" @@ -93,16 +134,40 @@ case "$OS" in alpine) case "$SERVICE" in cli) - DIRS="/var/www/ $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; fpm) - DIRS="/var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /var/www + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; apache) - DIRS="/etc/apache2 /etc/ssl/private /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /var/www + /etc/apache2 + /etc/ssl/private + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; nginx) - DIRS="/etc/nginx/ /var/log/nginx /etc/ssl/private /var/lib/nginx /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /var/www + /etc/nginx + /var/log/nginx + /var/lib/nginx + /etc/ssl/private + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; *) echo "$script_name: Unsupported SERVICE: $SERVICE" @@ -117,4 +182,16 @@ case "$OS" in esac # Change ownership of the directories -change_ownership $DIRS \ No newline at end of file +# Split DIRS into an array and properly quote each path +for dir in ${DIRS}; do + if [ -e "${dir}" ]; then + chown -R "${OWNER}" "${dir}" || { + echo "${script_name}: Failed to change ownership of ${dir}" + exit 1 + } + echo "${script_name}: Ownership of ${dir} changed to ${OWNER}." + else + echo "${script_name}: Directory not found: ${dir}" + exit 1 + fi +done \ No newline at end of file diff --git a/src/common/usr/local/bin/docker-php-serversideup-set-id b/src/common/usr/local/bin/docker-php-serversideup-set-id index 908507d7c..3da78d266 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-set-id +++ b/src/common/usr/local/bin/docker-php-serversideup-set-id @@ -17,6 +17,12 @@ if [ "$#" -ne 2 ]; then exit 1 fi +# Check for root privileges +if [ "$(id -u)" -ne 0 ]; then + echo "${script_name}: This script must be run as root within the container. Be sure to set \"USER root\" in your Dockerfile before running this script." + exit 1 +fi + username="$1" uid_gid="$2" From 792f1cb2904ed8edb96e003aa87a94ddd2793a47 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 6 Mar 2025 10:12:20 -0600 Subject: [PATCH 06/63] Added cursor rules --- .cursor/rules | 11 ++++ .../local/bin/docker-php-serversideup-s6-init | 51 +++++++++++++++++-- 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 .cursor/rules diff --git a/.cursor/rules b/.cursor/rules new file mode 100644 index 000000000..7a824e847 --- /dev/null +++ b/.cursor/rules @@ -0,0 +1,11 @@ +You are a highly skilled PHP system administrator tasked with maintaining open source PHP Docker images for Laravel applications. You possess deep knowledge of best practices of Docker, PHP, Laravel, GitHub Actions, and shell scripting.Your goal is to assist in creating production-ready Docker images that follow best practices for security, performance, and developer experience using the guidelines below. + +1. Development Guidelines: + + - Follow the best practices for security, performance, and developer experience. + - Write clean, maintainable and technically accurate code. + - All entrypoint scripts for the Docker images must be POSIX compliant and able to be executed with /bin/sh. + - Bash scripts must be compatible with Linux, WSL2, and MacOS (Bash v3). + - Never use an approach you're not confident about. If you're unsure about something, ask for clarity. + +This project is open source and the code is available on GitHub, so be sure to follow best practices to make it easy for others to understand, modify, and contribute to the project. \ No newline at end of file diff --git a/src/s6/usr/local/bin/docker-php-serversideup-s6-init b/src/s6/usr/local/bin/docker-php-serversideup-s6-init index a837f4a7f..e5628884a 100644 --- a/src/s6/usr/local/bin/docker-php-serversideup-s6-init +++ b/src/s6/usr/local/bin/docker-php-serversideup-s6-init @@ -7,6 +7,51 @@ set -e # This script is used to take scripts from "/etc/entrypoint.d" and move them # to the S6 Overlay structure. -echo "⚠️ CAUTION: This script is deprecated and will be removed in a future release. Please remove any references to this script from your Dockerfile." -echo "For the latest information on how to add your own start up scripts, please refer to the documentation:" -echo "https://serversideup.net/open-source/docker-php/docs/customizing-the-image/adding-your-own-start-up-scripts" +S6_HOME="/etc/s6-overlay" + +for file in /etc/entrypoint.d/*.sh; do + [ -e "$file" ] || continue # Skip if no files match + + # Get the base name of the file + base=$(basename "$file" .sh) + + # Proceed only if the script does not exist + if [ ! -e "${S6_HOME}/scripts/${base}" ]; then + # Create the service directory for that file + mkdir -p "${S6_HOME}/s6-rc.d/${base}" + + # Set service type to "oneshot" + echo "oneshot" > "${S6_HOME}/s6-rc.d/${base}/type" + + # Set the "up" script + echo "${S6_HOME}/scripts/${base}" > "${S6_HOME}/s6-rc.d/${base}/up" + + # Place empty file in contents.d + touch "${S6_HOME}/s6-rc.d/user/contents.d/${base}" + + # Ensure the ${S6_HOME}/scripts/ directory exists + mkdir -p "${S6_HOME}/scripts" + + # Link the script in the script directory + ln -s "${file}" "${S6_HOME}/scripts/${base}" + + # Ensure the script has the correct file header for S6 + sed -i '1s%^#!/bin/sh$%#!/command/with-contenv sh%' "${file}" + + # Find the script that should be the dependency based on alphabetical order + previous_base=$(find "${S6_HOME}/s6-rc.d/" -maxdepth 1 -type d -name '[0-9]*' | \ + sort | \ + grep -B1 "${base}" | \ + head -n 1 | \ + xargs basename) + + # Check if the previous script is not the current script and set as dependency + if [ "$previous_base" != "$base" ] && [ -n "$previous_base" ]; then + echo "$previous_base" >> "${S6_HOME}/s6-rc.d/${base}/dependencies" + fi + + # Set the previous file for the next loop + previous_base="$base" + fi + +done \ No newline at end of file From 6ff7b0cc2c4363c2811ce448411ddb9741696dc7 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 6 Mar 2025 10:14:44 -0600 Subject: [PATCH 07/63] Updated rules --- .cursor/rules | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.cursor/rules b/.cursor/rules index 7a824e847..6b0da61c6 100644 --- a/.cursor/rules +++ b/.cursor/rules @@ -1,6 +1,16 @@ -You are a highly skilled PHP system administrator tasked with maintaining open source PHP Docker images for Laravel applications. You possess deep knowledge of best practices of Docker, PHP, Laravel, GitHub Actions, and shell scripting.Your goal is to assist in creating production-ready Docker images that follow best practices for security, performance, and developer experience using the guidelines below. +You are a highly skilled PHP system administrator tasked with maintaining open source PHP Docker images for Laravel applications. Your goal is to assist in creating production-ready Docker images that follow best practices for security, performance, and developer experience using the guidelines below. -1. Development Guidelines: +1. Skills you posses deep knowledge and best practices of: + - Docker + - PHP + - Laravel + - GitHub Actions + - Shell scripting + - S6 Overlay + - Nginx + - Apache + +2. Development Guidelines: - Follow the best practices for security, performance, and developer experience. - Write clean, maintainable and technically accurate code. From 47634511f1aecbd50dfad55399ffcbb05d4bc3a8 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 6 Mar 2025 10:15:16 -0600 Subject: [PATCH 08/63] Add NGINX Unit and PHP-FPM to system administrator skills --- .cursor/rules | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.cursor/rules b/.cursor/rules index 6b0da61c6..a54d85222 100644 --- a/.cursor/rules +++ b/.cursor/rules @@ -8,7 +8,9 @@ You are a highly skilled PHP system administrator tasked with maintaining open s - Shell scripting - S6 Overlay - Nginx - - Apache + - Apache + - NGINX Unit + - PHP-FPM 2. Development Guidelines: From 09e5a2f96c51375dd26b05d86b5ae0103ff9a003 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 6 Mar 2025 11:21:25 -0600 Subject: [PATCH 09/63] Enhance S6 Overlay initialization script with robust error handling and improved script management. References #479 --- .../3.adding-your-own-start-up-scripts.md | 47 ++++++++++++++-- .../docs/7.reference/2.command-reference.md | 5 ++ .../local/bin/docker-php-serversideup-s6-init | 55 +++++++++++++------ 3 files changed, 85 insertions(+), 22 deletions(-) diff --git a/docs/content/docs/5.customizing-the-image/3.adding-your-own-start-up-scripts.md b/docs/content/docs/5.customizing-the-image/3.adding-your-own-start-up-scripts.md index b57c73ac2..2314e936d 100644 --- a/docs/content/docs/5.customizing-the-image/3.adding-your-own-start-up-scripts.md +++ b/docs/content/docs/5.customizing-the-image/3.adding-your-own-start-up-scripts.md @@ -37,6 +37,11 @@ Anything in the `/etc/entrypoint.d` directory are scripts that are intended to r Instead, learn about [using S6 overlay](/docs/guide/using-s6-overlay) so your services can be properly initialized and monitored. See the [S6 Overylay project](https://github.com/just-containers/s6-overlay) for more details on how to write your own S6 service. +## Don't use `exit 0` in your script +If you use `exit 0` in your script, it will stop the execution of the rest of the entrypoint scripts. We recommend using `return 0` instead. [See this discussion](https://github.com/serversideup/docker-php/issues/481#issuecomment-2463082306) for more details on why. + +Long story short, we don't use subshells to execute your scripts, so `exit 0` will not work as expected. We do this because we want to ensure your script has access to the environment variables that are set in the entrypoint scripts. + ## Example: Create a custom entrypoint script In this example, let's create a `99-my-script.sh` so it executes after all the other default scripts. @@ -104,11 +109,6 @@ services: In the above file, we're building our image using the `Dockerfile` in the current directory. We're also mounting our current directory to `/var/www/html` in the container. -## Don't use `exit 0` in your script -If you use `exit 0` in your script, it will stop the execution of the rest of the entrypoint scripts. We recommend using `return 0` instead. [See this discussion](https://github.com/serversideup/docker-php/issues/481#issuecomment-2463082306) for more details on why. - -Long story short, we don't use subshells to execute your scripts, so `exit 0` will not work as expected. We do this because we want to ensure your script has access to the environment variables that are set in the entrypoint scripts. - ## Running our example When we run `docker compose up`, we should see the following output: @@ -135,4 +135,39 @@ example-project | 2023/12/05 19:52:38 [info] 67#67 OpenSSL 3.0.11 19 Sep 2023, ``` :: -You can see our `👋 Hello, world!` is executing *after* the initialization of `10-init-unit.sh`. \ No newline at end of file +You can see our `👋 Hello, world!` is executing *after* the initialization of `10-init-unit.sh`. + +## Advanced Scenarios: S6 Overlay dependencies +If you want to customize an image that uses S6 Overlay (`fpm-nginx` or `fpm-apache`), you may have an advanced scenario where you have a custom S6 service that needs to be executed after one of our entrypoint scripts. In order to do this, you'll need to move all our scripts from the `/etc/entrypoint.d` directory to the `/etc/s6-overlay/scripts` directory. This would be a very time consuming scenario if you did this manually, but thankfully you can use our `docker-php-serversideup-s6-init` script to do this for you. + +::code-panel +--- +label: "Example Dockerfile" +--- +```dockerfile +FROM serversideup/php:8.4-fpm-nginx + +# Set the user to root for our build steps +USER root + +# If you have your own one-shot scripts, copy them to the entrypoint.d directory +COPY --chmod=755 ./entrypoint.d/ /etc/entrypoint.d/ + +# Copy our entrypoint scripts into the S6 Overlay scripts directory +RUN docker-php-serversideup-s6-init + +# If you have your own long running services, copy them to the s6 directory +COPY --chmod=755 ./my-s6-service/ /etc/s6-overlay/s6-rc.d/my-s6-service/ + +# Drop back to the non-root user +USER www-data +``` +:: + +In the above file, we're copying our "one-shot" scripts to the `/etc/entrypoint.d` directory and our long running services to the `/etc/s6-overlay` directory. One-shot scripts are scripts that are intended to run quickly and then move on. Long running services are services that are intended to run for a long time and need to be monitored and restarted if they crash. + +The magic happens when we run `docker-php-serversideup-s6-init`. This script will move all our scripts from the `/etc/entrypoint.d` directory to the `/etc/s6-overlay/scripts` directory and set the correct dependencies for our S6 services. + +You can now reference our script names as dependencies in your own S6 service. + +[Learn more about writing your own S6 services →](https://github.com/just-containers/s6-overlay) diff --git a/docs/content/docs/7.reference/2.command-reference.md b/docs/content/docs/7.reference/2.command-reference.md index 14942b9ba..a1b4e437c 100644 --- a/docs/content/docs/7.reference/2.command-reference.md +++ b/docs/content/docs/7.reference/2.command-reference.md @@ -88,6 +88,11 @@ docker-php-serversideup-set-id www-data 1000:1000 ``` :: +## docker-php-serversideup-s6-init +This command is used to copy our entrypoint scripts into the S6 Overlay scripts directory. This is useful if you're using S6 Overlay and want to ensure your scripts are executed in the correct order. + +[Learn more about using S6 Overlay dependencies →](/docs/customizing-the-image/adding-your-own-start-up-scripts#advanced-scenarios-s6-overlay-dependencies) + ## docker-php-serversideup-s6-install This is a command used at build time to install a specific version of S6 Overlay. diff --git a/src/s6/usr/local/bin/docker-php-serversideup-s6-init b/src/s6/usr/local/bin/docker-php-serversideup-s6-init index e5628884a..d61307140 100644 --- a/src/s6/usr/local/bin/docker-php-serversideup-s6-init +++ b/src/s6/usr/local/bin/docker-php-serversideup-s6-init @@ -8,50 +8,73 @@ set -e # to the S6 Overlay structure. S6_HOME="/etc/s6-overlay" +ENTRYPOINT_DIR="/etc/entrypoint.d" -for file in /etc/entrypoint.d/*.sh; do +# Sanity checks +if [ ! -d "$ENTRYPOINT_DIR" ]; then + echo "Error: $ENTRYPOINT_DIR directory not found" + exit 1 +fi + +if [ ! -d "$S6_HOME" ]; then + echo "Error: S6_HOME directory ($S6_HOME) not found" + exit 1 +fi + +# Check for root privileges +if [ "$(id -u)" -ne 0 ]; then + echo "$(basename "$0"): This script must be run as root within the container. Be sure to set \"USER root\" in your Dockerfile before running this script." + exit 1 +fi + +for file in "$ENTRYPOINT_DIR"/*.sh; do [ -e "$file" ] || continue # Skip if no files match # Get the base name of the file - base=$(basename "$file" .sh) + script_name=$(basename "$file" .sh) # Proceed only if the script does not exist - if [ ! -e "${S6_HOME}/scripts/${base}" ]; then + if [ ! -e "${S6_HOME}/scripts/${script_name}" ]; then # Create the service directory for that file - mkdir -p "${S6_HOME}/s6-rc.d/${base}" + mkdir -p "${S6_HOME}/s6-rc.d/${script_name}" # Set service type to "oneshot" - echo "oneshot" > "${S6_HOME}/s6-rc.d/${base}/type" + echo "oneshot" > "${S6_HOME}/s6-rc.d/${script_name}/type" # Set the "up" script - echo "${S6_HOME}/scripts/${base}" > "${S6_HOME}/s6-rc.d/${base}/up" + echo "${S6_HOME}/scripts/${script_name}" > "${S6_HOME}/s6-rc.d/${script_name}/up" # Place empty file in contents.d - touch "${S6_HOME}/s6-rc.d/user/contents.d/${base}" + touch "${S6_HOME}/s6-rc.d/user/contents.d/${script_name}" # Ensure the ${S6_HOME}/scripts/ directory exists mkdir -p "${S6_HOME}/scripts" - # Link the script in the script directory - ln -s "${file}" "${S6_HOME}/scripts/${base}" + # Move the script to the S6 Overlay scripts directory + mv "${file}" "${S6_HOME}/scripts/${script_name}" # Ensure the script has the correct file header for S6 - sed -i '1s%^#!/bin/sh$%#!/command/with-contenv sh%' "${file}" + sed -i '1s%^#!/bin/sh$%#!/command/with-contenv sh%' "${S6_HOME}/scripts/${script_name}" # Find the script that should be the dependency based on alphabetical order - previous_base=$(find "${S6_HOME}/s6-rc.d/" -maxdepth 1 -type d -name '[0-9]*' | \ - sort | \ - grep -B1 "${base}" | \ + previous_script_name=$(find "${S6_HOME}/s6-rc.d/" -maxdepth 1 -type d -name '[0-9]*' | \ + sort -V | \ + grep -B1 "${script_name}" | \ head -n 1 | \ xargs basename) # Check if the previous script is not the current script and set as dependency - if [ "$previous_base" != "$base" ] && [ -n "$previous_base" ]; then - echo "$previous_base" >> "${S6_HOME}/s6-rc.d/${base}/dependencies" + if [ "$previous_script_name" != "$script_name" ] && [ -n "$previous_script_name" ]; then + dependencies_file="${S6_HOME}/s6-rc.d/${script_name}/dependencies" + touch "$dependencies_file" + echo "$previous_script_name" >> "$dependencies_file" + chmod 644 "$dependencies_file" fi # Set the previous file for the next loop - previous_base="$base" + previous_script_name="$script_name" + else + echo "Skipping ${script_name} because it already exists at ${S6_HOME}/scripts/${script_name}" fi done \ No newline at end of file From 883501f29eccf937f3a75542d39a1a5c987be493 Mon Sep 17 00:00:00 2001 From: Rob Lyons <39706150+aSeriousDeveloper@users.noreply.github.com> Date: Thu, 6 Mar 2025 19:38:41 +0000 Subject: [PATCH 10/63] Add additional OPCache environment variables (#510) * Expose opcache.validate_timestamps to .env * Add PHP_OPCACHE_VALIDATE_TIMESTAMPS to CLI ENV * Add PHP_OPCACHE_VALIDATE_TIMESTAMPS to Apache ENV * Add PHP_OPCACHE_VALIDATE_TIMESTAMPS to Nginx Unit ENV * Add PHP_OPCACHE_VALIDATE_TIMESTAMPS to Nginx ENV * Add PHP_OPCACHE_VALIDATE_TIMESTAMPS to FPM ENV * Include PHP_OPCACHE_VALIDATE_TIMESTAMPS in docs * match capitalisation * Add PHP_OPCACHE_SAVE_COMMENTS environment variable for OPcache configuration * Add PHP_OPCACHE_FORCE_RESTART_TIMEOUT environment variable for OPcache configuration * Add PHP_OPCACHE_JIT_BUFFER_SIZE environment variable for OPcache JIT configuration * Add PHP_OPCACHE_JIT environment variable for OPcache JIT configuration --------- Co-authored-by: Jay Rogers --- .../1.environment-variable-specification.md | 5 +++++ .../local/etc/php/conf.d/serversideup-docker-php.ini | 10 ++++++++-- src/variations/cli/Dockerfile | 5 +++++ src/variations/fpm-apache/Dockerfile | 5 +++++ src/variations/fpm-nginx/Dockerfile | 5 +++++ src/variations/fpm/Dockerfile | 5 +++++ src/variations/unit/Dockerfile | 5 +++++ 7 files changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/content/docs/7.reference/1.environment-variable-specification.md b/docs/content/docs/7.reference/1.environment-variable-specification.md index 17e35fc3c..8a7f60792 100644 --- a/docs/content/docs/7.reference/1.environment-variable-specification.md +++ b/docs/content/docs/7.reference/1.environment-variable-specification.md @@ -57,10 +57,15 @@ We like to customize our images on a per app basis using environment variables. `PHP_MAX_INPUT_VARS`
*Default: "1000"*|Set the limits for number of input variables (e.g., POST, GET, or COOKIE variables) that PHP will process in a single request. (Official docs)|all `PHP_MEMORY_LIMIT`
*Default: "256M"*|Set the maximum amount of memory in bytes that a script is allowed to allocate. (Official docs)|all `PHP_OPCACHE_ENABLE`
*Default: "0" (to keep developers sane)*|Enable or disable OPcache. ⚠️ This will set **both values** for `opcache.enable` and `opcache.enable_cli`. (Official docs)|all +`PHP_OPCACHE_FORCE_RESTART_TIMEOUT`
*Default: "180"*|The number of seconds to wait for a scheduled restart to begin if the cache isn't active, in seconds. If the timeout is hit, then OPcache assumes that something is wrong and will kill the processes holding locks on the cache to permit a restart. (Official docs)|all `PHP_OPCACHE_INTERNED_STRINGS_BUFFER`
*Default: "8"*|The amount of memory used to store interned strings, in megabytes. (Official docs)|all +`PHP_OPCACHE_JIT`
*Default: "off"*|Enable or disable the JIT compiler. (Official docs)|all +`PHP_OPCACHE_JIT_BUFFER_SIZE`
*Default: "0"*|The amount of shared memory to reserve for compiled JIT code. A zero value disables the JIT. (Official docs)|all `PHP_OPCACHE_MAX_ACCELERATED_FILES`
*Default: "10000"*|The maximum number of keys (scripts) in the OPcache hash table. (Official docs)|all `PHP_OPCACHE_MEMORY_CONSUMPTION`
*Default: "128"*|The amount of memory used by the OPcache engine, in megabytes. (Official docs)|all `PHP_OPCACHE_REVALIDATE_FREQ`
*Default: "2"*|How often the OPcache checks for updates to cached files (in seconds). (Official docs)|all +`PHP_OPCACHE_SAVE_COMMENTS`
*Default: "1"*|Remove comments from OPcache to minify a bit further. Note: any code that depends on PHPDoc annotations can break from this. (Official docs)|all +`PHP_OPCACHE_VALIDATE_TIMESTAMPS`
*Default: "1"*|Whether OPcache checks for changes to files, or requires reload of PHP to revalidate OPcache. (Official docs)|all `PHP_OPEN_BASEDIR`
*Default: "None"* |Limit the files that can be accessed by PHP to the specified directory-tree, including the file itself. `open_basedir` is just an extra safety net, that is in no way comprehensive, and can therefore not be relied upon when security is needed. (Official docs)| all `PHP_POST_MAX_SIZE`
*Default: "100M"*|Sets max size of post data allowed. (Official docs)|all `PHP_SESSION_COOKIE_SECURE`
*Default: 1 (true)*|Specifies whether cookies should only be sent over secure connections. (Official docs)|all diff --git a/src/common/usr/local/etc/php/conf.d/serversideup-docker-php.ini b/src/common/usr/local/etc/php/conf.d/serversideup-docker-php.ini index a5da09e1e..2b2d0a642 100644 --- a/src/common/usr/local/etc/php/conf.d/serversideup-docker-php.ini +++ b/src/common/usr/local/etc/php/conf.d/serversideup-docker-php.ini @@ -1810,7 +1810,7 @@ opcache.max_accelerated_files=${PHP_OPCACHE_MAX_ACCELERATED_FILES} ; When disabled, you must reset the OPcache manually or restart the ; webserver for changes to the filesystem to take effect. -;opcache.validate_timestamps=1 +opcache.validate_timestamps=${PHP_OPCACHE_VALIDATE_TIMESTAMPS} ; How often (in seconds) to check file timestamps for changes to the shared ; memory storage allocation. ("1" means validate once per second, but only @@ -1822,7 +1822,13 @@ opcache.revalidate_freq=${PHP_OPCACHE_REVALIDATE_FREQ} ; If disabled, all PHPDoc comments are dropped from the code to reduce the ; size of the optimized code. -;opcache.save_comments=1 +opcache.save_comments=${PHP_OPCACHE_SAVE_COMMENTS} + +; Configure the JIT compiler. +opcache.jit=${PHP_OPCACHE_JIT} + +; The amount of shared memory to reserve for compiled JIT code. A zero value disables the JIT. +opcache.jit_buffer_size=${PHP_OPCACHE_JIT_BUFFER_SIZE} ; If enabled, compilation warnings (including notices and deprecations) will ; be recorded and replayed each time a file is included. Otherwise, compilation diff --git a/src/variations/cli/Dockerfile b/src/variations/cli/Dockerfile index 63a926b3a..68b49e477 100644 --- a/src/variations/cli/Dockerfile +++ b/src/variations/cli/Dockerfile @@ -38,10 +38,15 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ + PHP_OPCACHE_FORCE_RESTART_TIMEOUT="180" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ + PHP_OPCACHE_JIT="off" \ + PHP_OPCACHE_JIT_BUFFER_SIZE="0" \ PHP_OPCACHE_MAX_ACCELERATED_FILES="10000" \ PHP_OPCACHE_MEMORY_CONSUMPTION="128" \ PHP_OPCACHE_REVALIDATE_FREQ="2" \ + PHP_OPCACHE_SAVE_COMMENTS="1" \ + PHP_OPCACHE_VALIDATE_TIMESTAMPS="1" \ PHP_OPEN_BASEDIR="" \ PHP_POST_MAX_SIZE="100M" \ PHP_SESSION_COOKIE_SECURE=false \ diff --git a/src/variations/fpm-apache/Dockerfile b/src/variations/fpm-apache/Dockerfile index 52e82a294..3d7d62d74 100644 --- a/src/variations/fpm-apache/Dockerfile +++ b/src/variations/fpm-apache/Dockerfile @@ -71,10 +71,15 @@ ENV APACHE_DOCUMENT_ROOT=/var/www/html/public \ PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ + PHP_OPCACHE_FORCE_RESTART_TIMEOUT="180" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ + PHP_OPCACHE_JIT="off" \ + PHP_OPCACHE_JIT_BUFFER_SIZE="0" \ PHP_OPCACHE_MAX_ACCELERATED_FILES="10000" \ PHP_OPCACHE_MEMORY_CONSUMPTION="128" \ PHP_OPCACHE_REVALIDATE_FREQ="2" \ + PHP_OPCACHE_SAVE_COMMENTS="1" \ + PHP_OPCACHE_VALIDATE_TIMESTAMPS="1" \ PHP_OPEN_BASEDIR="" \ PHP_POST_MAX_SIZE="100M" \ PHP_SESSION_COOKIE_SECURE=false \ diff --git a/src/variations/fpm-nginx/Dockerfile b/src/variations/fpm-nginx/Dockerfile index 5360b39ca..9b65782b1 100644 --- a/src/variations/fpm-nginx/Dockerfile +++ b/src/variations/fpm-nginx/Dockerfile @@ -95,10 +95,15 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ + PHP_OPCACHE_FORCE_RESTART_TIMEOUT="180" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ + PHP_OPCACHE_JIT="off" \ + PHP_OPCACHE_JIT_BUFFER_SIZE="0" \ PHP_OPCACHE_MAX_ACCELERATED_FILES="10000" \ PHP_OPCACHE_MEMORY_CONSUMPTION="128" \ PHP_OPCACHE_REVALIDATE_FREQ="2" \ + PHP_OPCACHE_SAVE_COMMENTS="1" \ + PHP_OPCACHE_VALIDATE_TIMESTAMPS="1" \ PHP_OPEN_BASEDIR="" \ PHP_POST_MAX_SIZE="100M" \ PHP_SESSION_COOKIE_SECURE=false \ diff --git a/src/variations/fpm/Dockerfile b/src/variations/fpm/Dockerfile index 76a6a96ec..66cc6543f 100644 --- a/src/variations/fpm/Dockerfile +++ b/src/variations/fpm/Dockerfile @@ -46,10 +46,15 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ + PHP_OPCACHE_FORCE_RESTART_TIMEOUT="180" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ + PHP_OPCACHE_JIT="off" \ + PHP_OPCACHE_JIT_BUFFER_SIZE="0" \ PHP_OPCACHE_MAX_ACCELERATED_FILES="10000" \ PHP_OPCACHE_MEMORY_CONSUMPTION="128" \ PHP_OPCACHE_REVALIDATE_FREQ="2" \ + PHP_OPCACHE_SAVE_COMMENTS="1" \ + PHP_OPCACHE_VALIDATE_TIMESTAMPS="1" \ PHP_OPEN_BASEDIR="" \ PHP_POST_MAX_SIZE="100M" \ PHP_SESSION_COOKIE_SECURE=Off \ diff --git a/src/variations/unit/Dockerfile b/src/variations/unit/Dockerfile index d1e2723d4..601c84ea8 100644 --- a/src/variations/unit/Dockerfile +++ b/src/variations/unit/Dockerfile @@ -94,10 +94,15 @@ ENV APP_BASE_DIR=/var/www/html \ PHP_MAX_INPUT_VARS="1000" \ PHP_MEMORY_LIMIT="256M" \ PHP_OPCACHE_ENABLE="0" \ + PHP_OPCACHE_FORCE_RESTART_TIMEOUT="180" \ PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ + PHP_OPCACHE_JIT="off" \ + PHP_OPCACHE_JIT_BUFFER_SIZE="0" \ PHP_OPCACHE_MAX_ACCELERATED_FILES="10000" \ PHP_OPCACHE_MEMORY_CONSUMPTION="128" \ PHP_OPCACHE_REVALIDATE_FREQ="2" \ + PHP_OPCACHE_SAVE_COMMENTS="1" \ + PHP_OPCACHE_VALIDATE_TIMESTAMPS="1" \ PHP_OPEN_BASEDIR="" \ PHP_POST_MAX_SIZE="100M" \ PHP_SESSION_COOKIE_SECURE=false \ From 8f22dbfa939b0fb8a55caf55b49d2498445625f0 Mon Sep 17 00:00:00 2001 From: Rob Lyons <39706150+aSeriousDeveloper@users.noreply.github.com> Date: Tue, 11 Mar 2025 18:02:06 +0000 Subject: [PATCH 11/63] Refactor Laravel Autorun Script, adjust entrypoint behavior, contianer info improvements (#511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update 50-laravel-automations.sh * Update Command Reference * Mention optimize in automations documentation * Attempt to use --except when running artisan optimize * Update docs to mention new function of optimize * Mention --except in env spec * POSIX /bin/sh compatibility, refactor into functions * Remove erroneous concatenate * Fix typo Co-authored-by: Paul * typo fix Co-authored-by: Erik Gaal * typo fix Co-authored-by: Erik Gaal * typo fix Co-authored-by: Erik Gaal * Improve entrypoint script management and error handling - Update entrypoint script to use subshell for script execution - Enhance logging and error reporting for initialization scripts - Remove "Don't use exit 0" section from documentation - Modify exit handling in initialization scripts to preserve environment * Update shell script compatibility guidelines Clarify shell script compatibility requirements for different Linux distributions and operating systems, specifying separate guidelines for /bin/sh and /bin/bash scripts * Enhance container info script with more detailed system information - Improve welcome message layout and readability - Add memory and upload limit information - Include Docker CMD in runtime section - Replace `return 0` with `exit 0` for script termination - Update links and formatting for better user experience * Export Docker CMD for use in initialization scripts - Add DOCKER_CMD environment variable to capture the original Docker command - Enable easier access to the original command in subsequent initialization scripts * Major refactor of Laravel automations script - Add `AUTORUN_DEBUG` environment variable for detailed Laravel automation logging - Implement comprehensive Laravel version detection and compatibility checks - Improve optimization and migration scripts with granular control - Update documentation to reflect new automation features * Update docs to reflect change in optimize process * Fix Joel's Twitter profile 😃 * Update Laravel Automations documentation - Improve description and clarity of Laravel Automations script - Add comprehensive table of environment variables and their functions - Update links to Laravel documentation to version 12.x - Enhance explanations for each Laravel artisan command - Refactor content to provide more detailed and user-friendly information * Add debugging section for Laravel Automations script - Document troubleshooting steps for AUTORUN script - Explain how to enable debug output with environment variables - Provide best practices for preventing AUTORUN script issues * Improve NGINX health check logging and debug output - Add conditional debug logging for NGINX + PHP-FPM health checks - Display more informative message when service is not yet online - Only show HTTP status code when debug mode is enabled * Fix warning about /run not being correct permissions on Alpine * Update Laravel Automations documentation with optimize command details - Add explanation for disabling the `optimize` command - Highlight the benefits of using the `optimize` command - Improve formatting of section headers for better readability --------- Co-authored-by: Paul Co-authored-by: Erik Gaal Co-authored-by: Jay Rogers --- .cursor/rules | 3 +- README.md | 2 +- .../docs/4.laravel/1.laravel-automations.md | 74 +++- .../3.adding-your-own-start-up-scripts.md | 5 - .../1.environment-variable-specification.md | 2 + .../etc/entrypoint.d/0-container-info.sh | 60 +-- .../etc/entrypoint.d/1-log-output-level.sh | 2 +- .../entrypoint.d/50-laravel-automations.sh | 381 +++++++++++++----- .../bin/docker-php-serversideup-entrypoint | 23 +- ...cker-php-serversideup-set-file-permissions | 2 + .../entrypoint.d/10-init-webserver-config.sh | 6 +- .../etc/s6-overlay/s6-rc.d/nginx/data/check | 8 +- 12 files changed, 417 insertions(+), 151 deletions(-) diff --git a/.cursor/rules b/.cursor/rules index a54d85222..e869b89bd 100644 --- a/.cursor/rules +++ b/.cursor/rules @@ -17,7 +17,8 @@ You are a highly skilled PHP system administrator tasked with maintaining open s - Follow the best practices for security, performance, and developer experience. - Write clean, maintainable and technically accurate code. - All entrypoint scripts for the Docker images must be POSIX compliant and able to be executed with /bin/sh. - - Bash scripts must be compatible with Linux, WSL2, and MacOS (Bash v3). + - Any /bin/sh scripts must be compatible with Debian and Alpine Linux. + - For any /bin/bash scripts, these should work with MacOS, Linux, and WSL2. - Never use an approach you're not confident about. If you're unsure about something, ask for clarity. This project is open source and the code is available on GitHub, so be sure to follow best practices to make it easy for others to understand, modify, and contribute to the project. \ No newline at end of file diff --git a/README.md b/README.md index cfdb1a332..eecd140ce 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ We'd like to specifically thank a few folks for taking the time for being a soun Please check out all of their work: - [Chris Fidao](https://twitter.com/fideloper) -- [Joel Clermont](https://twitter.com/joelclermont) +- [Joel Clermont](https://x.com/jclermont) - [Patricio](https://twitter.com/PatricioOnCode) ## About Us diff --git a/docs/content/docs/4.laravel/1.laravel-automations.md b/docs/content/docs/4.laravel/1.laravel-automations.md index f0431c727..9ce8782ff 100644 --- a/docs/content/docs/4.laravel/1.laravel-automations.md +++ b/docs/content/docs/4.laravel/1.laravel-automations.md @@ -5,43 +5,85 @@ layout: docs --- # Laravel Automations -`serversideup/php` has a "Laravel Automations" script that helps you automate certain steps during your deployments. By default, the script is **DISABLED**. We only recommend enabling this script in production environments. +`serversideup/php` has a "Laravel Automations" script that helps automate common tasks to maintain your Laravel application and improve it's performance. By default, the script is **DISABLED**. We only recommend enabling this script in production environments. + +## What the script does ::note -`AUTORUN_ENABLED` must be set to `true` to enable the script. See our [variable reference document](/docs/reference/environment-variable-specification) for more details. +In order for this script to run,`AUTORUN_ENABLED` must be set to `true`. Once the main part of the script is enabled, you can control the individual tasks by setting the corresponding environment variables to `true` or `false`. See our [variable reference document](/docs/reference/environment-variable-specification) for more details. :: -## What the script does -This script executes on container start up and performs the following tasks: +| Environment Variable | Laravel Command | Description | +| -------------------- | -------------- | ----------- | +| `AUTORUN_LARAVEL_STORAGE_LINK` | `php artisan storage:link` | Creates a symbolic link from `public/storage` to `storage/app/public`. | +| `AUTORUN_LARAVEL_MIGRATION` | `php artisan migrate` | Runs migrations. | +| `AUTORUN_LARAVEL_OPTIMIZE` | `php artisan optimize` | Optimizes the application. | +| `AUTORUN_LARAVEL_CONFIG_CACHE` | `php artisan config:cache` | Caches the configuration files into a single file. | +| `AUTORUN_LARAVEL_ROUTE_CACHE` | `php artisan route:cache` | Caches the routes. | +| `AUTORUN_LARAVEL_VIEW_CACHE` | `php artisan view:cache` | Caches the views. | +| `AUTORUN_LARAVEL_EVENT_CACHE` | `php artisan event:cache` | Creates a manifest of all your application's events and listeners. | + +## php artisan storage:link +Creates a symbolic link from `public/storage` to `storage/app/public`. + +[Read more about storage links →](https://laravel.com/docs/12.x/filesystem#the-public-disk) -### php artisan migrate +## php artisan migrate Before running migrations, we ensure the database is online and ready to accept connections. By default, we will wait 30 seconds before timing out. -You can enable the [`--isolated`](https://laravel.com/docs/11.x/migrations#running-migrations) flag by setting `AUTORUN_LARAVEL_MIGRATION_ISOLATION` to `true`, which will ensure no other containers are running a migration. +You can enable the [`--isolated`](https://laravel.com/docs/12.x/migrations#running-migrations) flag by setting `AUTORUN_LARAVEL_MIGRATION_ISOLATION` to `true`, which will ensure no other containers are running a migration. **Special Notes for Isolated Migrations:** - Requires Laravel v9.38.0+ - Your database must support database locks (meaning SQLite is not supported) -### php artisan storage:link -This command creates a symbolic link from `public/storage` to `storage/app/public`. +[Read more about migrations →](https://laravel.com/docs/12.x/migrations#running-migrations) + +## php artisan optimize +Laravel comes with an artisan command called `optimize`, which will optimize the application by caching the configuration, routes, views, and events all in one command. + +You can disable any cache features by setting the corresponding environment variable to `false` (for example, `AUTORUN_LARAVEL_CONFIG_CACHE` would disable configuration caching). + +If your application is running Laravel v11.38.0 or higher, we will utilize the `optimize --except` parameter to exclude any cache features you have disabled. Otherwise, we will run the individual optimizations separately. + +It's possible to disable the `optimize` command by setting `AUTORUN_LARAVEL_OPTIMIZE` to `false`, but the major advantage of using the `optimize` command is other dependencies may hook into this action and run other commands. -### php artisan config:cache +[Read more about optimizing Laravel →](https://laravel.com/docs/12.x/deployment#optimization) + +## php artisan config:cache This command caches all configuration files into a single file, which can then be quickly loaded by Laravel. Once the configuration is cache, the `.env` file will no longer be loaded. -[Read more about configuration caching →](https://laravel.com/docs/11.x/configuration#configuration-caching) +[Read more about configuration caching →](https://laravel.com/docs/12.x/configuration#configuration-caching) -### php artisan route:cache +## php artisan route:cache This command caches the routes, dramatically decrease the time it takes to register all of your application's routes. After running this command, your cached routes file will be loaded on every request. -[Read more about route caching →](https://laravel.com/docs/11.x/routing#route-caching) +[Read more about route caching →](https://laravel.com/docs/12.x/routing#route-caching) -### php artisan view:cache +## php artisan view:cache This command caches all of the views in your application, which can greatly decrease the time it takes to render your views. -[Read more about view caching →](https://laravel.com/docs/11.x/views#optimizing-views) +[Read more about view caching →](https://laravel.com/docs/12.x/views#optimizing-views) -### php artisan event:cache +## php artisan event:cache This command creates a manifest of all your application's events and listeners, which can greatly speed up the process of registering them with Laravel. -[Read more about event caching →](https://laravel.com/docs/11.x/events#event-discovery-in-production) \ No newline at end of file +[Read more about event caching →](https://laravel.com/docs/12.x/events#event-discovery-in-production) + +## Debugging the AUTORUN script +It's very important to understand the nature of how containerized environments work when debugging the AUTORUN script. In some cases, some users may become frustrated when they push an update but their changes are never deployed. + +In most cases, this is due to a bug in their application code that causes a migration or some other process to fail. + +::note +If a failure occurs in the Laravel Automations script, it will exit with a non-zero exit code -- preventing the container from starting. +:: + +If you are experiencing issues, you can enable the `AUTORUN_DEBUG` environment variable to get more detailed ouput of what could be going wrong. + +If you need even more information, you can set `LOG_OUTPUT_LEVEL` to `debug` to get **A TON** of output of what's exactly happening. + +### Preventing issues with the AUTORUN script +- Ensure you are running the latest version of `serversideup/php` +- Ensure you have dependencies installed before calling this script +- Use automated testing to catch issues before deploying \ No newline at end of file diff --git a/docs/content/docs/5.customizing-the-image/3.adding-your-own-start-up-scripts.md b/docs/content/docs/5.customizing-the-image/3.adding-your-own-start-up-scripts.md index 2314e936d..5121c977a 100644 --- a/docs/content/docs/5.customizing-the-image/3.adding-your-own-start-up-scripts.md +++ b/docs/content/docs/5.customizing-the-image/3.adding-your-own-start-up-scripts.md @@ -37,11 +37,6 @@ Anything in the `/etc/entrypoint.d` directory are scripts that are intended to r Instead, learn about [using S6 overlay](/docs/guide/using-s6-overlay) so your services can be properly initialized and monitored. See the [S6 Overylay project](https://github.com/just-containers/s6-overlay) for more details on how to write your own S6 service. -## Don't use `exit 0` in your script -If you use `exit 0` in your script, it will stop the execution of the rest of the entrypoint scripts. We recommend using `return 0` instead. [See this discussion](https://github.com/serversideup/docker-php/issues/481#issuecomment-2463082306) for more details on why. - -Long story short, we don't use subshells to execute your scripts, so `exit 0` will not work as expected. We do this because we want to ensure your script has access to the environment variables that are set in the entrypoint scripts. - ## Example: Create a custom entrypoint script In this example, let's create a `99-my-script.sh` so it executes after all the other default scripts. diff --git a/docs/content/docs/7.reference/1.environment-variable-specification.md b/docs/content/docs/7.reference/1.environment-variable-specification.md index 8a7f60792..0e591a2bd 100644 --- a/docs/content/docs/7.reference/1.environment-variable-specification.md +++ b/docs/content/docs/7.reference/1.environment-variable-specification.md @@ -20,7 +20,9 @@ We like to customize our images on a per app basis using environment variables. `APACHE_THREAD_LIMIT`
*Default: "64"*|Set the maximum configured value for ThreadsPerChild for the lifetime of the Apache httpd process. (Official docs)|fpm-apache `APACHE_THREADS_PER_CHILD`
*Default: "25"*|This directive sets the number of threads created by each child process. (Official docs)|fpm-apache `APP_BASE_DIR`
*Default: "/var/www/html"*|Change this only if you mount your application to a different directory within the container. ℹ️ Be sure to change `NGINX_WEBROOT`, `APACHE_DOCUMENT_ROOT`, `UNIT_WEBROOT`, etc if it applies to your use case as well.|all +`AUTORUN_DEBUG`
*Default: "false"*|Enable debug mode for the Laravel automations. | all `AUTORUN_ENABLED`
*Default: "false"*|Enable or disable all automations. It's advised to set this to `false` in certain CI environments (especially during a composer install). If this is set to `false`, all `AUTORUN_*` behaviors will also be disabled.| all +`AUTORUN_LARAVEL_OPTIMIZE`
*Default: "false"*|Automatically run "php artisan optimize" on container, attempting to `--except` in Laravel > `v11.38.0` (Official docs)
ℹ️ Requires `AUTORUN_ENABLED = true` to run. | all `AUTORUN_LARAVEL_CONFIG_CACHE`
*Default: "true"*|Automatically run "php artisan config:cache" on container start.
ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all `AUTORUN_LARAVEL_EVENT_CACHE`
*Default: "true"*|Automatically run "php artisan event:cache" on container start.
ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all `AUTORUN_LARAVEL_MIGRATION`
*Default: "true"*|Automatically run `php artisan migrate --force` on container start.
ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all diff --git a/src/common/etc/entrypoint.d/0-container-info.sh b/src/common/etc/entrypoint.d/0-container-info.sh index 28d52a018..a71de57ca 100644 --- a/src/common/etc/entrypoint.d/0-container-info.sh +++ b/src/common/etc/entrypoint.d/0-container-info.sh @@ -4,9 +4,22 @@ if [ "$SHOW_WELCOME_MESSAGE" = "false" ] || [ "$LOG_OUTPUT_LEVEL" = "off" ] || [ echo "👉 $0: Container info was display was skipped." fi # Skip the rest of the script - return 0 + exit 0 fi +# Get OPcache status +PHP_OPCACHE_STATUS=$(php -r 'echo ini_get("opcache.enable");') + +if [ "$PHP_OPCACHE_STATUS" = "1" ]; then + PHP_OPCACHE_MESSAGE="✅ Enabled" +else + PHP_OPCACHE_MESSAGE="❌ Disabled" +fi + +# Get memory limits +MEMORY_LIMIT=$(php -r 'echo ini_get("memory_limit");') +UPLOAD_LIMIT=$(php -r 'echo ini_get("upload_max_filesize");') + echo ' -------------------------------------------------------------------- ____ ____ _ _ _ _ @@ -17,32 +30,33 @@ echo ' |_| Brought to you by serversideup.net ---------------------------------------------------------------------' - -PHP_OPCACHE_STATUS=$(php -r 'echo ini_get("opcache.enable");') - -if [ "$PHP_OPCACHE_STATUS" = "1" ]; then - PHP_OPCACHE_MESSAGE="✅ Enabled" -else - PHP_OPCACHE_MESSAGE="❌ Disabled" -fi +-------------------------------------------------------------------- -echo ' -🙌 To support Server Side Up projects visit: -https://serversideup.net/sponsor +📚 Documentation: https://serversideup.net/php/docs +💬 Get Help: https://serversideup.net/php/community +🙌 Sponsor: https://serversideup.net/sponsor ------------------------------------- ℹ️ Container Information --------------------------------------' -echo " -OS: $(. /etc/os-release; echo "${PRETTY_NAME}") -Docker user: $(whoami) -Docker uid: $(id -u) -Docker gid: $(id -g) -OPcache: $PHP_OPCACHE_MESSAGE -PHP Version: $(php -r 'echo phpversion();') -Image Version: $(cat /etc/serversideup-php-version) -" +------------------------------------- +📦 Versions +• Image: '"$(cat /etc/serversideup-php-version)"' +• PHP: '"$(php -r 'echo phpversion();')"' +• OS: '"$(. /etc/os-release; echo "${PRETTY_NAME}")"' + +👤 Container User +• User: '"$(whoami)"' +• UID: '"$(id -u)"' +• GID: '"$(id -g)"' + +⚡ Performance +• OPcache: '"$PHP_OPCACHE_MESSAGE"' +• Memory Limit: '"$MEMORY_LIMIT"' +• Upload Limit: '"$UPLOAD_LIMIT"' + +🔄 Runtime +• Docker CMD: '"$DOCKER_CMD"' +' if [ "$PHP_OPCACHE_STATUS" = "0" ]; then echo "👉 [NOTICE]: Improve PHP performance by setting PHP_OPCACHE_ENABLE=1 (recommended for production)." diff --git a/src/common/etc/entrypoint.d/1-log-output-level.sh b/src/common/etc/entrypoint.d/1-log-output-level.sh index 44f6cd9a3..6f93c6a0d 100644 --- a/src/common/etc/entrypoint.d/1-log-output-level.sh +++ b/src/common/etc/entrypoint.d/1-log-output-level.sh @@ -5,7 +5,7 @@ if [ "$DISABLE_DEFAULT_CONFIG" = true ]; then if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then echo "👉 $script_name: DISABLE_DEFAULT_CONFIG does not equal \"false\", so debug mode will NOT be automatically set." fi - return 0 # Exit if DISABLE_DEFAULT_CONFIG is true + exit 0 # Exit if DISABLE_DEFAULT_CONFIG is true fi ####################################### diff --git a/src/common/etc/entrypoint.d/50-laravel-automations.sh b/src/common/etc/entrypoint.d/50-laravel-automations.sh index b6f217de7..5b33e2b85 100644 --- a/src/common/etc/entrypoint.d/50-laravel-automations.sh +++ b/src/common/etc/entrypoint.d/50-laravel-automations.sh @@ -1,6 +1,263 @@ #!/bin/sh script_name="laravel-automations" +# Global configurations +: "${DISABLE_DEFAULT_CONFIG:=false}" +: "${APP_BASE_DIR:=/var/www/html}" + +# Set default values for Laravel automations +: "${AUTORUN_ENABLED:=false}" +: "${AUTORUN_DEBUG:=false}" + +# Set default values for storage link +: "${AUTORUN_LARAVEL_STORAGE_LINK:=true}" + +# Set default values for optimizations +: "${AUTORUN_LARAVEL_OPTIMIZE:=true}" +: "${AUTORUN_LARAVEL_CONFIG_CACHE:=true}" +: "${AUTORUN_LARAVEL_ROUTE_CACHE:=true}" +: "${AUTORUN_LARAVEL_VIEW_CACHE:=true}" +: "${AUTORUN_LARAVEL_EVENT_CACHE:=true}" + +# Set default values for Migrations +: "${AUTORUN_LARAVEL_MIGRATION:=true}" +: "${AUTORUN_LARAVEL_MIGRATION_ISOLATION:=false}" +: "${AUTORUN_LARAVEL_MIGRATION_TIMEOUT:=30}" + +# Set default values for Laravel version +INSTALLED_LARAVEL_VERSION="" + +############################################################################ +# Sanity Checks +############################################################################ + +debug_log() { + if [ "$LOG_OUTPUT_LEVEL" = "debug" ] || [ "$AUTORUN_DEBUG" = "true" ]; then + echo "👉 DEBUG ($script_name): $1" >&2 + fi +} + +if [ "$DISABLE_DEFAULT_CONFIG" = "true" ] || [ "$AUTORUN_ENABLED" = "false" ]; then + debug_log "Skipping Laravel automations because DISABLE_DEFAULT_CONFIG is true or AUTORUN_ENABLED is false." + exit 0 +fi + +############################################################################ +# Functions +############################################################################ + +artisan_migrate() { + count=0 + timeout=$AUTORUN_LARAVEL_MIGRATION_TIMEOUT + + debug_log "Starting migrations (timeout: ${timeout}s, isolation: $AUTORUN_LARAVEL_MIGRATION_ISOLATION)" + + echo "🚀 Clearing Laravel cache before attempting migrations..." + php "$APP_BASE_DIR/artisan" config:clear + + # Do not exit on error for this loop + set +e + echo "⚡️ Attempting database connection..." + while [ $count -lt "$timeout" ]; do + if [ "$AUTORUN_DEBUG" = "true" ]; then + # Show output when debug is enabled + test_db_connection + else + # Otherwise suppress output + test_db_connection > /dev/null 2>&1 + fi + status=$? + if [ $status -eq 0 ]; then + echo "✅ Database connection successful." + break + else + # Only log every 5 attempts to reduce noise + if [ $((count % 5)) -eq 0 ]; then + debug_log "Connection attempt $((count + 1))/$timeout failed (status: $status)" + fi + echo "Waiting on database connection, retrying... $((timeout - count)) seconds left" + count=$((count + 1)) + sleep 1 + fi + done + + # Re-enable exit on error + set -e + + if [ $count -eq "$timeout" ]; then + echo "❌ $script_name: Database connection failed after multiple attempts." + debug_log "Database connection timed out after $timeout seconds" + return 1 + fi + + if [ "$AUTORUN_LARAVEL_MIGRATION_ISOLATION" = "true" ] && laravel_version_is_at_least "9.38.0"; then + debug_log "Running migrations with --isolated flag" + echo "🚀 Running migrations: \"php artisan migrate --force --isolated\"..." + php "$APP_BASE_DIR/artisan" migrate --force --isolated + else + debug_log "Running standard migrations" + echo "🚀 Running migrations: \"php artisan migrate --force\"..." + php "$APP_BASE_DIR/artisan" migrate --force + fi +} + +artisan_storage_link() { + if [ -d "$APP_BASE_DIR/public/storage" ]; then + echo "✅ Storage already linked..." + else + echo "🔐 Running storage link: \"php artisan storage:link\"..." + php "$APP_BASE_DIR/artisan" storage:link + fi +} + +artisan_optimize() { + # Case 1: All optimizations are enabled - use simple optimize command + if [ "$AUTORUN_LARAVEL_OPTIMIZE" = "true" ] && \ + [ "$AUTORUN_LARAVEL_CONFIG_CACHE" = "true" ] && \ + [ "$AUTORUN_LARAVEL_ROUTE_CACHE" = "true" ] && \ + [ "$AUTORUN_LARAVEL_VIEW_CACHE" = "true" ] && \ + [ "$AUTORUN_LARAVEL_EVENT_CACHE" = "true" ]; then + echo "🚀 Running optimize command: \"php artisan optimize\"..." + if ! php "$APP_BASE_DIR/artisan" optimize; then + echo "❌ Laravel optimize failed" + return 1 + fi + return 0 + fi + + # Case 2: AUTORUN_LARAVEL_OPTIMIZE is true but some optimizations disabled + if [ "$AUTORUN_LARAVEL_OPTIMIZE" = "true" ] && laravel_version_is_at_least "11.38.0"; then + echo "🛠️ Preparing optimizations..." + except="" + + # Build except string for disabled optimizations + [ "$AUTORUN_LARAVEL_CONFIG_CACHE" = "false" ] && except="${except:+${except},}config" + [ "$AUTORUN_LARAVEL_ROUTE_CACHE" = "false" ] && except="${except:+${except},}routes" + [ "$AUTORUN_LARAVEL_VIEW_CACHE" = "false" ] && except="${except:+${except},}views" + [ "$AUTORUN_LARAVEL_EVENT_CACHE" = "false" ] && except="${except:+${except},}events" + + echo "🛠️ Running optimizations: \"php artisan optimize ${except:+--except=${except}}\"..." + if ! php "$APP_BASE_DIR/artisan" optimize ${except:+--except=${except}}; then + echo "$script_name: ❌ Laravel optimize failed" + return 1 + fi + return 0 + fi + + if [ "$AUTORUN_LARAVEL_OPTIMIZE" = "true" ] && ! laravel_version_is_at_least "11.38.0"; then + echo "ℹ️ Granular optimizations require Laravel v11.38.0 or above, using individual optimizations instead..." + fi + + # Case 3: Individual optimizations when AUTORUN_LARAVEL_OPTIMIZE is false + has_error=0 + + if [ "$AUTORUN_LARAVEL_CONFIG_CACHE" = "true" ]; then + echo "🚀 Caching config: \"php artisan config:cache\"..." + php "$APP_BASE_DIR/artisan" config:cache || has_error=1 + fi + + if [ "$AUTORUN_LARAVEL_ROUTE_CACHE" = "true" ]; then + echo "🚀 Caching routes: \"php artisan route:cache\"..." + php "$APP_BASE_DIR/artisan" route:cache || has_error=1 + fi + + if [ "$AUTORUN_LARAVEL_VIEW_CACHE" = "true" ]; then + echo "🚀 Caching views: \"php artisan view:cache\"..." + php "$APP_BASE_DIR/artisan" view:cache || has_error=1 + fi + + if [ "$AUTORUN_LARAVEL_EVENT_CACHE" = "true" ]; then + echo "🚀 Caching events: \"php artisan event:cache\"..." + php "$APP_BASE_DIR/artisan" event:cache || has_error=1 + fi + + return $has_error +} + +get_laravel_version() { + # Return cached version if already set + if [ -n "$INSTALLED_LARAVEL_VERSION" ]; then + debug_log "Using cached Laravel version: $INSTALLED_LARAVEL_VERSION" + echo "$INSTALLED_LARAVEL_VERSION" + return 0 + fi + + debug_log "Detecting Laravel version..." + # Use 2>/dev/null to handle potential PHP warnings + artisan_version_output=$(php "$APP_BASE_DIR/artisan" --version 2>/dev/null) + + # Check if command was successful + if [ $? -ne 0 ]; then + echo "❌ $script_name: Failed to execute artisan command" >&2 + return 1 + fi + + # Extract version number using sed (POSIX compliant) + # Using a more strict pattern that matches "Laravel Framework X.Y.Z" + laravel_version=$(echo "$artisan_version_output" | sed -e 's/^Laravel Framework \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$/\1/') + + # Validate that we got a version number (POSIX compliant regex) + if echo "$laravel_version" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null 2>&1; then + INSTALLED_LARAVEL_VERSION="$laravel_version" + debug_log "Detected Laravel version: $laravel_version" + echo "$laravel_version" + return 0 + else + echo "❌ $script_name: Failed to determine Laravel version" >&2 + return 1 + fi +} + +laravel_is_installed() { + if [ ! -f "$APP_BASE_DIR/artisan" ]; then + return 1 + fi + + if [ ! -d "$APP_BASE_DIR/vendor" ]; then + return 1 + fi + + return 0 +} + +laravel_version_is_at_least() { + required_version="$1" + + if [ -z "$required_version" ]; then + echo "❌ $script_name - Usage: laravel_version_is_at_least " >&2 + return 1 + fi + + # Validate required version format + if ! echo "$required_version" | grep -Eq '^[0-9]+\.[0-9]+(\.[0-9]+)?$'; then + echo "❌ $script_name - Invalid version requirement format: $required_version" >&2 + return 1 + fi + + current_version=$(get_laravel_version) + if [ $? -ne 0 ]; then + echo "❌ $script_name: Failed to get Laravel version" >&2 + return 1 + fi + + # normalize_version() takes a version string and ensures it has 3 parts + normalize_version() { + echo "$1" | awk -F. '{ print $1"."$2"."(NF>2?$3:0) }' + } + + normalized_current=$(normalize_version "$current_version") + normalized_required=$(normalize_version "$required_version") + + # Use sort -V to get the lower version, then compare it with required version + # This works in BusyBox because we only need to check the first line of output + lowest_version=$(printf '%s\n%s\n' "$normalized_required" "$normalized_current" | sort -V | head -n1) + if [ "$lowest_version" = "$normalized_required" ]; then + return 0 # Success: current version is >= required version + else + return 1 # Failure: current version is < required version + fi +} + test_db_connection() { php -r " require '$APP_BASE_DIR/vendor/autoload.php'; @@ -32,102 +289,38 @@ test_db_connection() { " } +############################################################################ +# Main +############################################################################ -# Set default values for Laravel automations -: "${AUTORUN_ENABLED:=false}" -: "${AUTORUN_LARAVEL_MIGRATION_TIMEOUT:=30}" - -if [ "$DISABLE_DEFAULT_CONFIG" = "false" ]; then - # Check to see if an Artisan file exists and assume it means Laravel is configured. - if [ -f "$APP_BASE_DIR/artisan" ] && [ "$AUTORUN_ENABLED" = "true" ]; then - echo "Checking for Laravel automations..." - ############################################################################ - # artisan migrate - ############################################################################ - if [ "${AUTORUN_LARAVEL_MIGRATION:=true}" = "true" ]; then - count=0 - timeout=$AUTORUN_LARAVEL_MIGRATION_TIMEOUT - - echo "🚀 Clearing Laravel cache before attempting migrations..." - php "$APP_BASE_DIR/artisan" config:clear - - # Do not exit on error for this loop - set +e - echo "⚡️ Attempting database connection..." - while [ $count -lt "$timeout" ]; do - test_db_connection > /dev/null 2>&1 - status=$? - if [ $status -eq 0 ]; then - echo "✅ Database connection successful." - break - else - echo "Waiting on database connection, retrying... $((timeout - count)) seconds left" - count=$((count + 1)) - sleep 1 - fi - done - - # Re-enable exit on error - set -e - - if [ $count -eq "$timeout" ]; then - echo "Database connection failed after multiple attempts." - return 1 - fi - - echo "🚀 Running migrations..." - if [ "${AUTORUN_LARAVEL_MIGRATION_ISOLATION:=false}" = "true" ]; then - php "$APP_BASE_DIR/artisan" migrate --force --isolated - else - php "$APP_BASE_DIR/artisan" migrate --force - fi - fi +if laravel_is_installed; then + debug_log "Laravel detected: v$(get_laravel_version)" + debug_log "Automation settings:" + debug_log "- Storage Link: $AUTORUN_LARAVEL_STORAGE_LINK" + debug_log "- Migrations: $AUTORUN_LARAVEL_MIGRATION" + debug_log "- Migrations Isolation: $AUTORUN_LARAVEL_MIGRATION_ISOLATION" + debug_log "- Optimize: $AUTORUN_LARAVEL_OPTIMIZE" + debug_log "- Config Cache: $AUTORUN_LARAVEL_CONFIG_CACHE" + debug_log "- Route Cache: $AUTORUN_LARAVEL_ROUTE_CACHE" + debug_log "- View Cache: $AUTORUN_LARAVEL_VIEW_CACHE" + debug_log "- Event Cache: $AUTORUN_LARAVEL_EVENT_CACHE" - ############################################################################ - # artisan storage:link - ############################################################################ - if [ "${AUTORUN_LARAVEL_STORAGE_LINK:=true}" = "true" ]; then - if [ -d "$APP_BASE_DIR/public/storage" ]; then - echo "✅ Storage already linked..." - else - echo "🔐 Linking the storage..." - php "$APP_BASE_DIR/artisan" storage:link - fi - fi - ############################################################################ - # artisan config:cache - ############################################################################ - if [ "${AUTORUN_LARAVEL_CONFIG_CACHE:=true}" = "true" ]; then - echo "🚀 Caching Laravel config..." - php "$APP_BASE_DIR/artisan" config:cache - fi - - ############################################################################ - # artisan route:cache - ############################################################################ - if [ "${AUTORUN_LARAVEL_ROUTE_CACHE:=true}" = "true" ]; then - echo "🚀 Caching Laravel routes..." - php "$APP_BASE_DIR/artisan" route:cache - fi - - ############################################################################ - # artisan view:cache - ############################################################################ - if [ "${AUTORUN_LARAVEL_VIEW_CACHE:=true}" = "true" ]; then - echo "🚀 Caching Laravel views..." - php "$APP_BASE_DIR/artisan" view:cache - fi + echo "🤔 Checking for Laravel automations..." + if [ "$AUTORUN_LARAVEL_STORAGE_LINK" = "true" ]; then + artisan_storage_link + fi + + if [ "$AUTORUN_LARAVEL_MIGRATION" = "true" ]; then + artisan_migrate + fi - ############################################################################ - # artisan event:cache - ############################################################################ - if [ "${AUTORUN_LARAVEL_EVENT_CACHE:=true}" = "true" ]; then - echo "🚀 Caching Laravel events..." - php "$APP_BASE_DIR/artisan" event:cache - fi + if [ "$AUTORUN_LARAVEL_OPTIMIZE" = "true" ] || \ + [ "$AUTORUN_LARAVEL_CONFIG_CACHE" = "true" ] || \ + [ "$AUTORUN_LARAVEL_ROUTE_CACHE" = "true" ] || \ + [ "$AUTORUN_LARAVEL_VIEW_CACHE" = "true" ] || \ + [ "$AUTORUN_LARAVEL_EVENT_CACHE" = "true" ]; then + artisan_optimize fi else - if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then - echo "👉 $script_name: DISABLE_DEFAULT_CONFIG does not equal 'false', so automations will NOT be performed." - fi -fi + echo "👉 $script_name: Skipping Laravel automations because Laravel is not installed." +fi \ No newline at end of file diff --git a/src/common/usr/local/bin/docker-php-serversideup-entrypoint b/src/common/usr/local/bin/docker-php-serversideup-entrypoint index 879c4e447..b678f29ee 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-entrypoint +++ b/src/common/usr/local/bin/docker-php-serversideup-entrypoint @@ -32,6 +32,9 @@ fi export SERVERSIDEUP_DEFAULT_COMMAND export S6_INITIALIZED +# Export the CMD for use in initialization scripts +export DOCKER_CMD="$*" + ############################################### # Usage: docker-php-serversideup-entrypoint ############################################### @@ -42,14 +45,24 @@ export S6_INITIALIZED find /etc/entrypoint.d/ -type f -name '*.sh' | sort -V | while IFS= read -r f; do if [ -e "$f" ]; then if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then - echo "Executing $f" + echo "🔄 Executing initialization script: $f" + fi + + # Source the script in a subshell to contain exits while preserving environment + (. "$f") + exit_code=$? + + if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then + echo "📝 Script $f completed with exit code: $exit_code" fi - if ! . "$f"; then - echo "Error executing $f" >&2 - exit 1 + + # Only stop on actual errors (non-zero exit codes) + if [ $exit_code -ne 0 ]; then + echo "❌ Error: Initialization script $f failed with exit code $exit_code" >&2 + exit $exit_code fi else - echo "Warning: $f not found" >&2 + echo "⚠️ Warning: Initialization script $f not found" >&2 fi done diff --git a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions index 63afb9cde..9d4bd6909 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions +++ b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions @@ -150,6 +150,7 @@ case "$OS" in apache) DIRS=" /composer + /run /var/www /etc/apache2 /etc/ssl/private @@ -160,6 +161,7 @@ case "$OS" in nginx) DIRS=" /composer + /run /var/www /etc/nginx /var/log/nginx diff --git a/src/s6/etc/entrypoint.d/10-init-webserver-config.sh b/src/s6/etc/entrypoint.d/10-init-webserver-config.sh index e501bc66e..3151f8ab3 100644 --- a/src/s6/etc/entrypoint.d/10-init-webserver-config.sh +++ b/src/s6/etc/entrypoint.d/10-init-webserver-config.sh @@ -9,8 +9,10 @@ script_name="init-webserver-config" # Check if S6 is initialized if [ "$S6_INITIALIZED" != "true" ]; then - echo "ℹ️ [NOTICE]: Running custom command instead of web server configuration: '$*'" - return 0 + if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then + echo "👉 $script_name: S6 is not initialized, so web server configuration will NOT be performed." + fi + exit 0 fi ########## diff --git a/src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/data/check b/src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/data/check index 45a273210..e718322f8 100644 --- a/src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/data/check +++ b/src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/data/check @@ -10,8 +10,10 @@ if is_online; then echo "✅ NGINX + PHP-FPM is running correctly." exit 0 else - echo "❌ There seems to be a failure in checking the NGINX + PHP-FPM." - status_code=$(curl $curl_options -w "%{http_code}" "$healthcheck_url") - echo "HTTP Status Code: $status_code" + echo "👉 NGINX + PHP-FPM is not online. Waiting for it to start..." + if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then + status_code=$(curl $curl_options -w "%{http_code}" "$healthcheck_url") + echo "HTTP Status Code: $status_code" + fi exit 1 fi From c73193f812633052a5a584a8631e8181c9c16b55 Mon Sep 17 00:00:00 2001 From: Guillaume Briday <8252238+guillaumebriday@users.noreply.github.com> Date: Tue, 11 Mar 2025 18:05:10 +0000 Subject: [PATCH 12/63] Add Cache-Control header to assets and media files (#487) Co-authored-by: Jay Rogers --- .../fpm-nginx/etc/nginx/server-opts.d/performance.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/variations/fpm-nginx/etc/nginx/server-opts.d/performance.conf b/src/variations/fpm-nginx/etc/nginx/server-opts.d/performance.conf index 86f0d78e3..46c35f34a 100644 --- a/src/variations/fpm-nginx/etc/nginx/server-opts.d/performance.conf +++ b/src/variations/fpm-nginx/etc/nginx/server-opts.d/performance.conf @@ -12,7 +12,7 @@ location = /robots.txt { # assets, media location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ { - expires 7d; + add_header Cache-Control "public, max-age=31536000, immutable"; access_log off; log_not_found off; # Pass to PHP to ensure PHP apps can handle routes that end in these filetypes @@ -22,7 +22,7 @@ location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp # svg, fonts location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ { add_header Access-Control-Allow-Origin "*"; - expires 7d; + add_header Cache-Control "public, max-age=31536000, immutable"; access_log off; } @@ -31,4 +31,4 @@ gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; -gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; \ No newline at end of file +gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; From 44d5e8e81c54a9ec291b2a24ad2d525676290430 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Tue, 11 Mar 2025 13:30:06 -0500 Subject: [PATCH 13/63] Update Laravel Automations documentation with detailed environment variables and database connection checks --- .../docs/4.laravel/1.laravel-automations.md | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/content/docs/4.laravel/1.laravel-automations.md b/docs/content/docs/4.laravel/1.laravel-automations.md index 9ce8782ff..24dd1a05c 100644 --- a/docs/content/docs/4.laravel/1.laravel-automations.md +++ b/docs/content/docs/4.laravel/1.laravel-automations.md @@ -13,15 +13,26 @@ layout: docs In order for this script to run,`AUTORUN_ENABLED` must be set to `true`. Once the main part of the script is enabled, you can control the individual tasks by setting the corresponding environment variables to `true` or `false`. See our [variable reference document](/docs/reference/environment-variable-specification) for more details. :: -| Environment Variable | Laravel Command | Description | +| Environment Variable | Default | Description | | -------------------- | -------------- | ----------- | -| `AUTORUN_LARAVEL_STORAGE_LINK` | `php artisan storage:link` | Creates a symbolic link from `public/storage` to `storage/app/public`. | -| `AUTORUN_LARAVEL_MIGRATION` | `php artisan migrate` | Runs migrations. | -| `AUTORUN_LARAVEL_OPTIMIZE` | `php artisan optimize` | Optimizes the application. | -| `AUTORUN_LARAVEL_CONFIG_CACHE` | `php artisan config:cache` | Caches the configuration files into a single file. | -| `AUTORUN_LARAVEL_ROUTE_CACHE` | `php artisan route:cache` | Caches the routes. | -| `AUTORUN_LARAVEL_VIEW_CACHE` | `php artisan view:cache` | Caches the views. | -| `AUTORUN_LARAVEL_EVENT_CACHE` | `php artisan event:cache` | Creates a manifest of all your application's events and listeners. | +| `AUTORUN_ENABLED` | `false` | Enables the Laravel Automations script.
**ℹ️ Note:** This must be set to `true` for the script to run. | +| `AUTORUN_DEBUG` | `false` | Enables a special debug mode, specifically for the Laravel Automations script. | +| `AUTORUN_LARAVEL_CONFIG_CACHE` | `true` | `php artisan config:cache`: Caches the configuration files into a single file. | +| `AUTORUN_LARAVEL_EVENT_CACHE` | `true` | `php artisan event:cache`: Creates a manifest of all your application's events and listeners. | +| `AUTORUN_LARAVEL_MIGRATION` | `true` | `php artisan migrate`: Runs migrations. | +| `AUTORUN_LARAVEL_MIGRATION_ISOLATION` | `false` | Run your migrations with the [`--isolated`](https://laravel.com/docs/12.x/migrations#running-migrations) flag.
**ℹ️ Note:** Requires Laravel v9.38.0+ | +| `AUTORUN_LARAVEL_MIGRATION_TIMEOUT` | `30` | Number of seconds to wait for database connection before timing out during migrations. | +| `AUTORUN_LARAVEL_OPTIMIZE` | `true` | `php artisan optimize`: Optimizes the application. | +| `AUTORUN_LARAVEL_ROUTE_CACHE` | `true` | `php artisan route:cache`: Caches the routes. | +| `AUTORUN_LARAVEL_STORAGE_LINK` | `true` | `php artisan storage:link`: Creates a symbolic link from `public/storage` to `storage/app/public`. | +| `AUTORUN_LARAVEL_VIEW_CACHE` | `true` | `php artisan view:cache`: Caches the views. | + +## Database Connection Checks +Before running migrations, the script performs database connection checks to ensure the database is ready. It will: +- Clear the Laravel configuration cache before attempting migrations +- Try to connect to the database for up to `AUTORUN_LARAVEL_MIGRATION_TIMEOUT` seconds (default: 30) +- Automatically detect and handle SQLite databases differently +- Show detailed connection attempts when `AUTORUN_DEBUG` is enabled ## php artisan storage:link Creates a symbolic link from `public/storage` to `storage/app/public`. From 8abfd75a127503a6d7deb6f6899a7a25be59366b Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Tue, 11 Mar 2025 13:37:58 -0500 Subject: [PATCH 14/63] Update Laravel Automations documentation for isolated migrations --- docs/content/docs/4.laravel/1.laravel-automations.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/content/docs/4.laravel/1.laravel-automations.md b/docs/content/docs/4.laravel/1.laravel-automations.md index 24dd1a05c..a37ac392d 100644 --- a/docs/content/docs/4.laravel/1.laravel-automations.md +++ b/docs/content/docs/4.laravel/1.laravel-automations.md @@ -31,7 +31,6 @@ In order for this script to run,`AUTORUN_ENABLED` must be set to `true`. Once th Before running migrations, the script performs database connection checks to ensure the database is ready. It will: - Clear the Laravel configuration cache before attempting migrations - Try to connect to the database for up to `AUTORUN_LARAVEL_MIGRATION_TIMEOUT` seconds (default: 30) -- Automatically detect and handle SQLite databases differently - Show detailed connection attempts when `AUTORUN_DEBUG` is enabled ## php artisan storage:link @@ -46,7 +45,7 @@ You can enable the [`--isolated`](https://laravel.com/docs/12.x/migrations#runni **Special Notes for Isolated Migrations:** - Requires Laravel v9.38.0+ -- Your database must support database locks (meaning SQLite is not supported) +- Your application must be using the memcached, redis, dynamodb, database, file, or array cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. [Read more about migrations →](https://laravel.com/docs/12.x/migrations#running-migrations) From 2ffee5f70ff5d66b8ad7a0a77e92d2a0f031df32 Mon Sep 17 00:00:00 2001 From: Italo Date: Tue, 11 Mar 2025 16:09:57 -0300 Subject: [PATCH 15/63] Add opt-int database seeding support (#404) * Adds opt-in seeding * Updated seeders to execute correctly. Configured defaults. * Add documentation for Laravel db:seed automation option --------- Co-authored-by: Jay Rogers --- .../docs/4.laravel/1.laravel-automations.md | 6 ++++++ .../entrypoint.d/50-laravel-automations.sh | 21 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/content/docs/4.laravel/1.laravel-automations.md b/docs/content/docs/4.laravel/1.laravel-automations.md index a37ac392d..b1f7bee47 100644 --- a/docs/content/docs/4.laravel/1.laravel-automations.md +++ b/docs/content/docs/4.laravel/1.laravel-automations.md @@ -23,6 +23,7 @@ In order for this script to run,`AUTORUN_ENABLED` must be set to `true`. Once th | `AUTORUN_LARAVEL_MIGRATION_ISOLATION` | `false` | Run your migrations with the [`--isolated`](https://laravel.com/docs/12.x/migrations#running-migrations) flag.
**ℹ️ Note:** Requires Laravel v9.38.0+ | | `AUTORUN_LARAVEL_MIGRATION_TIMEOUT` | `30` | Number of seconds to wait for database connection before timing out during migrations. | | `AUTORUN_LARAVEL_OPTIMIZE` | `true` | `php artisan optimize`: Optimizes the application. | +| `AUTORUN_LARAVEL_SEED` | `false` | `php artisan db:seed`: Runs the default seeder.
**ℹ️ Note:** Set to `true` to run the default seeder. If you want to run a custom seeder, set this to the name of the seeder class. | | `AUTORUN_LARAVEL_ROUTE_CACHE` | `true` | `php artisan route:cache`: Caches the routes. | | `AUTORUN_LARAVEL_STORAGE_LINK` | `true` | `php artisan storage:link`: Creates a symbolic link from `public/storage` to `storage/app/public`. | | `AUTORUN_LARAVEL_VIEW_CACHE` | `true` | `php artisan view:cache`: Caches the views. | @@ -65,6 +66,11 @@ This command caches all configuration files into a single file, which can then b [Read more about configuration caching →](https://laravel.com/docs/12.x/configuration#configuration-caching) +## php artisan db:seed +This command runs the default seeder. If you want to run a custom seeder, set `AUTORUN_LARAVEL_SEED` to the name of the seeder class. + +[Read more about seeding →](https://laravel.com/docs/12.x/seeding) + ## php artisan route:cache This command caches the routes, dramatically decrease the time it takes to register all of your application's routes. After running this command, your cached routes file will be loaded on every request. diff --git a/src/common/etc/entrypoint.d/50-laravel-automations.sh b/src/common/etc/entrypoint.d/50-laravel-automations.sh index 5b33e2b85..f60af69d1 100644 --- a/src/common/etc/entrypoint.d/50-laravel-automations.sh +++ b/src/common/etc/entrypoint.d/50-laravel-automations.sh @@ -24,6 +24,9 @@ script_name="laravel-automations" : "${AUTORUN_LARAVEL_MIGRATION_ISOLATION:=false}" : "${AUTORUN_LARAVEL_MIGRATION_TIMEOUT:=30}" +# Set default values for seeders +: "${AUTORUN_LARAVEL_SEED:=false}" + # Set default values for Laravel version INSTALLED_LARAVEL_VERSION="" @@ -136,7 +139,7 @@ artisan_optimize() { [ "$AUTORUN_LARAVEL_VIEW_CACHE" = "false" ] && except="${except:+${except},}views" [ "$AUTORUN_LARAVEL_EVENT_CACHE" = "false" ] && except="${except:+${except},}events" - echo "🛠️ Running optimizations: \"php artisan optimize ${except:+--except=${except}}\"..." + echo "🚀 Running optimizations: \"php artisan optimize ${except:+--except=${except}}\"..." if ! php "$APP_BASE_DIR/artisan" optimize ${except:+--except=${except}}; then echo "$script_name: ❌ Laravel optimize failed" return 1 @@ -174,6 +177,18 @@ artisan_optimize() { return $has_error } +artisan_seed(){ + # Run the default seeder if "true", otherwise use value as custom seeder + if [ "${AUTORUN_LARAVEL_SEED}" = "true" ]; then + echo "🚀 Running default seeder: \"php artisan db:seed\"" + php "${APP_BASE_DIR}/artisan" db:seed --force + else + echo "🚀 Running custom seeder: \"php artisan db:seed --seeder=${AUTORUN_LARAVEL_SEED}\"" + echo "ℹ️ Your application must have a seeder class named \"${AUTORUN_LARAVEL_SEED}\" or this command will fail." + php "${APP_BASE_DIR}/artisan" db:seed --seeder="${AUTORUN_LARAVEL_SEED}" + fi +} + get_laravel_version() { # Return cached version if already set if [ -n "$INSTALLED_LARAVEL_VERSION" ]; then @@ -314,6 +329,10 @@ if laravel_is_installed; then artisan_migrate fi + if [ "$AUTORUN_LARAVEL_SEED" != "false" ]; then + artisan_seed + fi + if [ "$AUTORUN_LARAVEL_OPTIMIZE" = "true" ] || \ [ "$AUTORUN_LARAVEL_CONFIG_CACHE" = "true" ] || \ [ "$AUTORUN_LARAVEL_ROUTE_CACHE" = "true" ] || \ From d123d4ac5f56cb98e10d5dae5e611e1e9290ce4e Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Tue, 11 Mar 2025 14:17:07 -0500 Subject: [PATCH 16/63] Bump PHP Extension Installer version to 2.7.28 --- .../local/bin/docker-php-serversideup-install-php-ext-installer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer index 6f071246a..3de683981 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer +++ b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer @@ -11,7 +11,7 @@ script_name="docker-php-serversideup-install-php-ext-installer" ############ # Environment variables ############ -PHP_EXT_INSTALLER_VERSION="2.7.27" +PHP_EXT_INSTALLER_VERSION="2.7.28" ############ # Main From f74e487cd750224d874038fd9587c025f69826ac Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Tue, 11 Mar 2025 14:40:10 -0500 Subject: [PATCH 17/63] Bump NGINX Unit version to 1.34.2 --- src/variations/unit/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/variations/unit/Dockerfile b/src/variations/unit/Dockerfile index 601c84ea8..58fec5e2a 100644 --- a/src/variations/unit/Dockerfile +++ b/src/variations/unit/Dockerfile @@ -9,7 +9,7 @@ ARG BASE_IMAGE="php:${PHP_VERSION}-cli-${BASE_OS_VERSION}" FROM ${BASE_IMAGE} AS build ARG DEPENDENCY_PACKAGES_ALPINE='build-base curl tar git openssl-dev pcre2-dev shadow' ARG DEPENDENCY_PACKAGES_DEBIAN='ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config' -ARG NGINX_UNIT_VERSION='1.33.0-1' +ARG NGINX_UNIT_VERSION='1.34.2' # copy our scripts COPY --chmod=755 src/common/ / From 19ca9c838dda776803788d87f8a9684393e06533 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Fri, 18 Apr 2025 10:13:30 -0500 Subject: [PATCH 18/63] Bump PHP Extension Installer version to 2.7.29 --- .../local/bin/docker-php-serversideup-install-php-ext-installer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer index 3de683981..051e89aea 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer +++ b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer @@ -11,7 +11,7 @@ script_name="docker-php-serversideup-install-php-ext-installer" ############ # Environment variables ############ -PHP_EXT_INSTALLER_VERSION="2.7.28" +PHP_EXT_INSTALLER_VERSION="2.7.29" ############ # Main From f784f8dc3837ac2990983b07dc63ea5575c6c8a5 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Fri, 18 Apr 2025 10:23:24 -0500 Subject: [PATCH 19/63] Bump PHP Extension Installer version to 2.7.31 --- .../local/bin/docker-php-serversideup-install-php-ext-installer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer index 051e89aea..50529650e 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer +++ b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer @@ -11,7 +11,7 @@ script_name="docker-php-serversideup-install-php-ext-installer" ############ # Environment variables ############ -PHP_EXT_INSTALLER_VERSION="2.7.29" +PHP_EXT_INSTALLER_VERSION="2.7.31" ############ # Main From 4af92bd27bcffdb9eb26ce93e354120fd5aab59e Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 18 Jun 2025 13:18:20 -0500 Subject: [PATCH 20/63] Bump PHP Extension Installer version to 2.8.0 --- .../local/bin/docker-php-serversideup-install-php-ext-installer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer index 50529650e..1684f9897 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer +++ b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer @@ -11,7 +11,7 @@ script_name="docker-php-serversideup-install-php-ext-installer" ############ # Environment variables ############ -PHP_EXT_INSTALLER_VERSION="2.7.31" +PHP_EXT_INSTALLER_VERSION="2.8.0" ############ # Main From 80c794fa2d99a8f5c423ebe47fbbf618ccf8d6fd Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 18 Jun 2025 13:20:55 -0500 Subject: [PATCH 21/63] Bump S6 version to 3.2.1.0 --- src/s6/usr/local/bin/docker-php-serversideup-s6-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/s6/usr/local/bin/docker-php-serversideup-s6-install b/src/s6/usr/local/bin/docker-php-serversideup-s6-install index 0458d3a8f..8af2faa87 100644 --- a/src/s6/usr/local/bin/docker-php-serversideup-s6-install +++ b/src/s6/usr/local/bin/docker-php-serversideup-s6-install @@ -9,7 +9,7 @@ set -oue # Be sure to set the S6_SRC_URL, S6_SRC_DEP, and S6_DIR # environment variables before running this script. -S6_VERSION=v3.2.0.2 +S6_VERSION=v3.2.1.0 mkdir -p $S6_DIR export SYS_ARCH=$(uname -m) case "$SYS_ARCH" in From 3a03547df94490b5fc956be5d8ee8cf81657a3b2 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Fri, 20 Jun 2025 15:04:16 -0500 Subject: [PATCH 22/63] Add support for skipping database connection checks during Laravel migrations - Introduced the `AUTORUN_LARAVEL_MIGRATION_SKIP_DB_CHECK` environment variable to allow users to bypass database connection checks before running migrations. - Updated the documentation to reflect this new environment variable and its default behavior. --- .../docs/7.reference/1.environment-variable-specification.md | 1 + src/common/etc/entrypoint.d/50-laravel-automations.sh | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/docs/content/docs/7.reference/1.environment-variable-specification.md b/docs/content/docs/7.reference/1.environment-variable-specification.md index 0e591a2bd..a2b046f34 100644 --- a/docs/content/docs/7.reference/1.environment-variable-specification.md +++ b/docs/content/docs/7.reference/1.environment-variable-specification.md @@ -27,6 +27,7 @@ We like to customize our images on a per app basis using environment variables. `AUTORUN_LARAVEL_EVENT_CACHE`
*Default: "true"*|Automatically run "php artisan event:cache" on container start.
ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all `AUTORUN_LARAVEL_MIGRATION`
*Default: "true"*|Automatically run `php artisan migrate --force` on container start.
ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all `AUTORUN_LARAVEL_MIGRATION_ISOLATION`
*Default: "false"*|Requires Laravel v9.38.0 or higher and a database that supports table locks. Automatically run `php artisan migrate --force --isolated` on container start.

ℹ️ Requires `AUTORUN_ENABLED = true` to run.
ℹ️ Does not work with SQLite.| all +`AUTORUN_LARAVEL_MIGRATION_SKIP_DB_CHECK`
*Default: "false"*|Skip the database connection check before running migrations.
ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all `AUTORUN_LARAVEL_MIGRATION_TIMEOUT`
*Default: "30"*|The number of seconds to wait for the database to come online before attempting `php artisan migrate`..
ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all `AUTORUN_LARAVEL_ROUTE_CACHE`
*Default: "true"*|Automatically run "php artisan route:cache" on container start.
ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all `AUTORUN_LARAVEL_STORAGE_LINK`
*Default: "true"*|Automatically run "php artisan storage:link" on container start.
ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all diff --git a/src/common/etc/entrypoint.d/50-laravel-automations.sh b/src/common/etc/entrypoint.d/50-laravel-automations.sh index f60af69d1..5ec637c2f 100644 --- a/src/common/etc/entrypoint.d/50-laravel-automations.sh +++ b/src/common/etc/entrypoint.d/50-laravel-automations.sh @@ -23,6 +23,7 @@ script_name="laravel-automations" : "${AUTORUN_LARAVEL_MIGRATION:=true}" : "${AUTORUN_LARAVEL_MIGRATION_ISOLATION:=false}" : "${AUTORUN_LARAVEL_MIGRATION_TIMEOUT:=30}" +: "${AUTORUN_LARAVEL_MIGRATION_SKIP_DB_CHECK:=false}" # Set default values for seeders : "${AUTORUN_LARAVEL_SEED:=false}" @@ -274,6 +275,10 @@ laravel_version_is_at_least() { } test_db_connection() { + if [ "$AUTORUN_LARAVEL_MIGRATION_SKIP_DB_CHECK" = "true" ]; then + return 0 + fi + php -r " require '$APP_BASE_DIR/vendor/autoload.php'; use Illuminate\Support\Facades\DB; From 2029bfafcb0711f81a9b0c287b67032658a46570 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Mon, 4 Aug 2025 10:44:51 -0500 Subject: [PATCH 23/63] Update php-fpm-healthcheck version to 0.6.0 in installation script --- src/s6/usr/local/bin/docker-php-serversideup-s6-install | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/s6/usr/local/bin/docker-php-serversideup-s6-install b/src/s6/usr/local/bin/docker-php-serversideup-s6-install index 8af2faa87..5dd2ea387 100644 --- a/src/s6/usr/local/bin/docker-php-serversideup-s6-install +++ b/src/s6/usr/local/bin/docker-php-serversideup-s6-install @@ -10,6 +10,7 @@ set -oue # environment variables before running this script. S6_VERSION=v3.2.1.0 +PHP_FPM_HEALTHCHECK_VERSION=v0.6.0 mkdir -p $S6_DIR export SYS_ARCH=$(uname -m) case "$SYS_ARCH" in @@ -34,5 +35,5 @@ untar ${S6_SRC_URL}/${S6_VERSION}/s6-overlay-${S6_ARCH}.tar.xz # Ensure "php-fpm-healthcheck" is installed echo "⬇️ Downloading php-fpm-healthcheck..." -curl -o /usr/local/bin/php-fpm-healthcheck https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/v0.5.0/php-fpm-healthcheck +curl -o /usr/local/bin/php-fpm-healthcheck https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/${PHP_FPM_HEALTHCHECK_VERSION}/php-fpm-healthcheck chmod +x /usr/local/bin/php-fpm-healthcheck \ No newline at end of file From 6eca49853f11f06562674bee1052e9e2b8da62f0 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Mon, 4 Aug 2025 11:32:49 -0500 Subject: [PATCH 24/63] Enhance get-php-versions.sh with DockerHub validation and fallback mechanism - Added functionality to validate PHP versions against DockerHub. - Implemented a fallback to the previous patch version if the current version is unavailable. - Introduced a new option `--skip-dockerhub-validation` to bypass validation for testing. - Updated usage documentation to reflect new features and options. --- scripts/get-php-versions.sh | 193 +++++++++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 2 deletions(-) diff --git a/scripts/get-php-versions.sh b/scripts/get-php-versions.sh index 9638e18f8..ac89505ce 100755 --- a/scripts/get-php-versions.sh +++ b/scripts/get-php-versions.sh @@ -1,14 +1,27 @@ #!/bin/bash ################################################### -# Usage: get-php-versions.sh [--skip-download] +# Usage: get-php-versions.sh [--skip-download] [--skip-dockerhub-validation] ################################################### # This file takes the official latest PHP releases from php.net merges them with our # "base php configuration". These files get merged into a final file called "php-versions.yml" # which is used to build our GitHub Actions jobs. # +# 🔍 DOCKERHUB VALIDATION & FALLBACK +# By default, this script validates that each PHP version from php.net is actually available +# on DockerHub before including it in the final configuration. If a version is not available: +# 1. The script attempts to fall back to the previous patch version (e.g., 8.3.24 -> 8.3.23) +# 2. A GitHub Actions warning is displayed explaining the fallback +# 3. If the fallback version is also unavailable, the script exits with an error +# +# This ensures that Docker builds won't fail due to non-existent base images. +# # 👉 REQUIRED FILES # - BASE_PHP_VERSIONS_CONFIG_FILE must be valid and set to a valid file path # (defaults to scripts/conf/php-versions-base-config.yml) +# +# 👉 OPTIONS +# --skip-download: Skip downloading from php.net and use existing base config +# --skip-dockerhub-validation: Skip DockerHub validation (useful for testing/development) set -oue pipefail @@ -16,13 +29,123 @@ set -oue pipefail # set -x # trap read DEBUG +########################## +# DockerHub API Functions + +# Check if a PHP version exists on DockerHub +check_dockerhub_php_version() { + local version="$1" + local variant="${2:-cli}" + local os="${3:-}" + + local image_tag + if [ -n "$os" ] && [ "$os" != "bullseye" ] && [ "$os" != "bookworm" ]; then + image_tag="${version}-${variant}-${os}" + else + image_tag="${version}-${variant}" + fi + + # Use Docker Hub API v2 to check if the tag exists with timeout and retry + local response + local max_retries=3 + local retry_count=0 + + while [ $retry_count -lt $max_retries ]; do + response=$(curl -s --max-time 10 --connect-timeout 5 \ + -o /dev/null -w "%{http_code}" \ + "https://registry.hub.docker.com/v2/repositories/library/php/tags/${image_tag}/") + + # Check if we got a valid HTTP response + if [ "$response" = "200" ]; then + return 0 # Version exists + elif [ "$response" = "404" ]; then + return 1 # Version definitely does not exist + else + # Network error or other issue, retry + retry_count=$((retry_count + 1)) + if [ $retry_count -lt $max_retries ]; then + echo_color_message yellow "⚠️ DockerHub API request failed (HTTP $response), retrying in 2 seconds..." + sleep 2 + fi + fi + done + + # If we get here, all retries failed + echo_color_message red "❌ Failed to check DockerHub after $max_retries attempts for $image_tag" + return 1 +} + +# Get previous patch version (e.g., 8.3.24 -> 8.3.23) +get_previous_patch_version() { + local version="$1" + local major_minor patch + + # Split version into major.minor and patch + major_minor=$(echo "$version" | cut -d'.' -f1-2) + patch=$(echo "$version" | cut -d'.' -f3) + + # Decrement patch version + if [ "$patch" -gt 0 ]; then + patch=$((patch - 1)) + echo "${major_minor}.${patch}" + else + # If patch is 0, we can't go lower + return 1 + fi +} + +# Validate and potentially fallback a PHP version +validate_php_version_with_fallback() { + local version="$1" + local original_version="$version" + local fallback_attempted=false + + echo_color_message yellow "🔍 Checking PHP version $version on DockerHub..." >&2 + + # Check if the version exists on DockerHub (using cli variant as reference) + if check_dockerhub_php_version "$version" "cli"; then + echo_color_message green "✅ PHP $version is available on DockerHub" >&2 + echo "$version" # Output to stdout for capture + return 0 + else + echo_color_message red "❌ PHP $version is not available on DockerHub" >&2 + + # Try to get previous patch version + local fallback_version + if fallback_version=$(get_previous_patch_version "$version"); then + fallback_attempted=true + echo_color_message yellow "⚠️ Attempting fallback to PHP $fallback_version..." >&2 + + if check_dockerhub_php_version "$fallback_version" "cli"; then + echo_color_message yellow "::warning title=PHP Version Fallback::PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." >&2 + echo_color_message green "✅ Fallback successful: Using PHP $fallback_version" >&2 + echo "$fallback_version" # Output to stdout for capture + return 0 + else + echo_color_message red "❌ Fallback version PHP $fallback_version is also not available on DockerHub" >&2 + fi + fi + + # If we get here, both original and fallback failed + if [ "$fallback_attempted" = true ]; then + echo_color_message red "::error title=PHP Version Unavailable::Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." >&2 + else + echo_color_message red "::error title=PHP Version Unavailable::PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." >&2 + fi + + return 1 + fi +} + ########################## # Argument Parsing SKIP_DOWNLOAD="${SKIP_DOWNLOAD:-false}" +SKIP_DOCKERHUB_VALIDATION="${SKIP_DOCKERHUB_VALIDATION:-false}" while [[ "$#" -gt 0 ]]; do case $1 in --skip-download) SKIP_DOWNLOAD=true ;; + --skip-dockerhub-validation) SKIP_DOCKERHUB_VALIDATION=true ;; *) echo "Unknown parameter passed: $1"; exit 1 ;; esac shift @@ -76,8 +199,74 @@ if [ "$SKIP_DOWNLOAD" = false ]; then # Fetch the JSON from the PHP website php_net_version_json=$(curl -s $PHP_VERSIONS_ACTIVE_JSON_FEED) + # Parse the fetched JSON data and optionally validate PHP versions on DockerHub + if [ "$SKIP_DOCKERHUB_VALIDATION" = true ]; then + echo_color_message yellow "⚠️ Skipping DockerHub validation as requested..." + processed_json="$php_net_version_json" + else + echo_color_message yellow "🔍 Parsing and validating PHP versions from php.net..." + + # First, extract versions from the JSON + php_versions_raw=$(echo "$php_net_version_json" | jq -r " + . as \$major | + to_entries[] | + .value | + to_entries[] | + .value.version" | grep -v "null" | sort -u) + + # Create temporary files to store validation results + validated_versions_file=$(mktemp) + version_map_file=$(mktemp) + + # Validate each version + validation_failed=false + while IFS= read -r version; do + if [ -n "$version" ]; then + echo_color_message yellow "🔍 Validating PHP $version..." + # Capture validation result without color codes + if validated_version=$(validate_php_version_with_fallback "$version" 2>/dev/null | tail -n1); then + # Double check that we got a valid version back + if [ -n "$validated_version" ] && [ "$validated_version" != "VALIDATION_FAILED" ]; then + echo "$version:$validated_version" >> "$validated_versions_file" + if [ "$version" != "$validated_version" ]; then + # Escape special characters for sed + escaped_original=$(echo "$version" | sed 's/[[\.*^$(){}?+|/]/\\&/g') + escaped_validated=$(echo "$validated_version" | sed 's/[[\.*^$(){}?+|/]/\\&/g') + echo "s/${escaped_original}/${escaped_validated}/g" >> "$version_map_file" + fi + else + echo_color_message red "❌ Validation failed for PHP $version" + validation_failed=true + fi + else + echo_color_message red "❌ Validation failed for PHP $version" + validation_failed=true + fi + fi + done <<< "$php_versions_raw" + + # Exit if any validation failed + if [ "$validation_failed" = true ]; then + echo_color_message red "❌ One or more PHP versions failed validation. Stopping build." + rm -f "$validated_versions_file" "$version_map_file" + exit 1 + fi + + # Apply version substitutions if any fallbacks were used + processed_json="$php_net_version_json" + if [ -s "$version_map_file" ]; then + echo_color_message yellow "📝 Applying version fallbacks..." + while IFS= read -r substitution; do + processed_json=$(echo "$processed_json" | sed "$substitution") + done < "$version_map_file" + fi + + # Clean up temporary files + rm -f "$validated_versions_file" "$version_map_file" + fi + # Parse the fetched JSON data and transform it to a specific YAML structure using jq and yq. - php_net_yaml_data=$(echo "$php_net_version_json" | jq -r " + php_net_yaml_data=$(echo "$processed_json" | jq -r " { \"php_versions\": [ . as \$major | From 08bb8106b1e743fad40410d444f7ee4c222f7efd Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Mon, 4 Aug 2025 11:37:15 -0500 Subject: [PATCH 25/63] Refine PHP version validation in get-php-versions.sh to preserve warnings - Updated the validation process to capture warnings during PHP version validation. - Ensured that the last line of the validation output is retained for further checks. --- scripts/get-php-versions.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/get-php-versions.sh b/scripts/get-php-versions.sh index ac89505ce..7606694bc 100755 --- a/scripts/get-php-versions.sh +++ b/scripts/get-php-versions.sh @@ -223,8 +223,8 @@ if [ "$SKIP_DOWNLOAD" = false ]; then while IFS= read -r version; do if [ -n "$version" ]; then echo_color_message yellow "🔍 Validating PHP $version..." - # Capture validation result without color codes - if validated_version=$(validate_php_version_with_fallback "$version" 2>/dev/null | tail -n1); then + # Capture validation result while preserving warnings + if validated_version=$(validate_php_version_with_fallback "$version" | tail -n1); then # Double check that we got a valid version back if [ -n "$validated_version" ] && [ "$validated_version" != "VALIDATION_FAILED" ]; then echo "$version:$validated_version" >> "$validated_versions_file" From 205b069dacafbab5cd23f90f6a47f9543a8a9d3c Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Mon, 4 Aug 2025 11:43:46 -0500 Subject: [PATCH 26/63] Refactor PHP version fallback warnings in get-php-versions.sh - Modified the output of PHP version fallback warnings to be sent directly to GitHub Actions. - Ensured error messages for unavailable PHP versions are also formatted for immediate output. - Updated comments to clarify the behavior of validation result capturing. --- scripts/get-php-versions.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/get-php-versions.sh b/scripts/get-php-versions.sh index 7606694bc..74e69c8cc 100755 --- a/scripts/get-php-versions.sh +++ b/scripts/get-php-versions.sh @@ -117,7 +117,8 @@ validate_php_version_with_fallback() { echo_color_message yellow "⚠️ Attempting fallback to PHP $fallback_version..." >&2 if check_dockerhub_php_version "$fallback_version" "cli"; then - echo_color_message yellow "::warning title=PHP Version Fallback::PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." >&2 + # Output GitHub Actions warning immediately + echo "::warning title=PHP Version Fallback::PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." echo_color_message green "✅ Fallback successful: Using PHP $fallback_version" >&2 echo "$fallback_version" # Output to stdout for capture return 0 @@ -128,9 +129,9 @@ validate_php_version_with_fallback() { # If we get here, both original and fallback failed if [ "$fallback_attempted" = true ]; then - echo_color_message red "::error title=PHP Version Unavailable::Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." >&2 + echo "::error title=PHP Version Unavailable::Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." else - echo_color_message red "::error title=PHP Version Unavailable::PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." >&2 + echo "::error title=PHP Version Unavailable::PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." fi return 1 @@ -223,7 +224,7 @@ if [ "$SKIP_DOWNLOAD" = false ]; then while IFS= read -r version; do if [ -n "$version" ]; then echo_color_message yellow "🔍 Validating PHP $version..." - # Capture validation result while preserving warnings + # Capture validation result (warnings output immediately) if validated_version=$(validate_php_version_with_fallback "$version" | tail -n1); then # Double check that we got a valid version back if [ -n "$validated_version" ] && [ "$validated_version" != "VALIDATION_FAILED" ]; then From b46699c24474fd0eee2635deec42cce27d314ac1 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Mon, 4 Aug 2025 11:50:40 -0500 Subject: [PATCH 27/63] Revert change to get-php-versions.sh --- scripts/get-php-versions.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/get-php-versions.sh b/scripts/get-php-versions.sh index 74e69c8cc..ac89505ce 100755 --- a/scripts/get-php-versions.sh +++ b/scripts/get-php-versions.sh @@ -117,8 +117,7 @@ validate_php_version_with_fallback() { echo_color_message yellow "⚠️ Attempting fallback to PHP $fallback_version..." >&2 if check_dockerhub_php_version "$fallback_version" "cli"; then - # Output GitHub Actions warning immediately - echo "::warning title=PHP Version Fallback::PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." + echo_color_message yellow "::warning title=PHP Version Fallback::PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." >&2 echo_color_message green "✅ Fallback successful: Using PHP $fallback_version" >&2 echo "$fallback_version" # Output to stdout for capture return 0 @@ -129,9 +128,9 @@ validate_php_version_with_fallback() { # If we get here, both original and fallback failed if [ "$fallback_attempted" = true ]; then - echo "::error title=PHP Version Unavailable::Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." + echo_color_message red "::error title=PHP Version Unavailable::Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." >&2 else - echo "::error title=PHP Version Unavailable::PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." + echo_color_message red "::error title=PHP Version Unavailable::PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." >&2 fi return 1 @@ -224,8 +223,8 @@ if [ "$SKIP_DOWNLOAD" = false ]; then while IFS= read -r version; do if [ -n "$version" ]; then echo_color_message yellow "🔍 Validating PHP $version..." - # Capture validation result (warnings output immediately) - if validated_version=$(validate_php_version_with_fallback "$version" | tail -n1); then + # Capture validation result without color codes + if validated_version=$(validate_php_version_with_fallback "$version" 2>/dev/null | tail -n1); then # Double check that we got a valid version back if [ -n "$validated_version" ] && [ "$validated_version" != "VALIDATION_FAILED" ]; then echo "$version:$validated_version" >> "$validated_versions_file" From 7e8beaa7503b768c9b7196dba3576a94d24a908f Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Mon, 4 Aug 2025 11:59:07 -0500 Subject: [PATCH 28/63] Add GitHub Actions annotation function to get-php-versions.sh - Introduced a new function to output GitHub Actions workflow commands without color formatting. - Updated PHP version fallback warnings and errors to utilize the new annotation function for direct output. - Refined validation result capturing to ensure proper handling of version checks. --- scripts/get-php-versions.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/scripts/get-php-versions.sh b/scripts/get-php-versions.sh index ac89505ce..1e71bc697 100755 --- a/scripts/get-php-versions.sh +++ b/scripts/get-php-versions.sh @@ -94,6 +94,12 @@ get_previous_patch_version() { fi } +# Add a new function for GitHub Actions annotations (around line 193) +function github_actions_annotation() { + # Output GitHub Actions workflow commands directly without color formatting + echo "$1" >&2 +} + # Validate and potentially fallback a PHP version validate_php_version_with_fallback() { local version="$1" @@ -117,7 +123,8 @@ validate_php_version_with_fallback() { echo_color_message yellow "⚠️ Attempting fallback to PHP $fallback_version..." >&2 if check_dockerhub_php_version "$fallback_version" "cli"; then - echo_color_message yellow "::warning title=PHP Version Fallback::PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." >&2 + # Output GitHub Actions annotation without color formatting + github_actions_annotation "::warning title=PHP Version Fallback::PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." echo_color_message green "✅ Fallback successful: Using PHP $fallback_version" >&2 echo "$fallback_version" # Output to stdout for capture return 0 @@ -128,9 +135,9 @@ validate_php_version_with_fallback() { # If we get here, both original and fallback failed if [ "$fallback_attempted" = true ]; then - echo_color_message red "::error title=PHP Version Unavailable::Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." >&2 + github_actions_annotation "::error title=PHP Version Unavailable::Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." else - echo_color_message red "::error title=PHP Version Unavailable::PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." >&2 + github_actions_annotation "::error title=PHP Version Unavailable::PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." fi return 1 @@ -224,7 +231,7 @@ if [ "$SKIP_DOWNLOAD" = false ]; then if [ -n "$version" ]; then echo_color_message yellow "🔍 Validating PHP $version..." # Capture validation result without color codes - if validated_version=$(validate_php_version_with_fallback "$version" 2>/dev/null | tail -n1); then + if validated_version=$(validate_php_version_with_fallback "$version" | tail -n1); then # Double check that we got a valid version back if [ -n "$validated_version" ] && [ "$validated_version" != "VALIDATION_FAILED" ]; then echo "$version:$validated_version" >> "$validated_versions_file" From 4cbf04c8477e41d3f588f51377ae2cfb5845a4e9 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Mon, 4 Aug 2025 12:17:01 -0500 Subject: [PATCH 29/63] Fix output of GitHub Actions annotation function in get-php-versions.sh - Adjusted the echo command in the github_actions_annotation function to remove redirection to stderr, ensuring proper output formatting for GitHub Actions. --- scripts/get-php-versions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/get-php-versions.sh b/scripts/get-php-versions.sh index 1e71bc697..06b0b5dfa 100755 --- a/scripts/get-php-versions.sh +++ b/scripts/get-php-versions.sh @@ -97,7 +97,7 @@ get_previous_patch_version() { # Add a new function for GitHub Actions annotations (around line 193) function github_actions_annotation() { # Output GitHub Actions workflow commands directly without color formatting - echo "$1" >&2 + echo "$1" } # Validate and potentially fallback a PHP version From 8a0b41c481f6484f9e2f9e656f546a5299726092 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Mon, 4 Aug 2025 12:25:32 -0500 Subject: [PATCH 30/63] Cleaner method for GitHub annotation warnings --- scripts/get-php-versions.sh | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/scripts/get-php-versions.sh b/scripts/get-php-versions.sh index 06b0b5dfa..2cdf2b5b3 100755 --- a/scripts/get-php-versions.sh +++ b/scripts/get-php-versions.sh @@ -96,8 +96,22 @@ get_previous_patch_version() { # Add a new function for GitHub Actions annotations (around line 193) function github_actions_annotation() { - # Output GitHub Actions workflow commands directly without color formatting - echo "$1" + local type="$1" # warning, error, notice, debug + local title="$2" # The title + local message="$3" # The message + + # Output the official workflow command format + echo "::${type} title=${title}::${message}" + + # Add to step summary for better visibility + case "$type" in + warning) + echo "⚠️ **Warning: ${title}** - ${message}" >> $GITHUB_STEP_SUMMARY + ;; + error) + echo "❌ **Error: ${title}** - ${message}" >> $GITHUB_STEP_SUMMARY + ;; + esac } # Validate and potentially fallback a PHP version @@ -124,7 +138,7 @@ validate_php_version_with_fallback() { if check_dockerhub_php_version "$fallback_version" "cli"; then # Output GitHub Actions annotation without color formatting - github_actions_annotation "::warning title=PHP Version Fallback::PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." + github_actions_annotation "warning" "PHP Version Fallback" "PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." echo_color_message green "✅ Fallback successful: Using PHP $fallback_version" >&2 echo "$fallback_version" # Output to stdout for capture return 0 @@ -135,9 +149,9 @@ validate_php_version_with_fallback() { # If we get here, both original and fallback failed if [ "$fallback_attempted" = true ]; then - github_actions_annotation "::error title=PHP Version Unavailable::Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." + github_actions_annotation "error" "PHP Version Unavailable" "Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." else - github_actions_annotation "::error title=PHP Version Unavailable::PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." + github_actions_annotation "error" "PHP Version Unavailable" "PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." fi return 1 From 55c03c5d481148cd91006407ee091f66d225eb9b Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Tue, 19 Aug 2025 14:40:33 -0500 Subject: [PATCH 31/63] Disable warning about secrets because they don't have any secrets to be concerned about --- src/variations/fpm-apache/Dockerfile | 1 + src/variations/fpm-nginx/Dockerfile | 1 + src/variations/unit/Dockerfile | 1 + 3 files changed, 3 insertions(+) diff --git a/src/variations/fpm-apache/Dockerfile b/src/variations/fpm-apache/Dockerfile index e6c4cdcf3..7d39b20ac 100644 --- a/src/variations/fpm-apache/Dockerfile +++ b/src/variations/fpm-apache/Dockerfile @@ -1,3 +1,4 @@ +# check=skip=SecretsUsedInArgOrEnv ARG BASE_OS_VERSION='bookworm' ARG PHP_VERSION='8.4' ARG PHP_VARIATION='fpm-apache' diff --git a/src/variations/fpm-nginx/Dockerfile b/src/variations/fpm-nginx/Dockerfile index a8197b6df..e13625c74 100644 --- a/src/variations/fpm-nginx/Dockerfile +++ b/src/variations/fpm-nginx/Dockerfile @@ -1,3 +1,4 @@ +# check=skip=SecretsUsedInArgOrEnv ARG BASE_OS_VERSION='bookworm' ARG PHP_VERSION='8.4' ARG BASE_IMAGE="php:${PHP_VERSION}-fpm-${BASE_OS_VERSION}" diff --git a/src/variations/unit/Dockerfile b/src/variations/unit/Dockerfile index 88aa98f08..41cc5fb99 100644 --- a/src/variations/unit/Dockerfile +++ b/src/variations/unit/Dockerfile @@ -1,3 +1,4 @@ +# check=skip=SecretsUsedInArgOrEnv ARG BASE_OS_VERSION='bookworm' ARG PHP_VERSION='8.4' ARG PHP_VARIATION='unit' From d344d53c4de3cbebd205e94cf8e6281e7040c5b7 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 20 Aug 2025 18:39:41 -0500 Subject: [PATCH 32/63] Add "Professionally Supported" section to README with service options for Docker and PHP integration --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 899dd531c..76dfcecad 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,13 @@ Experience the ***true difference*** of using these images vs the other options +## Professionally Supported +Are you looking for help on integreating Docker with your PHP application? We have multiple options to help your team out: + +- [Get Managed Hosting](https://serversideup.net/hire-us/): CI/CD design and engineering, managed hosting, guaranteed uptime, any host, any server. +- [Get Professional Help](https://schedule.serversideup.net/team/serversideup/quick-chat-with-jay): Get video + screen-sharing help directly from the core contributors. +- [Get a Full-Stack Development Team](https://serversideup.net/hire-us/): We can build your app from the ground up, or help you with your existing codebase. + ## Usage This repository creates a number of Docker image variations, allowing you to choose exactly what you need. From e4ff084d929b5d299992602bb088b28eb33bc098 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 20 Aug 2025 20:03:43 -0500 Subject: [PATCH 33/63] Add script to fetch latest NGINX versions for different operating systems and update configuration file with new OS versions --- scripts/conf/php-versions-base-config.yml | 36 ++++ scripts/view-nginx-versions.sh | 207 ++++++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100755 scripts/view-nginx-versions.sh diff --git a/scripts/conf/php-versions-base-config.yml b/scripts/conf/php-versions-base-config.yml index e2ea3e695..5e379a872 100644 --- a/scripts/conf/php-versions-base-config.yml +++ b/scripts/conf/php-versions-base-config.yml @@ -58,3 +58,39 @@ php_variations: supported_os: # Alpine with Unit is not supported yet. Submit a PR if you can help (https://github.com/serversideup/docker-php/issues/233) - bullseye - bookworm + +operating_systems: + - family: alpine + versions: + - name: "Alpine 3.16" + version: alpine3.16 + nginx_version: 1.26.1-r2 + - name: "Alpine 3.17" + version: alpine3.17 + nginx_version: 1.26.1-r2 + - name: "Alpine 3.18" + version: alpine3.18 + nginx_version: 1.28.0-r1 + - name: "Alpine 3.19" + version: alpine3.19 + nginx_version: 1.28.0-r1 + - name: "Alpine 3.20" + version: alpine3.20 + nginx_version: 1.28.0-r1 + - name: "Alpine 3.21" + version: alpine3.21 + nginx_version: 1.28.0-r1 + - name: "Alpine 3.22" + version: alpine3.22 + nginx_version: 1.28.0-r1 + - family: debian + versions: + - name: "Debian Bullseye" + version: bullseye + nginx_version: 1.28.0-1~bullseye + - name: "Debian Bookworm" + version: bookworm + nginx_version: 1.28.0-1~bookworm + - name: "Debian Trixie" + version: trixie + nginx_version: 1.28.0-1~trixie \ No newline at end of file diff --git a/scripts/view-nginx-versions.sh b/scripts/view-nginx-versions.sh new file mode 100755 index 000000000..0f9abf2a3 --- /dev/null +++ b/scripts/view-nginx-versions.sh @@ -0,0 +1,207 @@ +#!/bin/bash +################################################### +# Usage: view-nginx-versions.sh [--os ] +################################################### +# This script fetches the latest NGINX versions available for different +# operating systems from the official NGINX repositories. By default, it +# shows all operating systems, but you can filter to a specific OS. + +set -oe pipefail + +########################## +# Configuration +os_config() { + # Resolve config path relative to this script so --help works before SCRIPT_DIR is set + local this_script_dir + this_script_dir="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + local config_file="$this_script_dir/conf/php-versions-base-config.yml" + + if ! command -v yq >/dev/null 2>&1; then + echo "yq is required but not found. Install 'yq' (https://github.com/mikefarah/yq) to continue." 1>&2 + return 1 + fi + if ! command -v jq >/dev/null 2>&1; then + echo "jq is required but not found. Install 'jq' to continue." 1>&2 + return 1 + fi + + yq -r '.operating_systems[] | .family as $f | .versions[] | "\(.version)|\($f)|\(.name)"' "$config_file" \ + | while IFS='|' read -r version family name; do + if [[ "$family" == "alpine" ]]; then + # version comes as alpineX.Y (e.g., alpine3.20) + key="$version" + alpine_num_version="${version#alpine}" + url="http://nginx.org/packages/alpine/v${alpine_num_version}/main/x86_64/" + pattern='nginx-[0-9][^"\n]*\.apk' + else + key="$version" + url="http://nginx.org/packages/debian/dists/${version}/nginx/binary-amd64/Packages" + pattern='^Package: nginx$' + fi + printf '%s|%s|%s|%s\n' "$key" "$name" "$url" "$pattern" + done +} + +########################## +# Functions + +help_menu() { + echo "Usage: $0 [--os ]" + echo + echo "This script fetches the latest NGINX versions available for different" + echo "operating systems from the official NGINX repositories." + echo + echo "Options:" + echo " --os Filter to a specific operating system" + echo " --help, -h Show this help message" + echo + echo "Available Operating Systems:" + os_config | awk -F'|' '{printf " %-12s %s\n", $1, $2}' + echo + echo "Examples:" + echo " $0 # Show all operating systems" + echo " $0 --os alpine3.20 # Show only Alpine 3.20" + echo " $0 --os bookworm # Show only Debian Bookworm" +} + +########################## +# Argument Parsing + +FILTER_OS="" +while [[ "$#" -gt 0 ]]; do + case $1 in + --os) + FILTER_OS="$2" + shift 2 + ;; + --help|-h) + help_menu + exit 0 + ;; + *) + echo "Unknown parameter passed: $1" + help_menu + exit 1 + ;; + esac +done + +########################## +# Environment Settings + +# Script Configurations +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + +# UI Colors +function ui_set_yellow { + printf $'\033[0;33m' +} + +function ui_set_green { + printf $'\033[0;32m' +} + +function ui_set_red { + printf $'\033[0;31m' +} + +function ui_set_blue { + printf $'\033[0;34m' +} + +function ui_set_bold { + printf $'\033[1m' +} + +function ui_reset_colors { + printf "\e[0m" +} + +function echo_color_message (){ + color=$1 + message=$2 + + ui_set_$color + echo "$message" + ui_reset_colors +} + +########################## +# Fetch helpers + +get_alpine_version() { + local url="$1" + local pattern="$2" + + local version=$(curl -s "$url" | grep -o "$pattern" | sort -V | tail -1) + if [[ -n "$version" ]]; then + # Extract version number from package name (e.g., nginx-1.24.0-r7.apk -> 1.24.0-r7) + echo "$version" | sed 's/nginx-\(.*\)\.apk/\1/' + else + echo "Unable to fetch" + fi +} + +get_debian_version() { + local url="$1" + + local version=$(curl -s "$url" \ + | awk 'BEGIN{RS=""; FS="\n"} { pkg=0; ver=""; for (i=1;i<=NF;i++){ if ($i ~ /^Package: nginx$/) pkg=1; if ($i ~ /^Version:/){ split($i,a,": *"); ver=a[2]; } } if (pkg && ver!="") print ver; }' \ + | sort -V | tail -1) + if [[ -n "$version" ]]; then + echo "$version" + else + echo "Unable to fetch" + fi +} + +fetch_nginx_version() { + local os_key="$1" + local os_name="$2" + local url="$3" + local pattern="$4" + + echo_color_message blue "🔍 Fetching NGINX version for $os_name from $url..." + + local version="" + if [[ "$url" == *"alpine"* ]]; then + version=$(get_alpine_version "$url" "$pattern") + else + version=$(get_debian_version "$url") + fi + + ui_set_bold + ui_set_green + printf "%-20s" "$os_name:" + ui_reset_colors + echo " $version" +} + +########################## +# Main script starts here + +echo_color_message yellow "🌐 NGINX Version Checker" +echo + +# If a specific OS is requested, validate it exists +if [[ -n "$FILTER_OS" ]]; then + if ! grep -q "^$FILTER_OS|" < <(os_config); then + echo_color_message red "❌ Unknown operating system: $FILTER_OS" + echo + help_menu + exit 1 + fi +fi + +# Process operating systems +os_config | while IFS='|' read -r os_key os_name url pattern; do + # Skip if filtering and this isn't the requested OS + if [[ -n "$FILTER_OS" && "$os_key" != "$FILTER_OS" ]]; then + continue + fi + + fetch_nginx_version "$os_key" "$os_name" "$url" "$pattern" +done + +echo +echo_color_message green "✅ NGINX version check complete!" \ No newline at end of file From a20fc49808462a76358e914c342f03f107ff5a5c Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 20 Aug 2025 20:07:32 -0500 Subject: [PATCH 34/63] Update view-nginx-versions.sh script comments for clarity on OS filtering options --- scripts/view-nginx-versions.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/view-nginx-versions.sh b/scripts/view-nginx-versions.sh index 0f9abf2a3..30a9bb207 100755 --- a/scripts/view-nginx-versions.sh +++ b/scripts/view-nginx-versions.sh @@ -4,7 +4,8 @@ ################################################### # This script fetches the latest NGINX versions available for different # operating systems from the official NGINX repositories. By default, it -# shows all operating systems, but you can filter to a specific OS. +# shows all operating systems from the base config file, but you can filter +# to a specific OS if needed. set -oe pipefail From 831e272cfac0fa2888faab9611cdfe7cc553e1f2 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 20 Aug 2025 20:07:59 -0500 Subject: [PATCH 35/63] Update view-nginx-versions.sh help menu to clarify source of NGINX version information --- scripts/view-nginx-versions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/view-nginx-versions.sh b/scripts/view-nginx-versions.sh index 30a9bb207..4ca74640b 100755 --- a/scripts/view-nginx-versions.sh +++ b/scripts/view-nginx-versions.sh @@ -50,7 +50,7 @@ help_menu() { echo "Usage: $0 [--os ]" echo echo "This script fetches the latest NGINX versions available for different" - echo "operating systems from the official NGINX repositories." + echo "operating systems from the PHP base config file." echo echo "Options:" echo " --os Filter to a specific operating system" From 178174382006e9049fd5c2d0a5c2ae91ff248be9 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 20 Aug 2025 20:54:24 -0500 Subject: [PATCH 36/63] Add functionality to determine and add family alias tags for Docker images based on the latest OS within the build minor. Update PHP versions configuration to include new base OS versions and variations. --- scripts/assemble-docker-tags.sh | 65 ++++++++++++++++++++- scripts/conf/php-versions-base-config.yml | 70 +++++++++++++++-------- scripts/get-php-versions.sh | 4 +- 3 files changed, 110 insertions(+), 29 deletions(-) diff --git a/scripts/assemble-docker-tags.sh b/scripts/assemble-docker-tags.sh index 60d102eff..675335f08 100755 --- a/scripts/assemble-docker-tags.sh +++ b/scripts/assemble-docker-tags.sh @@ -132,6 +132,23 @@ function is_default_base_os() { [[ "$build_base_os" == "$default_base_os_within_build_minor" ]] } +function is_latest_family_os_for_build_minor() { + [[ "$build_base_os" == "$latest_family_os_within_build_minor" ]] +} + +add_family_alias_if_latest() { + # Emits a family alias tag if the current build base OS is the latest within its family for this minor + local docker_tag_suffix=$1 + if is_latest_family_os_for_build_minor; then + if [[ "$docker_tag_suffix" == *"-$build_base_os" ]]; then + local family_tag_suffix="${docker_tag_suffix%$build_base_os}$build_family" + add_docker_tag "$family_tag_suffix" + elif [[ "$docker_tag_suffix" == "$build_base_os" ]]; then + add_docker_tag "$build_family" + fi + fi +} + function ci_release_is_production_launch() { [[ -z "$DOCKER_TAG_PREFIX" && "$RELEASE_TYPE" == "latest" ]] } @@ -226,9 +243,42 @@ build_minor_version="${build_patch_version%.*}" latest_global_stable_major=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r '[.php_versions[] | select(.major | test("-rc") | not) | .major | tonumber] | max | tostring') latest_global_stable_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg latest_global_stable_major "$latest_global_stable_major" '.php_versions[] | select(.major == $latest_global_stable_major) | .minor_versions | map(select(.minor | test("-rc") | not) | .minor | split(".") | .[1] | tonumber) | max | $latest_global_stable_major + "." + tostring') latest_minor_within_build_major=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_major "$build_major_version" '.php_versions[] | select(.major == $build_major) | .minor_versions | map(select(.minor | test("-rc") | not) | .minor | split(".") | .[1] | tonumber) | max | $build_major + "." + tostring') -latest_patch_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" '.php_versions[] | .minor_versions[] | select(.minor == $build_minor) | .patch_versions | map( split(".") | map(tonumber) ) | max | join(".")') -latest_patch_global=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r '[.php_versions[] | .minor_versions[] | select(.minor | test("-rc") | not) | .patch_versions[] | select(test("-rc") | not) | split(".") | map(tonumber) ] | max | join(".")') -default_base_os_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" '.php_versions[] | .minor_versions[] | select(.minor == $build_minor) | .base_os[] | select(.default == true) | .name') +latest_patch_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" '.php_versions[] | .minor_versions[] | select(.minor == $build_minor) | (.patch_versions // []) | map( split(".") | map(tonumber) ) | max | join(".")') +latest_patch_global=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r '[.php_versions[] | .minor_versions[] | select(.minor | test("-rc") | not) | ((.patch_versions // [])[]) | select(test("-rc") | not) | split(".") | map(tonumber) ] | max | join(".")') +# Determine default base OS within the build minor using operating_systems default family and highest available version +default_base_os_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" ' + . as $root + | ($root.operating_systems[] | select(.default == true) | .family) as $defaultFamily + | ($root.operating_systems[] | select(.family == $defaultFamily) | .versions) as $familyVersions + | ($root.php_versions[] + | .minor_versions[] + | select(.minor == $build_minor) + | .base_os + | map(.name)) as $minorBaseOs + | $familyVersions + | map(select(.version as $v | $minorBaseOs | index($v))) + | max_by(.number) + | .version') + +# Determine the build family (alpine/debian) for the selected base OS +build_family=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg base_os "$build_base_os" ' + .operating_systems[] + | select(([.versions[] | .version] | index($base_os)) != null) + | .family' | head -n1) + +# Determine the latest OS within this family for the current minor +latest_family_os_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" --arg family "$build_family" ' + . as $root + | ($root.operating_systems[] | select(.family == $family) | .versions) as $familyVersions + | ($root.php_versions[] + | .minor_versions[] + | select(.minor == $build_minor) + | .base_os + | map(.name)) as $minorBaseOs + | $familyVersions + | map(select(.version as $v | $minorBaseOs | index($v))) + | max_by(.number) + | .version') check_vars \ "🚨 Missing critical build variable. Check the script logic and logs" \ @@ -251,11 +301,14 @@ echo "Latest Global Minor Version: $latest_global_stable_minor" echo "Latest Minor Version within Build Major: $latest_minor_within_build_major" echo "Latest Patch Version within Build Minor: $latest_patch_within_build_minor" echo "Default Base OS within Build Minor: $default_base_os_within_build_minor" +echo "Build Family: $build_family" +echo "Latest Family OS within Build Minor: $latest_family_os_within_build_minor" echo "Latest Global Patch Version: $latest_patch_global" # Set default tag DOCKER_TAGS="" add_docker_tag "$build_patch_version-$build_variation-$build_base_os" +add_family_alias_if_latest "$build_patch_version-$build_variation-$build_base_os" if is_default_base_os; then add_docker_tag "$build_patch_version-$build_variation" @@ -263,6 +316,7 @@ fi if is_latest_stable_patch_within_build_minor; then add_docker_tag "$build_minor_version-$build_variation-$build_base_os" + add_family_alias_if_latest "$build_minor_version-$build_variation-$build_base_os" if is_default_base_os; then add_docker_tag "$build_minor_version-$build_variation" @@ -270,6 +324,7 @@ if is_latest_stable_patch_within_build_minor; then if is_default_variation; then add_docker_tag "$build_minor_version-$build_base_os" + add_family_alias_if_latest "$build_minor_version-$build_base_os" fi if is_default_base_os && is_default_variation; then @@ -278,6 +333,7 @@ if is_latest_stable_patch_within_build_minor; then if is_latest_minor_within_build_major; then add_docker_tag "$build_major_version-$build_variation-$build_base_os" + add_family_alias_if_latest "$build_major_version-$build_variation-$build_base_os" if is_default_base_os; then add_docker_tag "$build_major_version-$build_variation" @@ -285,6 +341,7 @@ if is_latest_stable_patch_within_build_minor; then if is_default_variation; then add_docker_tag "$build_major_version-$build_base_os" + add_family_alias_if_latest "$build_major_version-$build_base_os" fi if is_default_base_os && is_default_variation; then @@ -294,9 +351,11 @@ if is_latest_stable_patch_within_build_minor; then if is_latest_global_patch; then add_docker_tag "$build_variation-$build_base_os" + add_family_alias_if_latest "$build_variation-$build_base_os" if is_default_variation; then add_docker_tag "$build_base_os" + add_family_alias_if_latest "$build_base_os" fi if is_default_base_os; then diff --git a/scripts/conf/php-versions-base-config.yml b/scripts/conf/php-versions-base-config.yml index 5e379a872..6a249370a 100644 --- a/scripts/conf/php-versions-base-config.yml +++ b/scripts/conf/php-versions-base-config.yml @@ -1,96 +1,116 @@ +php_variations: + - name: cli + default: true + - name: fpm + - name: fpm-apache + supported_os: # Open a discussion on serversideup/php if you want to see Alpine support for fpm-apache (https://github.com/serversideup/docker-php/discussions/66) + - bullseye + - bookworm + - trixie + - name: fpm-nginx + - name: unit + supported_os: # Alpine with Unit is not supported yet. Submit a PR if you can help (https://github.com/serversideup/docker-php/issues/233) + - bullseye + - bookworm + - trixie + php_versions: - major: "7" minor_versions: - minor: "7.4" base_os: - - name: alpine + - name: alpine3.16 - name: bullseye - default: true patch_versions: - 7.4.33 - major: "8" minor_versions: - minor: "8.0" base_os: - - name: alpine + - name: alpine3.16 - name: bullseye - default: true patch_versions: - 8.0.30 - minor: "8.1" base_os: - - name: alpine + - name: alpine3.20 + - name: alpine3.21 + - name: alpine3.22 - name: bookworm - default: true + - name: trixie patch_versions: # - 8.1.28 # Pull latest from Official PHP source - minor: "8.2" base_os: - - name: alpine + - name: alpine3.20 + - name: alpine3.21 + - name: alpine3.22 - name: bookworm - default: true + - name: trixie patch_versions: # - 8.2.18 # Pull latest from Official PHP source - minor: "8.3" base_os: - - name: alpine + - name: alpine3.20 + - name: alpine3.21 + - name: alpine3.22 - name: bookworm - default: true + - name: trixie patch_versions: # - 8.3.6 # Pull latest from Official PHP source - minor: "8.4" base_os: - - name: alpine + - name: alpine3.20 + - name: alpine3.21 + - name: alpine3.22 - name: bookworm - default: true + - name: trixie patch_versions: # - 8.4.1 # Pull latest from Official PHP source -php_variations: - - name: cli - default: true - - name: fpm - - name: fpm-apache - supported_os: # Open a discussion on serversideup/php if you want to see Alpine support for fpm-apache (https://github.com/serversideup/docker-php/discussions/66) - - bullseye - - bookworm - - name: fpm-nginx - - name: unit - supported_os: # Alpine with Unit is not supported yet. Submit a PR if you can help (https://github.com/serversideup/docker-php/issues/233) - - bullseye - - bookworm operating_systems: - family: alpine versions: - name: "Alpine 3.16" version: alpine3.16 + number: 3.16 nginx_version: 1.26.1-r2 - name: "Alpine 3.17" version: alpine3.17 + number: 3.17 nginx_version: 1.26.1-r2 - name: "Alpine 3.18" version: alpine3.18 + number: 3.18 nginx_version: 1.28.0-r1 - name: "Alpine 3.19" version: alpine3.19 + number: 3.19 nginx_version: 1.28.0-r1 - name: "Alpine 3.20" version: alpine3.20 + number: 3.20 nginx_version: 1.28.0-r1 - name: "Alpine 3.21" version: alpine3.21 + number: 3.21 nginx_version: 1.28.0-r1 - name: "Alpine 3.22" version: alpine3.22 + number: 3.22 nginx_version: 1.28.0-r1 - family: debian + default: true versions: - name: "Debian Bullseye" version: bullseye + number: 11 nginx_version: 1.28.0-1~bullseye - name: "Debian Bookworm" version: bookworm + number: 12 nginx_version: 1.28.0-1~bookworm - name: "Debian Trixie" version: trixie + number: 13 nginx_version: 1.28.0-1~trixie \ No newline at end of file diff --git a/scripts/get-php-versions.sh b/scripts/get-php-versions.sh index 9638e18f8..802a69fab 100755 --- a/scripts/get-php-versions.sh +++ b/scripts/get-php-versions.sh @@ -108,6 +108,7 @@ if [ "$SKIP_DOWNLOAD" = false ]; then # Use 'echo' to pass the JSON data to 'jq' merged_json=$(jq -s ' { + php_variations: (.[1].php_variations // []), php_versions: ( .[0].php_versions + .[1].php_versions | group_by(.major) @@ -124,7 +125,8 @@ if [ "$SKIP_DOWNLOAD" = false ]; then ) }) ), - php_variations: (. | map(.php_variations // []) | add) + operating_systems: (.[1].operating_systems // []) + } ' <(echo "$downloaded_and_normalized_json_data") <(echo "$base_json_data")) From 7a7fc6b427e387325413595b3142125932a6a4ed Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Wed, 20 Aug 2025 21:35:25 -0500 Subject: [PATCH 37/63] Add auto-resolution for NGINX version in dev.sh for fpm-nginx builds. Ensure yq is installed and validate NGINX version against the PHP versions configuration file. --- scripts/dev.sh | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/scripts/dev.sh b/scripts/dev.sh index d93de3f5e..860a7b402 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -17,6 +17,7 @@ set -oe pipefail # Script Configurations SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" PROJECT_ROOT_DIR="$(dirname "$SCRIPT_DIR")" +BASE_PHP_VERSIONS_CONFIG_FILE="${BASE_PHP_VERSIONS_CONFIG_FILE:-"$SCRIPT_DIR/conf/php-versions-base-config.yml"}" PHP_BUILD_VERSION="" @@ -94,12 +95,20 @@ build_docker_image() { PLATFORM=$(detect_platform) fi + # Assemble build arguments + local build_args=() + build_args+=(--build-arg PHP_VARIATION="$PHP_BUILD_VARIATION") + build_args+=(--build-arg PHP_VERSION="$PHP_BUILD_VERSION") + build_args+=(--build-arg BASE_OS_VERSION="$PHP_BUILD_BASE_OS") + + if [ -n "$NGINX_VERSION" ] && [ "$PHP_BUILD_VARIATION" = "fpm-nginx" ]; then + build_args+=(--build-arg "NGINX_VERSION=$NGINX_VERSION") + fi + docker buildx build \ "${DOCKER_ADDITIONAL_BUILD_ARGS[@]}" \ --platform "$PLATFORM" \ - --build-arg PHP_VARIATION="$PHP_BUILD_VARIATION" \ - --build-arg PHP_VERSION="$PHP_BUILD_VERSION" \ - --build-arg BASE_OS_VERSION="$PHP_BUILD_BASE_OS" \ + "${build_args[@]}" \ --tag "$build_tag" \ --file "$PROJECT_ROOT_DIR/src/variations/$PHP_BUILD_VARIATION/Dockerfile" \ "$PROJECT_ROOT_DIR" @@ -191,4 +200,23 @@ check_vars \ PHP_BUILD_VERSION \ PHP_BUILD_BASE_OS +# Auto-resolve NGINX version for fpm-nginx if not provided +if [ -z "$NGINX_VERSION" ] && [ "$PHP_BUILD_VARIATION" = "fpm-nginx" ]; then + if ! command -v yq >/dev/null 2>&1; then + echo_color_message red "yq is required but not found. Install 'yq' (https://github.com/mikefarah/yq) to continue." + exit 1 + fi + + NGINX_VERSION=$(BASE_OS="$PHP_BUILD_BASE_OS" yq -r '.operating_systems[].versions[] | select(.version == env(BASE_OS)) | .nginx_version' "$BASE_PHP_VERSIONS_CONFIG_FILE") + + if [ -z "$NGINX_VERSION" ] || [ "$NGINX_VERSION" = "null" ]; then + echo_color_message red "❌ Unable to determine NGINX version for OS '$PHP_BUILD_BASE_OS' from $BASE_PHP_VERSIONS_CONFIG_FILE" + echo + echo "Ensure an entry exists under 'operating_systems' with version: $PHP_BUILD_BASE_OS and a valid 'nginx_version' key." + exit 1 + fi + + echo_color_message green "✅ Using NGINX version '$NGINX_VERSION' for OS '$PHP_BUILD_BASE_OS'" +fi + build_docker_image \ No newline at end of file From 99d7bd4fd3298de9679ad7058a7b8cc4139451fd Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 08:16:13 -0500 Subject: [PATCH 38/63] Update NGINX version for Alpine 3.17 to 1.26.2-r1 and ensure proper formatting for Debian Trixie in php-versions-base-config.yml --- scripts/conf/php-versions-base-config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/conf/php-versions-base-config.yml b/scripts/conf/php-versions-base-config.yml index 6a249370a..95c8f1f55 100644 --- a/scripts/conf/php-versions-base-config.yml +++ b/scripts/conf/php-versions-base-config.yml @@ -78,7 +78,7 @@ operating_systems: - name: "Alpine 3.17" version: alpine3.17 number: 3.17 - nginx_version: 1.26.1-r2 + nginx_version: 1.26.2-r1 - name: "Alpine 3.18" version: alpine3.18 number: 3.18 @@ -113,4 +113,4 @@ operating_systems: - name: "Debian Trixie" version: trixie number: 13 - nginx_version: 1.28.0-1~trixie \ No newline at end of file + nginx_version: 1.28.0-1~trixie From e9d3bb8fdff0d5691d639f669a7edb94e8ea0aa6 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 08:16:37 -0500 Subject: [PATCH 39/63] Renamed NGINX script and added --write mode --- ...ginx-versions.sh => get-nginx-versions.sh} | 102 ++++++++++++++---- 1 file changed, 80 insertions(+), 22 deletions(-) rename scripts/{view-nginx-versions.sh => get-nginx-versions.sh} (66%) diff --git a/scripts/view-nginx-versions.sh b/scripts/get-nginx-versions.sh similarity index 66% rename from scripts/view-nginx-versions.sh rename to scripts/get-nginx-versions.sh index 4ca74640b..a57202526 100755 --- a/scripts/view-nginx-versions.sh +++ b/scripts/get-nginx-versions.sh @@ -1,6 +1,6 @@ #!/bin/bash ################################################### -# Usage: view-nginx-versions.sh [--os ] +# Usage: get-nginx-versions.sh [--os ] [--write] ################################################### # This script fetches the latest NGINX versions available for different # operating systems from the official NGINX repositories. By default, it @@ -21,10 +21,7 @@ os_config() { echo "yq is required but not found. Install 'yq' (https://github.com/mikefarah/yq) to continue." 1>&2 return 1 fi - if ! command -v jq >/dev/null 2>&1; then - echo "jq is required but not found. Install 'jq' to continue." 1>&2 - return 1 - fi + # jq is not required; all updates are handled via yq yq -r '.operating_systems[] | .family as $f | .versions[] | "\(.version)|\($f)|\(.name)"' "$config_file" \ | while IFS='|' read -r version family name; do @@ -47,13 +44,14 @@ os_config() { # Functions help_menu() { - echo "Usage: $0 [--os ]" + echo "Usage: $0 [--os ] [--write]" echo echo "This script fetches the latest NGINX versions available for different" echo "operating systems from the PHP base config file." echo echo "Options:" echo " --os Filter to a specific operating system" + echo " --write Write discovered versions back to the base config (yq)" echo " --help, -h Show this help message" echo echo "Available Operating Systems:" @@ -69,12 +67,17 @@ help_menu() { # Argument Parsing FILTER_OS="" +WRITE_MODE=false while [[ "$#" -gt 0 ]]; do case $1 in --os) FILTER_OS="$2" shift 2 ;; + --write) + WRITE_MODE=true + shift 1 + ;; --help|-h) help_menu exit 0 @@ -92,6 +95,7 @@ done # Script Configurations SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +CONFIG_FILE="$SCRIPT_DIR/conf/php-versions-base-config.yml" # UI Colors function ui_set_yellow { @@ -156,26 +160,65 @@ get_debian_version() { fi } -fetch_nginx_version() { - local os_key="$1" - local os_name="$2" - local url="$3" - local pattern="$4" - - echo_color_message blue "🔍 Fetching NGINX version for $os_name from $url..." - +compute_nginx_version() { + local url="$1" + local pattern="$2" + local version="" if [[ "$url" == *"alpine"* ]]; then version=$(get_alpine_version "$url" "$pattern") else version=$(get_debian_version "$url") fi - - ui_set_bold - ui_set_green - printf "%-20s" "$os_name:" - ui_reset_colors - echo " $version" + + echo "$version" +} + +update_config_nginx_version() { + local version_key="$1" # e.g., alpine3.20 or bookworm + local new_nginx_version="$2" + + if [[ -z "$new_nginx_version" || "$new_nginx_version" == "Unable to fetch" ]]; then + echo_color_message red "⚠️ Skipping update for $version_key (no version found)" + return 0 + fi + + # Preserve comments/spacing: perform a targeted line replacement using awk + # Strategy: when we encounter the specific 'version: ' line, the next + # 'nginx_version:' line in that list item will be replaced with the new value, + # while keeping indentation and any spacing after the colon intact. + local tmp_file + tmp_file="$(mktemp)" + + VERSION_KEY="$version_key" NEW_VER="$new_nginx_version" awk ' + BEGIN { target = ENVIRON["VERSION_KEY"]; replacement = ENVIRON["NEW_VER"]; in_block = 0 } + { + # Detect a version line and check if it matches our target value + if ($0 ~ /^[ \t]*version:[ \t]*/) { + line = $0 + gsub(/^[ \t]*version:[ \t]*/, "", line) + # Extract the first token before a space or comment + split(line, parts, /[ \t#]/) + val = parts[1] + in_block = (val == target) ? 1 : 0 + } + + if (in_block && $0 ~ /^[ \t]*nginx_version:[ \t]*/) { + # Capture exact prefix including key and any spaces after the colon + mpos = match($0, /^[ \t]*nginx_version:[ \t]*/) + if (mpos > 0) { + prefix = substr($0, RSTART, RLENGTH) + print prefix replacement + in_block = 0 + next + } + } + + print $0 + } + ' "$CONFIG_FILE" > "$tmp_file" && mv "$tmp_file" "$CONFIG_FILE" + + echo_color_message green "✍️ Updated nginx_version for $version_key -> $new_nginx_version" } ########################## @@ -201,8 +244,23 @@ os_config | while IFS='|' read -r os_key os_name url pattern; do continue fi - fetch_nginx_version "$os_key" "$os_name" "$url" "$pattern" + echo_color_message blue "🔍 Fetching NGINX version for $os_name from $url..." + version=$(compute_nginx_version "$url" "$pattern") + + ui_set_bold + ui_set_green + printf "%-20s" "$os_name:" + ui_reset_colors + echo " $version" + + if [[ "$WRITE_MODE" == true ]]; then + update_config_nginx_version "$os_key" "$version" + fi done echo -echo_color_message green "✅ NGINX version check complete!" \ No newline at end of file +if [[ "$WRITE_MODE" == true ]]; then + echo_color_message green "✅ NGINX version check and write complete!" +else + echo_color_message green "✅ NGINX version check complete!" +fi \ No newline at end of file From a39020658245174f96521658326ed3d92c2b623e Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 08:19:40 -0500 Subject: [PATCH 40/63] Add documentation for NGINX version management, including commands to view and update versions using the get-nginx-versions.sh script. --- .../docs/2.getting-started/7.contributing.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/content/docs/2.getting-started/7.contributing.md b/docs/content/docs/2.getting-started/7.contributing.md index 30ddda402..bd8532216 100644 --- a/docs/content/docs/2.getting-started/7.contributing.md +++ b/docs/content/docs/2.getting-started/7.contributing.md @@ -85,6 +85,31 @@ We use GitHub Actions exclusively to publish all of our releases. If the image e See `.github/workflows/action_publish-beta-images.yml` for an example of how we publish our beta images. +## NGINX Versions +We use the official NGINX repos to install the latest version of NGINX for each OS. The version to install is set by a build argument, which is loaded from the `scripts/conf/php-versions-base-config.yml` file. + +To view the current NGINX versions, run the following command: + +::code-panel +--- +label: "View NGINX versions" +--- +```bash +./scripts/get-nginx-versions.sh +``` +:: + +This script will look at the official NGINX repos to find the latest version of NGINX for each OS. If you want to update the version, you can run the script with the `--write` flag. + +::code-panel +--- +label: "Update NGINX versions" +--- +```bash +./scripts/get-nginx-versions.sh --write +``` +:: + ## Helping out If you're really eager to help out, here are a few places to get started: - Help answer questions on [our GitHub Discussions](https://github.com/serversideup/docker-php/discussions) and [our Discord](https://serversideup.net/discord) From 56668934c5789ad14aeeea12fa2e4e70e427a97f Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 08:45:36 -0500 Subject: [PATCH 41/63] Refactor to support installing NGINX from a specific version from the official NGINX repos --- src/variations/fpm-nginx/Dockerfile | 98 ++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/src/variations/fpm-nginx/Dockerfile b/src/variations/fpm-nginx/Dockerfile index e13625c74..9a5210ecf 100644 --- a/src/variations/fpm-nginx/Dockerfile +++ b/src/variations/fpm-nginx/Dockerfile @@ -20,42 +20,88 @@ RUN docker-php-serversideup-s6-install #################### # NGINX Signing Key #################### -FROM php:${PHP_VERSION}-fpm AS nginx-repo-config +FROM php:${PHP_VERSION}-fpm-${BASE_OS_VERSION} AS nginx-repo-config ARG SIGNING_KEY_URL="https://nginx.org/keys/nginx_signing.key" ARG SIGNING_FINGERPRINT="573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62" ARG SIGNING_KEY_OUTPUT_FILE="/usr/share/keyrings/nginx-archive-keyring.gpg" +ARG SIGNING_ALPINE_RSA_PUB_SHA256="03833138bf6288dcb545f7a154af24f5c63b94931fef6b078cd8061204a44327" # copy our scripts COPY --chmod=755 src/common/ / -RUN docker-php-serversideup-dep-install-debian "curl gnupg2 ca-certificates lsb-release debian-archive-keyring" && \ - \ - # Import signing key - curl "$SIGNING_KEY_URL" | gpg --dearmor | tee "$SIGNING_KEY_OUTPUT_FILE" && \ - \ - # Verify signing key - VALID_KEY=$(gpg --dry-run --quiet --no-keyring --import --import-options import-show "$SIGNING_KEY_OUTPUT_FILE" | grep "$SIGNING_FINGERPRINT") && \ - \ - if [ -z "$VALID_KEY" ]; then \ - echo "ERROR: Key did not match signing signature!" && \ +# Install NGINX signing key +RUN \ + if [ -f /etc/debian_version ]; then \ + # Install dependencies + apt-get update && \ + apt-get install -y curl gnupg2 ca-certificates lsb-release debian-archive-keyring && \ + \ + # Create GPG home directory + mkdir -p /root/.gnupg && \ + \ + # Import signing key + curl "$SIGNING_KEY_URL" | gpg --dearmor | tee "$SIGNING_KEY_OUTPUT_FILE" && \ + \ + # Verify signing key + VALID_KEY=$(gpg --dry-run --quiet --no-keyring --import --import-options import-show "$SIGNING_KEY_OUTPUT_FILE" | grep "$SIGNING_FINGERPRINT") && \ + if [ -z "$VALID_KEY" ]; then \ + echo "ERROR: Key did not match signing signature!" && \ + exit 1; \ + fi && \ + \ + # Setup apt repository for stable nginx packages + echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian $(lsb_release -cs) nginx" | tee /etc/apt/sources.list.d/nginx.list && \ + \ + # Setup repository pinning to prefer nginx packages over debian packages + printf "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" > /etc/apt/preferences.d/99nginx && \ + \ + # Create dummy APK files so subsequent unconditional COPY does not fail on Debian + mkdir -p /etc/apk/keys && \ + touch /etc/apk/repositories /etc/apk/keys/nginx_signing.rsa.pub; \ + elif [ -f /etc/alpine-release ]; then \ + # Install prerequisites for Alpine + apk add --no-cache openssl curl ca-certificates && \ + \ + # Set up the APK repository for stable NGINX packages + printf "%s%s%s%s%s\n" \ + "@nginx " \ + "http://nginx.org/packages/alpine/v" \ + "$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)" \ + "/main" \ + | tee -a /etc/apk/repositories && \ + \ + # Download the NGINX APK RSA repository key + curl -o /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub && \ + \ + # Verify the key by pinning the SHA-256 of the DER-encoded public key. + # Allow multiple hashes (comma-separated) for rotation via build args. + CALC_SHA256=$(openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -outform DER 2>/dev/null | sha256sum | awk '{print $1}') && \ + printf "%s" "$SIGNING_ALPINE_RSA_PUB_SHA256" | tr ',' '\n' | grep -qx "$CALC_SHA256" || { \ + echo "ERROR: Alpine NGINX RSA key SHA-256 mismatch. Expected one of [$SIGNING_ALPINE_RSA_PUB_SHA256], got: $CALC_SHA256"; \ + exit 1; \ + } && \ + \ + # Move the key to APK's trusted keys directory + mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/ && \ + \ + # Create dummy Debian files so subsequent unconditional COPY does not fail on Alpine + mkdir -p /usr/share/keyrings /etc/apt/sources.list.d /etc/apt/preferences.d && \ + touch /usr/share/keyrings/nginx-archive-keyring.gpg /etc/apt/sources.list.d/nginx.list /etc/apt/preferences.d/99nginx; \ + else \ + echo "ERROR: Unsupported or undetected OS. Only Debian-based and Alpine-based systems are handled." && \ exit 1; \ - fi && \ - \ - # setup apt repository for stable nginx packages - echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian `lsb_release -cs` nginx" | tee /etc/apt/sources.list.d/nginx.list && \ - \ - # setup repository pinning to prefer nginx packages over debian packages - printf "Package: *\nPin: origin nginx.org\nPin-Priority: 900\n" > /etc/apt/preferences.d/99nginx + fi ########## # FPM-NGINX: Main Image ########## FROM ${BASE_IMAGE} -ARG DEPENDENCY_PACKAGES_ALPINE='fcgi nginx gettext shadow' -ARG DEPENDENCY_PACKAGES_DEBIAN='libfcgi-bin nginx gettext-base procps zip' +ARG DEPENDENCY_PACKAGES_ALPINE='fcgi gettext shadow' +ARG DEPENDENCY_PACKAGES_DEBIAN='libfcgi-bin gettext-base procps zip' ARG DEPENDENCY_PHP_EXTENSIONS='opcache pcntl pdo_mysql pdo_pgsql redis zip' ARG REPOSITORY_BUILD_VERSION='dev' +ARG NGINX_VERSION='1.28.0-1' LABEL org.opencontainers.image.title="serversideup/php (fpm-nginx)" \ org.opencontainers.image.description="Supercharge your PHP experience. Based off the official PHP images, serversideup/php includes pre-configured PHP extensions and settings for enhanced performance and security. Optimized for Laravel and WordPress." \ @@ -125,10 +171,12 @@ COPY --from=s6-build /usr/local/bin/php-fpm-healthcheck /usr/local/bin/php-fpm-h COPY --from=nginx-repo-config /usr/share/keyrings/nginx-archive-keyring.gpg /usr/share/keyrings/ COPY --from=nginx-repo-config /etc/apt/sources.list.d/nginx.list /etc/apt/sources.list.d/ COPY --from=nginx-repo-config /etc/apt/preferences.d/99nginx /etc/apt/preferences.d/ +COPY --from=nginx-repo-config /etc/apk/repositories /etc/apk/repositories +COPY --from=nginx-repo-config /etc/apk/keys/nginx_signing.rsa.pub /etc/apk/keys/ # install pecl extensions, dependencies, and clean up -RUN docker-php-serversideup-dep-install-alpine "${DEPENDENCY_PACKAGES_ALPINE}" && \ - docker-php-serversideup-dep-install-debian "${DEPENDENCY_PACKAGES_DEBIAN}" && \ +RUN docker-php-serversideup-dep-install-alpine "${DEPENDENCY_PACKAGES_ALPINE} nginx@nginx=${NGINX_VERSION}" && \ + docker-php-serversideup-dep-install-debian "${DEPENDENCY_PACKAGES_DEBIAN} nginx=${NGINX_VERSION}" && \ docker-php-serversideup-install-php-ext-installer && \ \ # Ensure /var/www/ has the correct permissions @@ -157,10 +205,14 @@ RUN docker-php-serversideup-dep-install-alpine "${DEPENDENCY_PACKAGES_ALPINE}" & rm -rf /etc/nginx/http.d/ && \ rm /etc/nginx/nginx.conf && \ \ - # Docker doesn't support conditional COPY, so we have to remove the apt directory if we're on Alpine + # Docker doesn't support conditional COPY, so remove OS-specific repo/key artifacts that were copied unconditionally if cat /etc/os-release | grep -qi 'Alpine'; then \ + # On Alpine, remove Debian APT artifacts rm -rf /usr/share/keyrings/ && \ rm -rf /etc/apt/; \ + else \ + # On Debian, remove Alpine APK artifacts + rm -rf /etc/apk/; \ fi # Copy our nginx configurations From 83761e225d8aa9c5bac4841dafc1f0abc410d457 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 08:45:44 -0500 Subject: [PATCH 42/63] Add NGINX repository key verification details for Debian and Alpine, including instructions for hash computation and build arguments for key rotation. --- .../docs/2.getting-started/7.contributing.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/content/docs/2.getting-started/7.contributing.md b/docs/content/docs/2.getting-started/7.contributing.md index bd8532216..54547ea6f 100644 --- a/docs/content/docs/2.getting-started/7.contributing.md +++ b/docs/content/docs/2.getting-started/7.contributing.md @@ -110,6 +110,39 @@ label: "Update NGINX versions" ``` :: +### NGINX repository key verification + +- **Debian (APT)**: We import the official NGINX GPG key from `https://nginx.org/keys/nginx_signing.key` and verify it against a pinned fingerprint via the `SIGNING_FINGERPRINT` build arg. +- **Alpine (APK)**: APK uses a raw RSA public key (`nginx_signing.rsa.pub`). We verify this key by pinning the SHA‑256 of the DER‑encoded public key via the `SIGNING_ALPINE_RSA_PUB_SHA256` build arg. You can provide multiple comma‑separated hashes to support key rotation. + +Compute the Alpine key hash when updating: + +```bash +curl -sS https://nginx.org/keys/nginx_signing.rsa.pub -o /tmp/nginx_signing.rsa.pub +# macOS +openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -outform DER 2>/dev/null | shasum -a 256 | awk '{print $1}' +# Linux +openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -outform DER 2>/dev/null | sha256sum | awk '{print $1}' +``` + +Then build with the new hash (optionally include the old hash during rotation): + +```bash +docker build \ + --build-arg SIGNING_ALPINE_RSA_PUB_SHA256="," \ + -f src/variations/fpm-nginx/Dockerfile . +``` + +Reference: [Installing NGINX Open Source → Alpine packages](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/#prebuilt_alpine). + +Why allow multiple hashes? This is optional, but useful during a short rotation window: + +- Ensure CI builds across branches/runners succeed while the upstream key change propagates. +- Avoid flakes from CDN/caching delays where some environments still see the old key. +- Let you pre-stage the new value before the official switch, then remove the old afterwards. + +If you control all builds centrally and can update quickly, pass a single hash. + ## Helping out If you're really eager to help out, here are a few places to get started: - Help answer questions on [our GitHub Discussions](https://github.com/serversideup/docker-php/discussions) and [our Discord](https://serversideup.net/discord) From 85b4543e4743cfab1d42b58660dfeb48b3acf7c0 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 08:45:52 -0500 Subject: [PATCH 43/63] Update NGINX directory paths in docker-php-serversideup-set-file-permissions script to include /var/cache/nginx for improved file permission management. --- .../usr/local/bin/docker-php-serversideup-set-file-permissions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions index 4139a9f9e..d31c1950a 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions +++ b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions @@ -102,7 +102,7 @@ case "$OS" in DIRS="/etc/apache2 /etc/ssl/private /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; nginx) - DIRS="/etc/nginx/ /var/log/nginx /etc/ssl/private /var/lib/nginx /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS="/etc/nginx/ /var/log/nginx /etc/ssl/private /var/cache/nginx /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; *) echo "$script_name: Unsupported SERVICE: $SERVICE" From 238e208a358a0206a7da3477e1cd109c47aa0df5 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 08:55:45 -0500 Subject: [PATCH 44/63] Add dockerhub validation --- scripts/get-php-versions.sh | 212 +++++++++++++++++++++++++++++++++++- 1 file changed, 211 insertions(+), 1 deletion(-) diff --git a/scripts/get-php-versions.sh b/scripts/get-php-versions.sh index 802a69fab..bda9d8b03 100755 --- a/scripts/get-php-versions.sh +++ b/scripts/get-php-versions.sh @@ -1,14 +1,27 @@ #!/bin/bash ################################################### -# Usage: get-php-versions.sh [--skip-download] +# Usage: get-php-versions.sh [--skip-download] [--skip-dockerhub-validation] ################################################### # This file takes the official latest PHP releases from php.net merges them with our # "base php configuration". These files get merged into a final file called "php-versions.yml" # which is used to build our GitHub Actions jobs. # +# 🔍 DOCKERHUB VALIDATION & FALLBACK +# By default, this script validates that each PHP version from php.net is actually available +# on DockerHub before including it in the final configuration. If a version is not available: +# 1. The script attempts to fall back to the previous patch version (e.g., 8.3.24 -> 8.3.23) +# 2. A GitHub Actions warning is displayed explaining the fallback +# 3. If the fallback version is also unavailable, the script exits with an error +# +# This ensures that Docker builds won't fail due to non-existent base images. +# # 👉 REQUIRED FILES # - BASE_PHP_VERSIONS_CONFIG_FILE must be valid and set to a valid file path # (defaults to scripts/conf/php-versions-base-config.yml) +# +# 👉 OPTIONS +# --skip-download: Skip downloading from php.net and use existing base config +# --skip-dockerhub-validation: Skip DockerHub validation (useful for testing/development) set -oue pipefail @@ -16,13 +29,144 @@ set -oue pipefail # set -x # trap read DEBUG +########################## +# DockerHub API Functions + +# Check if a PHP version exists on DockerHub +check_dockerhub_php_version() { + local version="$1" + local variant="${2:-cli}" + local os="${3:-}" + + local image_tag + if [ -n "$os" ] && [ "$os" != "bullseye" ] && [ "$os" != "bookworm" ]; then + image_tag="${version}-${variant}-${os}" + else + image_tag="${version}-${variant}" + fi + + # Use Docker Hub API v2 to check if the tag exists with timeout and retry + local response + local max_retries=3 + local retry_count=0 + + while [ $retry_count -lt $max_retries ]; do + response=$(curl -s --max-time 10 --connect-timeout 5 \ + -o /dev/null -w "%{http_code}" \ + "https://registry.hub.docker.com/v2/repositories/library/php/tags/${image_tag}/") + + # Check if we got a valid HTTP response + if [ "$response" = "200" ]; then + return 0 # Version exists + elif [ "$response" = "404" ]; then + return 1 # Version definitely does not exist + else + # Network error or other issue, retry + retry_count=$((retry_count + 1)) + if [ $retry_count -lt $max_retries ]; then + echo_color_message yellow "⚠️ DockerHub API request failed (HTTP $response), retrying in 2 seconds..." + sleep 2 + fi + fi + done + + # If we get here, all retries failed + echo_color_message red "❌ Failed to check DockerHub after $max_retries attempts for $image_tag" + return 1 +} + +# Get previous patch version (e.g., 8.3.24 -> 8.3.23) +get_previous_patch_version() { + local version="$1" + local major_minor patch + + # Split version into major.minor and patch + major_minor=$(echo "$version" | cut -d'.' -f1-2) + patch=$(echo "$version" | cut -d'.' -f3) + + # Decrement patch version + if [ "$patch" -gt 0 ]; then + patch=$((patch - 1)) + echo "${major_minor}.${patch}" + else + # If patch is 0, we can't go lower + return 1 + fi +} + +# Add a new function for GitHub Actions annotations (around line 193) +function github_actions_annotation() { + local type="$1" # warning, error, notice, debug + local title="$2" # The title + local message="$3" # The message + + # Output the official workflow command format + echo "::${type} title=${title}::${message}" + + # Add to step summary for better visibility + case "$type" in + warning) + echo "⚠️ **Warning: ${title}** - ${message}" >> $GITHUB_STEP_SUMMARY + ;; + error) + echo "❌ **Error: ${title}** - ${message}" >> $GITHUB_STEP_SUMMARY + ;; + esac +} + +# Validate and potentially fallback a PHP version +validate_php_version_with_fallback() { + local version="$1" + local original_version="$version" + local fallback_attempted=false + + echo_color_message yellow "🔍 Checking PHP version $version on DockerHub..." >&2 + + # Check if the version exists on DockerHub (using cli variant as reference) + if check_dockerhub_php_version "$version" "cli"; then + echo_color_message green "✅ PHP $version is available on DockerHub" >&2 + echo "$version" # Output to stdout for capture + return 0 + else + echo_color_message red "❌ PHP $version is not available on DockerHub" >&2 + + # Try to get previous patch version + local fallback_version + if fallback_version=$(get_previous_patch_version "$version"); then + fallback_attempted=true + echo_color_message yellow "⚠️ Attempting fallback to PHP $fallback_version..." >&2 + + if check_dockerhub_php_version "$fallback_version" "cli"; then + # Output GitHub Actions annotation without color formatting + github_actions_annotation "warning" "PHP Version Fallback" "PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." + echo_color_message green "✅ Fallback successful: Using PHP $fallback_version" >&2 + echo "$fallback_version" # Output to stdout for capture + return 0 + else + echo_color_message red "❌ Fallback version PHP $fallback_version is also not available on DockerHub" >&2 + fi + fi + + # If we get here, both original and fallback failed + if [ "$fallback_attempted" = true ]; then + github_actions_annotation "error" "PHP Version Unavailable" "Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." + else + github_actions_annotation "error" "PHP Version Unavailable" "PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." + fi + + return 1 + fi +} + ########################## # Argument Parsing SKIP_DOWNLOAD="${SKIP_DOWNLOAD:-false}" +SKIP_DOCKERHUB_VALIDATION="${SKIP_DOCKERHUB_VALIDATION:-false}" while [[ "$#" -gt 0 ]]; do case $1 in --skip-download) SKIP_DOWNLOAD=true ;; + --skip-dockerhub-validation) SKIP_DOCKERHUB_VALIDATION=true ;; *) echo "Unknown parameter passed: $1"; exit 1 ;; esac shift @@ -76,6 +220,72 @@ if [ "$SKIP_DOWNLOAD" = false ]; then # Fetch the JSON from the PHP website php_net_version_json=$(curl -s $PHP_VERSIONS_ACTIVE_JSON_FEED) + # Parse the fetched JSON data and optionally validate PHP versions on DockerHub + if [ "$SKIP_DOCKERHUB_VALIDATION" = true ]; then + echo_color_message yellow "⚠️ Skipping DockerHub validation as requested..." + processed_json="$php_net_version_json" + else + echo_color_message yellow "🔍 Parsing and validating PHP versions from php.net..." + + # First, extract versions from the JSON + php_versions_raw=$(echo "$php_net_version_json" | jq -r " + . as \$major | + to_entries[] | + .value | + to_entries[] | + .value.version" | grep -v "null" | sort -u) + + # Create temporary files to store validation results + validated_versions_file=$(mktemp) + version_map_file=$(mktemp) + + # Validate each version + validation_failed=false + while IFS= read -r version; do + if [ -n "$version" ]; then + echo_color_message yellow "🔍 Validating PHP $version..." + # Capture validation result without color codes + if validated_version=$(validate_php_version_with_fallback "$version" | tail -n1); then + # Double check that we got a valid version back + if [ -n "$validated_version" ] && [ "$validated_version" != "VALIDATION_FAILED" ]; then + echo "$version:$validated_version" >> "$validated_versions_file" + if [ "$version" != "$validated_version" ]; then + # Escape special characters for sed + escaped_original=$(echo "$version" | sed 's/[[\.*^$(){}?+|/]/\\&/g') + escaped_validated=$(echo "$validated_version" | sed 's/[[\.*^$(){}?+|/]/\\&/g') + echo "s/${escaped_original}/${escaped_validated}/g" >> "$version_map_file" + fi + else + echo_color_message red "❌ Validation failed for PHP $version" + validation_failed=true + fi + else + echo_color_message red "❌ Validation failed for PHP $version" + validation_failed=true + fi + fi + done <<< "$php_versions_raw" + + # Exit if any validation failed + if [ "$validation_failed" = true ]; then + echo_color_message red "❌ One or more PHP versions failed validation. Stopping build." + rm -f "$validated_versions_file" "$version_map_file" + exit 1 + fi + + # Apply version substitutions if any fallbacks were used + processed_json="$php_net_version_json" + if [ -s "$version_map_file" ]; then + echo_color_message yellow "📝 Applying version fallbacks..." + while IFS= read -r substitution; do + processed_json=$(echo "$processed_json" | sed "$substitution") + done < "$version_map_file" + fi + + # Clean up temporary files + rm -f "$validated_versions_file" "$version_map_file" + fi + # Parse the fetched JSON data and transform it to a specific YAML structure using jq and yq. php_net_yaml_data=$(echo "$php_net_version_json" | jq -r " { From 0f41ccafa8c686a08bda9cdd81422de4d6a7bfe3 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 09:06:27 -0500 Subject: [PATCH 45/63] Implement NGINX version computation for fpm-nginx builds in Docker workflow, enhancing version resolution using yq or awk. Remove unused AWS runner configuration and cache settings. --- .../service_docker-build-and-publish.yml | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/service_docker-build-and-publish.yml b/.github/workflows/service_docker-build-and-publish.yml index 769f88cbe..e0350f269 100644 --- a/.github/workflows/service_docker-build-and-publish.yml +++ b/.github/workflows/service_docker-build-and-publish.yml @@ -79,11 +79,6 @@ jobs: docker-publish: needs: setup-matrix runs-on: ubuntu-24.04 - ## Use AWS runners - # runs-on: - # - runs-on - # - runner=4cpu-linux-x64 - # - run-id=${{ github.run_id }} strategy: matrix: ${{fromJson(needs.setup-matrix.outputs.php-version-map-json)}} @@ -161,20 +156,33 @@ jobs: echo "REPOSITORY_BUILD_VERSION=git-${SHORT_SHA}-${{ github.run_id }}" >> $GITHUB_ENV fi + - name: Compute NGINX build-arg (only for fpm-nginx) + id: compute_nginx + if: ${{ matrix.php_variation == 'fpm-nginx' }} + run: | + if command -v yq >/dev/null 2>&1; then + VERSION=$(yq -r '.operating_systems[].versions[] | select(.version == "${{ matrix.base_os }}") | .nginx_version' '${{ inputs.php-versions-file }}' | head -n1) + else + VERSION=$(awk -v key="${{ matrix.base_os }}" 'BEGIN{found=0} $1=="version:" && $2==key {found=1} found && $1=="nginx_version:" {print $2; exit}' "${{ inputs.php-versions-file }}") + fi + if [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then + echo "Unable to determine NGINX version for OS ${{ matrix.base_os }}" 1>&2 + exit 1 + fi + echo "nginx_arg=NGINX_VERSION=$VERSION" >> $GITHUB_OUTPUT + - name: Build images uses: docker/build-push-action@v6 with: file: src/variations/${{ matrix.php_variation }}/Dockerfile cache-from: type=gha cache-to: type=gha - ## Run-on cache - # cache-from: type=s3,blobs_prefix=cache/${{ github.repository }}/,manifests_prefix=cache/${{ github.repository }}/,region=${{ env.RUNS_ON_AWS_REGION }},bucket=${{ env.RUNS_ON_S3_BUCKET_CACHE }} - # cache-to: type=s3,blobs_prefix=cache/${{ github.repository }}/,manifests_prefix=cache/${{ github.repository }}/,region=${{ env.RUNS_ON_AWS_REGION }},bucket=${{ env.RUNS_ON_S3_BUCKET_CACHE }},mode=max build-args: | BASE_OS_VERSION=${{ matrix.base_os }} PHP_VERSION=${{ matrix.patch_version }} PHP_VARIATION=${{ matrix.php_variation }} REPOSITORY_BUILD_VERSION=${{ env.REPOSITORY_BUILD_VERSION }} + ${{ steps.compute_nginx.outputs.nginx_arg }} platforms: | linux/amd64 linux/arm64/v8 From 6e1703a2e7a614c5fb60e7c3a1a7375cff9c874a Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 09:10:39 -0500 Subject: [PATCH 46/63] Added Depot CI runners --- .github/workflows/service_docker-build-and-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/service_docker-build-and-publish.yml b/.github/workflows/service_docker-build-and-publish.yml index e0350f269..04ca2e03f 100644 --- a/.github/workflows/service_docker-build-and-publish.yml +++ b/.github/workflows/service_docker-build-and-publish.yml @@ -78,7 +78,7 @@ jobs: docker-publish: needs: setup-matrix - runs-on: ubuntu-24.04 + runs-on: depot-ubuntu-24.04-4 strategy: matrix: ${{fromJson(needs.setup-matrix.outputs.php-version-map-json)}} From a711abb0dcae14bfcb8f48eddd19449fad7f660a Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 10:14:41 -0500 Subject: [PATCH 47/63] Adjust filtering logic to supported "latest OS" within the "supported OS" filter --- scripts/assemble-docker-tags.sh | 53 ++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/scripts/assemble-docker-tags.sh b/scripts/assemble-docker-tags.sh index 675335f08..4ea8161da 100755 --- a/scripts/assemble-docker-tags.sh +++ b/scripts/assemble-docker-tags.sh @@ -117,7 +117,7 @@ function is_latest_stable_patch_within_build_minor() { } function is_latest_global_patch() { - [[ "$build_patch_version" == $latest_patch_global && "$build_patch_version" != *"rc"* ]] + [[ "$build_patch_version" == "$latest_patch_global" && "$build_patch_version" != *"rc"* ]] } function is_latest_minor_within_build_major() { @@ -129,11 +129,11 @@ function is_latest_major() { } function is_default_base_os() { - [[ "$build_base_os" == "$default_base_os_within_build_minor" ]] + [[ "$build_base_os" == "$default_supported_base_os_within_build_minor" ]] } function is_latest_family_os_for_build_minor() { - [[ "$build_base_os" == "$latest_family_os_within_build_minor" ]] + [[ "$build_base_os" == "$latest_family_supported_os_within_build_minor" ]] } add_family_alias_if_latest() { @@ -280,6 +280,49 @@ latest_family_os_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r -- | max_by(.number) | .version') +# Determine the default base OS within the build minor considering the variation's supported_os +default_supported_base_os_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" --arg variation "$build_variation" ' + . as $root + | ($root.php_variations[] | select(.name == $variation) | (.supported_os // [])) as $supported + | ($root.operating_systems[] | select(.default == true) | .family) as $defaultFamily + | ($root.operating_systems[] | select(.family == $defaultFamily) | .versions) as $familyVersions + | ($root.php_versions[] + | .minor_versions[] + | select(.minor == $build_minor) + | .base_os + | map(.name)) as $minorBaseOs + | $familyVersions + | map(select( + .version as $v + | ($minorBaseOs | index($v)) != null + and ( + ($supported | length) == 0 + or any($supported[]; . == $v or (. == "alpine" and ($v | startswith("alpine")))) + ) + )) + | if length > 0 then (max_by(.number) | .version) else empty end') + +# Determine the latest OS within this family for the current minor considering the variation's supported_os +latest_family_supported_os_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" --arg family "$build_family" --arg variation "$build_variation" ' + . as $root + | ($root.php_variations[] | select(.name == $variation) | (.supported_os // [])) as $supported + | ($root.operating_systems[] | select(.family == $family) | .versions) as $familyVersions + | ($root.php_versions[] + | .minor_versions[] + | select(.minor == $build_minor) + | .base_os + | map(.name)) as $minorBaseOs + | $familyVersions + | map(select( + .version as $v + | ($minorBaseOs | index($v)) != null + and ( + ($supported | length) == 0 + or any($supported[]; . == $v or (. == "alpine" and ($v | startswith("alpine")))) + ) + )) + | if length > 0 then (max_by(.number) | .version) else empty end') + check_vars \ "🚨 Missing critical build variable. Check the script logic and logs" \ build_patch_version \ @@ -303,6 +346,8 @@ echo "Latest Patch Version within Build Minor: $latest_patch_within_build_minor" echo "Default Base OS within Build Minor: $default_base_os_within_build_minor" echo "Build Family: $build_family" echo "Latest Family OS within Build Minor: $latest_family_os_within_build_minor" +echo "Default Supported Base OS within Build Minor: ${default_supported_base_os_within_build_minor:-}" +echo "Latest Supported Family OS within Build Minor: ${latest_family_supported_os_within_build_minor:-}" echo "Latest Global Patch Version: $latest_patch_global" # Set default tag @@ -377,5 +422,5 @@ echo_color_message green "🚀 Summary of Docker Tags Being Shipped: $DOCKER_TAG # Save to GitHub's environment if [[ $CI == "true" ]]; then echo "DOCKER_TAGS=${DOCKER_TAGS}" >> $GITHUB_ENV - echo_color_message green "✅ Saved Docker Tags to "GITHUB_ENV"" + echo_color_message green "✅ Saved Docker Tags to GITHUB_ENV" fi \ No newline at end of file From 304c52788b33ec60dafcaf7b50761b4ad0386425 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 10:15:05 -0500 Subject: [PATCH 48/63] Move matrix generation to a script --- .../service_docker-build-and-publish.yml | 2 +- scripts/generate-matrix.sh | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 scripts/generate-matrix.sh diff --git a/.github/workflows/service_docker-build-and-publish.yml b/.github/workflows/service_docker-build-and-publish.yml index 04ca2e03f..478135569 100644 --- a/.github/workflows/service_docker-build-and-publish.yml +++ b/.github/workflows/service_docker-build-and-publish.yml @@ -66,7 +66,7 @@ jobs: - name: Assemble PHP versions into the matrix. 😎 id: get-php-versions run: | - MATRIX_JSON=$(yq -o=json scripts/conf/php-versions.yml | jq -c '{include: [(.php_variations[] | {name, supported_os: (.supported_os // ["alpine", "bullseye", "bookworm"])} ) as $variation | .php_versions[] | .minor_versions[] | .patch_versions[] as $patch | .base_os[] as $os | select($variation.supported_os | if length == 0 then . else . | index($os.name) end) | {patch_version: $patch, base_os: $os.name, php_variation: $variation.name}]} | {include: (.include | sort_by(.patch_version | split(".") | map(tonumber) | . as $nums | ($nums[0]*10000 + $nums[1]*100 + $nums[2])) | reverse)}') + MATRIX_JSON=$(bash ./scripts/generate-matrix.sh '${{ inputs.php-versions-file }}') echo "php-version-map-json=${MATRIX_JSON}" >> $GITHUB_OUTPUT echo "${MATRIX_JSON}" | jq '.' diff --git a/scripts/generate-matrix.sh b/scripts/generate-matrix.sh new file mode 100644 index 000000000..765c6cf2a --- /dev/null +++ b/scripts/generate-matrix.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Usage: generate-matrix.sh [path/to/php-versions.yml] +# Reads the provided YAML (or $PHP_VERSIONS_FILE, or default scripts/conf/php-versions.yml) +# and prints a GitHub Actions matrix JSON of the form {"include": [...]} + +PHP_VERSIONS_FILE="${1:-${PHP_VERSIONS_FILE:-scripts/conf/php-versions.yml}}" + +if [ ! -f "$PHP_VERSIONS_FILE" ]; then + echo "YAML file not found: $PHP_VERSIONS_FILE" >&2 + exit 1 +fi + +# Convert YAML to JSON, then shape it with jq. +yq -o=json "$PHP_VERSIONS_FILE" | jq -c ' + + def version_weight: + # Convert "x.y.z" into a sortable integer weight + split(".") | map(tonumber) as $nums | ($nums[0]*10000 + $nums[1]*100 + $nums[2]); + + def os_family_match($os_name; $supported): + # Allow listing "alpine" to include any alpine3.xx base_os + # Exact matches like "bullseye", "bookworm", "trixie" must match exactly + ($supported == $os_name) or ($supported == "alpine" and ($os_name | startswith("alpine"))); + + def is_supported($variation; $os): + # If no supported_os specified, allow all; otherwise filter + (($variation.supported_os // []) | length) == 0 or + ((($variation.supported_os // []) | any(os_family_match($os.name; .)))); + + . as $root + | [ ($root.php_variations[] | {name, supported_os}) as $variation + | $root.php_versions[] + | .minor_versions[] + | .base_os[] as $os + | .patch_versions[] as $patch + | select(is_supported($variation; $os)) + | {patch_version: $patch, base_os: $os.name, php_variation: $variation.name} + ] + | { include: ( . | sort_by(.patch_version | version_weight) | reverse ) } +' + + From b015a35bfdba30af38fb1860f1c8206a6d78b9c6 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 10:15:16 -0500 Subject: [PATCH 49/63] Remove Trixie support for Unit --- scripts/conf/php-versions-base-config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/conf/php-versions-base-config.yml b/scripts/conf/php-versions-base-config.yml index 95c8f1f55..4ad994d96 100644 --- a/scripts/conf/php-versions-base-config.yml +++ b/scripts/conf/php-versions-base-config.yml @@ -12,7 +12,6 @@ php_variations: supported_os: # Alpine with Unit is not supported yet. Submit a PR if you can help (https://github.com/serversideup/docker-php/issues/233) - bullseye - bookworm - - trixie php_versions: - major: "7" From 4f7d9fab1f2a281dbb3222fb6e1cb81990a31fb5 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 10:18:17 -0500 Subject: [PATCH 50/63] Remove support for Alpine 3.20 in PHP 8.4 --- scripts/conf/php-versions-base-config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/conf/php-versions-base-config.yml b/scripts/conf/php-versions-base-config.yml index 4ad994d96..72a40e0ac 100644 --- a/scripts/conf/php-versions-base-config.yml +++ b/scripts/conf/php-versions-base-config.yml @@ -59,7 +59,6 @@ php_versions: # - 8.3.6 # Pull latest from Official PHP source - minor: "8.4" base_os: - - name: alpine3.20 - name: alpine3.21 - name: alpine3.22 - name: bookworm From c0e0f82fd6317bca60b80a73d93dc14991730e9b Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 10:24:18 -0500 Subject: [PATCH 51/63] Removed Alpine 3.20 --- scripts/conf/php-versions-base-config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/conf/php-versions-base-config.yml b/scripts/conf/php-versions-base-config.yml index 72a40e0ac..2cff3dd33 100644 --- a/scripts/conf/php-versions-base-config.yml +++ b/scripts/conf/php-versions-base-config.yml @@ -32,7 +32,6 @@ php_versions: - 8.0.30 - minor: "8.1" base_os: - - name: alpine3.20 - name: alpine3.21 - name: alpine3.22 - name: bookworm @@ -41,7 +40,6 @@ php_versions: # - 8.1.28 # Pull latest from Official PHP source - minor: "8.2" base_os: - - name: alpine3.20 - name: alpine3.21 - name: alpine3.22 - name: bookworm @@ -50,7 +48,6 @@ php_versions: # - 8.2.18 # Pull latest from Official PHP source - minor: "8.3" base_os: - - name: alpine3.20 - name: alpine3.21 - name: alpine3.22 - name: bookworm From 306bf0c1bc41a36891fbd559e75fb257c6dc2dc9 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 11:20:58 -0500 Subject: [PATCH 52/63] Added trixie support to Unit --- scripts/conf/php-versions-base-config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/conf/php-versions-base-config.yml b/scripts/conf/php-versions-base-config.yml index 2cff3dd33..3c770475e 100644 --- a/scripts/conf/php-versions-base-config.yml +++ b/scripts/conf/php-versions-base-config.yml @@ -12,6 +12,7 @@ php_variations: supported_os: # Alpine with Unit is not supported yet. Submit a PR if you can help (https://github.com/serversideup/docker-php/issues/233) - bullseye - bookworm + - trixie php_versions: - major: "7" From 9fefa9b3397c339bf8ca6cba9d03c4028ebda53f Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 12:17:59 -0500 Subject: [PATCH 53/63] Add validation for OS and variation in Docker tag assembly script; include support for PHP 8.5-rc in configuration --- scripts/assemble-docker-tags.sh | 113 +++++++++++++++++++--- scripts/conf/php-versions-base-config.yml | 8 ++ 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/scripts/assemble-docker-tags.sh b/scripts/assemble-docker-tags.sh index 4ea8161da..aa248b296 100755 --- a/scripts/assemble-docker-tags.sh +++ b/scripts/assemble-docker-tags.sh @@ -157,6 +157,55 @@ function is_default_variation() { [[ "$PHP_BUILD_VARIATION" == "$DEFAULT_IMAGE_VARIATION" ]] } +function is_rc_build() { + [[ "$build_patch_version" == *"-rc"* ]] +} + +validate_os_and_variation() { + local os_to_check="$build_base_os" + local variation_to_check="$build_variation" + + # Validate OS exists in config + if ! yq -o=json "$PHP_VERSIONS_FILE" | jq -e --arg os "$os_to_check" 'any(.operating_systems[] | .versions[]; .version == $os)' > /dev/null; then + echo_color_message red "🛑 ERROR: Unknown --os '$os_to_check'" + echo "Valid options are:" + yq -o=json "$PHP_VERSIONS_FILE" | jq -r '.operating_systems[].versions[].version' | sed 's/^/- /' + echo + help_menu + exit 1 + fi + + # Validate variation exists + if ! yq -o=json "$PHP_VERSIONS_FILE" | jq -e --arg v "$variation_to_check" 'any(.php_variations[]; .name == $v)' > /dev/null; then + echo_color_message red "🛑 ERROR: Unknown --variation '$variation_to_check'" + echo "Valid options are:" + yq -o=json "$PHP_VERSIONS_FILE" | jq -r '.php_variations[].name' | sed 's/^/- /' + echo + help_menu + exit 1 + fi + + # Validate variation supports OS if a constraint is defined + local supports_json + supports_json=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg v "$variation_to_check" '[.php_variations[] | select(.name == $v) | (.supported_os // [])[]]') + local has_constraints + has_constraints=$(echo "$supports_json" | jq 'length > 0') + + if [[ "$has_constraints" == "true" ]]; then + local is_supported + is_supported=$(echo "$supports_json" | jq -e --arg os "$os_to_check" ' + any(.[]; . == $os or (. == "alpine" and ($os | startswith("alpine")))) + ' >/dev/null 2>&1; echo $?) + if [[ "$is_supported" != "0" ]]; then + echo_color_message red "🛑 ERROR: Variation '$variation_to_check' does not support OS '$os_to_check'" + echo "Supported values for '$variation_to_check' are:" + echo "$supports_json" | jq -r '.[]' | sed 's/^/- /' + echo + exit 1 + fi + fi +} + help_menu() { echo "Usage: $0 --variation --os --patch-version [--stable-release --github-release-tag ]" echo @@ -235,16 +284,41 @@ build_patch_version=$PHP_BUILD_VERSION build_base_os=$PHP_BUILD_BASE_OS build_variation=$PHP_BUILD_VARIATION +# Validate inputs early +validate_os_and_variation + # Extract major and minor versions from build_patch_version build_major_version="${build_patch_version%%.*}" -build_minor_version="${build_patch_version%.*}" +if [[ "$build_patch_version" == *"-rc"* ]]; then + # For RC inputs like 8.5-rc, the minor identifier is the whole string + build_minor_version="$build_patch_version" +else + build_minor_version="${build_patch_version%.*}" +fi # Fetch version data from the PHP Versions file latest_global_stable_major=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r '[.php_versions[] | select(.major | test("-rc") | not) | .major | tonumber] | max | tostring') latest_global_stable_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg latest_global_stable_major "$latest_global_stable_major" '.php_versions[] | select(.major == $latest_global_stable_major) | .minor_versions | map(select(.minor | test("-rc") | not) | .minor | split(".") | .[1] | tonumber) | max | $latest_global_stable_major + "." + tostring') latest_minor_within_build_major=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_major "$build_major_version" '.php_versions[] | select(.major == $build_major) | .minor_versions | map(select(.minor | test("-rc") | not) | .minor | split(".") | .[1] | tonumber) | max | $build_major + "." + tostring') -latest_patch_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" '.php_versions[] | .minor_versions[] | select(.minor == $build_minor) | (.patch_versions // []) | map( split(".") | map(tonumber) ) | max | join(".")') -latest_patch_global=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r '[.php_versions[] | .minor_versions[] | select(.minor | test("-rc") | not) | ((.patch_versions // [])[]) | select(test("-rc") | not) | split(".") | map(tonumber) ] | max | join(".")') +latest_patch_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" ' + .php_versions[] + | .minor_versions[] + | select(.minor == $build_minor) + | (.patch_versions // []) as $patches + | ($patches | map(select(test("-rc") | not) | split(".") | map(tonumber))) as $parsed + | if ($parsed | length) > 0 then ($parsed | max | join(".")) else empty end +') +latest_patch_global=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r ' + [ + .php_versions[] + | .minor_versions[] + | select(.minor | test("-rc") | not) + | ((.patch_versions // [])[]) + | select(test("-rc") | not) + | split(".") | map(tonumber) + ] as $all + | if ($all | length) > 0 then ($all | max | join(".")) else empty end +') # Determine default base OS within the build minor using operating_systems default family and highest available version default_base_os_within_build_minor=$(yq -o=json "$PHP_VERSIONS_FILE" | jq -r --arg build_minor "$build_minor_version" ' . as $root @@ -339,26 +413,39 @@ echo "Build Major Version: $build_major_version" echo "Build Minor Version: $build_minor_version" echo_color_message yellow "🧐 Queried results from $PHP_VERSIONS_FILE" -echo "Latest Global Major Version: $latest_global_stable_major" -echo "Latest Global Minor Version: $latest_global_stable_minor" -echo "Latest Minor Version within Build Major: $latest_minor_within_build_major" -echo "Latest Patch Version within Build Minor: $latest_patch_within_build_minor" -echo "Default Base OS within Build Minor: $default_base_os_within_build_minor" -echo "Build Family: $build_family" -echo "Latest Family OS within Build Minor: $latest_family_os_within_build_minor" -echo "Default Supported Base OS within Build Minor: ${default_supported_base_os_within_build_minor:-}" -echo "Latest Supported Family OS within Build Minor: ${latest_family_supported_os_within_build_minor:-}" -echo "Latest Global Patch Version: $latest_patch_global" +echo "Latest Global Major Version: ${latest_global_stable_major:-N/A}" +echo "Latest Global Minor Version: ${latest_global_stable_minor:-N/A}" +echo "Latest Minor Version within Build Major: ${latest_minor_within_build_major:-N/A}" +echo "Latest Patch Version within Build Minor: ${latest_patch_within_build_minor:-N/A}" +echo "Default Base OS within Build Minor: ${default_base_os_within_build_minor:-N/A}" +echo "Build Family: ${build_family:-N/A}" +echo "Latest Family OS within Build Minor: ${latest_family_os_within_build_minor:-N/A}" +echo "Default Supported Base OS within Build Minor: ${default_supported_base_os_within_build_minor:-N/A}" +echo "Latest Supported Family OS within Build Minor: ${latest_family_supported_os_within_build_minor:-N/A}" +echo "Latest Global Patch Version: ${latest_patch_global:-N/A}" + +if is_rc_build; then + echo_color_message yellow "🔶 RC build detected. Stable aliases (minor/major/latest) will be skipped." +fi # Set default tag DOCKER_TAGS="" add_docker_tag "$build_patch_version-$build_variation-$build_base_os" add_family_alias_if_latest "$build_patch_version-$build_variation-$build_base_os" +# Always allow the variation-only alias for the default base OS, including RC builds if is_default_base_os; then add_docker_tag "$build_patch_version-$build_variation" fi +# For RC builds, allow publishing the root RC alias when both default OS and default variation are used +if is_rc_build && is_default_base_os && is_default_variation; then + add_docker_tag "$build_patch_version" + # Also publish the OS-specific RC alias and its family alias + add_docker_tag "$build_patch_version-$build_base_os" + add_family_alias_if_latest "$build_patch_version-$build_base_os" +fi + if is_latest_stable_patch_within_build_minor; then add_docker_tag "$build_minor_version-$build_variation-$build_base_os" add_family_alias_if_latest "$build_minor_version-$build_variation-$build_base_os" diff --git a/scripts/conf/php-versions-base-config.yml b/scripts/conf/php-versions-base-config.yml index 3c770475e..f23712545 100644 --- a/scripts/conf/php-versions-base-config.yml +++ b/scripts/conf/php-versions-base-config.yml @@ -63,6 +63,14 @@ php_versions: - name: trixie patch_versions: # - 8.4.1 # Pull latest from Official PHP source + - minor: "8.5-rc" + base_os: + - name: alpine3.21 + - name: alpine3.22 + - name: bookworm + - name: trixie + patch_versions: + - 8.5-rc operating_systems: - family: alpine From 9467a3ce2190b20b00c801e2fb39f37005a13c96 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 12:22:44 -0500 Subject: [PATCH 54/63] Enhance version weighting logic in matrix generation script to support numeric patches and RC minors --- scripts/generate-matrix.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/generate-matrix.sh b/scripts/generate-matrix.sh index 765c6cf2a..ddf5e7ec0 100644 --- a/scripts/generate-matrix.sh +++ b/scripts/generate-matrix.sh @@ -16,8 +16,14 @@ fi yq -o=json "$PHP_VERSIONS_FILE" | jq -c ' def version_weight: - # Convert "x.y.z" into a sortable integer weight - split(".") | map(tonumber) as $nums | ($nums[0]*10000 + $nums[1]*100 + $nums[2]); + # Support numeric patches x.y.z and RC minors x.y-rc + if test("-rc$") then + capture("^(?[0-9]+)\\.(?[0-9]+)-rc$") as $m + | ($m.maj|tonumber)*10000 + ($m.min|tonumber)*100 + 99 + else + capture("^(?[0-9]+)\\.(?[0-9]+)\\.(?[0-9]+)$") as $m + | ($m.maj|tonumber)*10000 + ($m.min|tonumber)*100 + ($m.pat|tonumber) + end; def os_family_match($os_name; $supported): # Allow listing "alpine" to include any alpine3.xx base_os From ae3a912d9d6370f94a1bc25f5738712d6964ea8d Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 12:27:18 -0500 Subject: [PATCH 55/63] Changed matrix to depot runner --- .github/workflows/service_docker-build-and-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/service_docker-build-and-publish.yml b/.github/workflows/service_docker-build-and-publish.yml index 478135569..6296f8d41 100644 --- a/.github/workflows/service_docker-build-and-publish.yml +++ b/.github/workflows/service_docker-build-and-publish.yml @@ -38,7 +38,7 @@ on: jobs: setup-matrix: - runs-on: ubuntu-24.04 + runs-on: depot-ubuntu-24.04 outputs: php-version-map-json: ${{ steps.get-php-versions.outputs.php-version-map-json }} steps: From 141deb06aa9a13eb20fb4a35c192eec65e658087 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 13:16:20 -0500 Subject: [PATCH 56/63] Update PHP extension installer version to 2.9.4 --- .../local/bin/docker-php-serversideup-install-php-ext-installer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer index 5685cd921..ddb8eb1bc 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer +++ b/src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer @@ -11,7 +11,7 @@ script_name="docker-php-serversideup-install-php-ext-installer" ############ # Environment variables ############ -PHP_EXT_INSTALLER_VERSION="2.7.0" +PHP_EXT_INSTALLER_VERSION="2.9.4" ############ # Main From 6642364c6cd9d5c159cb6469d8da276302bb37c5 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 13:17:13 -0500 Subject: [PATCH 57/63] Update php-fpm-healthcheck to version 0.6.0 in installation script --- src/s6/usr/local/bin/docker-php-serversideup-s6-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/s6/usr/local/bin/docker-php-serversideup-s6-install b/src/s6/usr/local/bin/docker-php-serversideup-s6-install index 0458d3a8f..903f1c8b1 100644 --- a/src/s6/usr/local/bin/docker-php-serversideup-s6-install +++ b/src/s6/usr/local/bin/docker-php-serversideup-s6-install @@ -34,5 +34,5 @@ untar ${S6_SRC_URL}/${S6_VERSION}/s6-overlay-${S6_ARCH}.tar.xz # Ensure "php-fpm-healthcheck" is installed echo "⬇️ Downloading php-fpm-healthcheck..." -curl -o /usr/local/bin/php-fpm-healthcheck https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/v0.5.0/php-fpm-healthcheck +curl -o /usr/local/bin/php-fpm-healthcheck https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/v0.6.0/php-fpm-healthcheck chmod +x /usr/local/bin/php-fpm-healthcheck \ No newline at end of file From e52ae67084534507fef0b29b52ff1d76685855a8 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 13:17:39 -0500 Subject: [PATCH 58/63] Update S6 version to 3.2.1.0 in installation script --- src/s6/usr/local/bin/docker-php-serversideup-s6-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/s6/usr/local/bin/docker-php-serversideup-s6-install b/src/s6/usr/local/bin/docker-php-serversideup-s6-install index 903f1c8b1..417d3f509 100644 --- a/src/s6/usr/local/bin/docker-php-serversideup-s6-install +++ b/src/s6/usr/local/bin/docker-php-serversideup-s6-install @@ -9,7 +9,7 @@ set -oue # Be sure to set the S6_SRC_URL, S6_SRC_DEP, and S6_DIR # environment variables before running this script. -S6_VERSION=v3.2.0.2 +S6_VERSION=v3.2.1.0 mkdir -p $S6_DIR export SYS_ARCH=$(uname -m) case "$SYS_ARCH" in From 57340cc5cf59b4af8c0f2e74aa6fde0c36b03fb6 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 13:18:38 -0500 Subject: [PATCH 59/63] Update NGINX Unit version to 1.34.2 in Dockerfile --- src/variations/unit/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/variations/unit/Dockerfile b/src/variations/unit/Dockerfile index 41cc5fb99..759fda10a 100644 --- a/src/variations/unit/Dockerfile +++ b/src/variations/unit/Dockerfile @@ -10,7 +10,7 @@ ARG BASE_IMAGE="php:${PHP_VERSION}-cli-${BASE_OS_VERSION}" FROM ${BASE_IMAGE} AS build ARG DEPENDENCY_PACKAGES_ALPINE='build-base curl tar git openssl-dev pcre2-dev shadow' ARG DEPENDENCY_PACKAGES_DEBIAN='ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config' -ARG NGINX_UNIT_VERSION='1.33.0-1' +ARG NGINX_UNIT_VERSION='1.34.2' # copy our scripts COPY --chmod=755 src/common/ / From 6aade3ec19f065d7c4e91a65bfaad2b0ab89c88e Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 13:27:34 -0500 Subject: [PATCH 60/63] Update php-fpm-healthcheck to version 0.6.0 in Dockerfile --- src/variations/fpm/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/variations/fpm/Dockerfile b/src/variations/fpm/Dockerfile index e97261604..86494ba5a 100644 --- a/src/variations/fpm/Dockerfile +++ b/src/variations/fpm/Dockerfile @@ -76,7 +76,7 @@ RUN rm -rf /usr/local/etc/php-fpm.d/*.conf && \ \ # Ensure "php-fpm-healthcheck" is installed echo "⬇️ Downloading php-fpm-healthcheck..." && \ - curl -o /usr/local/bin/php-fpm-healthcheck https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/v0.5.0/php-fpm-healthcheck && \ + curl -o /usr/local/bin/php-fpm-healthcheck https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/v0.6.0/php-fpm-healthcheck && \ chmod +x /usr/local/bin/php-fpm-healthcheck && \ \ # Install default PHP extensions From 74670a89309cf5485ac320563af2543610b44cf0 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 13:28:02 -0500 Subject: [PATCH 61/63] Comment out PHP 8.5-rc configuration due to a blocking bug in a dependency (https://github.com/phpredis/phpredis/issues/2688) --- scripts/conf/php-versions-base-config.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/conf/php-versions-base-config.yml b/scripts/conf/php-versions-base-config.yml index f23712545..c842d057d 100644 --- a/scripts/conf/php-versions-base-config.yml +++ b/scripts/conf/php-versions-base-config.yml @@ -63,14 +63,16 @@ php_versions: - name: trixie patch_versions: # - 8.4.1 # Pull latest from Official PHP source - - minor: "8.5-rc" - base_os: - - name: alpine3.21 - - name: alpine3.22 - - name: bookworm - - name: trixie - patch_versions: - - 8.5-rc + # PHP 8.5-rc has a blocking bug in a dependency that is not yet fixed. + # + # - minor: "8.5-rc" + # base_os: + # - name: alpine3.21 + # - name: alpine3.22 + # - name: bookworm + # - name: trixie + # patch_versions: + # - 8.5-rc operating_systems: - family: alpine From 26c1f6f59114df5f8e16f3daca1d18e3ce498460 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 14:17:52 -0500 Subject: [PATCH 62/63] Organized script for better readability --- ...cker-php-serversideup-set-file-permissions | 78 ++++++++++++++++--- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions index d31c1950a..93e50d7d3 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions +++ b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions @@ -70,19 +70,54 @@ case "$OS" in debian) case "$SERVICE" in cli) - DIRS="/var/www/ $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; fpm) - DIRS="/var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; apache) - DIRS="/run /etc/apache2 /etc/ssl/private /var/log/apache2 /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /etc/apache2 + /etc/ssl/private + /run + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + /var/log/apache2 + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; nginx) - DIRS="/run /etc/nginx/ /var/log/nginx /etc/ssl/private /var/cache/nginx/ /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /etc/nginx + /etc/ssl/private + /run + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + /var/cache/nginx + /var/log/nginx + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; unit) - DIRS="/var/log/unit /var/run/unit /etc/unit /etc/ssl/private/ /var/lib/unit/ /var/www/ $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /etc/unit + /etc/ssl/private + /var/lib/unit + /var/log/unit + /var/run/unit + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; *) echo "$script_name: Unsupported service: $SERVICE" @@ -93,16 +128,41 @@ case "$OS" in alpine) case "$SERVICE" in cli) - DIRS="/var/www/ $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; fpm) - DIRS="/var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; apache) - DIRS="/etc/apache2 /etc/ssl/private /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /etc/apache2 + /etc/ssl/private + /run + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + /var/www + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; nginx) - DIRS="/etc/nginx/ /var/log/nginx /etc/ssl/private /var/cache/nginx /var/www/ /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" + DIRS=" + /composer + /etc/nginx/ + /etc/ssl/private + /usr/local/etc/php-fpm.conf + /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf + /var/cache/nginx + /var/log/nginx + /var/www/ + $PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" ;; *) echo "$script_name: Unsupported SERVICE: $SERVICE" From a3a11f8157d6021a523edf13a046cd6e2c7774a2 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 21 Aug 2025 14:23:16 -0500 Subject: [PATCH 63/63] Enhance script to require root privileges and improve error handling with 'set -eu' --- .../bin/docker-php-serversideup-set-file-permissions | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions index 93e50d7d3..56c163883 100644 --- a/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions +++ b/src/common/usr/local/bin/docker-php-serversideup-set-file-permissions @@ -1,5 +1,5 @@ #!/bin/sh -set -e +set -eu ################################################### # Usage: docker-php-serversideup-set-file-permissions --owner USER:GROUP --service SERVICE @@ -17,6 +17,12 @@ usage() { exit 1 } +# Check for root privileges +if [ "$(id -u)" -ne 0 ]; then + echo "${script_name}: This script must be run as root within the container. Be sure to set \"USER root\" in your Dockerfile before running this script." + exit 1 +fi + # Check for minimum number of arguments if [ "$#" -ne 4 ]; then usage