Inside the Dev House: How We Handle Automatic Theme Deployment for LITE and PRO WordPress Themes

As you would imagine, theme development is something we do quite a lot of here at the company. With around 4-5 new theme projects in the works at any given moment, and 49 themes in our directory in total (which means active maintenance and further development of those as well), we have our hands rather full.

In a setting like this, some optimization and even automation, whenever possible, is key.

So today, we want to invite you through our dev house door at ThemeIsle, so to speak, and show you two specific pieces of our theme development puzzle.

I won’t hide that this kind of post is an experiment. If you enjoy it, we will make sure to bring out more stuff like this in the future.

Specifically, the topic of today is something that can be called, “automatic deployment and visual regression architecture for WordPress theme development.”

Wait, what is automatic theme deployment?

If you don’t speak developer then what it means in English is that you can develop themes for WordPress, have them deployed to a server, and then visually compare the differences against a predefined baseline without having to do anything manually.

It all happens automatically, or rather, “automagically.”

How does this work?

We developed two services to take care of this automatic theme deployment: “Pirate Bootstrap” and “Pirate Wraith.”

The first one, Pirate Bootstrap, can be activated via Webhooks from GitHub.

On Pull Request, it deploys a new WordPress instance, using a given theme from a set repository + all the packages and database settings taken from the theme’s default demo at ThemeIsle.

The latter, Pirate Wraith, does a Visual Regression Test (aka comparing images from two sources). The test checks the new deployment of the theme against the demo from ThemeIsle – visually – and then generates a report. Based on that report, you can quickly see if the recent changes had any impact on the theme’s appearance.

In other words, whenever you’re working on a theme, and you want to make sure that your latest code changes haven’t messed up the theme’s design, you can use Pirate Wraith to handle the task on autopilot.

Let’s explain each service in more detail:

Pirate Bootstrap – deploys a new instance of WordPress using a set theme

Pirate Bootstrap is hosted on http://forks.themeisle.com

Pirate Bootstrap is built on top of WP-CLI and has methods for generating complete WordPress deployments based on ThemeIsle theme packages and dependencies.

The elements:

GitHub Webhooks

Webhooks are used to call the Pirate Bootstrap’s API on (opened or sync) Pull Requests by sending a JSON payload to http://forks.themeisle.com

This kick-starts the deployment workflow on forks.themeisle.com. Like so:

Automatic Theme Deployment for LITE and PRO WordPress Themes

Example of a GitHub Pull Request payload:

{
  "action": "opened",
  "number": 1,
  "pull_request": {
  ...
    "head": {
      "label": "preda-bogdan:production",
      "ref": "production",
      "sha": "****",
     ...
      "repo": {
        "id": 82166596,
        "name": "zerif-lite",
        "full_name": "preda-bogdan/zerif-lite",
        "owner": {
          "login": "preda-bogdan",
         ...
        },
        "private": false,
       ...
        "git_url": "git://github.com/preda-bogdan/zerif-lite.git",
        "ssh_url": "git@github.com:preda-bogdan/zerif-lite.git",
        "clone_url": "https://github.com/preda-bogdan/zerif-lite.git",
        "svn_url": "https://github.com/preda-bogdan/zerif-lite",
       ...
      }
    },
   ...
  }
}
  • We use the “sha” key to check if it’s a valid request and whether we’re allowed to process the payload.
  • We use “login”, “name” and “ref” to generate a tenant if it doesn’t exist.

The file structure

The file structure on the server is set so that we store each tenant in its own public folder and have a core install of WordPress that we use to reference with a symlink for each tenant.

The tenant file structure is as follows:

tenant/
     |_ wp/              /** symlink core install of WordPress
     |_ contents/          /** tenant content folder for WordPress
     |       |_ themes/    /** tenant theme folder for WordPress
     |       |_ plugins/   /** tenant plugins folder for WordPress
     |_ .htaccess          /** auto-generated .htaccess for tenant 
     |_ vhost.conf         /** alias config file for apache
     |_ wp-config.php      /** auto-generated config file for tenant

The wp/ folder references the core install of WordPress, which is shared by all tenants. This way, we can have a single installation of WordPress and multiple isolated instances of WordPress sites, each with encapsulated settings, files and resources.

The wp-config.php core and tenant files

Core WordPress wp-config.php example:

/** Absolute path to the WordPress directory. */
 require_once( $_SERVER['CONTEXT_DOCUMENT_ROOT'].'wp-config.php');
 
 /** Sets up WordPress vars and included files. */
 require_once(ABSPATH . 'wp-settings.php');

Tenant wp-config.php example:

(Values contained inside double curly braces are replaced automatically on tenant creation.)

/** ADDED BY BOOTSTRAP API */
 {{MYSQL_CONNECTION_TENANT_DATA}}
 
 define( 'AUTH_KEY', '{{AUTH_KEY}}' );
 define( 'SECURE_AUTH_KEY', '{{SECURE_AUTH_KEY}}' );
 define( 'LOGGED_IN_KEY', '{{LOGGED_IN_KEY}}' );
 define( 'NONCE_KEY', '{{NONCE_KEY}}' );
 define( 'AUTH_SALT','{{AUTH_SALT}}' );
 define( 'SECURE_AUTH_SALT', '{{SECURE_AUTH_SALT}}' );
 define( 'LOGGED_IN_SALT', '{{LOGGED_IN_SALT}}' );
 define( 'NONCE_SALT', '{{NONCE_SALT}}' );
 
 define( 'WP_DEBUG', false );
 
 
 define( 'WP_CONTENT_DIR', '{{tenant_folder}}/content' );
 define( 'WP_CONTENT_URL', '{{tenant_folder}}/content' );
 define( 'WP_PLUGIN_DIR', '{{tenant_folder}}/content/plugins' );
 define( 'WP_PLUGIN_URL', '{{tenant_url}}/content/plugins' );
 
 if ( ! defined( 'ABSPATH' ) )
     define( 'ABSPATH', dirname(__FILE__) . '/wp' );

After the tenant creation, we query an endpoint to retrieve packages needed for theme deployment (plugins, child themes, database). The packages are cached/stored in a stash folder on the server and are refreshed every six hours.

The next step is to check if the theme we want to deploy is a single deployment or needs to generate additional themes from the base one.

  • If it’s a single deployment, we just do a git pull using “ssh_url” into tenant/content/themes/.
  • In case it’s not a single deployment, we do a git pull into tmp/, run grunt generate and then copy the resulting folders into tenant/content/themes/.

The grunt generate task is a standard for us when working with themes that have multiple versions (usually “lite” and “pro”) while using the same codebase (repository). For instance, if we run grunt generate for the hestia-pro repository, we will also get the lite version automatically.

After handling the above, we use WP-CLI to install all required packages (plugins and/or child themes) and import the database dump from demo.themeisle.com.

The last steps are to flush the .htaccess rewrite rules, update “siteurl” and “home” with the tenant URL and the URL for WordPress Core, update links for menu items and posts, then finally reload apache.

We then send the user an email with their forked URL for the Pull Request and the log generated during the deployment. (Each generated tenant follows this general URL model: http://forks.themeisle.com/github-login/theme-slug/branch/)

Pirate Bootstrap – tips & tricks, and other useful information

When you go to forks.themeisle.com, you can access a terminal-like interface by pressing the “~” (tilde key) and then run a bunch of useful commands from there. The most relevant ones are:

Resetting a tenant deployment

The command is pirate reset tenant [tenant] (*theme-slug) |

Example:

pirate reset tenant preda-bogdan/zerif-lite/development |  

Or:

pirate reset tenant preda-bogdan/hestia/development hestia-pro |

The reset command sets the tenant back to its original deploy state (database reset, reinstall plugins and/or child themes).

Viewing the logs

The command is show logs. This command is useful if you need to check the log files after a deployment and you did not receive an email yet or you need to debug.

Note: The log file is rotated if the file size gets larger than 9000 bytes, so if you can’t find what you are looking for, you might need to check the log archive directly on the server.

Pirate Wraith – compares two versions of a theme visually

Pirate Wraith is hosted at http://wraith.themeisle.com

Pirate Wraith is built on top of Wraith and has endpoints to interact with Slack, Travis and AJAX requests in order to leverage Wraith capabilities and generate Visual Regression Tests and Reports.

Travis

Pirate Wraith exposes an endpoint on wraith.themeisle.com that listens for requests from a Travis build, and “fails” or “passes” the build according to the Visual Regression Test results.

Inside the .travis.yml file, we added a new matrix that defines a new build on top of the existing ones. This sets permissions for running a bash script inside the project and then executes it.

The Travis  YML file:

matrix:
   include:
   - php: "7.0"
     before_install: chmod +x wraith.sh
     install:
     before_script:
     env: TEST_SUITE=Wraith_Visual_Regression_Testing WRAITH_FAIL=5
     script: ./wraith.sh

You can see that “install” and “before_script” are left blank. This is on purpose, so that the build does not inherit actions from previously defined builds. We are interested just in running the bash script ( script: ./wraith.sh) on this build.

Also to note; we are setting an environment variable called WRAITH_FAIL. This value is used to tell Pirate Wraith what the maximum allowed percentile difference for passing a test is.

The Bash script:

#!/bin/bash
WRAITH_SLUG=$(node -pe "require('./package.json').wraithSlug")
WRAITH_FAIL=${WRAITH_FAIL:-5}
body="{
   'request': {
       'travis_event_type': '$TRAVIS_EVENT_TYPE',
       'travis_pull_request': '$TRAVIS_PULL_REQUEST',
       'travis_repo_slug': '$TRAVIS_PULL_REQUEST_SLUG',
       'travis_branch': '$TRAVIS_PULL_REQUEST_BRANCH',
       'wraithSlug': $WRAITH_SLUG,
       'wraithFail': $WRAITH_FAIL,
   }
 }"
 echo "Triggering build of $TRAVIS_PULL_REQUEST_SLUG branch 
        $TRAVIS_PULL_REQUEST_BRANCH' on Travis."
 output=$(curl -sw "%{http_code}" -X POST \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Travis-API-Version: 3" \
     -d "${body//\'/"}" \
     'http://wraith.themeisle.com')
 http_code="${output:${#output}-3}"
 if [ ${#output} -eq 3 ]; then
   body=""
 else
   body="${output:0:${#output}-3}"
 fi

 if [ $http_code != 200 ]; then
   echo "$output";
   exit 1
 else
  echo "$output";
  exit 0
 fi

In short, this script creates a JSON payload containing default Travis environment variables and user-set ones. Also, it reads packages.json and gets the theme slug.

The second part of the script does a POST request via “curl” to Pirate Wraith and parses the response in order to retrieve the HTTP/1.1 status code of the response.

We use the status code to “fail” or “pass” the build. The Pirate Wraith API returns valid HTTP/1.1 codes for each request it processes.

  • It will return code 200 for complete, passed tests.
  • For everything else, the build will be failed with an exit code of 1 (exit 1)

You might be wondering what Wraith comparing is. The answer is simple; it compares the tenant deployment of the current Pull Request from Pirate Bootstrap with the demo of the target theme.

For a better understanding of the GitHub – Travis – Pirate Bootstrap – Pirate Wraith life-cycle, here is a diagram illustrating the workflow of these services:

Pirate Bootstrap / Pirate Wraith workflow

As you can see, GitHub notifies both Pirate Bootstrap and Travis about a new Pull Request. Bootstrap starts to deploy a tenant, Travis asks Pirate Wraith to start the tests.

Pirate Wraith compares the Tenant version of the Demo with the ThemeIsle Demo and returns the results to Travis, so that it can pass or fail the build.

Slack

In addition to Travis support, Pirate Wraith has an endpoint for integrating with Slack.

After a build from Travis is finished (pass or fail), a report is generated inside #eyepatch channel, containing a link to the generated gallery and a summary of the differences found.

Also built in are a few useful Slack commands that you can use in any channel (Note: The Pirate Wraith API will respond in that channel with a public response, so we recommend using the commands in self chat). Here are the commands for Slack:

Resetting a theme’s history baseline shots

/wraith-history [theme-slug]

Example:

/wraith-history zerif-lite |

Comparing with a theme’s history shots

/wraith-latest [theme-slug] [url]

Example:

/wraith-latest zerif-lite http://forks.url/pb/zerif-lite |

This command uses the provided slug to compare the given URL with that slug’s history.

Comparing two URLs

/wraith-compare [url] vs [url]

Example:

/wraith-compare http://url.one vs http://url.two 

Pirate Wraith – tips & tricks, and other useful information

Resetting a theme’s history baseline shots

wraith reset history [theme-slug]

This command resets the history for the given slug.

Comparing with a theme’s history shots

wraith check latest [theme-slug] [url]

This command uses the provided slug to compare the given URL with that slug’s history.

Comparing two URLs

wraith compare urls [url-one] [url-two]

Viewing the logs

The command is show logs. This command is useful if you need to check the log files. It works the same way as in Pirate Bootstrap.

Your take?

This pretty much sums up our two new services and how they can be used to automate WordPress theme deployment.

We created both Pirate Bootstrap and Pirate Wraith to serve our own needs, but we believe that these concepts can be just as useful to anyone working on similar dev projects, too. Especially if you’re building derivative products (like pro and lite WordPress themes in our case) and you want to check what kind of impact specific code changes are having on their appearances.

The thing with WordPress themes is that the code bases of most modern themes tend to grow quite rapidly, and some specific elements of those code bases can have an unpredictable impact on the appearance of other elements of the theme. Trying to catch all that manually – just by looking at things through your own human eyes – can be really challenging, so it’s always a huge help to introduce some form of an algorithm / automation into the mix.

But what do you think? Do you see the value of tools like these for other projects as well?

  • Hi Bogdan
    This is tricky as it is more into technology but I could understand to the max I could.
    Github servers are cool enough to handle the requests while one of my friend crashed our servers in this kind of things.
    Thanks for the insights. I have to do something like this for our theme store under construction.

    • Bogdan Preda

      Hi,
      It’s important to test on a separate server before integrating into production workflow. Crashes are part of the developing process.
      I wish you the best of luck!