This is a brief post on how I deploy my HTML source files from my local workspace to my website using Git. It describes how I’ve set things up so I can push live changes easily. My website is powered by Hugo, a static site generator, but this guide is applicable to most cases.
The quick summary: after compiling locally, I push my local repository to a managed repository (i.e. GitHub), and then I push it again to a remote repository (my website) and use a post-receive hook to update my website with the compiled HTML files.
There are lots of other ways, more powerful, to manage and deploy your website using Git – there is a great guide on hosting on GitHub pages, for example – but this one works for me and might work for you too. :)
- You are familiar with Git
- A remote server is set up with passwordless SSH access
- If you are using Hugo, your site is ready-to-publish and already a Git repository
Our local repository is a Git repository.
On the remote server, there are three workspaces:
- The live public directory: /home/www/domain.com
- The repository: /home/repos/domain.git
- The temporary directory: /home/temp/domain.com
We’re going to push into the remote repository, which will push the files to the temporary directory, and then we will push only the compiled HTML files (in Hugo, everything in /public) to the live public directory. Separately, we push everything from our local repository to the managed Git repository.
The remote repository
After SSHing in and moving into the /home, we’ll create a new /repos directory and initialise a bare repository that will mirror the version control files of our local repository. While we’re here, we’ll also create a new /temp directory for our site – this is where we want the source files to get pushed into.
mkdir temp && cd temp mkdir domain.com cd .. mkdir repos && cd repos mkdir domain.git && cd domain.git git init --bare
Now we’re going to set up a post-receive hook. This is where the magic happens.
cd hooks touch post-receive chmod +x post-receive
Open up post-receive in whichever text editor you like. This file will be executed every time the remote repository receives a push.
These are the instructions we’re going to push to this hook:
# Set bindings for clarity export WORK_DIR=/home/temp/domain.com export GIT_DIR=/home/repos/domain.git export LIVE_DIR=/home/www/domain.com echo -e "\033[0;33mDeleting the current temp/public directory...\033[0m" # If temp/public exists, then let's remove it if [ -d "$WORK_DIR/public" ]; then rm -rf $WORK_DIR/public; fi # Now we push the files into the temp directory git --work-tree=$WORK_DIR --git-dir=$GIT_DIR checkout -f echo -e "\033[0;33mChecked out to /temp! Now deleting the contents of the live directory...\033[0m" # And then we send only the contents of temp/public to the live directory rsync -avht $WORK_DIR/public/ $LIVE_DIR --delete echo -e "\033[0;33mSynced to live directory.\033[0m"
The local repository
There is only one necessary thing to do here and that is to tell Git to add a remote for the live repository:
git remote add live ssh://email@example.com/home/repos/domain.git
Now, let’s assume that we’re happy with what our website looks like and we’ve committed all our changes (after running
hugo) – let’s put it online! When we use
git push live master, the whole project will be pushed to the remote repository, and the post-receive hook starts up.
Try pushing it now.
Note: This borrows heavily from the Hugo documentation.
Besides compiling my site and pushing my changes to the live repository, I also want to push my changes to my managed Git repository (with remote name origin). I also have some Sass compiling to do for my theme, so I automate all of these things in a script called deploy.sh.
It looks like this:
echo -e "\033[0;32mDeploying updates...\033[0m" ## compile things here # Remove the public directory if [ -d public ]; then rm -rf public; fi # Build the project hugo # Add the new public directory git add public # Commit the changes msg="rebuilding site `date`" if [ $# -eq 1 ] then msg="$1" fi git commit -m "$msg" echo -e "\033[0;32mDeploying updates to production...\033[0m" git push live master echo -e "\033[0;32mDeploying updates to private repo...\033[0m" git push origin master
Make sure the file is executable (
chmod +x deploy.sh) and then whenever you want to push changes to your live site and the private repository, simply:
./deploy.sh "optional commit message"
There are a few things worth mentioning:
There are lots of other Git hooks to play around with. For example, if you want to reject pushes if your code fails a test or linter, you can adjust the pre-receive hook.
The work tree directory has to be writable by the user who is running the hook (
git checkout -f) so double-check permissions if you run into any issues.
If your local files are straight up what you want to push – i.e. there is no compilation and resulting /public directory – then you can skip the /temp and /public parts and push directly into the live directory.
My previous attempts used symbolic links (
ln -s) and some other reading offered
cp as an option to move files too. Both these solutions ignored deleted files, so I had to delete entire folders and remake them. This is an OK solution, to be fair, but I found
rsync and liked it a lot better. Depending on how big your site is, you might prefer differently.
That being said, an earlier version of this pipeline used temporary directories to hold dated versions of the project (i.e. backups), and then symlinked the newest directory to the live directory. This is a nice way to do backups, or even beta releases; just remember to purge so it doesn’t get too crowded after every push.