diff --git a/tools/cli/commands/docker.js b/tools/cli/commands/docker.js index 0c6a4f727d7f4..a14b17e076a9d 100644 --- a/tools/cli/commands/docker.js +++ b/tools/cli/commands/docker.js @@ -328,6 +328,8 @@ const buildExecCmd = argv => { opts.push( 'bash' ); } else if ( cmd === 'db' ) { opts.push( 'mysql', '--defaults-group-suffix=docker' ); + } else if ( cmd === 'select-php' ) { + opts.push( 'select-php', argv.version ); } else if ( cmd === 'phpunit' ) { // @todo: Make this scale. console.warn( chalk.yellow( 'This currently only run tests for the Jetpack plugin.' ) ); @@ -624,6 +626,18 @@ export function dockerDefine( yargs ) { builder: yargExec => defaultOpts( yargExec ), handler: argv => execDockerCmdHandler( argv ), } ) + .command( { + command: 'select-php ', + description: + 'Select the version of PHP for use inside the container. See documentation for important notes!', + builder: yargCmd => { + yargCmd.positional( 'version', { + describe: 'The version to select, or "default".', + type: 'string', + } ); + }, + handler: argv => execDockerCmdHandler( argv ), + } ) .command( { command: 'phpunit', description: 'Run PHPUnit tests inside container', diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index 86fa17e1f13d9..b688520b0783b 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -12,6 +12,13 @@ ENV LC_ALL en_US.UTF-8 WORKDIR /tmp +# Record ARGs +RUN \ + echo "PHP_VERSION=$PHP_VERSION" > /etc/docker-args.sh \ + && echo "NODE_VERSION=$NODE_VERSION" >> /etc/docker-args.sh \ + && echo "COMPOSER_VERSION=$COMPOSER_VERSION" >> /etc/docker-args.sh \ + && echo "PNPM_VERSION=$PNPM_VERSION" >> /etc/docker-args.sh + # Install basic packages, including Apache. RUN --mount=type=cache,target=/var/lib/apt/lists/,sharing=private \ export DEBIAN_FRONTEND=noninteractive \ @@ -41,31 +48,12 @@ RUN --mount=type=cache,target=/var/lib/apt/lists/,sharing=private \ RUN a2enmod rewrite # Install requested version of PHP. +COPY ./bin/select-php.sh /usr/local/bin/select-php +RUN chmod +x /usr/local/bin/select-php +COPY ./config/php.ini /var/lib/jetpack-config/php.ini RUN --mount=type=cache,target=/var/lib/apt/lists/,sharing=private \ : "${PHP_VERSION:?Build argument PHP_VERSION needs to be set and non-empty.}" \ - && export DEBIAN_FRONTEND=noninteractive \ - && apt-get install -y \ - libapache2-mod-php${PHP_VERSION} \ - php${PHP_VERSION} \ - php${PHP_VERSION}-bcmath \ - php${PHP_VERSION}-cli \ - php${PHP_VERSION}-curl \ - php${PHP_VERSION}-intl \ - php${PHP_VERSION}-ldap \ - php${PHP_VERSION}-mbstring \ - php${PHP_VERSION}-mysql \ - php${PHP_VERSION}-opcache \ - php${PHP_VERSION}-pgsql \ - php${PHP_VERSION}-soap \ - php${PHP_VERSION}-sqlite3 \ - php${PHP_VERSION}-xdebug \ - php${PHP_VERSION}-xml \ - php${PHP_VERSION}-xsl \ - php${PHP_VERSION}-zip \ - && apt-get install -y --no-install-recommends \ - php${PHP_VERSION}-apcu \ - php${PHP_VERSION}-gd \ - php${PHP_VERSION}-imagick \ + && /usr/local/bin/select-php "$PHP_VERSION" \ && find /var/ -name '*-old' -delete && rm -rf /var/log/dpkg.log /var/log/alternatives.log /var/log/apt/ ~/.launchpadlib # Install requested version of Composer. @@ -109,10 +97,6 @@ RUN mkdir /usr/local/src/psysh \ # Copy a default config file for an apache host. COPY ./config/apache_default /etc/apache2/sites-available/000-default.conf -# Copy a default set of settings for PHP (php.ini). -COPY ./config/php.ini /etc/php/${PHP_VERSION}/mods-available/jetpack-wordpress.ini -RUN phpenmod jetpack-wordpress - # Copy single site htaccess to /var/lib/jetpack-config. run.sh will move it to the site's base dir if there's none present. COPY ./config/htaccess /var/lib/jetpack-config/htaccess COPY ./config/htaccess-multi /var/lib/jetpack-config/htaccess-multi diff --git a/tools/docker/README.md b/tools/docker/README.md index 0d53ad61a1825..6e36cebffd97c 100644 --- a/tools/docker/README.md +++ b/tools/docker/README.md @@ -212,6 +212,25 @@ wp> get_bloginfo( 'name' ); Note that each `wp shell` session counts as a single request, causing unexpected situations with WP cache. You might want to run [`wp_cache_flush()`](https://developer.wordpress.org/reference/functions/wp_cache_flush/) between requests you expect to get cached by WordPress. +### Changing PHP versions + +You can select different versions of PHP. For example, to use PHP 8.0 inside the container: + +```sh +jetpack docker select-php 8.0 +``` + +If you're already inside the container after using `jetpack docker sh`, you can use `select-php 8.0` there too. + +Note some caveats: + +* Running `jetpack docker down` or otherwise recreating the containers will reset to the default PHP version. +* If you're wanting the new version of PHP to be used to serve web requests, you'll need to `jetpack docker stop && jetpack docker up -d` or the like. +* You may need to update Composer packages from inside the container before you can successfully run `phpunit` or the like, in order to install a version compatible with the new version of PHP. + * For example, `jetpack docker exec -- composer -d /usr/local/src/jetpack-monorepo/projects/plugins/jetpack update` + * Be careful not to commit any resulting changes to `composer.lock` files! + * On Linux systems, doing this may result in odd ownership of files in relevant `vendor/` and `jetpack_vendor/` directories. Removing them (probaby using `sudo`) is a valid fix. + ## MySQL database You can see your database files via local file system at `./tools/docker/data/mysql` diff --git a/tools/docker/bin/select-php.sh b/tools/docker/bin/select-php.sh new file mode 100644 index 0000000000000..daf81c5ffa89a --- /dev/null +++ b/tools/docker/bin/select-php.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +set -eo pipefail + +source /etc/docker-args.sh + +VER=$1 +if [[ "$1" == default ]]; then + VER="$PHP_VERSION" +elif [[ ! "$1" =~ ^[0-9]+\.[0-9]+$ ]]; then + cat <<-EOF + USAGE: $0 + + may be "default" or a two-part version number like "$PHP_VERSION". + EOF + exit 1 +fi + +export DEBIAN_FRONTEND=noninteractive + +# Determine packages to install. +PKGS=( + "libapache2-mod-php${VER}" + "php${VER}" + "php${VER}-bcmath" + "php${VER}-cli" + "php${VER}-curl" + "php${VER}-intl" + "php${VER}-ldap" + "php${VER}-mbstring" + "php${VER}-mysql" + "php${VER}-opcache" + "php${VER}-pgsql" + "php${VER}-soap" + "php${VER}-sqlite3" + "php${VER}-xdebug" + "php${VER}-xml" + "php${VER}-xsl" + "php${VER}-zip" +) +NO_RECOMMENDS_PKGS=( + "php${VER}-apcu" + "php${VER}-gd" + "php${VER}-imagick" +) + +# php-json is built in in 8.0+. +if [[ "$VER" == [57].* ]]; then + PKGS+=( "php${VER}-json" ) +fi + +# Install selected packages. +printf '\e[1m== Installing PHP %s ==\e[0m\n' "$VER" +apt-get update -q +apt-get install -qy "${PKGS[@]}" +apt-get install -qy --no-install-recommends "${NO_RECOMMENDS_PKGS[@]}" + +# Enable our custom config for the new version. +[[ -e "/etc/php/${VER}/mods-available/jetpack-wordpress.ini" ]] || ln -s /var/lib/jetpack-config/php.ini "/etc/php/${VER}/mods-available/jetpack-wordpress.ini" +phpenmod -v "$VER" jetpack-wordpress + +# Upgrade or downgrade Composer if necessary. +if command -v composer &> /dev/null; then + printf '\n\e[1m== Installing Composer ==\e[0m\n' + if [[ "$VER" == 5.6 || "$VER" == 7.[01] ]]; then + CV=2.2.18 + else + CV="$COMPOSER_VERSION" + fi + # Execute with whichever version of PHP is newer. + if php -r 'exit( version_compare( PHP_VERSION, $argv[1], ">" ) ? 0 : 1 );' "$PHP_VERSION"; then + composer self-update "$CV" + else + "php$VER" "$(command -v composer)" self-update "$CV" + fi +fi + +# Select the new version to be used for stuff. +printf '\n\e[1m== Setting PHP %s as default ==\e[0m\n' "$VER" +for name in php phar phar.phar; do + update-alternatives --quiet --set "$name" "/usr/bin/$name$VER" +done +if a2query -m | grep -q "^php$VER "; then + : +else + if a2query -m | grep -q '^php'; then + a2dismod 'php*' + fi + a2enmod "php$VER" + + printf '\n\e[30;43mThe web server is still running the old version of PHP!\e[0m\n' + printf '\e[30;43mRestart the docker container (e.g. `jetpack docker stop && jetpack docker up -d`) to use the new version.\e[0m\n' +fi