To quote my colleague, Chad, WordPress “remained tremendously popular since its release in 2003”. For many, WordPress remains by far the CMS that is easiest to adopt, and that provides a fast time to market in the majority of use cases. There is so much high-quality material out there for WordPress, be it OSS or Premium, that one can have beautiful sites powered by an easy-to-use CMS up and running in no time.
True, but…
There was a time in my career when building CMS solutions for enterprise was the main thing I did. In those circles the idea that WordPress was a toy not suitable for enterprise-grade projects was common. This was especially true when I was working with teams that had already been initiated to best practices that later became known as 12 Factor. The traditional way of “doing WordPress” couldn’t really espouse the 12-Factor way easily.
WordPress, traditionally
Traditionally, a WordPress project’s codebase would consist of the entire WordPress core, plus all the plugins and themes necessary for the project to work. The entire codebase would then be deployed to a hosting of choice, usually via—cough— (S)FTP. The slightly evolved model is that the very same codebase is checked into a SCM system of choice, with a CI tool moving it onto the hosting’s servers.
Navigating WordPress Automatic Background Updates
Automatic Background Updates were introduced in WordPress 3.7 for the WordPress core, and later extended to plugins, themes, and translation files. Whilst a degree of automation for updates is definitely desirable, the way this is implemented in WordPress (i.e., in situ updates) means that if your codebase is in a repository, it’ll quickly get out of date, and you’ll have to manage updates in the repo on the side, to avoid problems and regressions down the road. If instead, you manage everything via FTP alone, it means each update will be very risky anyway. Either way, I can guarantee that things will quickly escalate into a nightmare.
Wait, it’s not all
It gets more complicated. To quote Chad again from the same article:
“Many hosting solutions, Platform.sh included, enforce read-only filesystems at runtime. These solutions deploy a highly reproducible build image as a consequence of your codebase and its explicitly defined build process, all committed to version control. [...] External code (dependencies) [...] and ideally definitions of your infrastructure itself are committed to exact versions so that your entire DevOps process from start to finish is repeatable and reproducible.”
This is excellent also for security reasons. Yet, as you may have gathered, it clashes completely with the way WordPress handles automatic updates, and these might need to be disabled entirely where a read-only file system is employed.
Leveraging Bedrock and Composer for modern WordPress development
In his article, Chad talks about how Bedrock leverages composer
to provide a more modern approach to WordPress development, thanks also to the great work behind WordPress Packagist. Chad shows that combining these efforts with our very own Source Operations, it’s possible to achieve a good degree of automation in updates, with greater control on versions and a greater repeatability of the process.
Our Goal: Efficient WordPress workflow and development
The goal of this article is to go one step further, and show how to combine Platform.sh with other tools, to achieve the best WordPress workflow—including auto-updates—that allows also for a greater degree of complexity, control, and configuration.
To do so, I’ll be using a Platform.sh template that I developed starting from our own composer-based WordPress template, before Bedrock came onto the scene.
Maximize automation with a template and WordPress workflow plugins
This template hosts the codebase for two sites (ita
and eng
), both
- are built using WordPress.org
- have a rudimentary “distro” support
- are deployed on Platform.sh in Multi-App setup
- have their entire codebase managed via
composer
, thanks tojohnpbloch/wordpress
, WordPress Packagist (for plugins and themes), andinpsyde/wp-translation-downloader
(acomposer
plugin to manage WordPress translations for core, plugins, and themes) - have their dependencies automatically upgraded by Dependabot
- have their Dependabot PRs automatically merged by Mergify when builds pass
- have new code deployed to production automatically on every PR merge
- use Redis as back-end caching
- use Cloudflare as CDN
- use GitHub Actions as additional CI/CD and Cron Scheduler
In addition to the above:
- the
ita
site uses Elasticsearch - the
eng
site uses Algolia
Finally:
- the template is meant to be used as a GitHub repository linked to a Platform.sh project via our GitHub integration.
If you are thinking this template was derived from a real-life project, then you’re right!
In the remainder of this article I’ll be focussing on the points that show how this template is all about automating as much of your Wordpress development workflow as possible, and I’ll avoid getting into some other details. Thus, if you want more insights into composer-based WordPress development, do refer back to Chad’s article or directly to the code in my template.
Automating WordPress installation with Distros and Composer
Distros or install profiles are essentially a way to have your own default installation configuration. Whilst some software have built-in support for that, WordPress does not. My goal was to completely automate the installation on the first deployment, so I decided to take matters into my own hands.
The rudimentary support I implemented for this relies on two things. First, a bespoke section in the composer.json
file:
"distro": {
"default-theme": "lovecraft",
"enable-plugins": [
"academic-bloggers-toolkit",
"akismet",
"contextual-category-widget",
"elasticpress",
"jetpack",
"really-simple-ssl",
"redis-cache",
"series",
"social-pug",
"wp-cloudflare-page-cache",
"wpforms-lite"
]
},
Second, a script that uses the information in that section to perform some initial setup:
#!/usr/bin/env bash
cd wordpress
if ! wp core is-installed; then
WP_URL=$(echo $PLATFORM_ROUTES | base64 --decode | jq -r 'keys[]' | grep $PLATFORM_APPLICATION_NAME | grep https | grep www)
wp core install --url="${WP_URL}" --title="Modern WordPress" --admin_user=admin --admin_password=changeme --admin_email=change@me.com
DEFAULT_THEME=$( jq -r '.[ "distro" ][ "default-theme" ]' ../composer.json )
wp theme activate ${DEFAULT_THEME}
jq -r '.[ "distro" ][ "enable-plugins" ][]' ../composer.json |
while read PLUGIN; do
wp plugin activate ${PLUGIN}
done
else
wp core update-db
fi
The script is then executed as part of the deploy
hook in Platform.sh, i.e. after the build phase has downloaded and built WordPress and the other dependencies via composer
. The result will be a fully installed WordPress application, avoiding you from having to go through the default manual (albeit simple) WordPress setup wizard.
If you are wondering why we didn't simply use the scripts.postbuild
section in composer.json
to run such a script, the primary reason is that during the build
phase (when composer
is executed) the database service is not yet available, since the build phase is designed only to create a deployable artifact.
Automatic WordPress database updates
We’ll talk about code updates later, but since we have referenced the script above, I wanted to highlight that the same script takes care of updating the database in case there have been code updates that demand such operation to take place.
If you have worked with WordPress before, you know that at the very least, when WordPress core is upgraded, you might be required to run a very simple routine that upgrades the database schema accordingly. This can be done through the administrative UI, which will present you with a simple button to click; or it can be done via the wp-cli
. The latter is what this script is using (see how the wp-cli
is installed): if WordPress is already installed, on each deploy it will simply run a database update routine, which will only do something if there is something to do.
Automated dependency updates
Part of the GitHub family for quite some time now, Dependabot provides a free plan for public repositories, and it supports PHP+Composer. You can configure it to decide what kind of upgrades you want to perform on your dependencies, and the bot will issue pull requests periodically, avoiding stale dependencies. Our configuration looks like this:
version: 2
updates:
- package-ecosystem: composer
directory: "/eng"
schedule:
interval: daily
open-pull-requests-limit: 10
ignore:
- dependency-name: wpackagist-plugin/really-simple-ssl
versions:
- 4.0.10
- package-ecosystem: composer
directory: "/ita"
schedule:
interval: daily
open-pull-requests-limit: 10
For both apps, we have a daily check on dependencies (remember, WordPress core itself is a dependency!), with a limit of maximum 10 open pull requests at any one time. You can also do clever things like ignoring specific versions of a dependency, if you know they are buggy, for example.
You might ask: ok, but how does opening PRs automatically actually achieve automatic dependency updates? Well, it does not :)
Enter auto-merge
Opening loads of PRs in an automated fashion is hardly going to make your life easier and keep your dependencies up to date effortlessly. Thus, a tool like Dependabot must be combined with another one that provides merge queues and the ability to auto-merge pull requests that meet certain criteria. I used Mergify, but it is likely that in the near future you might be able to use GitHub merge queues if they decide to implement a system similar to Mergify, where you can define a pattern for the PRs that must be auto-merged:
pull_request_rules:
- name: automatic merge for Dependabot pull requests
conditions:
- author~=^dependabot(|-preview)\[bot\]$
- status-success=platformsh
actions:
merge:
method: squash
The configuration above essentially instructs Mergify to automatically squash-merge all PRs issued by Dependabot and that have been built successfully on Platform.sh.
As I mentioned already, this template is meant to be used via GitHub integration with Platform.sh. This makes it possible for every PR raised by Dependabot to have an associated development environment where the build and deployment of the application with the updated dependencies will take place. Thanks to the integration with GitHub, this process is added as a status check for the PRs on the repository. That status check is what Mergify uses here to make sure that everything is fine with the PR; if it is, the PR will be merged.
This, of course, is a very basic configuration; nothing is stopping us from using additional status-success
conditions in the configuration, which could be unit tests run by a CI and other things like this. The more automation and status checks you have, the more confident you can be that the PR can be automatically merged.
Achieving control and assurance with Platform.sh and automation tools
Combining tools like Dependabot, Mergify and Platform.sh gives you a much greater degree of control and assurance over your process of automated updates for WordPress core, plugins, themes, and translation files. This is because for each update you can be certain that a live-like environment will be built with the changes, and additional tests can be run to make sure that everything still works just fine. You can decide whether you want to auto-update just minor versions or also major versions. You can exclude specific versions or specific items (a particular plugin or theme, for instance). And much more. You have finer-grained control, more power, and a higher degree of assurance.
Further Developments
The WordPress development workflow I’ve shown you here can be applied to extended scenarios. For instance, you can configure Mergify to auto-merge other or even all types of PRs, given the right conditions (remember that even manual peer reviews and other manual steps can be added as status checks). You can also extract this workflow and apply it to other type of applications, not just WordPress.
However, one way one could really take this to another level is by automating infrastructure upgrades! All you’d need is a way to check if there’s a new release of one of the Platform.sh managed services you use in your project, and then issue a new PR with the update (just like Dependabot does with application dependencies). Once you have that component (which could be implemented within your external CI tool, such as GitHub Actions), then you could configure Mergify to auto-merge those PRs, again, given the right conditions.
Start WordPress development workflow automation with Platform.sh
Whilst you wait for the ability to auto-upgrade your infrastructure (in fact, I promise I’ll be updating my template with that very feature as soon as it becomes possible to do so), you can still start using this template and this workflow right now, and begin saving precious time to your development team:
- Fork this repository on Github
- Create a new Platform.sh project
- Link your forked repository to the newly created project via our GitHub integration (requires you to install our CLI).
And as usual, you know where to find us, if you need us!