Leveraging WP-CLI Aliases in Your WordPress Development Workflow


#1

Originally published at: https://roots.io/leveraging-wp-cli-aliases-in-your-wordpress-development-workflow/

Not to be confused with the hit TV series starring Jennifer Garner, alias support was added to WP-CLI from an update in July, 2016. If you aren’t familiar, WP-CLI is a tool for interacting with WordPress via the command line. It allows you to perform many admin actions such as activating plugins or users and…


Backup DB from Production
Issue exporting a database using wp-cli from local development to remote staging server
Trellis & Bedrock is deactivating themes and plugins after destroy
Wordpress local site migration using free migration plugin?
#2

If you’re looking for an example script that does the opposite of what’s in the post, going from development to production (useful during initial solo-dev), here’s sync-dev-to-prod-FOR-REAL.sh :slight_smile: :

read -r -p "Would you really like to reset THE PRODUCTION DATABASE and send up the latest from dev? [y/N] " response
if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]]
then
    wp @development db export - > sql-dump-development.sql
    scp sql-dump-development.sql web@example.com:/srv/www/example.com/current

    wp @production db export just-in-case.sql
    wp @production db reset --yes

    wp @production db import sql-dump-development.sql
    wp @production search-replace https://example.dev http://example.com
    scp -r web/app/uploads/ web@example.com:/srv/www/example.com/shared
else
    exit 0
fi


Permission error on msql dump when using wp-cli aliases
#3

This looks really cool, but I just started using wp-cli and I don’t quite get it. As I understand it, you’d normally need to ssh into a server and go into the web woot where WordPress resides and run “wp” commands there. I was able to ssh into vagrant and run “wp --info” and “which wp”. If I try to run “wp” commands from my command line I get “wp not found”.

So how is this supposed to work?

  1. Normally “wp” needs to be run on the server from within the folder where WordPress lives? Requires ssh-ing into a server?
  2. However, now with aliases you can run “wp” commands without ssh-ing into other servers?
  3. Am I supposed to be able to run “wp” commands from my computer’s command line?

#4

wp-cli tips for newbies (also some questions, someone knowledgeable please verify this is correct)

  1. You may need to install wp-cli on your system, it’s not listed as a requirement, so I didn’t have it installed. (follow the instructions at http://wp-cli.org/).

  2. Trellis does install wp-cli on vagrant and other servers.

  3. Even if you haven’t installed wp-cli on your system, you can run “wp” commands when you ssh into vagrant or other environments.

  4. The aliases look like this. Add them to an existing file (wp-cli.yml) in the “site” folder. (aliases were appended after “path: web/wp” which was alrealy in the file)

    path: web/wp
    @development:
    ssh: vagrant@example.dev/srv/www/example.com/current
    @staging:
    ssh: admin@staging.example.com/srv/www/example.com/current
    @production:
    ssh: admin@example.com/srv/www/example.com/current

  5. The whole point of aliases is so that you can run “wp” commpands on your system that will run on vagrant or remote servers without having to ssh in.

  6. You need to run your aliases from the same folder that has the wp-cli.yml file (sites) or it won’t find your alias. (If you’re running other “wp” commands that don’t require an alias, you should be able to run “wp” commands globally if you installed it globally.)

  7. The example in the tutorial (wp @production plugin activate soil) (https://roots.io/leveraging-wp-cli-aliases-in-your-wordpress-development-workflow/) may not work if you don’t have soil installed. You can try a different plugin. Here I’m installing wp-migrate-db with composer and then activating it with wp-cli. (very very cool!)

composer require wpackagist-plugin/wp-migrate-db
wp @development plugin activate wp-migrate-db


#5

Ben Word’s database syncing shell script above works great! And it copies over the media also!! Awesome!

My questions:

  1. I have a permissions problem. Any idea how to fix this?

mysqldump: Can't create/write to file 'just-in-case.sql' (Errcode: 13 "Permission denied")

  1. what’s the best practice for where to store this script in “trellis/bin”?
  2. it leaves a copy of sql-dump-development.sql (in “sites”). Guessing that would be easy to change in the script unless it’s good to keep.
  3. you say this is good for “initial solo dev”. What do you see as the limitations of this as compared to the wp-migrate-db plugin?

#6

Thanks for trying out aliases!

This should probably be moved to a support thread rather than discussion of the post.

You can include your questions there, and additionally please include information about where you are running the script to provide more context.

Consider separating out #4 as another thread as it’s own can of worms.


#8

Nice post Raquelle! Aliases are awesome.

I found you can remove the need for the sql-dump-development.sql file and run it in one go like this from the environment you want to run the import on:

wp @remote db export - | wp db import -

This won’t be as practical for large databases where you might need to compress it to get a reasonable transfer time, but it makes for a nice one-liner :ok_hand:


#9

Hold on … you’re telling me we can duplicate WP Migrate DB Pro with just a shell script?


#10

Just getting round to trying this now and I can certainly see a lot of value in this.

On a Trellis + Bedrock set up, I can get staging and prod aliases working fine but I’m having trouble with development.

@development:
  ssh: vagrant@example.dev/srv/www/example.com/current

Fairly new to Trellis but this gives me:

Error: Cannot connect over SSH using provided configuration.

And if I manually try:

vagrant@example.dev

I get:

Permission denied (publickey).

Maybe I’m totally missing something or maybe the Trellis setup has changed since this post?

Edit

OK, got it figured out. I wasn’t sure as the post referenced Trellis and Vagrant separately, so I tried the Vagrant part of the post, running the following in the Trellis directory:

$ vagrant ssh-config --host example.dev >> ~/.ssh/config

This then let the SSH connection work. Probably my bad for not realising this needed to be done first.


#11

I made a script to auto-update my local SSH config on vagrant up to make sure that the port always works. I noticed that after rebooting or having different Vagrant boxes running that my SSH attempts would fail.

Add the following to your Vagrantfile within the VM provisioner (config.vm.provision provisioner do |ansible|)

    # Run script to update local SSH config for .dev hostname after `vagrant up`
    if Vagrant.has_plugin?('vagrant-triggers')
      config.trigger.after :up do
        run "./bin/ssh-config-development.sh"
      end
    else
      fail_with_message "vagrant-triggers missing, please install the plugin with this command:\nvagrant plugin install vagrant-triggers"
    end

Then create bin/ssh-config-development.sh with the following:

host="example.dev"
sed "/^$/d;s/Host /$NL&/" ~/.ssh/config | sed '/^Host '"$host"'$/,/^$/d;' > config &&
cat config > ~/.ssh/config &&
rm config &&
vagrant ssh-config --host example.dev >> ~/.ssh/config

#12

Also, here’s the latest version of the sync script I’ve been using that lives at site/scripts/sync.sh

#!/bin/sh

DEVDIR="web/app/uploads/"
DEVSITE="https://example.dev"

PRODDIR="web@example.com:/srv/www/example.com/shared/uploads/"
PRODSITE="https://example.com"

STAGDIR="web@staging.example.com:/srv/www/example.com/shared/uploads/"
STAGSITE="https://staging.example.com"

FROM=$1
TO=$2

case "$1-$2" in
  development-production) DIR="up";  FROMSITE=$DEVSITE;  FROMDIR=$DEVDIR;  TOSITE=$PRODSITE; TODIR=$PRODDIR; ;;
  development-staging)    DIR="up"   FROMSITE=$DEVSITE;  FROMDIR=$DEVDIR;  TOSITE=$STAGSITE; TODIR=$STAGDIR; ;;
  production-development) DIR="down" FROMSITE=$PRODSITE; FROMDIR=$PRODDIR; TOSITE=$DEVSITE;  TODIR=$DEVDIR; ;;
  staging-development)    DIR="down" FROMSITE=$STAGSITE; FROMDIR=$STAGDIR; TOSITE=$DEVSITE;  TODIR=$DEVDIR; ;;
  *) echo "usage: $0 development production | development staging | production development | production staging" && exit 1 ;;
esac

read -r -p "Would you really like to reset the $TO database and sync $DIR from $FROM? [y/N] " response

if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
  cd ../ &&
  wp "@$TO" db export &&
  wp "@$FROM" db export - | wp "@$TO" db import - &&
  wp "@$TO" search-replace "$FROMSITE" "$TOSITE" &&
  rsync -az --progress "$FROMDIR" "$TODIR"
fi

Usage:

$ ./sync.sh
usage: ./sync.sh development production | development staging | production development | production staging

eg.

$ ./sync.sh development production
$ ./sync.sh production development

#13

@benword that looks great. I had already tweaked around with your last script but that’s a whole let better than what I had put together. Just gave this a test run and worked perfectly.

Question: I had added to mine the removal of the resulting .sql files. It looks like in this script we get left with a single .sql file on the remote server. Should this be removed after import?


#14

I left it as a way of leaving a backup incase you didn’t mean to push up a new DB, but have considered moving that to a more temporary area. Feel free to remove/tweak :slight_smile:


#15

Makes sense, appreciate the reply :slight_smile:


#16

WP-CLI supports a global config file! Per the docs you can have a global config in ~/.wp-cli/config.yml - I just set it up with aliases, and now I can use my aliases from any folder :grin:


#17

I just noticed the last case doesn’t match up with the last usage example - I added in production <> staging and updated the usage text. I also made the site URLs root relative, just for compatibility reasons.

The first case also has ; after DIR="up" whereas the others don’t; I added to make consistent though I’m not sure it actually matters since each statement is just a variable declaration.

#!/bin/sh

DEVDIR="web/app/uploads/"
DEVSITE="//example.dev"

PRODDIR="web@example.com:/srv/www/example.com/shared/uploads/"
PRODSITE="//example.com"

STAGDIR="web@staging.example.com:/srv/www/example.com/shared/uploads/"
STAGSITE="//staging.example.com"

FROM=$1
TO=$2

case "$1-$2" in
  development-production) DIR="up";  FROMSITE=$DEVSITE;  FROMDIR=$DEVDIR;  TOSITE=$PRODSITE; TODIR=$PRODDIR; ;;
  development-staging)    DIR="up";   FROMSITE=$DEVSITE;  FROMDIR=$DEVDIR;  TOSITE=$STAGSITE; TODIR=$STAGDIR; ;;
  production-development) DIR="down"; FROMSITE=$PRODSITE; FROMDIR=$PRODDIR; TOSITE=$DEVSITE;  TODIR=$DEVDIR; ;;
  production-staging)     DIR="horizontally"; FROMSITE=$PRODSITE; FROMDIR=$PRODDIR; TOSITE=$STAGSITE; TODIR=$STAGDIR; ;;
  staging-development)    DIR="down"; FROMSITE=$STAGSITE; FROMDIR=$STAGDIR; TOSITE=$DEVSITE;  TODIR=$DEVDIR; ;;
  *) echo "usage: $0 development production | development staging | production development | production staging | staging development" && exit 1 ;;
esac

read -r -p "Would you really like to reset the $TO database and sync $DIR from $FROM? [y/N] " response

if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
  cd ../ &&
  wp "@$TO" db export &&
  wp "@$FROM" db export - | wp "@$TO" db import - &&
  wp "@$TO" search-replace "$FROMSITE" "$TOSITE" &&
  rsync -az --progress "$FROMDIR" "$TODIR"
fi

Edit: though I’m still using WP Migrate DB, I figured I’d add in the option to skip syncing uploads, so after the case block I changed to the following:

read -r -p "Would you like to sync the uploads folder as well? [y/N] " uploads
read -r -p "Would you really like to reset the $TO database and sync $DIR from $FROM? [y/N] " response

if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
  cd ../ &&
  wp "@$TO" db export &&
  wp "@$FROM" db export - | wp "@$TO" db import - &&
  if [[ "$uploads" =~ ^([yY][eE][sS]|[yY])$ ]]; then
    wp "@$TO" search-replace "$FROMSITE" "$TOSITE" &&
    rsync -az --progress "$FROMDIR" "$TODIR"
  else
    wp "@$TO" search-replace "$FROMSITE" "$TOSITE"
  fi
fi

#18

Wouldn’t this be a great candidate for a GitHub repository?


#19

–skip-columns=guid should be used when replacing, see
https://codex.wordpress.org/Changing_The_Site_URL#Important_GUID_Note


#20

I am working on a repo here. Raquelle as author (@cedarstay ) as well as @benword have been mentioned for the script they made and I added there. Thanks so much for sharing this and teaching me more about aliases and shell scripts! README is not quite done yet and tweaks me be needed. Feel free to submit pull requests or clone it.


How to manage image URLs through development to staging to production
#21

Also a plain backup option would be nice that dumps the database and uploads/ folder to a specific local path (for remote regular backups).