Using WP-CLI with Mina

In the first part of this series, we were able to use Mina to deploy WordPress. We learned that Mina creates a shell script file, then pushes WordPress to the server to run. Since all commands are run in a single SSH connection, Mina very fast. 

Utilizing this, we can write tasks to automate WordPress control. We can turn normal WordPress tasks such as creating users, installing a plugins, backing up our data, and so on into a sequence of shell commands. Then we combine these shell commands into Mina task. 

But how can we create the shell commands to perform those tasks? WP-CLI is the answer. It helps to control WordPress through command line. You don't need to sign in to the WordPress dashboard to do administration tasks. WP-CLI gives us a set of commands that is corresponding with WordPress admin tasks. Once we know the WP-CLI commands for a special task, we only need to put these commands into a Mina task. 

What Do I Need to Know Upfront?

  • You will need either a Mac OS or Linux OS in order to have access to a terminal. If you are on Windows, it is still possible but it won't be covered in this tutorial. 
  • You should have a very basic understanding of shell commands and the terminal. As long as you've opened the Terminal or iTerm app before, the tutorial should be very easy for you.
  • You need to have SSH access to your remote machine.

A Quick Look at WP-CLI

A very quick example so you can have a task of what we have with WP-CLI. When starting to develop a WordPress site, you have to create dummy posts or dummy user accounts for testing purpose. 

Typing this (once you've installed wp-cli) on your terminal will create you 100 dummy posts without minimal effort.

php wp-cli.phar post generate --count 100

See how much time it will save you? But do we really need to run these things via command line? 

Saving time is nice, but not a need.

Here is a very good reason to do this: security. Some action from the WordPress dashboard requires write permission to the wp-content folder. Example: uploading plugins,  uploading themes, upgrading WordPress, and so on. The more permission we grant to the public, the more danger we are exposing to our site. 

Let's imagine that an attacker gains access to our WordPress dashboard. The attacker will be able to upload malicious scripts as a plugin. A way to prevent this is to disable write permission to any folder or file of WordPress except the wp-content/uploads folder. Therefore, no one can upload a plugin or theme to web server. 

In first part of this tutorial, we learn to give write privilege to Apache on wp-content/uploads folder. We changed owner ship of wp-content/uploads to the web server user. That way, the web server can write to the uploads folder; therefore, WordPress can upload pictures and write into our uploads folder. 

Any action that requires writing to the WordPress folder (except the uploads folder) should be done via the command line. In other words, we will add the file manually from our local machine, commit, and deploy to server with Mina. With this setup, our WordPress directory becomes a read-only box (except for the uploads folder which is used for storing our media). 

The media upload feature of WordPress already has a security layer to prevent uploading PHP files. If you want to go further, you can config to not run PHP files inside uploads folder, but that's the idea and won't be cover in this tutorial. We will focus more on command line action with WP-CLI.

Update WordPress Without Write Permissions

Once we removed write permission, then the auto update will no longer work, and you cannot simply update the application from WordPress dashboard. Since we have managed our WordPress code with Git, we should do the update manually

We can download the WordPress zip/tar file, uncompress it, overwriting current WordPress file. Next, We commit the new code, push to our repository, and do a deployment with Mina.

A Review of Mina Task Theory

A task in Mina is just a normal Rake task with a queue method that queued the shell commands to run on server later. For any command that we want to run on server, we can use the queue method as following.

desc "Describe this task"<br>task :sample do 
  queue "ls #{deploy_to}/current"
  queue "touch /tmp/test.log"

The desc block is optional but it's nice to have. If you type mina without any parameter, the desc information will be printed out on screen for your reference.

$ mina
mina - Really fast server deployment and automation tool

  -h, --help        Show help
  -V, --version     Show program version
  -v, --verbose     Show commands as they happen
  -S, --simulate    Run in simulation mode
  -t, --trace       Show backtraces when errors occur
  -f FILE           Use FILE as the deploy configuration

Basic usage:
  mina help    # Show help.
  mina init    # Creates a sample config file.
  mina tasks   # Show all tasks.

Server tasks:
  mina clean_cache             # Clean nginx cache
  mina db_export               # Export WordPress database to shared/backup
  mina deploy                  # Deploys the current version to the server.
  mina rollback                # Rollback to previous version
  mina run[command]            # Runs a command in the server.
  mina setup                   # Sets up a site.
  mina toggle_plugin[plugin]   # Toggle a plugin
  mina toggle_theme[theme]     # Activate theme
  mina wp_version              # Show current WordPress version

All of Rake's options are also available as 'mina' options. See 'rake --help'
for more information.

For any command that we want to run on local machine, we can use the backtick(`) character.

task :sample do 
  puts `pwd`
  puts `ls -la`

Let's test it with mina sample to see what it prints out on your local machine.

So, basically a Mina task is built by creating a block of task :task_name do //... end
The task can be invoke with mina task_name. If we need to pass argument into the task, then we can use this syntax:

task :task_name, :arg, :another_arg do |cmd, args|
    puts args[:arg]
    puts args[:another_arg]

The bracket [] is just the Ruby way to access a hash. You can think of it like PHP associative array. We name the argument, then inside the task body, we can easily access its value with args[:argument_name]. In this example, argument_name is :arg and :another_arg

The task can be invoked with this syntax: mina taskname[param1, param2]. More arguments can be added and we will pass more parameter into it such as mina taskname[param1, param2, param3, param4].

You can imagine that the taskname is a method name and we are calling a method and passing its argument. If the parameter has a space, we should wrap the whole parameter with a quote.

 Let try above task now. It just prints out whatever we pass into it.

$ mina task_name[value1," value 2 with some space"]<br>value1<br>value 2 with some space<br><br>

If you used zsh shell, you need to escape [ and ] with the backslash:

$ mina task_name\[value1," value 2 with some space"\]<br><br>

Another thing to note is the in_directory method of Mina. It's very likely that you will need to cd into a directory and run a couple of commands. Instead of manually queueingcd command, we can use the handy in_directory method. It will run the command inside the directory we indicate.

task :sample do 
  in_directory "#{deploy_to}/current" do
    queue "pwd"<br>  end

Okay, enough for theory. Let's start the fun part: We will install wp-cli, then setup a few tasks.

Install WP-CLI

All we need is to download the PHAR file of WP-CLI. It's a single file that is executable to the PHP engine. Instead of having a bunch of PHP files, you can compress them into a single file that is executable by PHP. 

It's a very easy way to distribute PHP packages. Just like on Windows, you download a software in a single .EXE file and run it. To do this, cd into your WordPress code on local machine, run this command:

curl -L > wp-cli.phar
chmod +x wp-cli.phar<br><br>

Let's verify it works. On your local machine, run wp-cli.phar --info and you should get the following output.

./wp-cli.phar --info<br>PHP binary:	/usr/bin/php<br>PHP version:	5.4.24<br>php.ini used:	/etc/php.ini<br>WP-CLI root dir:	phar://wp-cli.phar<br>WP-CLI global config:<br>WP-CLI project config:<br>WP-CLI version:	0.14.1<br><br>

Now, wp-cli is in same directory with your WordPress, let's commit it and
push to your repository and do a deployment.

git add wp-cli.phar
git commit -m "Add wpcli"
git push origin master
mina deploy

At this point, wp-cli is in our WordPress directory on server. Let's do some magic with it: writing Mina task with wp-cli.

Writing a Mina Task With WP-CLI

In this section, I will show you how to do some basic tasks with wp-cli as well as how to put them into Mina so you can have a taste of how thing look like and go write your own task to match your workflow and your need. First things first: Hello World.

1. Hello World

The very first, very simple command of wp-cli is to show current WordPress version. Let create one more Mina task. Append this to your config/deploy.rb file:

desc "Show current WordPress version"
task :wp_version => :environment do 
  queue %[echo "-----> Hello world. I'm at this version now:"]
  queue "cd #{deploy_to}/current && ./wp-cli.phar core version"

Now, try to run it:

$ mina wp_version
-----> Hello world. I'm at this version now:
       Elapsed time: 0.00 seconds

2. Installing a Plugin

We will download a plugin by hand, extract it, add it to repository, commit it, then deploy and run another mina task to activate a plugin. 

Here is the code we need for this task

desc "Toggle a plugin"
task :toggle_plugin , :arg1 do |cmd, plugin|
  queue %[echo "----> Activate theme #{theme}"]
  in_directory "#{deploy_to}/current" do
    queue %[./wp-cli.phar plugin toggle #{plugin}]

We queue an echo command to output some text. The command to activate a plugin from wp-cli is plugin toggle plugin_name

For example, say that we want to install the Mathjax Latex plugin, and we already put it in wp-content/plugins

We can install this way:

git add wp-content/plugins/mathjax-latex
git commit -m "Add mathjax plugin"
git push origin master
mina deploy

Now the plugin is installed. Activation can be done via the WordPress dashboard. But since we have our Mina task let try it:

$ mina toggle_plugin\["mathjax-latex"\]
# Should output

----> Toggle plugin mathjax-latex
Success: Plugin 'mathjax-latex' activated.

Updating a plugin is just the same procedure. You download the plugin, copy it over to wp-content/plugins/[plugin-need-to-update], commit and re-deploy.

3. Installing a Theme

The workflow is just like how we do with plugin, only a different wp-cli command to activate the theme. We only need to add the theme into Git, commit, push, deploy.

Take a look at the following example in which we will install this theme. We already downloaded and put it into wp-content/themes/natsume.

git add wp-content/themes/natsume
git commit -m "Add mathjax plugin"
git push origin master
mina deploy

Again, the theme activating can be done on WordPress admin UI. But let make a Mina task so we can activate it once we deploy it, from the command line. 

Here is the task:

desc "Activate theme"<br>task :toggle_theme , :theme do |cmd, args|<br>  theme = args[:theme]<br>  queue %[echo "----> Activate theme #{theme}"]<br>  in_directory "#{deploy_to}/current" do<br>    queue %[./wp-cli.phar theme activate #{theme}]<br>  end<br>end<br><br>

To activate a theme, run our task:

$ mina toggle_theme\["natsume"\]

4. Database Backup

This task will dump the WordPress database to a gzip file in shared/backup folder. If the backup folder doesn't exist yet, we will create it, then we call the wp-cli db command to dump the database to a file. 

The file name will be the combination of date and time when we export in this format "[year][month][day]_[hour][minute][second].sql.gz". 

Let's compose our Mina task.

desc "Export WordPress database to shared/backup"
task :db_export do 
  # create backup directory if it isn't exit
  backup_to = "#{deploy_to}/shared/backup"
  queue %[[ -d #{backup_to} ] || mkdir -p #{backup_to}]

  in_directory "#{deploy_to}/current" do
    time =
    file = "#{time.year}#{time.month}#{}_#{time.hour}#{time.min}#{time.sec}.sql.gz"
    queue %[echo "----> Back up database to #{file}"]
    queue %[./wp-cli.phar db export - | gzip > #{backup_to}/#{file}]

[ -d name ] is a syntax to check the existence of the folder. If not, we use mkdir to create it.
Then, we use Time object of Ruby to extract date and time and concatenate together to create our file name.

When you want to back up, from your local machine, type:

$ mina db_export

Simple and elegant. One command and you have it all.

5. Update Domain

Very often, when you change the domain, you have to re-config it in wp-config.php, or manually update the domain in database. Either way, it's lot of work. 

Let's just solve this one time. The command to update an option is : wp-cli option update [option_key] [option_value]. According to the WordPress documentation,  the domain is in two options: home and siteurl


So, here is our task:

desc "Change domain to a new one"<br>task :update_domain, :domain do |cmd, args|<br>  domain = args[:domain]<br>  puts domain<br>  in_directory "#{deploy_to}/current" do<br>    queue %[./wp-cli.phar option update home #{domain}]<br>    queue %[./wp-cli.phar option update siteurl #{domain}]<br>  end<br>end<br><br>

Invoke it from command line:

$ mina update_domain\[""\]

6. Generate a Dummy Post

A very handy features of wp-cli is post generation. During testing time, you can find yourself manually creating lots of dummy posts. The command to create post with wp-cli is wp-cli post generate --count=quantity. As always, you can read the syntax yourself on wp-cli website.

We will create a task call gen_dummy_post with a parameter is the number of posts. To get content of post, we will use curl to get a lorem ipsum text via the Loripsum API. cURL is a command line tool which is usually pre-installed on Linux/Mac OS that allows you to send HTTP request.

You can think of it like a browser, but run on command line. So instead of typing an address into your browser, and see the output on your browser, you give cURL an website address, and it returns the website content.

Let's try below command:

$ curl
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ab his oratores, ab his imperatores ac rerum publicarum principes extiterunt. Cupiditates non Epicuri divisione finiebat, sed sua satietate. Venit ad extremum; Duo Reges: constructio interrete. Respondeat totidem verbis. Quis Aristidem non mortuum diligit? Quid Zeno? </p>

<p>Nihil acciderat ei, quod nollet, nisi quod anulum, quo delectabatur, in mari abiecerat. Nondum autem explanatum satis, erat, quid maxime natura vellet. Quod autem satis est, eo quicquid accessit, nimium est; Tum ille: Ain tandem? Sed ille, ut dixi, vitiose. Quantum Aristoxeni ingenium consumptum videmus in musicis? Quid enim possumus hoc agere divinius? Ut enim consuetudo loquitur, id solum dicitur honestum, quod est populari fama gloriosum. </p>

<p>Sed quid minus probandum quam esse aliquem beatum nec satis beatum? Primum cur ista res digna odio est, nisi quod est turpis? Utinam quidem dicerent alium alio beatiorem! Iam ruinas videres. Beatum, inquit. Deinde disputat, quod cuiusque generis animantium statui deceat extremum. Nihil enim iam habes, quod ad corpus referas; </p>

<p>Quod ea non occurrentia fingunt, vincunt Aristonem; Non semper, inquam; Quid de Platone aut de Democrito loquar? Quem Tiberina descensio festo illo die tanto gaudio affecit, quanto L. Non enim ipsa genuit hominem, sed accepit a natura inchoatum. Scaevolam M. Recte, inquit, intellegis. Uterque enim summo bono fruitur, id est voluptate. Sic exclusis sententiis reliquorum cum praeterea nulla esse possit, haec antiquorum valeat necesse est. Sed fortuna fortis; Mihi quidem Antiochum, quem audis, satis belle videris attendere. </p>

<p>Nam si propter voluptatem, quae est ista laus, quae possit e macello peti? Nonne igitur tibi videntur, inquit, mala? </p>

So when you run it, five paragraphs of lorem ipsum are returned. You can change the number after /api to get more paragraph.

We know have content, we will feed it into wp-cli to create post. The syntax for doing that is:

curl | wp post generate --post_content --count=10

Notice the chracter |, it means that we are piping the content into next commad. We know have the syntax, we can start composing the task

desc "Generate random post"
task :gen_dummy_post, :howmany do |cmd, args|
  howmany = args[:howmany]
  puts howmany
  in_directory "#{deploy_to}/current" do
    queue %[curl | ./wp-cli.phar post generate --count=#{howmany} --post_content]

Once you knew the syntax of Mina task, you can read more about WP-CLI command to write your own task such as creating user, generate dummy users for testing...

If you want to lean more about wp-cli, you can visit these links:


We are now have a good enough understanding of how to write Mina tasks, how to utilize the power of wp-cli. With the combination these two tools, hope you will deploy very fast, and your WordPress installation will be more secure than before, and reduce the time for administration tasks. 

Don't limit yourself. The information in this tutorial is very brief, please add more things to your task files and let us know what you came up in comment. 

If you have any issue, please leave a comment, I like to help and talk. Happy WordPress automation!