Push to deploy using Git
Deploying from source control to a server
A few years ago I switched to Git for source control.
Although I use C# and DotNet for my day job, my employers use Git in preference to TFS and once I became familiar with it I agreed more and more with that decision.
This site is created automatically from markdown-style content files, so changing the site is all done in the file system on my local Macbook Air or Surface Pro. I make changes locally and push them to the server (for TFS mavens, this is like checking in). Using push to deploy those changes can also make their way live automatically - when I reach the point where I'm ready, I push to the repository on my web server instead of the origin on GitHub and a git 'hook' automatically copies the result into place and bumps the server.
The way it works sounds lengthy, but is actually quite straightforward.
Setting things up
- On my web server I created a
--bare
repository which I can access via SSH. - In the
hooks
folder of that repository I created apost-receive
script that runs automatically when pushes occur. - In the home area of the user (not the root user) I have a live folder which Node runs from to serve this site. (I now have a non-static site running in Go, but it is still deployed using a variant of this method.)
- Source Tree (I now use Git Extentions) on my local box has the web server's bare repository set up as a remote.
The process I follow
- I make my changes and test them locally, then commit them in Source Tree.
- Then I push that commit to my GitHub repository for versioning/backup.
- When one or more commits form a release-ready version I also push to the remote repository on the web server.
- Upon that push, Git updates the bare repository on the web server then runs the
post-receive
script. - The
post-receive
script checks out a copy of the site into a temporary folder then copies the subfolder holding the servable content from there into the web server'shtml
folder for serving.
Provisos and options
- Make sure you do not use the root user on the web server - use sudo from a normal user. If things go wrong when you attempt to push, permissions are usually the issue and this avoids most pitfalls.
- You need to have your SSH key accepted by the web server for pushes. Whilst not 100% tamper-proof, this method is fantastically safer than leaving an FTP server running on your production box.
- Remember I said a bare repository - try using
git init --bare
when creating it on the server. - You can set up multiple repositories on your server, as the remote repository details contain a path so each can be push-deployed independently using their own scripts. You could then have separate repositories/deploys for each site so you can run a test/demo version on the same server as the live one using a different port (only do this for trivial stuff that is not going to affect your live site in any way).
- Don't forget to set executable or write permissions wherever needed (including
post-receive
).
Push-deploy permissions
By default, your html
folder will be set up so that git cannot write to it.
Edit the sudo users:
sudo visudo
Add the git user:
git ALL = (root) NOPASSWD: /var/www/html
Save and close the editor. You may need to add your user to the sudo group:
adduser <your-user> sudo
If you've reached this point and already have files deployed, remove them first:
sudo rm -r /var/www/html/*
You may also need to change the ownership of your web root:
sudo chown -R <your-git-user> /var/www/html
You can test your hook without pushing by:
cd [your git repository folder]
hooks/post-receive whatever triggered
Sample post-receive script
Your requirements will vary. Here is a small example.
#!/bin/bash
LOGFILE=./post-receive.log
TEMPDIR=/home/my-user/build
DEPLOYDIR=/var/www/html
mkdir -p $TEMPDIR
echo "PROCESSING GIT POST-RECEIVE"
while read oldrev newrev refname
do
echo -e "Received Push Request at $( date +%F )" >> $LOGFILE
echo " - Old SHA: $oldrev New SHA: $newrev Branch Name: $refname" >> $LOGFILE
echo "Starting Deploy" >> $LOGFILE
echo " - Starting code update"
GIT_WORK_TREE="$TEMPDIR" git checkout -f
echo " - Finished code update"
echo "Finished code update." >> $LOGFILE
cd $DEPLOYDIR
cp -r $TEMPDIR/output/* .
echo "Deployed (subject to any errors displayed)."
echo "Finished deploy attempt." >> $LOGFILE
done
Your relevant paths are set at the top (no checking is done).
Notes
There are a few assumptions here, like knowing how to set up a repository or add SSH keys. I've not gone into more detail on those as it is out of the scope of this post, those details are easily found with your favourite search engine, and if this post is relevant to you at all then there's a good chance you are technically-minded enough to investigate further yourself.