Deploying Applications with Git and SSH


Tim Kye

This is part of my complete guide to Setting up a CentOS Digital Ocean droplet with Nginx for beginners.

I've written about this before, but my methods have changed somewhat since then. Digital Ocean also makes it quite a bit easier than webfaction. Hopefully you have already gone though the DNS Managment guide. if you have not installed git and setup a CNAME for, you should do that now.

Setting up the remote server

If you are just using SSH to connect to git, instead of HTTP, the setup is actually pretty simple. You just need a git workspace somewhere on your remote machine (droplet, our this case).

I recommend keeping all of your git workspaces in ~/git.

mkdir ~/git

For this guide I am going to be using my portfolio as an example. This is how you create a workspace.

cd ~/git
git init --bare portfolio.git

The .git suffix is conventional, not necessary. The --bare flag just tells git not to stick things into the standard .git hidden directory. Since we aren't using this directory to do work, the extra nesting doesn't help. Its still a full repo, but getting to the very useful /hooks directoy will be /portfolio.git/hooks instead of /portfolio.git/.git/hooks. Much cleaner.

That's... actually all you need to do on the server. This directory is ready to be pushed to over ssh.

Confuring the local machine

Since we are pushing over ssh, we need to tell git where the server is at. This is done by configuring your remote branches. You usually only have one remote, origin, which is the default remote that receives your changes when you git push. If you are not using GitHub or some other public repository as your primary, you can alter this remote

git remote set-url origin

However, you are probably using Github, and want to continue using it. In that case, you need to add a new remote branch. I am going to call this remote digi, for Digital Ocean

git remote add digi

We can push to this remote with

git push digi master

You will be prompted for your SSH password, and then the branch will push. Your droplet now has all your source code in it.

Updating your server on push

Once your code has been pushed into the repository, you're probably going to want to do something with it. Like move it into the directory it is hosted from.

Unfortunately this guide is going to hit a chicken/egg problem, since we haven't talked about that yet. If you haven't setup your application yet, you might want to hop over to the Application Managment guide first. Otherwise, continue.

We can do this very easily by setting up a post-receive hook in git. This is a script that will run when a git receives a push. The perfect spot for copying files our of the repository, and running any setup commands our application requires.

I use one of two post receive hooks.

The single branch hook

This hook will fire after every push, which implies that you are really only working with one branch. For applications that I host in GitHub, which contains all my branches and tags, I only ever push the master branch to my hosting server. In this case, a hook that fires every time is good enough, since only production code is ever going to be received.

Open a file editor to put our post receive hook into

cd ~/git/portfolio.git/hooks
nano post-receive

Then enter the script

GIT_WORK_TREE=/home/tyrsius/webapps/portfolio/app git checkout -f master
GIT_WORK_TREE=/home/tyrsius/webapps/portfolio/app git reset --hard
. /home/tyrsius/webapps/portfolio/run/restart-install

This script assumes you are using the structure I use in the Application Managment guide, where PROJECT/app contians the application code, and PROJECT/run contains scripts for managing it.

This hook updates the /app directory with the latest code before running the restart-install script. This makes it easy to manage to the actual process of stopping the app, building the new code, and restarting it from the app, instead of from git. Once git is setup, we shouldn't ever have to come back here.

The multiple branch hook

Some of my projects are not open source. Actually, right now just one. Its the project that runs my house (Nest, my Hue Lights, some other smart stuff). I don't have a solid grasp on how to secure these things yet, and I don't want anyone trolling my energy bill. You may also have the occasional need to keep your code off of GitHub. If you do, you probably still want your source code to live off of your home computer, and this means keeping the masteranddev branch on your remote server.

The single branch hook is going to give you trouble in this situation. Luckily, we can make this script conditional on the branch name.

while read oldrev newrev refname  
    branch=$(git rev-parse --symbolic --abbrev-ref $refname)
    if [ "master" == "$branch" ]; then
        GIT_WORK_TREE=/home/username/webapps/YOURAPP/app git checkout -f master
        GIT_WORK_TREE=/home/username/webapps/YOURAPP/app git reset --hard
        . /home/username/webapps/YOURAPP/run/restart

You may want to use this for all your hooks anyway, just to be safe.