Automated Deployment of PHP Applications using git

Heroku is sweet hosting platform which offers a pure git deployment workflow. I love using it to deploy my Rails projects. I miss using when I deploy a PHP application.

Lets fix that…. Here is a quick heroku-ish git deployment workflow for your PHP applications. We will be using WordPress for this example. The same steps can be adapted for Drupal or any other PHP application.

Our goal is to development and test new features, themes, and plugins for WordPress locally on our development machine. When changes are working properly, we want to deploy them to our live site with one command:

git push origin master

#1: Set up password-less login to your webserver with SSH keys

To run deployments we will need to access our live webserver without a password. Follow the steps  in Setting up ssh public keys to login to webserver without a password.

For each user or machine from which you want to allow deploys you will need to install its SSH key using the steps above.

#2: Setup Code Repository on the Webserver

I connect to the webserver via SSH. My home directory is /home/saintsjd.

I have a /home/saintsjd/www folder for my website and PHP files. I also have a /home/saintsjd/code folder for my git repositories.

cd into /home/saintsjd/code and create a folder called blog.git

cd /home/saintsjd/code
mkdir blog.git

Next, initialize a bare git repository

cd blog.git
git init --bare blog.git

This gives me a git repository to store my code at /home/saintsjd/code/blog.git. I then disconnect from the SSH connection to the webserver.

#3: Setup Development Box

I have php, mysql, and apache running locally on my laptop. I create my WordPress themes, plugins, and updates on my laptop. After I test them, I deploy them to the live webserver for the world to see.

My local website folder is /home/jon/Website. To get started I clone my git repo locally:

git clone ssh://saintsjd@saintsjd.com/home/saintsjd/code/blog.git ./blogdev

This give me:

/home/jon/Website/blogdev

Now i can start filling in code!

Into the /home/jon/Website/blogdev/ folder I copy in all the files for WordPress and run the installer.

Next, I need to add a gitignore file to the project. I use the one here. https://github.com/github/gitignore/blob/master/Wordpress.gitignore. I copy the contents of that file into a .gitignore file in /home/jon/Website/blogdev/.gitignore. I then:

git add .
git commit -m "inital commit"

To send my changes to the server I type:

git push origin master

#4: Setup Live Webserver

Now I install WordPress on my server. I SSH to my server. There my website directory is /home/saintsjd/www. I need to remove this folder so I backup everything in this directory.  Then I remove it and recreate it using git clone. I type:

rm -rf /home/saintsjd/www
git clone /home/saintsjd/code/blog.git /home/saintsjd/www

I then browse to my website http://www.saintsjd.com and run the wordpress installer so the database gets installed on the server.

IMPORTANT: Protect your .git repository from prying eyes

I don’t really want others reading my source code from the git repository via http://www.saintsjd.com/.git/

I prevent this by running:

chmod -R og-rx /home/saintsjd/www/.git

I also add a .htaccess file to the .git folder containing:

Deny from all

#5: Automated Deployment Script

Now that wordpress is installed on both my local machine and my live webserver, I want to be able to deploy any changes I make on my local copy with one git push command. I can do this using a Git hook.

I SSH to my webserver and create a file /home/saintsjd/code/blog.git/hooks/post-receive

touch /home/saintsjd/code/blog.git/hooks/post-receive

I make the file executable

chmod u+x /home/saintsjd/code/blog.git/hooks/post-receive

Then I edit the file

nano -w /home/saintsjd/code/blog.git/hooks/post-receive

and place this code in it (adjusting the config variables to match your server settings)

#!/bin/bash
#CONFIG
LIVE="/home/saintsjd/www"

read oldrev newrev refname
if [ $refname = "refs/heads/master" ]; then  
  echo "===== DEPLOYING TO LIVE SITE ====="  
  unset GIT_DIR  
  cd $LIVE  
  git pull origin master  
  echo "===== DONE ====="
fi

#6 Let the (heroku-like) Fun Begin: Automated Deployment for WordPress

Now that everything is setup. I am free to develop new themes, install new plugins and update on my local development box. Once everything is working properly locally commit and push. The post-receive hook automatically deploys the code changes to my live webserver for me.

Here are my steps:

  1. Make changes to my local site’s code: new theme, new plug-in, etc
  2. test, test and test so more!
  3. When I am happy I commit to the local git repository:
    git add .
    git commit -m "new theme css for header"
  4. I push and deploy to the server using one command:
    git push origin master
  5. After the post-receive hook moves the new code to the live website. I open http://www.saintsjd.com/wp-admin on the live server to trigger and database updates that need to run

That’s it… heroku-ish git deployment for WordPress websites.

Continuous deployment for WordPress using git and fabric

——

UPDATE: This post is out of date. Please see the new and improved version of this page Automated Deployment of WordPress using git

——-

Continuous Deployment is a strategy you’ll want to implement for any successful web project. According to Eric Reis, “It’s a process whereby all code that is written for an application is immediately deployed into production. The result is a dramatic lowering of cycle time and freeing up of individual initiative. It has enabled companies I’ve worked with to deploy new code to production as often as fifty times every day.”

This article describes one method for configuring a continuous deployment infrastructure using git, fabric, and SSH. Its fairly lightweight and should work with basic hosting systems.

Our goal here is to create new features locally on a development machine, commit happy changes to a code repository, and finally deploy the changes to a live web server with ease using server side scripts. It makes deploying new changes to a live wordpress website so easy you’ll want to do it 50 times a day!

Install Deployment Tools on your Dev Machine:

I use fabric for all of my non-rails applications (PHP, Python, etc). Fabric allows you to execute command on remote servers by running a local script on your machine.

Its available at http://docs.fabfile.org/

To install on my local Ubuntu Linux development machine I just:

sudo apt-get install fabric

Set up SSH keys

To run deployments we will need to access our remote SSH server without a password. Follow the steps  in Setting up ssh public keys to login to SSH servers without a password.

For each user or machine from which you want to allow deploys you will need to install its SSH key using the steps above.

Use git to Setup Code Repositories

I use two separate git repositories: one for my wordpress website and another to track changes to my fabric deployment scripts. I store my repositories on my web server in the folder

/home/jon/code/saintsjd.com
/home/jon/code/saintsjd.com/www.git (is my wordpress website git repo)
/home/jon/code/saintsjd.com/deploy.git (is my repo for deployment scripts)

So I cd into /home/jon/code/saintsjd then:

git init --bare www.git
git init --bare deploy.git

Setup a Local Development Space

I have php, mysql, and apache running locally on my development machine. My local website folder is /home/jon/Website. There I create a folder /home/jon/Website/saintsjd.com. I then clone my git repos locally:

git clone ssh://jon@saintsjd.com/home/jon/code/saintsjd.com/deploy.git ./deploy
git clone ssh://jon@saintsjd.com/home/jon/code/saintsjd.com/www.git ./www

This give me:

/home/jon/Website/saintsjd.com/www
/home/jon/Website/saintsjd.com/deploy

Now i can start filling in code!

Install WordPress in Dev Space

Into the /home/jon/Website/saintsjd.com/www/ folder I copy in all the files for wordpress and run the wordpress installer. After WP is installed properly on my local machine I initialize a git repository to start tracking my code.

First, I need to add a gitignore file to the project. I use the one here. https://github.com/github/gitignore/blob/master/Wordpress.gitignore. I copy the contents of that file into a .gitignore file in /home/jon/Website/saintsjd.com/www/.gitignore. I then:

git add .
git commit -m "inital commit"

To send my changes to the server I type:

git push origin master

Install WordPress on the server

Now I install wordpress on my server. I ssh to my server. There my website directory is /home/jon/public_html. I backup everything in this directory then I remove this directory and recreate it using git clone. I type:

rm -rf public_html
git clone /home/jon/code/saintsjd.com/www.git public_html

I then run the wordpress installer.

Writing the deploy scripts

With wordpress installed locally and on my server. I am ready to write a fabric deploy script to easily synchronize changes I make locally to my live version of wordpress on the web server.

On my development machine I:

cd /home/jon/Website/saintsjd.com/deploy

There I create fabfile.py and fill the file with the code below:

from fabric.api import *
env.hosts = ['jon@saintsjd.com']
WEBSITE_PATH = "/home/jon/public_html"

def deploy():
    with cd(WEBSITE_PATH):
        run('git pull')

You will need to change the env.hosts value to use your ssh login for your server. You will also need to change WEBSITE_PATH to reflect the path to your website on the server.

Now when ever we want to deploy changes to the live website we can just

  1. commit our changes with git and push
  2. go to the command line and
    fab deploy

If you want to (I do), you can get fancy and make a backup before each deployment. This is great in case something goes wrong. My fabfile.py with backups looks like:

from fabric.api import *

env.hosts = ['jon@saintsjd.com']
LIVE = "/home/jon/www/saintsjd.com"
BACKUPS = "/home/jon/backups/saintsjd.com"
MYSQL_USER = ""
MYSQL_PASSWORD = ""
MYSQL_DATABASE = ""

def deploy():
    backup()
    with cd(LIVE):
        run('git pull origin master')

def backup():
    run('mysqldump -u %s -p %s --add-drop-table --password=%s  > %s/saintsjd.com-database-current.mysql' % (MYSQL_USER, MYSQL_DATABASE, MYSQL_PASSWORD, BACKUPS) )
    run('/usr/bin/nice tar -czf %s/saintsjd-`date +%%Y%%m%%d%%H%%M%%S`.tar.gz %s/saintsjd.com-database-current.mysql %s' % (BACKUPS,BACKUPS,LIVE) )

With fabfile.py written and working I add it to my git repo and push to the server for safe keeping:

git commit -am “initial commit”
git push origin master

Protect your .git folder on the live website using .htaccess

I don’t really want others reading my git repository via http://www.saintsjd.com/.git/ I prevent this by running:

chmod -R og-rx /home/public_html/.git

I also add a .htaccess file to the .git folder containing:

Deny from all

Continuous deployment of WordPress using git and fabric

My continuous deployment for the website is all set. Now I

  1. make changes to my local site’s code: new theme, new plug-in, etc
  2. test, test test
  3. when I am happy I commit to the local git repository
  4. I push to the server
  5. I then change directory into my ../deploy folder and run
    fab deploy

    on the command line to send the change to the live website

  6. finally I open wp-admin on the live server to trigger and database updates that need to run