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-jekyll
Copy 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-push
To get the root directory of my repository I use this command
❯ git rev-parse --show-toplevel
/media/Works/repository/manual/manual-jekyll
And 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 --force
Pre 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 0
When 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-hexo
And 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 --force
When 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
master
The 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
...
fi
Pre 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 0
Do 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.