· Git how to Hugo web

Automating Your Hugo Website with Git

As I wrote in my first post here, I wanted to use version control for this website. Version control is a software development management tool that gives separate layers–in this case, I can be working on a new feature or design and not have it appear on the website until I’m ready. It also lets me undo things and change course easily.

I chose to use Git mainly because it’s pretty standard but also to give me a chance to learn it in a real-world environment.

Years from now, I don’t want to have to know what version of Hugo to use to regenerate an old website if needed. So I actually have two Git repositories for this site:

  • Source, the raw data Hugo uses to generate the website.
  • Public, the complete website you’re reading right now.

Writing Workflow

Writing for the website is pretty simple.

  1. I’ve cloned the source repo onto my daily-use Mac. When I’m ready to write something new, I run hugo new blog/title.md then edit the file that’s created.
  2. When I’m ready to test, I’ll run hugo server -Dw. Hugo will watch the files and rebuild the test site if there are changes. I can check to see how the new post will appear using a web browser.
  3. Once I’m happy with it, I commit the changes: git add, git commit, and git push.

But remember, this is only the source used by Hugo to make the website. (Since I want to separate the two repositories, I added the public/ directory Hugo uses to store the test files to the .gitignore list.)

How to Automatically Deploy

The key part in making the updated website visible on the Internet is the last part of step three: git push. On the server, I run a Git hook located under hooks/post-receive:

#!/bin/bash
GIT_REPO=$HOME/website-source.git
TMP_GIT_CLONE=$HOME/tmp/website-source
PUBLIC_WWW=/srv/www

# Wait up to 2 seconds for the STDIN to get here.
read -t 2 from_commit to_commit branch_name

# Only deploy if the master branch was pushed. Full variable is similar to "refs/heads/master".
if [[ "$branch_name" != */master ]]; then
  echo "Received branch $branch_name, not deploying."
  exit 1;
fi

# Make temporary clone for site generation.
git clone $GIT_REPO $TMP_GIT_CLONE

# Run hugo using config in repo, no draft or future content will be created.
cd $TMP_GIT_CLONE
hugo

# Copy files to www server root. Some files are excluded so they don't overwrite or delete
# required files in the server root.
rsync -rvh --delete --exclude='.git/' --exclude='.gitignore' $TMP_GIT_CLONE/public/ $PUBLIC_WWW/

# We're done, so clean up.
rm -rf $TMP_GIT_CLONE

When I push the updates from my Mac, the server runs this script and I don’t have to run each command manually!

More Details

The script is based on the ones at Setting up Push-to-Deploy with git and Deploy Process. It could easily be adapted for use with other static website generators.

Some sections that might require more explanation are:

  • Line 10: Check if the master branch was pushed. I don’t need to regenerate the website if I’m working on a feature branch that isn’t live yet.
  • Line 20: Run hugo. I don’t need any parameters here because my config.toml file for Hugo lists the theme to use, etc.
  • Line 24: The rsync command line options.
    • --delete makes sure there aren’t any orphaned files on the public site. Basically, if I remove a file in the source, that change carries over.
    • --exclude='.git/' --exclude='.gitignore' leaves the special Git files in the public directory alone. This means I can easily maintain the second Git repository I mentioned near the beginning. (That public repository is managed manually.)

If you do something similar, make sure to run the same Hugo version on your local and server computers. That way, the site built for local testing will be the same as the one generated on the server.