Skip to content

Commit

Permalink
actions: Test upgrading of built plugins (#23271)
Browse files Browse the repository at this point in the history
After several problems with plugin upgrades failing since #23039, we
need a more robust CI test for plugin upgrades failing.

For each plugin built, this will test upgrading via the web UI and
wp-cli, from the latest stable (if any), from master, and from the built
version to a hypothetical future version.
  • Loading branch information
anomiex authored Mar 9, 2022
1 parent b047fb1 commit 2ae075b
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 26 deletions.
59 changes: 59 additions & 0 deletions .github/files/test-plugin-update/mu-plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
/**
* Plugin Name: Plugin Upgrade Test hacks
* Plugin URI: https://github.com/automattic/jetpack
* Author: Jetpack Team
* Version: 1.0.0
* Text Domain: jetpack
*
* @package automattic/jetpack
*/

// Force user ID 1 as the logged in user.
add_filter(
'determine_current_user',
function () {
return 1;
}
);

/** Disable the login cookie check. */
function wp_validate_auth_cookie() {
return true;
}

/** Disable the nonce check. */
function wp_verify_nonce() {
return true;
}

// Allow for forcing an "update" of a particular plugin.
add_filter(
'site_transient_update_plugins',
function ( $value ) {
$plugin = get_option( 'fake_plugin_update_plugin' );
$url = get_option( 'fake_plugin_update_url' );
if ( $plugin && $url ) {
if ( ! is_object( $value ) ) {
$value = (object) array(
'response' => array(),
'no_update' => array(),
);
}
if ( ! isset( $value->response[ $plugin ] ) ) {
if ( isset( $value->no_update[ $plugin ] ) ) {
$value->response[ $plugin ] = $value->no_update[ $plugin ];
unset( $value->no_update[ $plugin ] );
} else {
$value->response[ $plugin ] = (object) array(
'plugin' => dirname( $plugin ),
'slug' => dirname( $plugin ),
);
}
}
$value->response[ $plugin ]->new_version = '1000000.0.0';
$value->response[ $plugin ]->package = $url;
}
return $value;
}
);
65 changes: 65 additions & 0 deletions .github/files/test-plugin-update/prepare-zips.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/bash

set -eo pipefail

BETAJSON="$(curl -L --fail --url "https://betadownload.jetpack.me/plugins.json")"
jq -e '.' <<<"$BETAJSON" &>/dev/null

mkdir work
mkdir zips
while IFS=$'\t' read -r SRC MIRROR SLUG; do
echo "::group::Creating $SLUG-dev.zip"
mv "build/$MIRROR" "work/$SLUG"
touch "work/$SLUG/ci-flag.txt"
(cd work && zip -r "../zips/${SLUG}-dev.zip" "$SLUG")
rm -rf "work/$SLUG"
echo "::endgroup::"

echo "::group::Fetching $SLUG-master.zip..."
BETASLUG="$(jq -r '.extra["beta-plugin-slug"] // .extra["wp-plugin-slug"] // ""' "monorepo/$SRC/composer.json")"
if [[ -z "$BETASLUG" ]]; then
echo "No beta-plugin-slug or wp-plugin-slug in composer.json, skipping"
else
URL="$(jq -r --arg slug "$BETASLUG" '.[$slug].manifest_url // ""' <<<"$BETAJSON")"
if [[ -z "$URL" ]]; then
echo "Beta slug $BETASLUG is not in plugins.json, skipping"
else
JSON="$(curl -L --fail --url "$URL")"
if jq -e '.' <<<"$JSON" &>/dev/null; then
URL="$(jq -r '.master.download_url // ""' <<<"$JSON")"
if [[ -z "$URL" ]]; then
echo "Plugin has no master build."
else
curl -L --fail --url "$URL" --output "work/tmp.zip" 2>&1
(cd work && unzip -q tmp.zip)
mv "work/$BETASLUG-dev" "work/$SLUG"
(cd work && zip -qr "../zips/${SLUG}-master.zip" "$SLUG")
rm -rf "work/$SLUG" "work/tmp.zip"
fi
else
echo "::error::Unexpected response from betadownload.jetpack.me for $SLUG"
echo "$JSON"
exit 1
fi
fi
fi
echo "::endgroup::"

echo "::group::Fetching $SLUG-stable.zip..."
JSON="$(curl "https://api.wordpress.org/plugins/info/1.0/$SLUG.json")"
if jq -e --arg slug "$SLUG" '.slug == $slug' <<<"$JSON" &>/dev/null; then
URL="$(jq -r '.download_link // ""' <<<"$JSON")"
if [[ -z "$URL" ]]; then
echo "Plugin has no stable release."
else
curl -L --fail --url "$URL" --output "zips/$SLUG-stable.zip" 2>&1
fi
elif jq -e '.error == "Plugin not found."' <<<"$JSON" &>/dev/null; then
echo "Plugin is not published."
else
echo "::error::Unexpected response from WordPress.org API for $SLUG"
echo "$JSON"
exit 1
fi
echo "::endgroup::"
done < build/plugins.tsv
32 changes: 32 additions & 0 deletions .github/files/test-plugin-update/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/bash

set -eo pipefail

echo "::group::Setup database"
cat <<EOF > ~/.my.cnf
[client]
host=${MYSQL_HOST%:*}
port=${MYSQL_HOST#*:}
user=$MYSQL_USER
password=$MYSQL_PASSWORD
EOF
chmod 0600 ~/.my.cnf
mysql -e "set global wait_timeout = 3600;"
mysql -e "drop database if exists wordpress;"
mysql -e "create database wordpress;"
echo "::endgroup::"

echo "::group::Setup WordPress"
mkdir -p /var/log/php/ /var/scripts/
cd /var/www/html
sed -i 's/apachectl -D FOREGROUND/apachectl start/' /usr/local/bin/run
echo '#!/bin/bash' > /var/scripts/run-extras.sh
/usr/local/bin/run
echo "::endgroup::"

echo "::group::Install WordPress"
wp --allow-root core install --url="$WP_DOMAIN" --title="$WP_TITLE" --admin_user="$WP_ADMIN_USER" --admin_password="$WP_ADMIN_PASSWORD" --admin_email="$WP_ADMIN_EMAIL" --skip-email
rm -f index.html
mkdir -p wp-content/mu-plugins
cp "$GITHUB_WORKSPACE/monorepo/.github/files/test-plugin-update/mu-plugin.php" wp-content/mu-plugins/hack.php
echo "::endgroup::"
64 changes: 64 additions & 0 deletions .github/files/test-plugin-update/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/bin/bash

set -eo pipefail

ZIPDIR="$GITHUB_WORKSPACE/zips"
EXIT=0

SLUGS=()
cd "$ZIPDIR"
for ZIP in *-dev.zip; do
SLUGS+=( "${ZIP%-dev.zip}" )
done

cd /var/www/html
for SLUG in "${SLUGS[@]}"; do
for FROM in stable master dev; do
for HOW in web cli; do
[[ -e "$ZIPDIR/$SLUG-$FROM.zip" ]] || continue

echo "::group::Installing $SLUG $FROM"
wp --allow-root plugin install --activate "$ZIPDIR/$SLUG-$FROM.zip"
rm -f "/var/www/html/wp-content/plugins/$SLUG/ci-flag.txt"
# TODO: Connect Jetpack, since most upgrades will happen while connected.
echo "::endgroup::"

echo "::group::Upgrading $SLUG via $HOW"
P="$(wp --allow-root plugin path "$SLUG" | sed 's!^/var/www/html/wp-content/plugins/!!')"
wp --allow-root --quiet option set fake_plugin_update_plugin "$P"
wp --allow-root --quiet option set fake_plugin_update_url "$ZIPDIR/$SLUG-dev.zip"
: > /var/www/html/wp-content/debug.log
if [[ "$HOW" == 'cli' ]]; then
if ! wp --allow-root plugin upgrade "$SLUG" 2>&1 | tee "$GITHUB_WORKSPACE/out.txt"; then
echo "::error::CLI upgrade of $SLUG from $FROM exited with a non-zero status"
EXIT=1
fi
else
# Everything needs to be owned by www-data for the web upgrade to proceed.
chown -R www-data:www-data /var/www/html
curl -v --get --url 'http://localhost/wp-admin/update.php?action=upgrade-plugin&_wpnonce=bogus' --data "plugin=$P" --output "$GITHUB_WORKSPACE/out.txt" 2>&1
cat "$GITHUB_WORKSPACE/out.txt"
fi
echo '== Debug log =='
cat /var/www/html/wp-content/debug.log
echo "::endgroup::"
ERR="$(grep -i 'Fatal error' /var/www/html/wp-content/debug.log || true)"
if [[ -n "$ERR" ]]; then
echo "::error::Mid-upgrade fatal detected for $SLUG $HOW update from $FROM!%0A$ERR"
EXIT=1
elif [[ ! -e "/var/www/html/wp-content/plugins/$SLUG/ci-flag.txt" ]]; then
echo "::error::Plugin $SLUG ($HOW update from $FROM) does not seem to have been updated?"
EXIT=1
fi

echo "::group::Uninstalling $SLUG"
wp --allow-root plugin deactivate "$SLUG"
wp --allow-root plugin uninstall "$SLUG"
wp --allow-root --quiet option delete fake_plugin_update_plugin
wp --allow-root --quiet option delete fake_plugin_update_url
echo "::endgroup::"
done
done
done

exit $EXIT
102 changes: 76 additions & 26 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jobs:
env:
# Hard-code a specific directory to avoid paths in vendor/composer/installed.json changing every build.
BUILD_BASE: /tmp/jetpack-build
outputs:
any_plugins: ${{ steps.plugins.outputs.any }}

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -60,6 +62,28 @@ jobs:
pnpx jetpack build -v --no-pnpm-install --for-mirrors="$BUILD_BASE" "${PROJECTS[@]}"
fi
- name: Filter mirror list for release branch
if: contains( github.ref, '/branch-' )
env:
BUILD_BASE: ${{ github.workspace }}/build
run: .github/files/filter-mirrors-for-release-branch.sh

- name: Determine plugins to publish
id: plugins
run: |
jq -r 'if .extra["mirror-repo"] and ( .extra["beta-plugin-slug"] // .extra["wp-plugin-slug"] ) then [ ( input_filename | sub( "/composer\\.json$"; "" ) ), .extra["mirror-repo"], .extra["beta-plugin-slug"] // .extra["wp-plugin-slug"] ] else empty end | @tsv' projects/plugins/*/composer.json | while IFS=$'\t' read -r SRC MIRROR SLUG; do
if [[ -e "$BUILD_BASE/$MIRROR" ]] && grep -q --fixed-strings --line-regexp "$MIRROR" "$BUILD_BASE/mirrors.txt"; then
printf '%s\t%s\t%s\n' "$SRC" "$MIRROR" "$SLUG"
fi
done > "$BUILD_BASE/plugins.tsv"
if [[ -s "$BUILD_BASE/plugins.tsv" ]]; then
cat "$BUILD_BASE/plugins.tsv"
echo "::set-output name=any::true"
else
echo "No plugins were built"
echo "::set-output name=any::false"
fi
# GitHub's artifact stuff doesn't preserve permissions or file case. Sigh.
# This is the official workaround: https://github.com/actions/upload-artifact#maintaining-file-permissions-and-case-sensitive-files
# It'll also make it faster to upload and download though, so maybe it's a win anyway.
Expand All @@ -74,11 +98,61 @@ jobs:
# Only need to retain for a day since the beta builder slurps it up to distribute.
retention-days: 1

upgrade_test:
name: Test plugin upgrades
runs-on: ubuntu-latest
needs: build
if: needs.build.outputs.any_plugins == 'true'
timeout-minutes: 10 # 2022-03-04: Successful runs seem to take about 3 minutes, but give some extra time for the downloads.
services:
db:
image: mariadb:latest
env:
MARIADB_ROOT_PASSWORD: wordpress
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
container:
image: ghcr.io/automattic/jetpack-wordpress-dev:latest
env:
WP_DOMAIN: localhost
WP_ADMIN_USER: wordpress
WP_ADMIN_EMAIL: wordpress@example.com
WP_ADMIN_PASSWORD: wordpress
WP_TITLE: Hello World
MYSQL_HOST: db:3306
MYSQL_DATABASE: wordpress
MYSQL_USER: root
MYSQL_PASSWORD: wordpress
HOST_PORT: 80
ports:
- 80:80
steps:
- uses: actions/checkout@v2
with:
path: monorepo

- name: Download build artifact
uses: actions/download-artifact@v2
with:
name: jetpack-build
- name: Extract build archive
run: tar --xz -xvvf build.tar.xz

- name: Setup WordPress
run: monorepo/.github/files/test-plugin-update/setup.sh

- name: Prepare plugin zips
run: monorepo/.github/files/test-plugin-update/prepare-zips.sh

- name: Test upgrades
run: monorepo/.github/files/test-plugin-update/test.sh

jetpack_beta:
name: Create artifact for Jetpack Beta plugin
runs-on: ubuntu-latest
needs: build
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Automattic/jetpack'
if: ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Automattic/jetpack' ) && needs.build.outputs.any_plugins == 'true'
timeout-minutes: 10 # 2021-06-24: Successful runs should take just a few seconds now. But sometimes the upload is slow.
steps:
- uses: actions/checkout@v2
Expand All @@ -92,22 +166,6 @@ jobs:
- name: Extract build archive
run: tar --xz -xvvf build.tar.xz

- name: Filter mirror list for release branch
if: contains( github.ref, '/branch-' )
working-directory: monorepo
env:
BUILD_BASE: ${{ github.workspace }}/build
run: .github/files/filter-mirrors-for-release-branch.sh

- name: Determine plugins to publish
run: |
jq -r 'if .extra["mirror-repo"] and ( .extra["beta-plugin-slug"] // .extra["wp-plugin-slug"] ) then [ ( input_filename | sub( "/composer\\.json$"; "" ) ), .extra["mirror-repo"], .extra["beta-plugin-slug"] // .extra["wp-plugin-slug"] ] else empty end | @tsv' monorepo/projects/plugins/*/composer.json > plugins.tsv
cat plugins.tsv
if [[ ! -s plugins.tsv ]]; then
echo "No plugins were detected?!"
exit 1
fi
- name: Prepare plugin zips
id: prepare
run: |
Expand Down Expand Up @@ -160,7 +218,7 @@ jobs:
PLUGIN_DATA=$( jq -c --arg slug "$SLUG" --arg ver "$CURRENT_VERSION" '.[ $slug ] = { version: $ver }' <<<"$PLUGIN_DATA" )
echo "::endgroup::"
done < plugins.tsv
done < build/plugins.tsv
if [[ "$PLUGIN_DATA" == "{}" ]]; then
echo "No plugins were built"
fi
Expand Down Expand Up @@ -212,14 +270,6 @@ jobs:
run: tar --xz -xvvf build.tar.xz
timeout-minutes: 1 # 2021-01-18: Successful runs seem to take a few seconds

- name: Filter mirror list for release branch
if: contains( github.ref, '/branch-' )
working-directory: monorepo
env:
BUILD_BASE: ${{ github.workspace }}/build
run: .github/files/filter-mirrors-for-release-branch.sh
timeout-minutes: 1 # 2021-01-29: Guessing a successful run should only take a few seconds

- name: Wait for prior instances of the workflow to finish
uses: softprops/turnstyle@v1
env:
Expand Down

0 comments on commit 2ae075b

Please sign in to comment.