diff --git a/Dockerfile.template b/Dockerfile.template index 2296928274..8af48cf052 100644 --- a/Dockerfile.template +++ b/Dockerfile.template @@ -2,7 +2,11 @@ def is_alpine: env.variant | index("alpine") -}} +{{ if env.variant == "unit" then ( -}} +FROM unit:{{ .unit.version }}-php{{ env.phpVersion }} +{{ ) else ( -}} FROM php:{{ env.phpVersion }}-{{ env.variant }} +{{ ) end -}} {{ if env.version != "cli" then ( -}} # persistent dependencies @@ -252,12 +256,38 @@ VOLUME /var/www/html {{ if env.version != "cli" then ( -}} COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ +{{ if env.variant == "unit" then ( -}} + +# NGINX Unit specific bits +WORKDIR /var/www/html +RUN set -eux; \ +# save Unit's entrypoint so we can successfully/correctly configure Unit at runtime + sed -e 's/docker-entrypoint/unit-entrypoint/g' /usr/local/bin/docker-entrypoint.sh > /usr/local/bin/unit-entrypoint.sh; \ + grep -F '/unit-entrypoint.d/' /usr/local/bin/unit-entrypoint.sh; \ + chmod +x /usr/local/bin/unit-entrypoint.sh; \ + \ + { \ + echo; \ + echo '// WordPress does not support NGINX Unit (yet), so we have to do a little hackery to let it know that Unit supports nice permalinks (and prevent it from insisting on injecting "/index.php" in all the permalink options) -- the simplest way is teaching it to recognize Unit as if it were NGINX (which is only semi-true, but true enough for these purposes)'; \ + echo '$is_nginx = $is_nginx || str_starts_with($_SERVER["SERVER_SOFTWARE"], "Unit/");'; \ + echo '// see also https://github.com/WordPress/WordPress/blob/39f7f558d91afdd2f3afc7f3b049a6a800cd3f80/wp-includes/vars.php#L127'; \ + } >> /usr/src/wordpress/wp-config-docker.php +COPY unit.json /unit-entrypoint.d/wordpress.json + +{{ ) else "" end -}} {{ ) else "" end -}} COPY docker-entrypoint.sh /usr/local/bin/ ENTRYPOINT ["docker-entrypoint.sh"] {{ if env.version != "cli" then ( -}} -CMD {{ [ if env.variant == "apache" then "apache2-foreground" else "php-fpm" end ] | @json }} +CMD {{ + { + apache: [ "apache2-foreground" ], + # https://github.com/nginx/unit/blob/fb33ec86a3b6ca6a844dfa6980bb9e083094abec/pkg/docker/Dockerfile.php8.2#L89 + unit: [ "unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock" ], + }[env.variant] // [ "php-fpm" ] + | @json +}} {{ ) else ( -}} USER www-data CMD ["wp", "shell"] diff --git a/apply-templates.sh b/apply-templates.sh index 99ac8785a4..af1a34443b 100755 --- a/apply-templates.sh +++ b/apply-templates.sh @@ -43,6 +43,10 @@ for version; do for variant in "${variants[@]}"; do export variant + if [ "$variant" = 'unit' ] && jq -e '.[env.version][env.variant].phpVersions | index(env.phpVersion) | not' versions.json > /dev/null; then + continue + fi + dir="$version/php$phpVersion/$variant" mkdir -p "$dir" @@ -58,6 +62,10 @@ for version; do else cp -a docker-entrypoint.sh wp-config-docker.php "$dir/" fi + + if [ "$variant" = 'unit' ]; then + cp -a unit.json "$dir/" + fi done done done diff --git a/beta/php8.1/apache/docker-entrypoint.sh b/beta/php8.1/apache/docker-entrypoint.sh index 1034f1dec1..4843caa7d3 100755 --- a/beta/php8.1/apache/docker-entrypoint.sh +++ b/beta/php8.1/apache/docker-entrypoint.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -Eeuo pipefail -if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ] || [[ "$1" == unit* ]]; then uid="$(id -u)" gid="$(id -g)" if [ "$uid" = '0' ]; then @@ -15,6 +15,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then user="${user#$pound}" group="${group#$pound}" ;; + unit*) + user='unit' + group='unit' + ;; *) # php-fpm user='www-data' group='www-data' @@ -94,6 +98,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then fi done fi + + if [[ "$1" == unit* ]] && [ "$1" != 'unit-entrypoint.sh' ]; then + set -- unit-entrypoint.sh "$@" + fi fi exec "$@" diff --git a/beta/php8.1/fpm-alpine/docker-entrypoint.sh b/beta/php8.1/fpm-alpine/docker-entrypoint.sh index 1034f1dec1..4843caa7d3 100755 --- a/beta/php8.1/fpm-alpine/docker-entrypoint.sh +++ b/beta/php8.1/fpm-alpine/docker-entrypoint.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -Eeuo pipefail -if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ] || [[ "$1" == unit* ]]; then uid="$(id -u)" gid="$(id -g)" if [ "$uid" = '0' ]; then @@ -15,6 +15,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then user="${user#$pound}" group="${group#$pound}" ;; + unit*) + user='unit' + group='unit' + ;; *) # php-fpm user='www-data' group='www-data' @@ -94,6 +98,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then fi done fi + + if [[ "$1" == unit* ]] && [ "$1" != 'unit-entrypoint.sh' ]; then + set -- unit-entrypoint.sh "$@" + fi fi exec "$@" diff --git a/beta/php8.1/fpm/docker-entrypoint.sh b/beta/php8.1/fpm/docker-entrypoint.sh index 1034f1dec1..4843caa7d3 100755 --- a/beta/php8.1/fpm/docker-entrypoint.sh +++ b/beta/php8.1/fpm/docker-entrypoint.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -Eeuo pipefail -if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ] || [[ "$1" == unit* ]]; then uid="$(id -u)" gid="$(id -g)" if [ "$uid" = '0' ]; then @@ -15,6 +15,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then user="${user#$pound}" group="${group#$pound}" ;; + unit*) + user='unit' + group='unit' + ;; *) # php-fpm user='www-data' group='www-data' @@ -94,6 +98,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then fi done fi + + if [[ "$1" == unit* ]] && [ "$1" != 'unit-entrypoint.sh' ]; then + set -- unit-entrypoint.sh "$@" + fi fi exec "$@" diff --git a/beta/php8.2/apache/docker-entrypoint.sh b/beta/php8.2/apache/docker-entrypoint.sh index 1034f1dec1..4843caa7d3 100755 --- a/beta/php8.2/apache/docker-entrypoint.sh +++ b/beta/php8.2/apache/docker-entrypoint.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -Eeuo pipefail -if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ] || [[ "$1" == unit* ]]; then uid="$(id -u)" gid="$(id -g)" if [ "$uid" = '0' ]; then @@ -15,6 +15,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then user="${user#$pound}" group="${group#$pound}" ;; + unit*) + user='unit' + group='unit' + ;; *) # php-fpm user='www-data' group='www-data' @@ -94,6 +98,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then fi done fi + + if [[ "$1" == unit* ]] && [ "$1" != 'unit-entrypoint.sh' ]; then + set -- unit-entrypoint.sh "$@" + fi fi exec "$@" diff --git a/beta/php8.2/fpm-alpine/docker-entrypoint.sh b/beta/php8.2/fpm-alpine/docker-entrypoint.sh index 1034f1dec1..4843caa7d3 100755 --- a/beta/php8.2/fpm-alpine/docker-entrypoint.sh +++ b/beta/php8.2/fpm-alpine/docker-entrypoint.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -Eeuo pipefail -if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ] || [[ "$1" == unit* ]]; then uid="$(id -u)" gid="$(id -g)" if [ "$uid" = '0' ]; then @@ -15,6 +15,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then user="${user#$pound}" group="${group#$pound}" ;; + unit*) + user='unit' + group='unit' + ;; *) # php-fpm user='www-data' group='www-data' @@ -94,6 +98,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then fi done fi + + if [[ "$1" == unit* ]] && [ "$1" != 'unit-entrypoint.sh' ]; then + set -- unit-entrypoint.sh "$@" + fi fi exec "$@" diff --git a/beta/php8.2/fpm/docker-entrypoint.sh b/beta/php8.2/fpm/docker-entrypoint.sh index 1034f1dec1..4843caa7d3 100755 --- a/beta/php8.2/fpm/docker-entrypoint.sh +++ b/beta/php8.2/fpm/docker-entrypoint.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -Eeuo pipefail -if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ] || [[ "$1" == unit* ]]; then uid="$(id -u)" gid="$(id -g)" if [ "$uid" = '0' ]; then @@ -15,6 +15,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then user="${user#$pound}" group="${group#$pound}" ;; + unit*) + user='unit' + group='unit' + ;; *) # php-fpm user='www-data' group='www-data' @@ -94,6 +98,10 @@ if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then fi done fi + + if [[ "$1" == unit* ]] && [ "$1" != 'unit-entrypoint.sh' ]; then + set -- unit-entrypoint.sh "$@" + fi fi exec "$@" diff --git a/beta/php8.2/unit/Dockerfile b/beta/php8.2/unit/Dockerfile new file mode 100644 index 0000000000..549810639c --- /dev/null +++ b/beta/php8.2/unit/Dockerfile @@ -0,0 +1,165 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM unit:1.31.1-php8.2 + +# persistent dependencies +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# Ghostscript is required for rendering PDF previews + ghostscript \ + ; \ + rm -rf /var/lib/apt/lists/* + +# install the PHP extensions we need (https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions) +RUN set -ex; \ + \ + savedAptMark="$(apt-mark showmanual)"; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + libfreetype6-dev \ + libicu-dev \ + libjpeg-dev \ + libmagickwand-dev \ + libpng-dev \ + libwebp-dev \ + libzip-dev \ + ; \ + \ + docker-php-ext-configure gd \ + --with-freetype \ + --with-jpeg \ + --with-webp \ + ; \ + docker-php-ext-install -j "$(nproc)" \ + bcmath \ + exif \ + gd \ + intl \ + mysqli \ + zip \ + ; \ +# https://pecl.php.net/package/imagick + pecl install imagick-3.6.0; \ + docker-php-ext-enable imagick; \ + rm -r /tmp/pear; \ + \ +# some misbehaving extensions end up outputting to stdout 🙈 (https://github.com/docker-library/wordpress/issues/669#issuecomment-993945967) + out="$(php -r 'exit(0);')"; \ + [ -z "$out" ]; \ + err="$(php -r 'exit(0);' 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ]; \ + \ + extDir="$(php -r 'echo ini_get("extension_dir");')"; \ + [ -d "$extDir" ]; \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies + apt-mark auto '.*' > /dev/null; \ + apt-mark manual $savedAptMark; \ + ldd "$extDir"/*.so \ + | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); print so }' \ + | sort -u \ + | xargs -r dpkg-query --search \ + | cut -d: -f1 \ + | sort -u \ + | xargs -rt apt-mark manual; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ! { ldd "$extDir"/*.so | grep 'not found'; }; \ +# check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) + err="$(php --version 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ] + +# set recommended PHP.ini settings +# see https://secure.php.net/manual/en/opcache.installation.php +RUN set -eux; \ + docker-php-ext-enable opcache; \ + { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +RUN set -eux; \ + version='6.4.2'; \ + sha1='d1aedbfea77b243b09e0ab05b100b782497406dd'; \ + \ + curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \ + echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \ + \ +# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress + tar -xzf wordpress.tar.gz -C /usr/src/; \ + rm wordpress.tar.gz; \ + \ +# https://wordpress.org/support/article/htaccess/ + [ ! -e /usr/src/wordpress/.htaccess ]; \ + { \ + echo '# BEGIN WordPress'; \ + echo ''; \ + echo 'RewriteEngine On'; \ + echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \ + echo 'RewriteBase /'; \ + echo 'RewriteRule ^index\.php$ - [L]'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \ + echo 'RewriteRule . /index.php [L]'; \ + echo ''; \ + echo '# END WordPress'; \ + } > /usr/src/wordpress/.htaccess; \ + \ + chown -R www-data:www-data /usr/src/wordpress; \ +# pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root +# wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507 + mkdir wp-content; \ + for dir in /usr/src/wordpress/wp-content/*/ cache; do \ + dir="$(basename "${dir%/}")"; \ + mkdir "wp-content/$dir"; \ + done; \ + chown -R www-data:www-data wp-content; \ + chmod -R 1777 wp-content + +VOLUME /var/www/html + +COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ + +# NGINX Unit specific bits +WORKDIR /var/www/html +RUN set -eux; \ +# save Unit's entrypoint so we can successfully/correctly configure Unit at runtime + sed -e 's/docker-entrypoint/unit-entrypoint/g' /usr/local/bin/docker-entrypoint.sh > /usr/local/bin/unit-entrypoint.sh; \ + grep -F '/unit-entrypoint.d/' /usr/local/bin/unit-entrypoint.sh; \ + chmod +x /usr/local/bin/unit-entrypoint.sh; \ + \ + { \ + echo; \ + echo '// WordPress does not support NGINX Unit (yet), so we have to do a little hackery to let it know that Unit supports nice permalinks (and prevent it from insisting on injecting "/index.php" in all the permalink options) -- the simplest way is teaching it to recognize Unit as if it were NGINX (which is only semi-true, but true enough for these purposes)'; \ + echo '$is_nginx = $is_nginx || str_starts_with($_SERVER["SERVER_SOFTWARE"], "Unit/");'; \ + echo '// see also https://github.com/WordPress/WordPress/blob/39f7f558d91afdd2f3afc7f3b049a6a800cd3f80/wp-includes/vars.php#L127'; \ + } >> /usr/src/wordpress/wp-config-docker.php +COPY unit.json /unit-entrypoint.d/wordpress.json + +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["unitd","--no-daemon","--control","unix:/var/run/control.unit.sock"] diff --git a/beta/php8.2/unit/docker-entrypoint.sh b/beta/php8.2/unit/docker-entrypoint.sh new file mode 100755 index 0000000000..4843caa7d3 --- /dev/null +++ b/beta/php8.2/unit/docker-entrypoint.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ] || [[ "$1" == unit* ]]; then + uid="$(id -u)" + gid="$(id -g)" + if [ "$uid" = '0' ]; then + case "$1" in + apache2*) + user="${APACHE_RUN_USER:-www-data}" + group="${APACHE_RUN_GROUP:-www-data}" + + # strip off any '#' symbol ('#1000' is valid syntax for Apache) + pound='#' + user="${user#$pound}" + group="${group#$pound}" + ;; + unit*) + user='unit' + group='unit' + ;; + *) # php-fpm + user='www-data' + group='www-data' + ;; + esac + else + user="$uid" + group="$gid" + fi + + if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then + # if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory) + if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then + chown "$user:$group" . + fi + + echo >&2 "WordPress not found in $PWD - copying now..." + if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then + echo >&2 "WARNING: $PWD is not empty! (copying anyhow)" + fi + sourceTarArgs=( + --create + --file - + --directory /usr/src/wordpress + --owner "$user" --group "$group" + ) + targetTarArgs=( + --extract + --file - + ) + if [ "$uid" != '0' ]; then + # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted" + targetTarArgs+=( --no-overwrite-dir ) + fi + # loop over "pluggable" content in the source, and if it already exists in the destination, skip it + # https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded) + for contentPath in \ + /usr/src/wordpress/.htaccess \ + /usr/src/wordpress/wp-content/*/*/ \ + ; do + contentPath="${contentPath%/}" + [ -e "$contentPath" ] || continue + contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc. + if [ -e "$PWD/$contentPath" ]; then + echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)" + sourceTarArgs+=( --exclude "./$contentPath" ) + fi + done + tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}" + echo >&2 "Complete! WordPress has been successfully copied to $PWD" + fi + + wpEnvs=( "${!WORDPRESS_@}" ) + if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then + for wpConfigDocker in \ + wp-config-docker.php \ + /usr/src/wordpress/wp-config-docker.php \ + ; do + if [ -s "$wpConfigDocker" ]; then + echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})" + # using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables) + awk ' + /put your unique phrase here/ { + cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\ -f1" + cmd | getline str + close(cmd) + gsub("put your unique phrase here", str) + } + { print } + ' "$wpConfigDocker" > wp-config.php + if [ "$uid" = '0' ]; then + # attempt to ensure that wp-config.php is owned by the run user + # could be on a filesystem that doesn't allow chown (like some NFS setups) + chown "$user:$group" wp-config.php || true + fi + break + fi + done + fi + + if [[ "$1" == unit* ]] && [ "$1" != 'unit-entrypoint.sh' ]; then + set -- unit-entrypoint.sh "$@" + fi +fi + +exec "$@" diff --git a/beta/php8.2/unit/unit.json b/beta/php8.2/unit/unit.json new file mode 100644 index 0000000000..c58a5cb111 --- /dev/null +++ b/beta/php8.2/unit/unit.json @@ -0,0 +1,45 @@ +{ + "listeners": { + "*:80": { + "pass": "routes" + } + }, + + "routes": [ + { + "match": { + "uri": [ + "*.php", + "*.php/*", + "/wp-admin/" + ] + }, + "action": { + "pass": "applications/wordpress/direct" + } + }, + { + "action": { + "share": "/var/www/html$uri", + "fallback": { + "pass": "applications/wordpress/index" + } + } + } + ], + + "applications": { + "wordpress": { + "type": "php", + "targets": { + "direct": { + "root": "/var/www/html/" + }, + "index": { + "root": "/var/www/html/", + "script": "index.php" + } + } + } + } +} diff --git a/beta/php8.2/unit/wp-config-docker.php b/beta/php8.2/unit/wp-config-docker.php new file mode 100644 index 0000000000..9cd3babf01 --- /dev/null +++ b/beta/php8.2/unit/wp-config-docker.php @@ -0,0 +1,133 @@ +&1 1>&2 2>&3)"; \ + [ -z "$err" ]; \ + \ + extDir="$(php -r 'echo ini_get("extension_dir");')"; \ + [ -d "$extDir" ]; \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies + apt-mark auto '.*' > /dev/null; \ + apt-mark manual $savedAptMark; \ + ldd "$extDir"/*.so \ + | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); print so }' \ + | sort -u \ + | xargs -r dpkg-query --search \ + | cut -d: -f1 \ + | sort -u \ + | xargs -rt apt-mark manual; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ! { ldd "$extDir"/*.so | grep 'not found'; }; \ +# check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) + err="$(php --version 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ] + +# set recommended PHP.ini settings +# see https://secure.php.net/manual/en/opcache.installation.php +RUN set -eux; \ + docker-php-ext-enable opcache; \ + { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +RUN set -eux; \ + version='6.4.2'; \ + sha1='d1aedbfea77b243b09e0ab05b100b782497406dd'; \ + \ + curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \ + echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \ + \ +# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress + tar -xzf wordpress.tar.gz -C /usr/src/; \ + rm wordpress.tar.gz; \ + \ +# https://wordpress.org/support/article/htaccess/ + [ ! -e /usr/src/wordpress/.htaccess ]; \ + { \ + echo '# BEGIN WordPress'; \ + echo ''; \ + echo 'RewriteEngine On'; \ + echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \ + echo 'RewriteBase /'; \ + echo 'RewriteRule ^index\.php$ - [L]'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \ + echo 'RewriteRule . /index.php [L]'; \ + echo ''; \ + echo '# END WordPress'; \ + } > /usr/src/wordpress/.htaccess; \ + \ + chown -R www-data:www-data /usr/src/wordpress; \ +# pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root +# wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507 + mkdir wp-content; \ + for dir in /usr/src/wordpress/wp-content/*/ cache; do \ + dir="$(basename "${dir%/}")"; \ + mkdir "wp-content/$dir"; \ + done; \ + chown -R www-data:www-data wp-content; \ + chmod -R 1777 wp-content + +VOLUME /var/www/html + +COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ + +# NGINX Unit specific bits +WORKDIR /var/www/html +RUN set -eux; \ +# save Unit's entrypoint so we can successfully/correctly configure Unit at runtime + sed -e 's/docker-entrypoint/unit-entrypoint/g' /usr/local/bin/docker-entrypoint.sh > /usr/local/bin/unit-entrypoint.sh; \ + grep -F '/unit-entrypoint.d/' /usr/local/bin/unit-entrypoint.sh; \ + chmod +x /usr/local/bin/unit-entrypoint.sh; \ + \ + { \ + echo; \ + echo '// WordPress does not support NGINX Unit (yet), so we have to do a little hackery to let it know that Unit supports nice permalinks (and prevent it from insisting on injecting "/index.php" in all the permalink options) -- the simplest way is teaching it to recognize Unit as if it were NGINX (which is only semi-true, but true enough for these purposes)'; \ + echo '$is_nginx = $is_nginx || str_starts_with($_SERVER["SERVER_SOFTWARE"], "Unit/");'; \ + echo '// see also https://github.com/WordPress/WordPress/blob/39f7f558d91afdd2f3afc7f3b049a6a800cd3f80/wp-includes/vars.php#L127'; \ + } >> /usr/src/wordpress/wp-config-docker.php +COPY unit.json /unit-entrypoint.d/wordpress.json + +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["unitd","--no-daemon","--control","unix:/var/run/control.unit.sock"] diff --git a/latest/php8.2/unit/docker-entrypoint.sh b/latest/php8.2/unit/docker-entrypoint.sh new file mode 100755 index 0000000000..4843caa7d3 --- /dev/null +++ b/latest/php8.2/unit/docker-entrypoint.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ] || [[ "$1" == unit* ]]; then + uid="$(id -u)" + gid="$(id -g)" + if [ "$uid" = '0' ]; then + case "$1" in + apache2*) + user="${APACHE_RUN_USER:-www-data}" + group="${APACHE_RUN_GROUP:-www-data}" + + # strip off any '#' symbol ('#1000' is valid syntax for Apache) + pound='#' + user="${user#$pound}" + group="${group#$pound}" + ;; + unit*) + user='unit' + group='unit' + ;; + *) # php-fpm + user='www-data' + group='www-data' + ;; + esac + else + user="$uid" + group="$gid" + fi + + if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then + # if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory) + if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then + chown "$user:$group" . + fi + + echo >&2 "WordPress not found in $PWD - copying now..." + if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then + echo >&2 "WARNING: $PWD is not empty! (copying anyhow)" + fi + sourceTarArgs=( + --create + --file - + --directory /usr/src/wordpress + --owner "$user" --group "$group" + ) + targetTarArgs=( + --extract + --file - + ) + if [ "$uid" != '0' ]; then + # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted" + targetTarArgs+=( --no-overwrite-dir ) + fi + # loop over "pluggable" content in the source, and if it already exists in the destination, skip it + # https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded) + for contentPath in \ + /usr/src/wordpress/.htaccess \ + /usr/src/wordpress/wp-content/*/*/ \ + ; do + contentPath="${contentPath%/}" + [ -e "$contentPath" ] || continue + contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc. + if [ -e "$PWD/$contentPath" ]; then + echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)" + sourceTarArgs+=( --exclude "./$contentPath" ) + fi + done + tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}" + echo >&2 "Complete! WordPress has been successfully copied to $PWD" + fi + + wpEnvs=( "${!WORDPRESS_@}" ) + if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then + for wpConfigDocker in \ + wp-config-docker.php \ + /usr/src/wordpress/wp-config-docker.php \ + ; do + if [ -s "$wpConfigDocker" ]; then + echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})" + # using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables) + awk ' + /put your unique phrase here/ { + cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\ -f1" + cmd | getline str + close(cmd) + gsub("put your unique phrase here", str) + } + { print } + ' "$wpConfigDocker" > wp-config.php + if [ "$uid" = '0' ]; then + # attempt to ensure that wp-config.php is owned by the run user + # could be on a filesystem that doesn't allow chown (like some NFS setups) + chown "$user:$group" wp-config.php || true + fi + break + fi + done + fi + + if [[ "$1" == unit* ]] && [ "$1" != 'unit-entrypoint.sh' ]; then + set -- unit-entrypoint.sh "$@" + fi +fi + +exec "$@" diff --git a/latest/php8.2/unit/unit.json b/latest/php8.2/unit/unit.json new file mode 100644 index 0000000000..c58a5cb111 --- /dev/null +++ b/latest/php8.2/unit/unit.json @@ -0,0 +1,45 @@ +{ + "listeners": { + "*:80": { + "pass": "routes" + } + }, + + "routes": [ + { + "match": { + "uri": [ + "*.php", + "*.php/*", + "/wp-admin/" + ] + }, + "action": { + "pass": "applications/wordpress/direct" + } + }, + { + "action": { + "share": "/var/www/html$uri", + "fallback": { + "pass": "applications/wordpress/index" + } + } + } + ], + + "applications": { + "wordpress": { + "type": "php", + "targets": { + "direct": { + "root": "/var/www/html/" + }, + "index": { + "root": "/var/www/html/", + "script": "index.php" + } + } + } + } +} diff --git a/latest/php8.2/unit/wp-config-docker.php b/latest/php8.2/unit/wp-config-docker.php new file mode 100644 index 0000000000..9cd3babf01 --- /dev/null +++ b/latest/php8.2/unit/wp-config-docker.php @@ -0,0 +1,133 @@ +[0-9]+([.][0-9]+)*)-php(?[0-9]+[.][0-9]+)$") + | .split = (.version | split(".") | map(tonumber? // .)) # pre-parse version numbers (for filtering and sorting) + | select(.split[0] == 1) # filter down to just 1.x versions (attempt to avoid major breakage) + ] + # sort the list in descending version sort order + | unique_by([ .split, .php ]) + | reverse + | ( + # find the highest and least specific version number (2 preferred over 1 over 2.3 over 2.3.4) + map(.version) + | unique_by(indices(".") | length) + | .[0] // error("no suitable unit version found") + ) as $version + | { + version: $version, + phpVersions: map( + select(.version == $version) + | .php + ), + } + ' +)" + for version in "${versions[@]}"; do export version @@ -55,7 +84,7 @@ for version in "${versions[@]}"; do export fullVersion json="$( - jq <<<"$json" -c --argjson doc "$doc" ' + jq <<<"$json" -c --argjson doc "$doc" --argjson unit "$unit" ' .[env.version] = { version: env.fullVersion, phpVersions: [ "8.1", "8.2", "8.3" ], @@ -63,10 +92,12 @@ for version in "${versions[@]}"; do if env.version == "cli" then [ "alpine" ] else - [ "apache", "fpm", "fpm-alpine" ] + [ "apache", "fpm", "fpm-alpine", "unit" ] end ), - } + $doc + } + if env.version == "cli" then {} else { + unit: $unit, + } end + $doc ' )" done