Wp-cli subcommand for managing WordPress installations. Automates installation, configuration and importing/exporting content.
The central idea with WP Bootstrap is to provide a tool that makes it easier to create a decent WordPress deployment workflow. WP Bootstrap lets you use configuration files and a few command line commands to install WordPress, set it up with the correct plugins and themes, import options as well as pages, posts, menus etc. To top it of, it also maintains ID integrity during import so that the site looks the same when it's imported as it did when it was exported, even if all the underlying post ID's have changed.
Tutorial for pre-subcommand usage (before 0.4.0): Tutorial on wpessentials.io
To add this package as a local, per-project dependency to your project, simply add a dependency on eriktorsner/wp-bootstrap
to your project's composer.json
file. Here is a minimal example of a composer.json
file that just defines a dependency wp-bootstrap:
{
"require": {
"eriktorsner/wp-bootstrap": "~0.5.0"
}
}
or from the command line:
$ composer require eriktorsner/Wp-bootstrap
Once WP Bootstrap is installed, wp-cli needs to know about it. Find (or create) your wp-cli.yml config file and add:
require:
- vendor/autoload.php
WP Bootstrap uses configuration files to control the installation of WordPress as well as plugins and themes.
File | Format | Descriptions |
---|---|---|
appsettings.yml | Yaml | Defines plugins, themes and import/export settings |
.env | Dotenv | Environment variables needed to install WordPress |
.env-development | Dotenv | Optional. Environment variable for the development environment |
.env-test | Dotenv | Optional. Environment variable for the test environment |
wp-cli.yml | Yaml | The standard wp-cli config file. Sub command setenv manages path and writes the environment in this file |
Step 1: Creating .env files
As a first step, create a .env file in your project root with settings matching your local database and apache/nginx configuration. Note that since WP Bootstrap maintains settings file in the project root folder, it's recommended to install WordPress in different location like a sub folder.
# file: .env
wppath=/path/to/target/wordpress
wpurl=www.example.com
dbhost=localhost
dbname=wordpress
dbuser=wordpress
dbpass=secret
wpuser=admin
wppass=anothersecret
Optionally, create an 'overlay' .env file for a specific environment, i.e 'development':
# file: .env-development
wppath=/path/to/target/wordpress-dev
wpurl=dev.example.com
dbname=wordpress-dev
Update the wp-cli.yml file with path and environment info:
$ wp setenv standard
Or (if you created a .env-development):
$ wp setenv development
Step 2: Create an appsettings.yml file (optional)
Create the appsettings.yml file that defines this WordPress installation
# file: appsettings.yml
# set a title for the WordPress site (defaults to '[title]')
title: Testing Bootstrap
# keep default content such as themes, posts, pages etc.
keepDefaultContent: true
# add a few plugins from the repo
plugins:
standard:
- wp-cfm
- disable-comments:1.3
content:
posts:
page: '*'
Step 3: Install WordPress
Install WordPress
$ wp bootstrap Install
...and install plugins etc.
$ wp bootstrap setup
Once WordPress is up and running, you will typically run the setup command over and over again as you add config to the appsettings.yml
Step 4: Exporting and importing content
To export the content defined:
$ wp bootstrap export
... and to import it back again:
$ wp bootstrap import
To make a quick test. Use the appsettings.yml defined in step 2 above and execute the export command. The defined content (all pages) will be serialized and stored under the bootstrap subfolder in your project root. Edit (or even delete) the default sample page in WordPress and run the import command. The page should then be completely restored.
The WP Bootstrap export command will take all content defined in appsettings.yml and serialize it to disk. The target folder is 'bootstrap' in your project root. This folder is supposed to be managed in Git so that it can be easily transferred to a another environment. As you might note, not all content in a WordPress installation is exported, actually it's the opposite, as little as possible but enough to maintain a working site. The idea is that you only export the content that you deem are part of you application meaning for instance the static front page, the menu structure, all the content in various about pages and terms and conditions and anything similar. When the site is deployed to production, the same content will be imported there.
The benefit of this might not be apparent when you deploy the site the first time. But when the site have been live for a few weeks and it's time to overhaul the menu structure and make a few changes to the About Us page, the benefits will become more apparent. At this time the production site might have lots of new blog posts, even more comments and perhaps even e-commerce orders. You don't want to make a complete database migration from your development environment because that would destroy all this other content. But by using WP Bootstrap you can import and overwrite just exactly those pieces of content that are defined in the appsettings.yml file. No other content on the target site gets touched.
Besides just exporting and importing content. WP Boostrap does three other things:
WP Bootstrap is tightly integrated with the plugin WP-CFM. It's not mandatory to use it and you'll have to specify WP-CFM manually among the plugins in appsettings.yml. When exporting content, WP Bootstrap will use WP-CFM to export options defined in a WP-CFM bundle named 'wpbootstrap'. The resulting json file is copied to the bootstrap/config sub folder and should be managed under git.
At import, that file is copied back into the expected location under wp-content and pushed back into the WordPress database.
The actual management of what options to include is managed using the WP-CFM UI in the WordPress admin area. Read more about WP-CFM here.
To help you identify what options to include and which ones that are changed, please refer to the section about the optiosnap command below.
WP Bootstrap adds the following commands to wp-cli:
The below commands are executed using:
$ wp bootstrap <command>
Command | Arguments | Description |
---|---|---|
install | Add Wp-Bootstrap bindings to composer.json | |
setup | Download and install WordPress core. Creates a default WordPress installation | |
export | Add themes and plugins and import content | |
import | Alias for wp-install followed by wp-setup | |
reset | Updates core, themes or plugins that are installed from the WordPress repository |
Setenv is a separate command that updates the wp-cli.yml file with settings from the .env files
Command | Arguments | Description |
---|---|---|
setenv | Updates the variables 'path' and 'environment' in wp-cli.yml |
Note: setenv uses the output from wp --info to determine name and location of the project specific wp-cli.yml file
Optionsnap is a utility that helps you keep track of which WordPress options that are in use in the WordPress installation. It's essentially dumping the contents of the wp_options table into a file located in sub folder bootstrap/snapshots. It's mainly intended to help developers understand which option values that are modified between two points in time.
The below commands are executed using:
$ wp optionsnap <command> <args>
Command | Arguments | Description |
---|---|---|
snap | Creates a new snapshot file with an optional comment | |
list | Lists all available snapshots | |
show | Lists alls options values in the snapshot named |
|
show | Shows the option identified by |
|
diff | Shows the diff between the current state of the wp_options table the snapshot identified by |
|
diff | Shows the diff between the two snapshots identified by |
Settings that are unique to a specific WordPress installation environment are kept in dotenv files. These settings are only used when installing WordPress for the first time. To make it easier to maintain multiple WordPress installs on the same physical or virtual machine, one dotenv file can overlay the base one.
When executing the install command, WP Bootstrap first looks in the wp-cli.yml file to read the name of the current environment. In the next step, it reads the variables contained in the base dotenv file (.env). In the last step, it looks for a file named .env-
Variable | Description |
---|---|
wppath | The path where WordPress should be installed. Also used by setenv to update wp-cli.yml when switching environments |
wpurl | The URL for the WordPress installation. Determined by the web server settings |
dbhost | The host name for the MySql database |
dbname | The database name for the MySql database |
dbuser | MySql user name |
dbpass | MySql user password |
wpuser | Default WordPress user name (often 'admin') |
wppass | Password for the above WordPress user |
The Application settings file, appsettings.yml defines WP Bootstraps behavior when installing plugins and themes and when importing and exporting data to and from WordPress.
The settings file consists of several sections:
This section consists of up to three sub sections named "standard", "local" and "localcopy". In each section, a list of plugins is defined. Plugins that are available in the WordPress plugin repository or via a URL are defined in the "standard" section. Plugins that exists locally in the same Git repository as the project are defined in the "local" section and are linked into the target WordPress install using a symlink. Some plugins won't behave very well as a symlink and they need to be copied into the correct place, these plugins are defined in the "localcopy" section. The "local" and "localcopy" sections are mostly used for premium plugins that can't be installed via the standard WordPress repository.
Each of the three plugin sections define a list. In it's simplest form, the list is just a string that identifies the slug of the plugin and an optional version like shown here:
plugins:
standard:
- akismet
- woocommerce
- google-sitemap-generator:4.0.6
local:
- mycustomplugin
In some (rare) cases, a plugin can't be installed unless another plugin or theme is already installed. To handle this, the plugin list can also contain an object with some optional extra parameters. To ensure that a specific plugin isn't installed before another one:
plugins:
standard:
- akismet
- woocommerce:
requires:
plugins: ['google-sitemap-generator']
themes: ['mycustomtheme']
- google-sitemap-generator
local:
- mycustomplugin
In this example, the WooCommerce plugin will not be installed until after Google Sitemap Generator and the theme Mycustomthme are installed.
For standard plugins, the slug refers to the unique identifier (last part of the URL) in the WordPress repository. If the slug is a full URL, the plugin will be installed from that URL. But for local and localcopy plugins, the slug refers to the folder name that the plugin is stored in on the local file system. The above definition would assume a project file tree that looks something like this:
.
└── wp-content
├── plugins
│ └── mycustomplugin
│ ├── file1.php
│ └── file2.php
└── themes
└── mycustomtheme
├── functions.php
└── style.css
When defining a plugin as an object rather than a simple string. The object can have the following properties:
Similar to the plugins section but for themes. The only real difference is that the themes section also has the parameter 'active' that identifies the theme that should be activated.
A list of options (settings) that will be applied to the WordPress installation using the wp-cli command "option update %s". Currently only supports simple scalar values (strings and integers). Example:
settings:
admin_email: foobar@example.com
blogname: My first blog
blogdescription: Just anohter blog tagline
For the most part, it's recommended to manage options using the WP-CFM plugin. WP Bootstrap has build in support for importing and exporting options using WP-CFM. Managing options directly in the appsettings.yml can quickly become overwhelming.
This sections defines how to handle content during export and import of data using the wp-export or wp-import command.
posts Used during the export process. Contains zero or more keys with an associated array. The key specifies a post_type (page, post etc) and the array contains post_name for each post to include. The export process also includes any media (images) that are attached to the specific post or are referred to in the post content or post meta.
menus Used during the export process. Contains zero or more keys with an associated array. The key represents the menu name (as defined in WordPress admin) and the array should contain each location that the menu appears in. Note that location identifiers are unique for each theme.
taxonomies Used during the export process. Contains zero or more keys with either a string or array as the value. Use asterisk (*) if you want to include all terms in the taxonomy. Use an array of term slugs if you want to only include specific terms from that taxonomy.
Used during the import process. This is a structure that describes option values (in the wp_option table) that contains references to a page or a taxonomy term. The reference item can contain a "posts" and a "terms" object describing settings that points to either posts or taxonomy terms. Each of these objects contains one single member "options" referring to the wp_options table (support for other references will be added later). The "options" member contains an array with names of options in the wp_option table. There are three ways to refer to an option:
Reference resolving will only look at the pages/posts/terms included in your import set. The import set might include an option "mypage" in the config/wpbootstrap.json file that points to post ID=10. Also in the import set, there is that page with id=10. When this page is imported in the target WordPress installation, it might get another ID, 22 for instance. By telling wp-bootstrap that the setting "mypage" in the wp_options table refers to a page, wp-bootstrap will update that option to the new value 22 as part of the process.
If the basic functionality in Wp-Bootstrap can't handle content in a certain situation, it's often possible to handle it via an extension. An extension is a PHP class that implements WordPress filters and actions to respond to certain events. For instance, if a plugin uses a custom table, an extension can hook into the 'wp-bootstrap_after_export' action to serialize that table to a file when the site is being exported. During import, the extension would hook into the 'wp-bootstrap_after_import' action to read the serialized file back into the database.
Extensions can be made specifically for a certain plugin or for a specific site project.
Name | Type | Description |
---|---|---|
wp-bootstrap_before_import | Action | Called before all import activities |
wp-bootstrap_after_import_settings | Action | Called after settings have been imported with WP-CFM |
wp-bootstrap_after_import_content | Action | Called after content (posts, menus etc) have been imported |
wp-bootstrap_after_import | Action | Called after all import activities are done |
wp-bootstrap_before_export | Action | Called before any export activities starts |
wp-bootstrap_after_export | Action | Called after all exports activities are done |
wp-bootstrap_option_post_references | Filter | Lets the extension add names of option values that refer to a post id. |
wp-wp-bootstrap_option_term_references | Filter | Lets the extension add names of option values that refer to a term id |
To use an extension, WP Bootstrap must be able to autoload the class. The easiest way to achieve this is to add a PSR4 namespace to the composer.json file of your project:
{
"require": {
"eriktorsner/wp-bootstrap": "~0.4.0"
},
"autoload": {
"psr-4": {
"MyNamespace\\": "src"
}
}
Then place your extension in the sub folder "src" (relative to the project root). Any extension classes will be instantiated at the beginning of the process and a method "init" will be called. In this method the extension can add filters and actions that will be executed in various stages in the installation/import process:
<?php
namespace MyNamespace;
use Wpbootstrap\Container;
class MyClass
{
public function init()
{
add_action('wp-bootstrap_after_import', [$this, 'AfterImport']);
}
public function AfterImport()
{
// do something useful...note that WordPress is loaded in most
// filters and actions. Go experiment
$options = get_option('FOOBAR', false);
...
}
}
A complete appsettings.yml example
keepDefaultContent: false
version: 4.4.1
title: 'Your WordPress site title'
# section plugins
plugins:
# using yaml list with each value on a separate row
standard:
- google-analyticator
- if-menu:0.2.1
- someplugin:
version: 3.4
slug: jetpack
requires:
themes: [mychildtheme]
plugins: [if-menu]
- woocommerce
# Defining local plugins using a yaml inline list
local: [myplugin]
# section themes
themes:
standard: [twentyfourteen]
local: [mychildtheme]
active: mychildtheme
# section settings
# simple name - value pairs
settings:
blogname: 'New title 2'
blogdescription: 'The next tagline'
# section content
# conists of sub sections: posts, menus and taxonomies
content:
posts:
# A separate section for each post type
# The list contains of post slugs (post_name) to
# uniquely identify the post. Any type of Yaml list works
page: [about, members]
post:
- hello-world
- another
# Instead of specifying a list you can use the
# asterisk to include all posts of post type 'custom'
custom: '*'
# Menus are identified by the menu name and includes
# a list of locations where this menu should be imported
# back
menus:
main:
- primary
- footer
# Taxonomies are similar to posts.
# identify the taxonomies to inlcude as well
# as the individual terms. Optionally, include
# all terms in a taxonomy using the asterisk
taxonomies:
category: '*'
customcat:
- This_term
- That_term
# The reference section is used during import. It identifies
# options (in wp_options) that refer to a post or a term.
# and helps WP Bootstrap maintain data integrity during import
references:
# references to post_id's
posts:
# references in the options table
options:
- some_setting
- { mysettings: '->term_id' }
- { mysettings2: '[2]' }
- { mysettings3: ['->term_id', '->other_term_id']}
# references to term id's
terms:
# references in the options table
options:
- some_term
- { other_term: '->term_id'}
extensions:
- MyNamespace\MyClass
Wp-bootstrap tries it's hardest to preserve references between exported WordPress objects. If you export a page that is the child of another page, the parent page will be included in the exported data regardless if that page was included in the settings. Similar, if you export a menu that points to a page or taxonomy term was not specified, that page and taxonomy term will also be included in the exported data.
When importing into a WordPress installation, wp-bootstrap will use the slug to match pages, menus and taxonomy terms. So if the dataset to be imported contains a page with the slug 'foobar', that page will be (a) created if it didn't previously exist or (b) updated if it did. The same logic applies to posts (pages, attachments, posts etc), menu items and taxonomy terms.
Note: Taxonomies are defined in code rather than in the database. So the exact taxonomies that exist in a WordPress installation are defined at load time. The built in taxonomies are always there, but some taxonomies are defined in a theme or plugin. In order for your taxonomy terms to be imported during the wp-import process, the theme or plugin that defined the taxonomy needs to exist.
Since wp-bootstrap relies a lot on WordPress, there's a separate Github repository for testing using Vagrant. The test repo is available at https://github.com/eriktorsner/wp-bootstrap-test.
Contributions are welcome. Apart from code, the project is in need of better documentation, more test cases, testing with popular themes and plugins and so on. Any type of help is appreciated.