Blog

Some of my thought on technology and programming.

Deploying via FTP is fraught with issues. When you delete a file locally you must ensure that the delete is replicated to the remote server and how can you be sure that all new files are replicated to the server? Dreamweaver does a good job of handling all of this for you but there are better ways…

The better way (in my opinion) is via your existing version control system (you are using version control, right?) like Git, Mercurial, Bzr or (God forbid) Subversion.

I shall assume that you are using and are fairly familiar with Git. So, lets get started.

We are going to create a deployment script which will:

  • Fetch your repository from a remote repository (BitBucket)
  • Checkout your chosen branch
  • Process and checkout any submodules you may have
  • Copy all of this content into your web server folder (you will be able to exclude certain content from the copy process)

Our architecture will be as follows:

  1. Work locally, using WAMP or MAMP (or whatever) with Git for Version Control (I use SourceTree)
  2. Push changes to your remote repository (I will be using BitBucket because of it's free private repositories)
  3. Pull changes from the remote repository and drop them into your Apache document root
5VhNk6M2EP01Po7LIH8e17PO5rCpSsWHTU4pGWRQraAdIfwxv366oWVg8Iy3Ut6dqfIF1E9Car0ndQsNxGN2/GLlLv0DYmUG4Sg+DsTnQRguFgt8EnCqgdloVgOJ1XENBQ2w1k+KwRGjpY5V0WnoAIzTuy4YQZ6ryHWwLZjuEDuZ+O4bYB1J00e/6diljAZTngRV/K50kvI483BaVxTu5PuI1VaWxj1UENZRdSZ9Xzyr46g2Q+74xPZY+IHyjkdPAFkHsKpoqOLZavaKx9iAjZXtQEbn39u0iRUqZwHwQyplx0dlSD2vTP3Zb6/UntmyKu8M/doHk2kczsK5HMdqMRFR9BAIdmwvTcmz6RGKfaDYaCyLVO4IjAyU2OXykGqn1jsZEXjA5YdY6jKDVoDFWBapoqHZ0b2yTvG6vOB7wwiuZQWZcvZEUtW13lNexuNxbR6aZTJecJO0tULCKYOSeU/OPTdEYYG5+lHeAh7+x3i7wtMN2Jl32Qlnkx49vkmbHY/dmBwe/KOQE4667AR+O74POxyyPgo74sXOemd2ODu9xQ5NV2PS+Co3yvwJhXYaKFxvwDkK022CfNtPRifUxgExWDgL39UjGKD4nENOVG+1MR4ahGI7moyEaEW97JhQhh3Kp9KqYaLdv1btaHTASd9EigVvHL+LfUBrKTG5oITHbqzE/I6VEOOuFIHoB9RfKAWfUu5SitCnkw8hRcgHxHawjvEMyyZYl0ICuTSrBl1Gpd3zQUgsLZR5XFl45myroo7a/d0q/0NNhhOycvSbquiLyvB1lS/kwNvkor9Q2iqHvJmxnbSJ8vy9krn6QlllpNP7rg83pp3PWz+RdqbaE/9LaOdZXaG9Gu99aO9n42CI9lfA6IHvz2qPz1W+1xbyjCb/UiXc9zixNt9XwwxDkoNThN3Sb1QvamU6jiudL52RAFtvDRwQSbGdwg9u+z/i/ys4KolJPyqdT1LtsOQTy42F6ifrkIT6S2XgFBa+4M8pmefYfDdCBfMXpyr/N9BWyt92dJT6OQmkn8sFKjWVGbGRbwp6oW1IjYLuIdoyTf8r6bJgiRcJ6sE7+wmbBEO6CvHVWErofZb/m9rgc60wIFrfOfpe91+3vZ8VIcLrK+LSXcL/WBBoNtc7VV3rlk6sngE=

Solution

Set up SSH

First, we will need to give the remote respository server a Deployment Key. To do this, we will need to generate an SSH key pair so that our server can talk to our code repository.

So lets run the following command:

ssh-keygen -t rsa

Now, we need to copy the public key so that we can give it to BitBucket. You can access this by entering the following command and copying the output to the clipboard:

cat ~/.ssh/id_rsa.pub

Paste the public key into the deployment keys section of your repository's settings (refer to either Github or BitBucket's help to see how to do this).

Next, create a config file in your ~/.ssh folder. This will allow ssh to associate the correct private ssh key with our server:

Host bitbucket.org
    User git
    IdentityFile ~/.ssh/id_rsa

Set up Git

Create a new folder to hold the git repo:

mkdir [folder_name]

Now, we need to create a new git repository on the server:

Most git deployment methods require the use of bare repositories but I have found that this does not work with projects which use submodules — therefore, we will not be using a bare repository.

cd [folder_name]
git init

Next, add your BitBucket or Github repo as a remote (replacing [name] with the actual name you would like for the repo and [URL] with the repo URL from the BitBucket or Github:

git remote add [remote_name] [URL]

Create the Deployment Script

Next, create the following script and make it executable (chmod +x <em>[filename]</em>):

#!/bin/sh
set -eu
export BRANCH="master"
export REPO_PATH="/path/to/your/git/repo/"
export SITE_PATH="/path/to/your/document_root/"

# give the user some feedback
echo fetch git data from origin
echo and rsync from ${REPO_PATH} to ${SITE_PATH}

cd $REPO_PATH
git reset --hard
git pull
git checkout -f ${BRANCH}
git submodule init
git submodule sync
git submodule update --force
su​do rsync -avz --exclude-from 'exclude.rsync' --delete --force ${REPO_PATH} ${SITE_PATH}
# You can continue to add more commands to do stuff like chmod 777 some folders etc...

Above, you may notice these two awesome little snippets:

set -eu and sudo rsync -avz --exclude-from &#39;exclude.rsync&#39; --delete --force

set -e will cause your script to exit if an error occurs and set -eu means your script will exit if variables are not set (see http://www.alexecollins.com/tips-robust-bash-scripts/ for more details).

This launches rsync with elevated privileges (I'm not happy with the use of sudo here as it requires you to enter your password every time; if I find a better solution I will post it), --delete means that any files which have been deleted from the repo will also be deleted from the copied site. The really interesting bit here is --exclude-from &#39;exclude.rsync&#39; we haven't covered this yet. It allows us to stop certain files being copied into the website (you can remove it or leave it blank if you don't want to exclude anything). Let's look into this a bit more.

Excludes

​Inside your repo, create a file called exclude.rsync (the name doesn't matter – just change the filename in the command if you want a different one) and in this file, on each new line, name files and folders you don't want copying to the website root. Here is a sample of what to put in this file:

build/
build/*
tests/
tests/*
.git/
.git/*
*.git
*.gitignore
*.gitmodules
*.travis.yml
*.sublime-*
LICENSE
phpdoc.dist.xml
README.md
exclude.rsync

​Well, that's it. However you choose to trigger your script (I ssh into the server and trigger it manually) your site should be deployed flawlessly, every time.

Coming soon

How to backup you site from your NAS…