using git hooks to deploy server with forever

Mon Feb 17, 2014

If you love deploying with automation and you love the fact you dont need to touch your server (which you should!). I've setup this up on an ec2 ubuntu 12.04 LTS, shouldnt differ too much with other ubuntu versions. At this time of writing i'm setting up a nodejs / flask server

On your remote server

#install forever and give it global access
sudo npm install -g forever

#setup git bare at your deployment server
git clone --bare [repo url]

#add the hooks
#navigate into your git folder
cd path/to/repo/hooks
vi pre-receive

echo "stopping server service"
service nodejs-www stop

vi post-receive

echo "checkout the files"

branch=$(git branch | grep "*" | sed "s/* //")

if [ "$branch" = "master" ]
        echo "Master branch"
        git --work-tree=/home/ubuntu/app/www checkout -f
        echo "Successfully checked out master branch"
        echo "Not master branch"

echo "start service"
service nodejs-www start

sudo chmod +x pre-receive;
sudo chmod +x post-receive;

FYI: Just make sure the folders are created for the "git --work-tree"

#navigate into init.d to create a service
cd /etc/init.d
sudo vi nodejs-www

#! /bin/sh
# /etc/init.d/nodejs-www

export PATH=$PATH:/usr/local/bin/

case "$1" in
    cd $APP_DIR
    echo "Starting $NAME"
    #I detached the message to 'screen', it is not a requirement
    screen -d -m /usr/bin/sudo -u ubuntu $forever --minUptime 5000 --spinSleepTime 2000 -a -l $LOG start $APP
    #if to run a python server use
    #screen -d -m /usr/bin/sudo -u ubuntu $forever --minUptime 5000 --spinSleepTime 2000 -a -l $LOG start -c python $APP
    echo "Started $Name"
    exit 0
    echo "Stopping script $NAME"
    cd $APP_DIR
    /usr/bin/sudo -u ubuntu $forever stop $APP
    exit 0
    echo "List"
    /usr/bin/sudo -u ubuntu $forever list
    exit 0
    echo "Usage: /etc/init.d/nodejs-www {start|stop|list}"
    exit 1

exit 0

FYI: Many others were able to run forever directly without having "/usr/bin/sudo -u ubuntu" (ubuntu is the user account) or substituting it with "sudo". For my case on an amazon aws, without it, i ran into permission issues with forever and nodejs directory mapping issues.

A permission error similar to this:

(function (exports, require, module, __filename, __dirname) { // Copyright Joy
Error: EACCES, permission denied '/root/.forever/pids/'
    at Object.fs.openSync (fs.js:410:18)
    at Object.fs.writeFileSync (fs.js:956:15)
    at writePid (/usr/local/lib/node_modules/forever/bin/monitor:13:6)
    at null. (/usr/local/lib/node_modules/forever/bin/monitor:46:5)
    at EventEmitter.emit (/usr/local/lib/node_modules/forever/node_modules/forever-monitor/node_modules/broadway/node_modules/eventemitter2/lib/eventemitter2.js:332:22)
    at /usr/local/lib/node_modules/forever/node_modules/forever-monitor/lib/forever-monitor/monitor.js:153:10

Now to wrap up the server side

sudo chmod a+x nodejs-www
cd /var/log
sudo touch nodejs-www.log
sudo chmod 777 nodejs-www.log

Add to rc.local to start upon server start

sudo vi /etc/rc.local

#!/bin/sh -e
service nodejs-www start
exit 0

For some you may need to change the rc.local permission to executable.

If i didnt miss anything this should it for the server

Now on client side

cd path/to/repo
git remote add deploy [remote server repo url]
git commit -am 'test deploy'
git push deploy master

This should be it, and the service should automatically stop and restart itself. If i missed anything let me know :)

Git Cheat Sheet

Mon Dec 17, 2012

#Setting up git from scratch
git init                                             #initialize git
git remote add < remote_label > < destination >      #adds a new remote destination with a label
git fetch < remote_label >                           #fetches the head of the repo
git checkout master                                  #checkouts the master branch

#Setting up git from another repo git clone < remote_label > < optional_dir_location > #clone a repo with git config ready

#Branching git branch #list all branches git branch < branch_label > #creates a new local branch with name < branch_label > git branch -d < branch_label > #deletes the local branch with name < branch_label > git push < remote_label> :< branch_label > #deletes remote branch

#Merging git checkout < branch to be merge >
git merge < branch_name > #merge recursively and auto commit

additional info - git merge man page

#fetch & pulling git pull < remote_label > < branch_label> #it will recursively merge with your repo git fetch < remote_label >

#Commit git commit -a -m < message > #local commit git commit -amend -m < message > #edit last commit message git push < remote_label > < branch_label > #commits to remote branch

#Tagging git tag -a < comment > #to remember a specific point git push –tag #pushes tag

#To delete a tag git tag -d < tag_name > git push origin :< tag_name >

#if tag name is same as one of your branch name git tag -d < tag_name > git push origin :refs/tag/< tag_name >

#Logging git log #shows the commit log

#Untrack a file git rm –cache < file_name > #untracks a file, thus not included in repo

#Stashing git stash

#cloning a bare repo for deployment purpose git clone –bare

additional info git stash man page

#reverting to previous commits  [Advance]
git checkout < commit_hash >                     #checkout the indicated commit hash
git checkout -b < branch_label > < commit_hash>  #branches with < branch_label > and checkout to the commit hash
git reset –hard < commit_hash >                 #destroys the local modification, you will loose the uncommit work
git revert < commit_hash >                       #reverts to the previous commit

additional info - git revert man page & stack over flow git revert qa

#splitting a subpath out into a new repo
git filter-branch –prune-empty –subdirectory-filter lib master

additional info - github:help