Now that I’ve moved my course websites to Hugo via ox-hugo, life is definitely easier than when I used WordPress, and my teaching is unquestionably crawling a little bit closer to actual reproducibility. It’s been non-trivial to figure out a good deployment process, though. My course materials live in a slightly broader context than my websites: I have my readings, my private notes, my grades, and a few other documents like teaching contracts, etc., in the main course directory. Some but not all of these materials can be committed to Git, so it makes intuitive sense to me to have a course repository which exists independently of the website. If I didn’t use ox-hugo to manage the org-to-hugo process – if, say, I used the somewhat more limited googrgeous support which is built into Hugo – I might try to build the website directly from the course materials. Instead, I’ve come up with a somewhat more convoluted process. It’s by no means perfect, and there are probably better solutions , but for now this process is working pretty well for me. Documenting here in case it’s useful to anyone else, and also in case I forget what I’ve done!
For this post I’ll take my Digital History course as an example, but I’m hoping to move all my courses to the same system, semester by semester.
Each course has two repositories:
Assignments.org
, an entry for each individual assignment. These are markdown files created by org-hugo-export-subtree
.
The website repo has two relevant branches: a master
branch that contains the uncompiled source files, and a gh-pages
branch that holds the Hugo-generated files that are actually served on the server. The gh-pages branch is checked out as a git worktree as described in the Hugo docs. It took me a little while to figure this out, but once you understand the concept of git worktrees it’s kind of fun to do things this way.
For Digital History, I’m using the excellent docdock theme, and as they suggest, I’ve installed the theme as a git submodule.
I do all my editing inside Emacs, in the original org-mode files that live in the “main” repo. Usually changes belong to one of two categories:
In either case, the workflow should look like this:
hugo -s
in the website master dir; commit changes to gh-pages
branch; push gh-pages
to upstream.
There are a lot of steps there! Fortunately we can automate or semi-automate them.
I’ve left the first two as manual stages, because it’s smart to check changes before committing. However, I now make it a little easier by following Kaushal’s advice to run hugo server with the -p
and --navigateToChanged
switches. I now keep a pinned tab up in Firefox at localhost:xxxx
(choose your own port with -p
), and as soon as I export a page, that tab will update to the appropriate page. Fantastic! Makes checking my work much quicker. As for the rest, those steps are all done via 2 git post-commit
hooks, one for each repo:
public
directory (where we have the gh-pages site checked out as a git subtree), cd’s there, and commits to the gh-pages brnach. Then both branches are pushed to gh-pages
hugo server -s -d myserver
; I also have to update config.toml
before doing this to fix a couple of paths. This is all pretty easy to do.
Here’s the first, somewhat simpler script from the main repo:
push_branch=master cur_branch=$(git rev-parse --abbrev-ref HEAD) cur_hash=$(git log -1 --pretty=%h) cur_msg=$(git log -1 --pretty=%B) web_dir=dh-website cur_dir=$PWD# only run if commit is to master
if [ "$cur_branch" == "$push_branch" ]; then # if [ “$(git status -uno –porcelain)” ]; then # echo “uncommitted changes, can’t push”; # else # cd to website cd $web_dir # commit hcanges echo “committing changes to website master branch\n\n” git commit -a -m “push changes to website from titaniumbones/Digital-History#$cur_hash
$cur_msg” cd $cur_dir echo “pushing master to origin” git push -f origin master fi
And the slightly more baroque version from the website repo, which pushes to both gh-pages and to hackinghistory (shimano
is just an alias for hackinghistory.ca).
Note that I had to set up the hackinghistory repo as a non-bare repo with receive.denyCurrentBranch
set to updateInstead
. Thank you, stackoverflow!.
push_branch=master cur_branch=$(git rev-parse --abbrev-ref HEAD) cur_hash=$(git log -1 --pretty=%h) cur_msg=$(git log -1 --pretty=%B)# only run if commit is to master
if [ "$cur_branch" == "$push_branch" ]; then if [ "$(git status -uno –porcelain)" ]; then echo “uncommitted changes, can’t push”; else echo “pushing master to origin” git push -f origin master echo “Rebuilding Site” hugo -s ./ cd public && echo “changing env variables” && GIT_WORK_TREE="../.git/worktrees/public" && GIT_INDEX_FILE="../.git/worktrees/public/index"&& echo “adding all files\n\n” && git add –all echo “commitchanges to gh-pages\n\n” git commit -a -m “push changes to gh-pages from $cur_hash
$cur_msg” echo “pushing gh-pages to origin” git push -f origin gh-pages && echo “successfully pushed gh-pages” cd ..
sed -i.gh ’s/baseURL = "https://digitalhistory.github.io/dh-website/"/baseURL = "https://digital.hackinghistory.ca/"/' config.toml hugo -s ~/DH/dh-website/ -d ~/DH/dh-website/shimano/
# now shimano dir, shimano-deploy branch, shimano remote cd shimano && echo “changing env variables” && GIT_WORK_TREE="../.git/worktrees/shimano" && GIT_INDEX_FILE="../.git/worktrees/shimano/index"&& echo “adding all files\n\n” && git add –all echo “commit changes to shimano\n\n” git commit -a -m “push changes to shimano from $cur_hash
$cur_msg” echo “pushing shimano-deploy to shimano” git push -f shimano shimano-deploy && echo “successfully pushed shimano” cd ..
# rsync -azvbP ~/DH/dh-website/shimano/ shimano:/var/www/digital.hackinghistory.ca/ mv config.toml.gh config.toml fi
fi