Preventing yourself from accidentally breaking production with Drush

For all the sites I maintain, I have at least a local and production environment. Some projects warrant a dev, qa, etc. as well, but for the purposes of this post, let's just assume you often run drush commands on local or development environments during development, and eventually run a similar command on production during a deployment.

What happens if, at some point, you are churning through some Drush commands, using aliases (e.g. drush @site.local break-all-the-things to break things for testing), and you accidentally enter @site.prod instead of @site.local? Or what if you were doing something potentially disastrous, like deleting a database table locally so you can test a module install file, using drush sqlq to run a query?

$ drush @site.prod break-all-the-things -y
Everything is broken!                                    [sadpanda]

Most potentially-devastating drush commands will ask for confirmation (which could be overridden with a -y in the command), but I like having an extra layer of protection to make sure I don't do something dumb. If you use Bash for your shell session, you can put the following into your .profile or .bash_profile, and Bash will warn you whenever the string .prod is in one of your commands:

prod_command_trap () {
  if [[ $BASH_COMMAND == *.prod* ]]
  then
    read -p "Are you sure you want to run this command on prod [Y/n]? " -n 1 -r
    if [[ $REPLY =~ ^[Yy]$ ]]
    then
      echo -e "\nRunning command "$BASH_COMMAND" \n"
    else
      echo -e "\nCommand was not run.\n"
      return 1
    fi
  fi
}
shopt -s extdebug
trap prod_command_trap DEBUG

Now if I accidentally run a command on production I get a warning/confirmation before the command is run:

$ drush @site.prod break-all-the-things -y
Are you sure you want to run this command on prod [Y/n]?

This code, as well as other aliases and configuration I use to help my command-line usage more efficient, is also viewable in my Dotfiles repository on GitHub.

Comments

Good stuff Jeff!

I think you can also use a drush policy file to do some of the same: (see this function for an example).

That's a nice way to achieve a similar goal; it's a little more overhead when you're working with simpler sites/stacks, but it would be very helpful when you want to allow drush access to your entire team but restrict certain parts of drush to only privileged members. Learn something new every day!

The original site is down, here is an Acquia article about the drush policy file:

https://docs.acquia.com/articles/using-policy-file-make-drush-safer

To help prevent accidental sql-sync and rsync there is a simpler way with drush-aliases:

<?php
$aliases['mysite_production'] = array(
  // ...
  'target-command-specific' => array(
    'sql-sync' => array('no' => TRUE),
    'rsync' => array('no' => TRUE),
  ),
);

Warning though: this doesn't help if you pass --yes to the command, because (at the moment) Drush interprets a combination of both --yes and --no as 'yes'.