Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 261 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
# Nothing here is allowed to fail softly. A security plugin whose test matrix is
# advisory is a security plugin nobody has tested.

# 8.1 is the floor, declared in composer.json, in the plugin header, in
# readme.txt, and enforced at runtime by the guard in basic-firewall.php. It is
# also what config.platform resolves the lock against, so it is the version the
# shipped dependency tree is chosen for.
PHP_FLOOR: &PHP_FLOOR "8.1"

# Not the floor. PHP-Scoper needs 8.3, and it is the only thing that runs on
# this PHP: the tree it rewrites is installed from the plugin's own lock. The
# zip is identical whichever PHP builds it.
PHP_TOOLS: &PHP_TOOLS "8.3"

MYSQL_IMAGE: &MYSQL_IMAGE "cimg/mysql:8.0"

version: 2.1

commands:
composer_install:
description: Install dependencies, cached against the lock file.
steps:
- restore_cache:
name: Restore Composer cache
keys:
# The PHP version is in the key because the cache holds vendor/
# itself. The lock resolves identically on every version -- that is
# what config.platform buys -- but a cache shared across images is
# a bet on that staying true.
- composer-v1-{{ checksum "composer.lock" }}-{{ arch }}-<< parameters.php >>
- run:
name: Install dependencies
command: composer install --no-interaction --no-progress
- save_cache:
name: Save Composer cache
key: composer-v1-{{ checksum "composer.lock" }}-{{ arch }}-<< parameters.php >>
paths:
- vendor
parameters:
php:
type: string

install_wp_cli:
description: WP-CLI, which the cimg/php images do not carry.
steps:
- run:
name: Install WP-CLI
command: |
curl -fsSL -o /tmp/wp-cli.phar \
https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x /tmp/wp-cli.phar
sudo mv /tmp/wp-cli.phar /usr/local/bin/wp
wp --info --allow-root

jobs:
static:
docker:
- image: cimg/php:<< parameters.php >>
parameters:
php:
type: string
default: *PHP_FLOOR
steps:
- checkout
- composer_install:
php: << parameters.php >>

# The plugin claims PHP 8.1, so nothing it ships may require more. The
# lock is resolved against that floor by config.platform; this asserts it,
# because the failure mode is a fatal on a customer's site rather than
# anything a test would catch.
- run:
name: The shipped tree must install on the declared floor
command: composer check-platform-reqs --no-dev
- run:
name: WordPress Coding Standards
command: composer lint
- run:
name: PHPStan
command: composer analyse

unit:
docker:
- image: cimg/php:<< parameters.php >>
parameters:
php:
type: string
steps:
- checkout
- composer_install:
php: << parameters.php >>
- run:
name: Unit tests
command: |
mkdir -p /tmp/test-results
vendor/bin/phpunit --testsuite unit \
--log-junit /tmp/test-results/unit.xml
- store_test_results:
path: /tmp/test-results

integration:
docker:
- image: cimg/php:<< parameters.php >>
environment:
# The plugin is symlinked into a throwaway site, and PHP resolves
# __DIR__ through the symlink back to the checkout, so the test
# bootstrap cannot find WordPress by walking up. It is told where the
# site is instead.
BASIC_FIREWALL_WP_ROOT: /tmp/wp
- image: *MYSQL_IMAGE
environment:
MYSQL_ROOT_PASSWORD: << pipeline.parameters.db_password >>
MYSQL_DATABASE: wordpress_test
parameters:
php:
type: string
wordpress:
type: string
steps:
- checkout
- composer_install:
php: << parameters.php >>
- install_wp_cli
- run:
name: Wait for MySQL
command: dockerize -wait tcp://127.0.0.1:3306 -timeout 2m
- run:
name: Install WordPress << parameters.wordpress >>
command: |
wp core download --version=<< parameters.wordpress >> --path=/tmp/wp --allow-root
wp config create --path=/tmp/wp --dbname=wordpress_test --dbuser=root \
--dbpass='<< pipeline.parameters.db_password >>' --dbhost=127.0.0.1 --allow-root
wp core install --path=/tmp/wp --url=http://localhost:8080 \
--title=CI --admin_user=admin --admin_password=password \
--admin_email=ci@example.com --skip-email --allow-root
- run:
name: Link the plugin into the site
command: |
mkdir -p /tmp/wp/wp-content/plugins
ln -s "$PWD" /tmp/wp/wp-content/plugins/basic-firewall
wp plugin activate basic-firewall --path=/tmp/wp --allow-root
- run:
name: Integration tests
command: |
mkdir -p /tmp/test-results
vendor/bin/phpunit -c phpunit-integration.xml.dist \
--log-junit /tmp/test-results/integration.xml

# The only tests here that exercise the mu-plugin, the compiled file on
# disk, the library's parse cache and the web server together -- which is
# where every defect this plugin has shipped so far lived.
- run:
name: Serve the site
command: php -S 127.0.0.1:8080 -t /tmp/wp
background: true
- run:
name: Wait for the site
command: dockerize -wait tcp://127.0.0.1:8080 -timeout 1m
- run:
name: End-to-end tests over real HTTP
command: |
mkdir -p /tmp/test-results
BFW_E2E_URL=http://127.0.0.1:8080 vendor/bin/phpunit -c phpunit-e2e.xml.dist \
--log-junit /tmp/test-results/e2e.xml

# Every subcommand, through `wp` rather than through PHP. What PHPUnit
# cannot reach is everything WP-CLI owns: whether a method is registered
# as a subcommand at all, whether its `## OPTIONS` block parses, and what
# the process exits with -- including that a destructive command exits
# NON-zero when it refuses, which is the one that has actually broken.
#
# Last in the job, and destructive: it replaces the rule set and empties
# the block list on a site that is thrown away with the container.
- run:
name: WP-CLI commands
environment:
BFW_WP: wp --path=/tmp/wp --allow-root
BFW_CLI_DESTRUCTIVE: 1
command: bash tests/cli/commands.sh
- store_test_results:
path: /tmp/test-results

package:
docker:
- image: cimg/php:<< parameters.php >>
parameters:
php:
type: string
default: *PHP_TOOLS
steps:
- checkout
- install_wp_cli
- run:
name: Build the zip
command: bash build/build-zip.sh

# The point of the whole exercise: a zip that works where there is no
# Composer at all. Installed onto a clean WordPress with the Composer
# binary removed from PATH, then actually evaluated against a request.
- run:
name: Install the zip on a clean WordPress with no Composer
command: |
wp core download --path=/tmp/clean --allow-root
wp config create --path=/tmp/clean --dbname=wp --dbuser=root \
--dbpass=root --dbhost=127.0.0.1 --skip-check --allow-root
sudo mv "$(command -v composer)" /usr/local/bin/composer.hidden
! command -v composer
unzip -q build/dist/*.zip -d /tmp/clean/wp-content/plugins/
test -d /tmp/clean/wp-content/plugins/basic-firewall/vendor
echo "vendor/ is present in the installed plugin."
- store_artifacts:
path: build/dist
destination: zip

parameters:
db_password:
type: string

# Not a secret: an ephemeral database that exists for one job and is thrown
# away with the container. It is long on purpose. CredentialHandlingTest
# searches the compiled configuration for the literal password to prove it
# never reaches disk, and skips that search -- loudly -- when the password
# is short enough for the search to prove nothing. A two-character password
# makes the assertion meaningless, which is the state the ddev site is in.
#
# A pipeline parameter rather than a YAML anchor because it is needed inside
# a command string, and an anchor cannot be referenced from one.
default: "circleci-integration-password-not-a-secret"

workflows:
ci:
jobs:
- static
- unit:
matrix:
parameters:
# 8.1 is the floor and 8.4 is what the library is developed
# against. Testing only the floor would let the plugin rot on
# modern PHP; testing only the top would let it break on the
# version it claims.
php: ["8.1", "8.2", "8.3", "8.4"]
name: unit-php-<< matrix.php >>
- integration:
matrix:
parameters:
php: ["8.1"]
wordpress: ["6.4"]
name: integration-wp6.4-php8.1
- integration:
matrix:
parameters:
php: ["8.3"]
wordpress: ["latest"]
name: integration-wplatest-php8.3
- integration:
matrix:
parameters:
php: ["8.4"]
wordpress: ["nightly"]
name: integration-wpnightly-php8.4
- package
Loading