WordPress Site Replicator

For WordPress plugin software developers like myself, I often need to be able to replicate a WordPress website quickly for the purpose of doing offline development. To assist in this endeavor, I created a bash script to do the work.

The script is interactive and simply executes as follows:


$ replicate_site.sh –from=sourcefolder –to=targetfolder –dbsrc=db:username:password –dbtarget=db:sandboxuser:password

The script is interactive, it will prompt you to confirm the operation. A backup image of the target site is created as well. However, please be sure to look over the script to ensure it will meet your needs.

     1  #!/bin/bash --norc
     2  # File: replicate_site.sh
     3  # Author: Frank Koenen mail@frankkoenen.com
     4  #
     5  # Purpose: replicate a wordpress site, filesystem and DB.
     6  #
     7  # Usage: ./replicate_site.sh [-V] --from={site} --to={site} --dbsrc={databasesource} --dbtarget={databasetarget}
     8  #
     9  #    databasesource ::= databasename:username:password  OR 'fromconfigs'
    10  #    databasetarget ::= databasename:username:password
    11  #
    12  # Example:
    13  #    ./replicate_site.sh --from=bikeshophub --to=sandbox3 --dbsrc=wpbikeshophub:bshwpuser:password --dbtarget=wpsandbox3:sandboxuser:password
    14  #
    15  #--------------------------------------------------
    16  typeset -r ExeName=${0##*/}
    17
    18  # get commandline arguments.
    19  allargs="$*"
    20  export verbose=1
    21  export from=""
    22  export to=""
    23  export dbsrc=""
    24  export dbtarget=""
    25  while [ $# -gt 0 ]; do
    26    case "${1}" in
    27      -V) export verbose=0 ;;
    28      --from=*) export from="${1#--from=}" ;;
    29      --to=*) export to="${1#--to=}" ;;
    30      --dbsrc=*) export dbsrc="${1#--dbsrc=}" ;;
    31      --dbtarget=*) export dbtarget="${1#--dbtarget=}" ;;
    32    esac
    33    shift
    34  done
    35
    36  # Get the terminal line.
    37  mytty="$(tty 2> /dev/null || echo 'not a tty')";case "${mytty}" in *"not a tty"*) mytty="not_a_tty" ;; esac
    38
    39  if [ "${from}x" == "x" -o "${to}x" == "x" -o "${dbsrc}x" == "x" -o "${dbtarget}x" == "x" ]; then
    40    echo 1>&2 Usage: $ExeName missing required argument: --from= , --to= , --dbsrc= , --dbtarget=
    41    echo 1>&2   Example:   $ExeName --from=sandbox1 --to=sandbox3 --dbsrc=wpsandbox1:sandboxuser:password --dbtarget=wpsandbox3:sandboxuser:password
    42    exit 1
    43  fi
    44
    45  if [ ! -d $from -o ! -d $to ]; then
    46    echo 1>&2 $ExeName source or target site folder missing?!
    47    exit 1
    48  fi
    49
    50  # Main --------------------------------------------
    51
    52  cat << !
    53  
    54    Hello from $ExeName
    55  
    56    This routine is very powerful!
    57  
    58    If you are not sure of how to use it, don't.
    59  
    60    This routine will destroy the "target" DB and filesystem.  Double-check your calling arguments.
    61  
    62         ./$ExeName $allargs
    63  
    64    Enter   yes    to continue.
    65  
    66  !
    67  echo -e "> \c"
    68  
    69  read ok
    70  
    71  if [ "${ok}" != "yes" ]; then
    72    exit 1
    73  fi
    74  
    75  case $to in
    76    sandbox*)
    77      umask 0222
    78    ;;
    79    *)
    80      echo 1>&2 $ExeName replication can only occur to a sandbox at this time. Sorry.
    81      exit 3
    82    ;;
    83  esac
    84  
    85  servername=$(cd $from && (grep 'ServerName ' etc/apache.conf | head -1 | sed -e 's/^.*ServerName //i' -e 's/\./\\./g'))
    86  nodename=$(echo $servername | cut -d. -f1 | sed -e 's/\\$//g')
    87  domain=$(echo $servername | cut -d. -f2-)
    88  newnodename="wp$to"
    89  newservername="$newnodename\\.wandertec\\.com"
    90  newdomain="wandertec.com"
    91  if [ $verbose == 1 ];then
    92    echo 1>&2 $ExeName, source server information:
    93    echo 1>&2 '      ' servername = $servername
    94    echo 1>&2 '      ' nodename   = $nodename
    95    echo 1>&2 '      ' domain     = $domain
    96    echo 1>&2 '      '
    97    echo 1>&2 '      ' new servername = $newservername
    98    echo 1>&2 '      ' new nodename   = $newnodename
    99    echo 1>&2 '      ' new domain     = $newdomain
   100    echo 1>&2 '      '
   101  fi
   102  
   103  if [ $dbtarget = 'fromconfigs' ]; then
   104    echo 1>&2 $ExeName, 'fromconfigs' is not allowed on the target.
   105    exit 5
   106  fi
   107  
   108  if [ $dbsrc = 'fromconfigs' ]; then
   109    dbsrc=$(egrep "^define\(.DB_NAME.," $from/html/wp-config.php | head -1 | sed -e 's/define..DB_NAME.,//' -e 's|..;.*$||' -e "s/^ *'//")
   110    if [ "${dbsrc}x" == "x" ]; then
   111      echo 1>&2 $ExeName, could not obtain dbsrc info.
   112      exit 4
   113    fi
   114    u=$(egrep "^define\(.DB_USER.," $from/html/wp-config.php | head -1 | sed -e 's/define..DB_USER.,//' -e 's|..;.*$||' -e "s/^ *'//")
   115    p=$(egrep "^define\(.DB_PASSWORD.," $from/html/wp-config.php | head -1 | sed -e 's/define..DB_PASSWORD.,//' -e 's|..;.*$||' -e "s/^ *'//")
   116    dbsrc="$dbsrc:$u:$p"
   117  fi
   118  
   119  if [ $verbose == 1 ];then echo "$ExeName: extracting DB from source ..." >&2;fi
   120  db=$(echo $dbsrc | cut -d: -f1);dbuser=$(echo $dbsrc | cut -d: -f2);dbpass=$(echo $dbsrc | cut -d: -f3)
   121  mysqldump -h 127.0.0.1 -u$dbuser -p$dbpass $db > /tmp/${$}_srcdb ; file="/tmp/${$}_srcdb"
   122  sed -e "s|/home/wordpress/$from|/home/wordpress/$to|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   123  sed -e "s|/etc/apache2/conf.d/$from.conf|/etc/apache2/conf.d/$to.conf|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   124  sed -e "s|$servername|$newservername|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   125  sed -e "s|$domain|$newdomain|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   126  sed -e "s|$nodename\.$domain|$newnodename\.$newdomain|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   127  sed -e "s|AuthName .*|AuthName \"WP $to Administration\"|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   128  
   129  if [ ! -s $file ]; then
   130    echo 1>&2 $ExeName, Error retrieving DB from src.
   131    exit 5
   132  fi
   133  
   134  if [ ! -f /tmp/SAVE.replicate_site.$to ]; then
   135    if [ $verbose == 1 ];then echo "$ExeName: saving revertable files to /tmp/SAVE.replicate_site.$to ..." >&2;fi
   136    cp $file $to/mysqldump.sql
   137    (cd $to && tar cjf /tmp/SAVE.replicate_site.$to .)
   138  fi
   139  
   140  if [ $verbose == 1 ];then echo "$ExeName: purging files in folder $to ..." >&2;fi
   141  (cd $to && rm -rf *)
   142  
   143  if [ $verbose == 1 ];then echo "$ExeName: relicating files from site $from to site $to ..." >&2;fi
   144  (cd $from && tar cf - . 2> /dev/null | (cd ../$to ; tar xpf - 2> /dev/null))
   145  
   146  (cd $to && (
   147    echo -e "User-agent: *\nDisallow: /" >> html/robots.txt
   148  ))
   149  
   150  (cd $to && (
   151    for file in etc/apache.conf html/wp-config.php ; do
   152      if [ -f $file ]; then
   153        sed -e "s|/home/wordpress/$from|/home/wordpress/$to|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   154        sed -e "s|/etc/apache2/conf.d/$from.conf|/etc/apache2/conf.d/$to.conf|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   155        sed -e "s|$servername|$newservername|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   156        sed -e "s|$nodename\.$domain|$newnodename\.$newdomain|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   157        sed -e "s|AuthName .*|AuthName \"WP $to Administration\"|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   158      fi
   159    done
   160  
   161    (cd html/wp-content && (
   162      for file in $(find . -type f | xargs egrep "$nodename|$domain|$servername" 2> /dev/null | cut -d: -f1 | sort -u) ; do
   163        if [ -f $file ]; then
   164          sed -e "s|/home/wordpress/$from|/home/wordpress/$to|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   165          sed -e "s|$servername|$newservername|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   166          sed -e "s|$nodename\.$domain|$newnodename\.$newdomain|g" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   167        fi
   168      done
   169    ))
   170  
   171    file="etc/apache.conf"
   172    sed -e 's|SSLCertificateFile .*$|SSLCertificateFile /etc/ssl/certs/sandbox.wandertec.com/sandbox_wandertec_com.cert.crt|' $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   173    sed -e 's|SSLCertificateKeyFile .*$|SSLCertificateKeyFile /etc/ssl/certs/sandbox.wandertec.com/sandbox_wandertec_com.cert.key|' $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   174  
   175  ))
   176  
   177  if [ $verbose == 1 ];then echo "$ExeName: installing DB to target ..." >&2;fi
   178  tdb=$(echo $dbtarget | cut -d: -f1);tdbuser=$(echo $dbtarget | cut -d: -f2);tdbpass=$(echo $dbtarget | cut -d: -f3)
   179  echo 'SHOW TABLES;' | mysql -h 127.0.0.1 -u$tdbuser -p$tdbpass $tdb | grep -v '^Tables_in' | sed -e 's/^/DROP TABLE /' -e 's/$/;/' | mysql -h 127.0.0.1 -u$tdbuser -p$tdbpass $tdb
   180  cat $file | mysql -h 127.0.0.1 -u$tdbuser -p$tdbpass $tdb
   181  blogname=$(echo "SELECT option_value FROM wp_options WHERE option_name = 'blogname';" | mysql --skip-column-names -h 127.0.0.1 -u$tdbuser -p$tdbpass $tdb)
   182  blogdesc=$(echo "SELECT option_value FROM wp_options WHERE option_name = 'blogdescription';" | mysql --skip-column-names -h 127.0.0.1 -u$tdbuser -p$tdbpass $tdb)
   183  echo "UPDATE wp_options SET option_value = '$blogname (SANDBOX!)' WHERE option_name = 'blogname';" | mysql -h 127.0.0.1 -u$tdbuser -p$tdbpass $tdb
   184  echo "UPDATE wp_options SET option_value = '$blogdesc (SANDBOX!)' WHERE option_name = 'blogdescription';" | mysql -h 127.0.0.1 -u$tdbuser -p$tdbpass $tdb
   185  
   186  file="$to/html/wp-config.php"
   187  sed -e "s|^define('DB_NAME',.*$|define('DB_NAME', '$tdb');  // The name of the database|" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   188  sed -e "s|^define('DB_USER',.*$|define('DB_USER', '$tdbuser'); // Your MySQL username|" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   189  sed -e "s|^define('DB_PASSWORD',.*$|define('DB_PASSWORD', '$tdbpass'); // ...and password|" $file > /tmp/$$ ; cat /tmp/$$ > $file ; rm -f /tmp/$$
   190  
   191  /bin/rm -f /tmp/$$*
   192  
   193  /etc/init.d/apache2 reload
   194  
   195  exit 0