Setting up SSH Git deploy with Webfaction

#git#deploy#webfaction#node#ssh

Tim Kye

I spent about three days trying to get this to work with an https server, but eventually gave in. There were too many issues related to the account the hook would run under for my limited linux experience to solve. So the SSH method will have to do for now.

Setting up Git

If you follow this guide through the Creating a new repository section, you should be good. For the sake of history, here are the installation steps from it

  • Log in to the control panel.
  • Click Domains / websites ‣ Applications. The list of applications appears.
  • Click the Add new application button. The Create a new application form appears.
  • In the Name field, enter a name for the Git application.
  • In the App category menu, click to select Git.
  • In the Extra info field, enter a password for the default user.
  • Click the Save button.

Creating the Deploy Hook

Once you have a git repo made, it will contain a hooks directory with a bunch of sample code in it. cd into the repo and run rm -rf hooks/* to clear it out.

You will want to make a file named post-receive (note that there is no file extensions). Using nano is my preferred way. Inside of this file you will want to do 2 things.

  • Update your application with the most recent checkin
  • Start the application with w/e process you are using

The first step is straightforward, just put this at the top of the file

#!/bin/sh
GIT_WORK_TREE=/home/username/webapps/YOURAPP/app git checkout -f master
GIT_WORK_TREE=/home/username/webapps/YOURAPP/app git reset --hard

This will clear the application directory, and put it at the HEAD of your repo.

Organizing your app code

Now, you can start your app however you want, but I took a page out of Ghost's book, and setup a run directory with three scripts in it: restart, start, and stop. I put this folder next to my application's actual code, under a root folder for the whole app.

|ApplicationFolder
-app
--**actual contents of app**
-run
--**scripts to start and stop the app**

It contains things nicely, and seperates these scripts from the application code.

So, the scripts.restart calls stop then start. The reason for the other two are just consolidation: you need to start your app from the deploy hook, you probably want to have crontab start your app, and you may need to do it manually from time to time.

So what does start look like?

#!/bin/sh
procs=$(forever list | grep -F /home/username/webapps/YOURAPP/app/server.js)
if [ -z "$procs" ]; then
    PORT=3000 forever start /home/username/webapps/YOURAPP/app/server.js
fi

So lets break that down. We are going to scan for any current instances of the app. If we don't find any, we are doing to start it. This makes the start script safe to run. This is handy if you want your crontab entry to run every X minutes, instead of just on reboot.

To make a cron job for this, add this like to crontab, where xx is the interval in minutes.

*/XX * * * * ~/webapps/YOURAPP/run/start

Here is the stop script:

#!/bin/sh
forever stop /home/username/webapps/YOURAPP/app/server.js

Now, you can just call your start script at the end of your post-receive hook.

One more note on the scripts. For the post-receive hook, as well as the scripts, you will need to make them executable. The shell command for that is chmod +x /path/to/file

Setting up the client

The last thing you will need to do is setup a new remote repository on your clients. It's pretty easy.

git remote add NAME username@username.webfactional.com:/path/to/repo

Now you can just push to this remote, and your application should deploy and start up! Your done.

Advanced Hook (specific branch)

If you only want to run off of a specific branch (like master), you can employ this post-receive hook

#!/bin/sh
while read oldrev newrev refname
do
    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
    fi
done