Skip to content

Commit

Permalink
docker: Add a --php option to jetpack docker phpunit (#32979)
Browse files Browse the repository at this point in the history
To make it easier to run tests locally with a specified version of PHP.

This turns out to be a bit more complex than it sounds, as we need to be
able to install the necessary version of PHP inside the container and we
need to make sure the correct versions of Composer deps (particularly
phpunit) are installed too. And then use those rather than whatever is
in the monorepo install.
  • Loading branch information
anomiex authored Sep 26, 2023
1 parent 54b94c5 commit 8cff793
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: other
Comment: Update phpunit setup for `jetpack docker phpunit --php`


Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: other
Comment: Remove unnecessary Patchwork include from test. Bootstrap should already have loaded it.


Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

require_once JETPACK__PLUGIN_DIR . '/_inc/blogging-prompts.php';
require_once JETPACK__PLUGIN_DIR . '/vendor/antecedent/patchwork/Patchwork.php';

/**
* Class for testing Jetpack Blogging Prompt functions.
Expand Down
5 changes: 5 additions & 0 deletions projects/plugins/jetpack/tests/php/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@
exit( 1 );
}

// If we're running under `jetpack docker phpunit --php`, load the autoloader for that.
if ( getenv( 'DOCKER_PHPUNIT_BASE_DIR' ) ) {
require getenv( 'DOCKER_PHPUNIT_BASE_DIR' ) . '/vendor/autoload.php';
}

require $jp_autoloader;

if ( '1' !== getenv( 'WP_MULTISITE' ) && ( ! defined( 'WP_TESTS_MULTISITE' ) || ! WP_TESTS_MULTISITE ) ) {
Expand Down
7 changes: 6 additions & 1 deletion projects/plugins/jetpack/tests/php/redefine-exit.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
class ExitException extends Exception {
}

require_once __DIR__ . '/../../vendor/antecedent/patchwork/Patchwork.php';
// If we're running under `jetpack docker phpunit --php`, Patchwork is located in DOCKER_PHPUNIT_BASE_DIR.
if ( getenv( 'DOCKER_PHPUNIT_BASE_DIR' ) ) {
require_once getenv( 'DOCKER_PHPUNIT_BASE_DIR' ) . '/vendor/antecedent/patchwork/Patchwork.php';
} else {
require_once __DIR__ . '/../../vendor/antecedent/patchwork/Patchwork.php';
}

$exitfunc = function ( $arg = null ) {
// While Patchwork does have a way to exclude files from replacement,
Expand Down
42 changes: 36 additions & 6 deletions tools/cli/commands/docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ const buildExecCmd = argv => {

opts.splice( 1, 0, '-w', '/var/www/html/wp-content/plugins/jetpack' ); // Need to add this option to `exec` before the container name.
opts.push(
'vendor/bin/phpunit',
...( argv.php
? [ '/var/scripts/phpunit-version-wrapper.sh', argv.php ]
: [ 'vendor/bin/phpunit' ] ),
'--configuration=/var/www/html/wp-content/plugins/jetpack/phpunit.xml.dist',
...unitArgs
);
Expand All @@ -356,7 +358,9 @@ const buildExecCmd = argv => {

opts.splice( 1, 0, '-w', '/var/www/html/wp-content/plugins/jetpack' ); // Need to add this option to `exec` before the container name.
opts.push(
'vendor/bin/phpunit',
...( argv.php
? [ '/var/scripts/phpunit-version-wrapper.sh', argv.php ]
: [ 'vendor/bin/phpunit' ] ),
'--configuration=/var/www/html/wp-content/plugins/jetpack/tests/php.multisite.xml',
...unitArgs
);
Expand All @@ -367,7 +371,9 @@ const buildExecCmd = argv => {

opts.splice( 1, 0, '-w', '/var/www/html/wp-content/plugins/crm' ); // Need to add this option to `exec` before the container name.
opts.push(
'vendor/bin/phpunit',
...( argv.php
? [ '/var/scripts/phpunit-version-wrapper.sh', argv.php ]
: [ 'vendor/bin/phpunit' ] ),
'--configuration=/var/www/html/wp-content/plugins/crm/phpunit.xml.dist',
...unitArgs
);
Expand Down Expand Up @@ -624,10 +630,26 @@ export function dockerDefine( yargs ) {
builder: yargExec => defaultOpts( yargExec ),
handler: argv => execDockerCmdHandler( argv ),
} )
.command( {
command: 'select-php <version>',
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',
builder: yargExec => defaultOpts( yargExec ),
builder: yargCmd =>
defaultOpts( yargCmd ).option( 'php', {
describe: 'Use the specified version of PHP.',
type: 'string',
} ),
handler: argv => execDockerCmdHandler( argv ),
} )
.command( {
Expand All @@ -652,14 +674,22 @@ export function dockerDefine( yargs ) {
command: 'phpunit-multisite',
alias: 'phpunit:multisite',
description: 'Run multisite PHPUnit tests inside container ',
builder: yargExec => defaultOpts( yargExec ),
builder: yargCmd =>
defaultOpts( yargCmd ).option( 'php', {
describe: 'Use the specified version of PHP.',
type: 'string',
} ),
handler: argv => execDockerCmdHandler( argv ),
} )
.command( {
command: 'phpunit-crm',
alias: 'phpunit:crm',
description: 'Run Jetpack CRM PHPUnit inside container',
builder: yargExec => defaultOpts( yargExec ),
builder: yargCmd =>
defaultOpts( yargCmd ).option( 'php', {
describe: 'Use the specified version of PHP.',
type: 'string',
} ),
handler: argv => execDockerCmdHandler( argv ),
} )
.command( {
Expand Down
37 changes: 10 additions & 27 deletions tools/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -41,31 +48,11 @@ RUN --mount=type=cache,target=/var/lib/apt/lists/,sharing=private \
RUN a2enmod rewrite

# Install requested version of PHP.
COPY ./config/php.ini /var/lib/jetpack-config/php.ini
RUN --mount=type=cache,target=/var/lib/apt/lists/,sharing=private \
--mount=type=bind,source=./bin/ensure-php-version.sh,target=/usr/local/bin/ensure-php-version.sh \
: "${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 \
&& ensure-php-version.sh "$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.
Expand Down Expand Up @@ -109,10 +96,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
Expand Down
60 changes: 60 additions & 0 deletions tools/docker/bin/ensure-php-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/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 <version>
<version> 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
48 changes: 48 additions & 0 deletions tools/docker/bin/phpunit-version-wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/bash

set -eo pipefail

VER="$1"
DIR="/usr/local/src/phpunit-for-$VER/$( basename "$PWD" )"
shift

/var/scripts/ensure-php-version.sh "$VER"

printf '\n\e[1m== Installing Composer deps for PHP %s externally ==\e[0m\n' "$VER"
mkdir -p "$DIR"
jq --arg PWD "$PWD" --arg VER "$( "php$VER" -r 'printf( "%d.%d.%d", PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION );' )" '
{
config: {
platform: {
php: $VER,
},
},
repositories: [
{
type: "path",
url: ( $PWD + "/../../packages/*" ),
options: {
monorepo: true,
},
}
],
"require-dev": .["require-dev"],
}
' composer.json > "$DIR/composer.json"
composer --working-dir="$DIR" update

printf '\n\e[1m== Uninstalling Composer dev deps from monorepo ==\e[0m\n'
composer install --no-dev
# The above may have created files owned by the in-docker user ID, which probably doesn't match the user ID on the host system.
# Avoid confusing users later by changing the ownership of such files.
if [[ $(stat -c %u .) -ne $EUID ]]; then
find -P vendor jetpack_vendor -xdev -user "$EUID" -exec chown --reference=. -h {} + &>/dev/null || true
fi

echo
printf '\e[30;43m ** Note contents of vendor/ have been changed! ** \e[0m\n'
printf '\e[30;43m ** You may want to run `jetpack install` when done testing to fix it. ** \e[0m\n'

printf '\n\e[1m== Running phpunit ==\e[0m\n'
export DOCKER_PHPUNIT_BASE_DIR="$DIR"
exec "php$VER" "$DIR/vendor/bin/phpunit" "$@"

0 comments on commit 8cff793

Please sign in to comment.