diff --git a/.circleci/.gitignore b/.circleci/.gitignore new file mode 100644 index 0000000000..b0e880b773 --- /dev/null +++ b/.circleci/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +local.test +local.env +local.ssh diff --git a/.circleci/behat.yml b/.circleci/behat.yml new file mode 100644 index 0000000000..12c5964322 --- /dev/null +++ b/.circleci/behat.yml @@ -0,0 +1,14 @@ +# behat.yml +default: + suites: + default: + paths: + - features + contexts: + - Behat\MinkExtension\Context\MinkContext + - AdminLogIn + - ResponseHeader + extensions: + Behat\MinkExtension: + # base_url set by ENV + goutte: ~ diff --git a/.circleci/cleanup.sh b/.circleci/cleanup.sh new file mode 100755 index 0000000000..43f85c472a --- /dev/null +++ b/.circleci/cleanup.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Echo commands as they are executed, but don't allow errors to stop the script. +set -x + +if [ -z "$TERMINUS_SITE" ] || [ -z "$TERMINUS_ENV" ]; then + echo "TERMINUS_SITE and TERMINUS_ENV environment variables must be set" + exit 1 +fi + +# Only delete old environments if there is a pattern defined to +# match environments eligible for deletion. Otherwise, delete the +# current multidev environment immediately. +# +# To use this feature, set MULTIDEV_DELETE_PATTERN to '^ci-' or similar +# in the CI server environment variables. +if [ -z "$MULTIDEV_DELETE_PATTERN" ] ; then + terminus env:delete $TERMINUS_SITE.$TERMINUS_ENV --delete-branch --yes + exit 0 +fi + +# List all but the newest two environments. +OLDEST_ENVIRONMENTS=$(terminus env:list "$TERMINUS_SITE" --format=list | grep -v dev | grep -v test | grep -v live | sort -k2 | grep "$MULTIDEV_DELETE_PATTERN" | sed -e '$d' | sed -e '$d') + +# Exit if there are no environments to delete +if [ -z "$OLDEST_ENVIRONMENTS" ] ; then + exit 0 +fi + +# Go ahead and delete the oldest environments. +for ENV_TO_DELETE in $OLDEST_ENVIRONMENTS ; do + terminus env:delete $TERMINUS_SITE.$ENV_TO_DELETE --delete-branch --yes +done diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..e0dc6fb359 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,58 @@ +test-defaults: &test-defaults + docker: + - image: quay.io/pantheon-public/terminus-plugin-test:1.x + working_directory: ~/work/wp + environment: + TZ: "/usr/share/zoneinfo/America/Los_Angeles" + TERM: dumb + +merge-defaults: &merge-defaults + docker: + - image: quay.io/getpantheon/upstream-update-build:1.x + working_directory: ~/work/wp + environment: + TZ: "/usr/share/zoneinfo/America/Los_Angeles" + TERM: dumb + +version: 2 +jobs: + test: + <<: *test-defaults + steps: + - checkout + - run: + name: Set up environment + command: ./.circleci/set-up-globals.sh + - run: + name: Prepare + command: ./.circleci/prepare.sh + - run: + name: Test + command: ./.circleci/test.sh --strict + - run: + name: Cleanup + command: ./.circleci/cleanup.sh + - run: + name: Confirm that it is safe to merge + command: ./.circleci/confirm-safety.sh + merge: + <<: *merge-defaults + steps: + - checkout + - run: + # https://github.com/pantheon-systems/upstream-update-build/blob/1.x/bin/automerge.sh + name: Merge the default branch back to the master branch + command: automerge.sh + +workflows: + version: 2 + wordpress: + jobs: + - test + - merge: + requires: + - test + filters: + branches: + only: + - default diff --git a/.circleci/confirm-safety.sh b/.circleci/confirm-safety.sh new file mode 100755 index 0000000000..1a6f717101 --- /dev/null +++ b/.circleci/confirm-safety.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# +# The purpose of this script is to examine the base branch that this PR is +# set to merge into by usig the GitHub API. We are only querying a public +# repo here, so we do not need to use the GITHUB_TOKEN. +# + +# Exit if we are not running on Circle CI. +if [ -z "$CIRCLECI" ] ; then + exit 0 +fi + +# We only need to make this check for branches forked from default (right) / master (wrong). +# Skip the test for the default branch. (The .circleci directory will never be added to the master branch). +if [ "$CIRCLE_BRANCH" == "default" ] ; then + exit 0 +fi + +# We cannot continue unless we have a pull request. +if [ -z "$CIRCLE_PULL_REQUEST" ] ; then + echo "No CIRCLE_PULL_REQUEST defined; please create a pull request." + exit 1 +fi + +# CIRCLE_PULL_REQUEST=https://github.com/ORG/PROJECT/pull/NUMBER +PR_NUMBER=$(echo $CIRCLE_PULL_REQUEST | sed -e 's#.*/pull/##') + +# Display the API call we are using +echo curl https://api.github.com/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pulls/$PR_NUMBER + +base=$(curl https://api.github.com/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pulls/$PR_NUMBER 2>/dev/null | jq .base.ref) + +echo "The base branch is $base" + +# If the PR merges into 'default', then it is safe to merge. +if [ "$base" == '"default"' ] ; then + echo "It is safe to merge this PR into the $base branch" + exit 0 +fi + +# Force a test failure if the PR's base is the master branch. +if [ "$base" == '"master"' ] ; then + echo "ERROR: merging this PR into the $base branch is not allowed. Change the base branch for the PR to merge into the \"default\" branch instead." + exit 1 +fi + +echo "Merging probably okay, if you are merging one PR into another. Use caution; do not merge to the \"master\" branch." diff --git a/.circleci/features/0-install.feature b/.circleci/features/0-install.feature new file mode 100644 index 0000000000..fa61da7311 --- /dev/null +++ b/.circleci/features/0-install.feature @@ -0,0 +1,59 @@ +Feature: Install WordPress through the web UI + + @upstreamonly + Scenario: Install WordPress with the en_US locale + When I go to "/" + Then print current URL + And I should be on "/wp-admin/install.php" + + When I press "language-continue" + Then print current URL + And I should be on "/wp-admin/install.php?step=1" + And I should see "Welcome to the famous five-minute WordPress installation process!" + + When I fill in "weblog_title" with "Pantheon WordPress Upstream" + And I fill in "user_name" with the command line global variable: "WORDPRESS_ADMIN_USERNAME" + And I fill in "admin_password" with the command line global variable: "WORDPRESS_ADMIN_PASSWORD" + And I fill in "admin_password2" with the command line global variable: "WORDPRESS_ADMIN_PASSWORD" + And I check "pw_weak" + And I fill in "admin_email" with "wordpress-upstream@getpantheon.com" + And I press "submit" + Then print current URL + And I should be on "/wp-admin/install.php?step=2" + And I should see "WordPress has been installed." + And I follow "Log In" + And I fill in "Username or Email Address" with the command line global variable: "WORDPRESS_ADMIN_USERNAME" + And I fill in "Password" with the command line global variable: "WORDPRESS_ADMIN_PASSWORD" + And I press "Log In" + And I should see "Welcome to WordPress!" + + Scenario: Attempting to install WordPress a second time should error + When I go to "/wp-admin/install.php" + Then I should see "You appear to have already installed WordPress." + + Scenario: Verify the active theme is Twenty-Something (no point breaking every year for no good reason) + When I go to "/" + Then the response should contain "minkContext->visit('wp-login.php'); + $this->minkContext->fillField('log', getenv('WORDPRESS_ADMIN_USERNAME')); + $this->minkContext->fillField('pwd', getenv('WORDPRESS_ADMIN_PASSWORD')); + $this->minkContext->pressButton('wp-submit'); + $this->minkContext->assertPageAddress("wp-admin/"); + } + + /** + * Fills in form field with specified id|name|label|value + * Example: When I fill in "admin_password2" with the command line global variable: "WORDPRESS_ADMIN_PASSWORD" + * + * @When I fill in :arg1 with the command line global variable: :arg2 + */ + public function fillFieldWithGlobal($field, $value) + { + $this->minkContext->fillField($field, getenv($value)); + } +} diff --git a/.circleci/features/bootstrap/ResponseHeader.php b/.circleci/features/bootstrap/ResponseHeader.php new file mode 100644 index 0000000000..ba463d366b --- /dev/null +++ b/.circleci/features/bootstrap/ResponseHeader.php @@ -0,0 +1,46 @@ +(?:[^"]|\\")*)" should be "(?P(?:[^"]|\\")*)"$/ + */ + public function assertResponseHeader($name, $value) + { + $this->assertSession()->responseHeaderEquals($name, $value); + } + + /** + * Checks, that current page response header is not equal to specified. + * + * @Then /^the response header "(?P(?:[^"]|\\")*)" should not be "(?P(?:[^"]|\\")*)"$/ + */ + public function assertResponseHeaderIsNot($name, $value) + { + $this->assertSession()->responseHeaderNotEquals($name, $value); + } + + /** + * Checks, that current page response header contains specified value. + * + * @Then /^the response header "(?P(?:[^"]|\\")*)" should contain "(?P(?:[^"]|\\")*)"$/ + */ + public function assertResponseHeaderContains($name, $value) + { + $this->assertSession()->responseHeaderContains($name, $value); + } + /** + * Checks, that current page response header does not contain specified value. + * + * @Then /^the response header "(?P(?:[^"]|\\")*)" should not contain "(?P(?:[^"]|\\")*)"$/ + */ + public function assertResponseHeaderNotContains($name, $value) + { + $this->assertSession()->responseHeaderNotContains($name, $value); + } + +} diff --git a/.circleci/features/options.feature b/.circleci/features/options.feature new file mode 100644 index 0000000000..ac2e02d9b1 --- /dev/null +++ b/.circleci/features/options.feature @@ -0,0 +1,27 @@ +Feature: Manage WordPress options + + Background: + Given I log in as an admin + + Scenario: Update the site tagline + When I go to "/" + Then I should see "Just another WordPress site" in the ".site-description" element + And I should not see "Pantheon upstream testing site" in the ".site-description" element + + When I go to "/wp-admin/options-general.php" + And I fill in "blogdescription" with "Pantheon upstream testing site" + And I press "submit" + Then I should see "Settings saved." + + When I go to "/" + Then I should see "Pantheon upstream testing site" in the ".site-description" element + Then I should not see "Just another WordPress site" in the ".site-description" element + + When I go to "/wp-admin/options-general.php" + And I fill in "blogdescription" with "Just another WordPress site" + And I press "submit" + Then I should see "Settings saved." + + When I go to "/" + Then I should see "Just another WordPress site" in the ".site-description" element + And I should not see "Pantheon upstream testing site" in the ".site-description" element diff --git a/.circleci/features/pantheon-logged-out.feature b/.circleci/features/pantheon-logged-out.feature new file mode 100644 index 0000000000..1cf898edf8 --- /dev/null +++ b/.circleci/features/pantheon-logged-out.feature @@ -0,0 +1,7 @@ +Feature: Verify various Pantheon features as a logged-out user + + Scenario: Cache-Control should default to TTL=600 + When I go to "/" + And the response header "Cache-Control" should be "public, max-age=600" + And the response header "Pragma" should not contain "no-cache" + diff --git a/.circleci/features/pantheon.feature b/.circleci/features/pantheon.feature new file mode 100644 index 0000000000..49c94fba46 --- /dev/null +++ b/.circleci/features/pantheon.feature @@ -0,0 +1,35 @@ +Feature: Perform Pantheon-specific actions + + Background: + Given I log in as an admin + + Scenario: Change the cache TTL + When I go to "/wp-admin/options-general.php?page=pantheon-cache" + Then I should see "Pantheon Page Cache" + And the "pantheon-cache[default_ttl]" field should contain "600" + + When I fill in "pantheon-cache[default_ttl]" with "300" + And I press "Update TTL" + Then I should see "Settings saved." + And the "pantheon-cache[default_ttl]" field should contain "300" + + When I fill in "pantheon-cache[default_ttl]" with "600" + And I press "Update TTL" + Then I should see "Settings saved." + And the "pantheon-cache[default_ttl]" field should contain "600" + + Scenario: Clear the site cache + When I go to "/wp-admin/options-general.php?page=pantheon-cache" + Then I should see "Clear Site Cache" + And I should not see "Site cache flushed." + + When I press "Clear Cache" + Then print current URL + And I should be on "/wp-admin/options-general.php?page=pantheon-cache&cache-cleared=true" + And I should see "Site cache flushed." in the ".updated" element + + Scenario: Verify the Pantheon MU plugin is present + When I go to "/wp-admin/plugins.php?plugin_status=mustuse" + Then I should see "Files in the /wp-content/mu-plugins directory are executed automatically." in the ".tablenav" element + And I should see "Pantheon" in the "#the-list" element + And I should see "Building on Pantheon's and WordPress's strengths, together." in the "#the-list" element diff --git a/.circleci/features/plugins.feature b/.circleci/features/plugins.feature new file mode 100644 index 0000000000..c3beef2b04 --- /dev/null +++ b/.circleci/features/plugins.feature @@ -0,0 +1,33 @@ +Feature: Manage WordPress plugins + + Background: + Given I log in as an admin + + @upstreamonly + Scenario: Install, activate, deactivate, and delete a plugin + When I go to "/wp-admin/plugin-install.php?tab=search&s=hello+dolly" + And I follow "Hello Dolly" + Then print current URL + Then I should see "Hello Dolly" in the "#plugin-information-title" element + + When I follow "Install Now" + Then print current URL + And I should see "Successfully installed the plugin Hello Dolly" + + When I follow "Activate Plugin" + Then print current URL + And I should see "Plugin activated." in the "#message" element + And I should see a "#dolly" element + And I should see "1 item" in the ".displaying-num" element + + When I follow "Deactivate" + Then print current URL + And I should see "Plugin deactivated." in the "#message" element + + When I follow "Delete" + Then I should see "You are about to remove the following plugin:" + + When I press "submit" + Then print current URL + And I should see "The selected plugin has been deleted." in the "#message" element + And I should see "You do not appear to have any plugins available at this time." diff --git a/.circleci/features/terms.feature b/.circleci/features/terms.feature new file mode 100644 index 0000000000..fa0c4455fb --- /dev/null +++ b/.circleci/features/terms.feature @@ -0,0 +1,12 @@ +Feature: Manage WordPress terms + + Background: + Given I log in as an admin + + Scenario: Create a new tag + When I go to "/wp-admin/edit-tags.php?taxonomy=post_tag" + And I fill in "tag-name" with "Pantheon Testing Tag" + And I press "submit" + Then print current URL + And I should see "Tag added." + And I should see "Pantheon Testing Tag" diff --git a/.circleci/features/themes.feature b/.circleci/features/themes.feature new file mode 100644 index 0000000000..25ed5642e6 --- /dev/null +++ b/.circleci/features/themes.feature @@ -0,0 +1 @@ +Feature: Manage WordPress themes diff --git a/.circleci/features/users.feature b/.circleci/features/users.feature new file mode 100644 index 0000000000..c07b34f60a --- /dev/null +++ b/.circleci/features/users.feature @@ -0,0 +1,40 @@ +Feature: Manage WordPress users + + Background: + Given I log in as an admin + + Scenario: User create, update and delete + When I go to "/wp-admin/user-new.php" + And I fill in "user_login" with "pantheontestuser" + And I fill in "email" with "test@example.com" + And I fill in "pass1" with "password" + And I fill in "pass2" with "password" + And I press "createuser" + Then print current URL + And I should be on "/wp-admin/users.php?id=2" + And I should see "New user created." in the "#message" element + And I should see "2 items" in the ".displaying-num" element + + When I go to "/wp-admin/users.php" + And I follow "pantheontestuser" + Then print current URL + And I should be on "/wp-admin/user-edit.php?user_id=2&wp_http_referer=%2Fwp-admin%2Fusers.php" + And the "first_name" field should not contain "Pantheon Test" + + When I fill in "first_name" with "Pantheon Test" + And I press "submit" + Then print current URL + And I should be on "/wp-admin/user-edit.php?user_id=2&wp_http_referer=%2Fwp-admin%2Fusers.php" + And I should see "User updated." in the "#message" element + And the "first_name" field should contain "Pantheon Test" + + When I go to "/wp-admin/users.php" + And I follow "Delete" + Then print current URL + And I should see "You have specified this user for deletion:" + + When I press "submit" + Then print current URL + And I should be on "/wp-admin/users.php?delete_count=1" + And I should see "User deleted." in the "#message" element + And I should see "1 item" in the ".displaying-num" element diff --git a/.circleci/local.test.dist b/.circleci/local.test.dist new file mode 100755 index 0000000000..1a15254072 --- /dev/null +++ b/.circleci/local.test.dist @@ -0,0 +1,12 @@ +#!/bin/bash + +# Copy to local.test and customize +circleci \ + -e CIRCLE_BUILD_NUM=0 \ + -e TERMINUS_TOKEN=$TERMINUS_TOKEN \ + -e TERMINUS_SITE=$TERMINUS_SITE \ + -e TERMINUS_ENV=ci-$CIRCLE_BUILD_NUM \ + -e TERMINUS_HIDE_UPDATE_MESSAGE=1 \ + -e WORDPRESS_ADMIN_USERNAME=admin \ + -e WORDPRESS_ADMIN_PASSWORD=$ADMIN_PASSWORD \ + build --job test diff --git a/.circleci/prepare.sh b/.circleci/prepare.sh new file mode 100755 index 0000000000..92e982aae6 --- /dev/null +++ b/.circleci/prepare.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +### +# Prepare a Pantheon site environment for the Behat test suite, by pushing the +# requested upstream branch to the environment. This script is architected +# such that it can be run a second time if a step fails. +### + +set -ex + +if [ -z "$TERMINUS_SITE" ] || [ -z "$TERMINUS_ENV" ]; then + echo "TERMINUS_SITE and TERMINUS_ENV environment variables must be set" + exit 1 +fi + +### +# Create a new environment for this particular test run. +### +terminus --yes env:info $TERMINUS_SITE.$TERMINUS_ENV 2>/dev/null || terminus --yes env:create $TERMINUS_SITE.dev $TERMINUS_ENV +terminus --yes env:wipe $TERMINUS_SITE.$TERMINUS_ENV + +### +# Get all necessary environment details. +### +PANTHEON_GIT_URL=$(terminus connection:info $TERMINUS_SITE.$TERMINUS_ENV --field=git_url) +PANTHEON_SITE_URL="$TERMINUS_ENV-$TERMINUS_SITE.pantheonsite.io" +BASH_DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +### +# Switch to git mode for pushing the files up +### +terminus --yes connection:set $TERMINUS_SITE.$TERMINUS_ENV git + +### +# Push the upstream branch to the environment +### +git remote add pantheon $PANTHEON_GIT_URL +git push -f pantheon $CIRCLE_BRANCH:$TERMINUS_ENV + +### +# Switch to SFTP mode so the site can install plugins and themes +### +terminus --yes connection:set $TERMINUS_SITE.$TERMINUS_ENV sftp diff --git a/.circleci/set-up-globals.sh b/.circleci/set-up-globals.sh new file mode 100755 index 0000000000..4e689efe8c --- /dev/null +++ b/.circleci/set-up-globals.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# Create a local .ssh directory if needed & available +SELF_DIRNAME="`dirname -- "$0"`" +[ -d "$HOME/.ssh" ] || [ ! -d "$SELF_DIRNAME/local.ssh" ] || cp -R "$SELF_DIRNAME/local.ssh" "$HOME/.ssh" + +# If an admin password has not been defined, write one to ~/WORDPRESS_ADMIN_PASSWORD +if [ ! -f ~/WORDPRESS_ADMIN_PASSWORD ] && [ -z "$WORDPRESS_ADMIN_PASSWORD" ] ; then + echo $(openssl rand -hex 8) > ~/WORDPRESS_ADMIN_PASSWORD +fi + +# If an admin password has not been defined, read it from ~/WORDPRESS_ADMIN_PASSWORD +if [ ! -f ~/WORDPRESS_ADMIN_PASSWORD ] && [ -z "$WORDPRESS_ADMIN_PASSWORD" ] ; then + WORDPRESS_ADMIN_PASSWORD="$(cat ~/WORDPRESS_ADMIN_PASSWORD)" +fi + +#===================================================================================================================== +# EXPORT needed environment variables +# +# Circle CI 2.0 does not yet expand environment variables so they have to be manually EXPORTed +# Once environment variables can be expanded this section can be removed +# See: https://discuss.circleci.com/t/unclear-how-to-work-with-user-variables-circleci-provided-env-variables/12810/11 +# See: https://discuss.circleci.com/t/environment-variable-expansion-in-working-directory/11322 +# See: https://discuss.circleci.com/t/circle-2-0-global-environment-variables/8681 +#===================================================================================================================== +mkdir -p $(dirname $BASH_ENV) +touch $BASH_ENV +( + echo 'export PATH=$PATH:$HOME/bin' + echo 'export TERMINUS_HIDE_UPDATE_MESSAGE=1' + echo 'export TERMINUS_ENV=ci-$CIRCLE_BUILD_NUM' + echo 'export WORDPRESS_ADMIN_USERNAME=pantheon' + echo "export WORDPRESS_ADMIN_PASSWORD=$WORDPRESS_ADMIN_PASSWORD" +) >> $BASH_ENV +source $BASH_ENV + +echo "Test site is $TERMINUS_SITE.$TERMINUS_ENV" +echo "Logging in with a machine token:" +terminus auth:login -n --machine-token="$TERMINUS_TOKEN" +terminus whoami +touch $HOME/.ssh/config +echo "StrictHostKeyChecking no" >> "$HOME/.ssh/config" +git config --global user.email "$GIT_EMAIL" +git config --global user.name "Circle CI" +# Ignore file permissions. +git config --global core.fileMode false diff --git a/.circleci/test.sh b/.circleci/test.sh new file mode 100755 index 0000000000..781b41a592 --- /dev/null +++ b/.circleci/test.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +### +# Execute the Behat test suite against a prepared Pantheon site environment. +### + +set -ex + +SELF_DIRNAME="`dirname -- "$0"`" + +# Require a target site +if [ -z "$TERMINUS_SITE" ] || [ -z "$TERMINUS_ENV" ]; then + echo "TERMINUS_SITE and TERMINUS_ENV environment variables must be set" + exit 1 +fi + +# Require admin username and password +if [ -z "$WORDPRESS_ADMIN_USERNAME" ] || [ -z "$WORDPRESS_ADMIN_PASSWORD" ]; then + echo "WORDPRESS_ADMIN_USERNAME and WORDPRESS_ADMIN_PASSWORD environment variables must be set" + exit 1 +fi + +export BEHAT_PARAMS='{"extensions" : {"Behat\\MinkExtension" : {"base_url" : "http://'$TERMINUS_ENV'-'$TERMINUS_SITE'.pantheonsite.io"} }}' + +# We expect 'behat' to be in our PATH. Our container symlinks it at /usr/local/bin +cd $SELF_DIRNAME && behat --config=behat.yml $*