Preface
Goal: Deploy SSG in Git Hooks
You should know that you can do CI/CD locally,
just pure git and bash, without any third party service.
Doing deployment from local, instead of server side,
sometimes needed if you have your own VPS,
or maybe just writing to /var/www/html for use in intranet.
There are so many potential use of git hooks.
This article is only an example.
git hooks can be utiliized for many thing else.
Official Documentation
1: Refspec: Jekyll Case
I know it sound silly. Why would I need to build Jekyll manually, while github already doing it for us? As I already said, it is only for curiosity reason, not foir daily use.
Create Master Branch
Let’s get it started.
❯ git clone git@github.com:epsi-rns/manual-jekyll.git
Cloning into 'manual-jekyll'...
warning: You appear to have cloned an empty repository.
❯ cd manual-jekyllCopy Jekyll Source Files
As usual, I already have a ready to use Jeykll example.
❯ touch .nojekyll
❯ git add .
❯ git commit -m "First Commit" --quiet
[master (root-commit) e6ac8ca] First Commit
209 files changed, 19053 insertions(+)
❯ git push -u origin master --quiet
Branch 'master' set up to track remote branch 'master' from 'origin'.Also note that, recent github recently using main instead of master.
Preparing Script
git hooks just a script. You can use bash, perl, python, ruby or else.
There are many event that can utilized as a hooked.
In this case, we use pre push hook.
First thing to do is to make it executable.
❯ chmod +x .git/hooks/pre-pushTo get the root directory of my repository I use this command
❯ git rev-parse --show-toplevel
/media/Works/repository/manual/manual-jekyllAnd we can build Jekyll directly to any directory,
instead of the default _site.
❯ jekyll build -d "$builddir"And last, we need a new directory,
that would be pushed refs/heads/gh-pages branch.
git commit -m "Update at $(date)" --quiet
git push origin master:refs/heads/gh-pages --forcePre Push Hook Script
I copy paste my steps from my refspec article. After trial and error, I got this script:
#!/bin/sh
remote="$1"
url="$2"
sourcedir=$(git rev-parse --show-toplevel)
builddir="${sourcedir}/../build-jekyll"
mkdir $builddir
jekyll build -d "$builddir"
cd "$builddir"
git init
git remote add origin git@github.com:epsi-rns/manual-jekyll.git
git add .
git commit -m "Update at $(date)" --quiet
git push origin master:refs/heads/gh-pages --force
cd "$sourcedir"
rm -rf "$builddir"
exit 0When you push master branch.
The screen is showing push to gh-pages instead.

It is very simple really.
Preview
Please enjoy the render at
2: Refspec: Hexo Case
What if, we should face an SSG, that do not have feature to generate to specific directory, such as Hexo?
Use the
bash, luke.
Preparing Repository
As usual we need to preapre repository.
From creating master branch
❯ git clone git@github.com:epsi-rns/manual-hexo.git
Cloning into 'manual-hexo'...
warning: You appear to have cloned an empty repository.
❯ cd manual-hexoAnd then copy Hexo’s source files
❯ git add .
❯ git commit -m "First Commit" --quiet
[master (root-commit) e6ac8ca] First Commit
209 files changed, 19053 insertions(+)
❯ git push -u origin master --quiet
Branch 'master' set up to track remote branch 'master' from 'origin'.
Also note that, recent github recently using main instead of master.
Preparing Script
All we need is to move the generated file,
to a new directory that would be pushed refs/heads/gh-pages branch.
hexo generate --silent
cd "$builddir"
rm -rf *
mv ${sourcedir}/public/* .Pre Push Hook Script
I copy paste my steps from my Jekyll configuration above, with very few adjustment.
#!/bin/sh
remote="$1"
url="$2"
sourcedir=$(git rev-parse --show-toplevel)
builddir="${sourcedir}/../build-hexo"
mkdir $builddir
hexo generate --silent
cd "$builddir"
rm -rf *
mv ${sourcedir}/public/* .
git init
git remote add origin git@github.com:epsi-rns/manual-hexo.git
git add .
git commit -m "Update at $(date)" --quiet
git push origin master:refs/heads/gh-pages --force
cd "$sourcedir"
rm -rf "$builddir"
exit 0
When you push master branch.
The screen is showing push to gh-pages instead.
Preview
Please enjoy the render at
3: Worktree: Hugo Case
Can we do it using worktree instead of refpec?
Recursive Issue
Be aware of recursive hooks calls.
My previous worktree example contain something like this:
git push --set-upstream origin gh-pages --forceWhen you push to gh-pages,
this will trigger another pre-push event hook.
The solution is to check,
whether the branch is gh-pages or not,
buy using this git command:
❯ git rev-parse --abbrev-ref HEAD
masterThe conditional skeleton is as below.
current=$(git rev-parse --abbrev-ref HEAD)
if [ "$current" != "gh-pages" ]; then
...
git push --set-upstream origin gh-pages --force
...
fiPre Push Hook Script
After trial and error, I finally got it working. The complete script is a little bit long, as shown below:
#!/bin/sh
remote="$1"
url="$2"
current=$(git rev-parse --abbrev-ref HEAD)
if [ "$current" != "gh-pages" ]; then
# generate
hugo='/media/Works/bin/hugo-62'
$hugo --quiet
# gh-pages branch
sourcedir=$(git rev-parse --show-toplevel)
builddir="${sourcedir}/../build-hugo"
echo "$builddir"
git worktree add $builddir gh-pages
cd "$builddir"
pwd
find . -maxdepth 1 ! -name '.git' -exec rm -rf {} \;
mv ${sourcedir}/public/* .
git add .
git commit --allow-empty -m "$(git log master -1 --pretty=%B)"
git push --set-upstream origin gh-pages --force
cd "$sourcedir"
git worktree remove $builddir --force
fi
exit 0Do not forget to clean up with git worktree remove.

Preview
Please enjoy the render at
Conclusion
You can use either refspec method or worktree method.
Talking about build locally. We are going to have fun with different situation using Jenkins.
Have fun with configuring.