Migrating From Jekyll to Hugo, deploying with Github Pages
Moving away form Jekyll. Import is easy, but tackling important details (conserve links to article, conserve comments, migrate some jekyll liquid template, theme tweaking deployment) has been challenging sometime.
Motivation
When started this blog in 2010 I went like a lot other to Wordpress as this was the one of the most common and easy way to write and publish something, then I got pissed at Wordpress as it was a pain to maintain and to write code, not to mention the speed, it was bad, then Github announced Github Pages, this was a revelation : articles written in pure Markdown, no database or server to maintain, fast writing, fast page thanks to static files. There were quite a few things that were icky like theme modifications, and some Wordpress plugins that were simply not there, but once done it was a robust machine.
Now would like to write using Asciidoc.
So what’s wrong with Jekyll with Github Pages.
-
GitHub Pages for good reasons limit what can be executed on their server, that means limited jekyll plugins, and with limited plugins comes limited theme choice.
-
Generating the files locally is hazardous, because you need to keep the same configuration as the one used by Github Pages, I was fist installing Ruby packages, but that was resolved when I switched to a docker images.
-
You basically to fork or copy the theme files in the repo, making difficult to track change from upstream while keeping your local changes.
-
I want to write in Asciidoc as it offers more way to layout information in the article.
I could have chosen to simply use a Jekyll Asciidoc plugin, generate the static
files and push them to Github in an automated way. There’s now decent
alternatives to consider, like Hugo, to be fair it’s the only thing I tried
because of it’s native asciidoctor
support. And it has other interesting
aspects:
-
Just one executable
-
Really fast to generate (with markdown content), not that jekyll was too slow for me, but the speed is noticeable.
-
Theme (from another repo) and content are well separated
-
Templating and overrides is superior in my opinion
-
Support more than markdown if external tool are available like
asciidoctor
. -
Multi lingual content supported
This long article won’t talk much about Asciidoc, but much more about the migration phase as there was a lot of rocks on the road. Let’s start by the first step: Importing Jekyll blog.
Importing from Jekyll
❯ hugo import jekyll --log ../bric3.github.io .
Importing...
Congratulations! 42 post(s) imported!
Now, start Hugo by yourself:
$ git clone https://github.com/spf13/herring-cove.git ./themes/herring-cove
$ cd .
$ hugo server --theme=herring-cove
❯ git clone https://github.com/achary/engimo.git ./themes/engimo
Cloning into './themes/herring-cove'...
remote: Enumerating objects: 40, done.
remote: Counting objects: 100% (40/40), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 4367 (delta 14), reused 26 (delta 12), pack-reused 4327
Receiving objects: 100% (4367/4367), 4.77 MiB | 6.88 MiB/s, done.
Resolving deltas: 100% (2270/2270), done.
Let’s run the server to see that basic import works
❯ hugo server --theme=engimo
| EN
-------------------+------
Pages | 294
Paginator pages | 4
Non-page files | 0
Static files | 106
Processed images | 0
Aliases | 127
Sitemaps | 1
Cleaned | 0
Built in 170 ms
Watching for changes in /Users/bric3/private/bric3-hugo/{archetypes,content,data,layouts,static,themes}
Watching for config changes in /Users/bric3/private/bric3-hugo/config.yaml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop
But nothing’s there.
What I missed is that I actually needed to copy the config.yaml|toml
configuration
example file from the theme’s exampleSite
folder. Eventually depending on the theme,
this file format may be yaml
or tool
format, my theme one was a toml
file, so
I had to remove the yaml
configuration file that was generated by the Hugo import;
the generated content was pretty thin so that was easy to port over the config.toml
.
cp themes/hyde-hyde/exampleSite/config.toml .
rm config.yaml
Also, I noticed it’s easier to switch theme by not using the command line flag --theme=<theme>
and instead use the configuration file config.yaml|toml
instead by using the
theme
field. And simply run hugo server
.
theme = "hyde-hyde"
By the way here’s what my directory structure looks like, before I perform some changes to fix some issues
/Users/bric3/private/bric3-hugo
├──archetypes
├──config.toml
├──content
│ ├──post
│ │ ├──2010-01-24-hello-world.md
│ │ ├──2010-02-09-a-propos-de-joda-time.md
│ │ ├──2010-02-12-une-fuite-memoire-beaucoup-de-reflection-et-pas-de-outofmemoryerror.md
│ │ ├──2010-02-16-les-collections-par-google-comment-sy-retrouver.md
│ │ └──37 unlisted
├──data
├──layouts
├──resources
│ └──_gen
│ ├──assets …
│ └──images
├──static
│ ├──assets
│ │ ├──0x304D-ki-hiragana.png
│ │ ├──0x6C23-chi.png
│ │ ├──add_new_jsdk.png
│ │ ├──application-gc.png
│ │ └──20 unlisted
│ ├──CNAME
│ ├──css
│ │ ├──highlightjs.piperita.scss
│ │ ├──jquery.mmenu.all.css
│ │ ├──simplebar.css
│ │ └──style.scss
│ ├──favicons
│ │ ├──android-chrome-144x144.png
│ │ ├──android-chrome-192x192.png
│ │ ├──android-chrome-36x36.png
│ │ └──26 unlisted
│ ├──js
│ │ ├──jekyll-search.js
│ │ └──jquery.mmenu.min.all.js
│ ├──search.json
│ └──serve.sh
└──themes
├──hyde-hyde
│ ├──archetypes …
│ ├──assets …
│ ├──CHANGELOG.md
│ ├──exampleSite …
│ └──7 unlisted
└──slick
├──_assets …
├──_sites …
├──archetypes …
├──build-site.js
└──12 unlisted
A long way to fix the small issues
Right now finding the theme that works for your blog is one of the most time-consuming tasks, as you need to identify which feature you need and how to migrate, I’ve been trying several themes to see how they work, one aspect that I didn’t quite like is that you need to adapt for each theme the configuration file.
Especially the section [params]
which is used by the theme templates,
and each theme differ well enough for that to be a cumbersome process.
At this point it’s really a good thing to read the
Hugo configuration documentation.
Fixing the post permalinks
My current blog expose posts with the following path
https://blog.arkey.fr/2020/04/02/manage_dotfiles_with_chezmoi/
The default configuration exposed these links as
http://localhost:1313/posts/2020-04-01-manage_dotfiles_with_chezmoi/
After some search I found these can tweaked with the
permalink setting, I don’t
know exactly why but this was only taken in account after the [params]
section.
[permalinks]
posts = "/:year/:month/:day/:title/"
Which made the post accessible to this url.
http://localhost:1313/2020/04/01/managing-dotfiles-and-secret-with-chezmoi/
At some point I might be considering the use of Hugo aliases and change the urls of the post to something like
http://localhost:1313/posts/2020/04/01/managing-dotfiles-and-secret-with-chezmoi/
…
Actually I got this wrong, Jekyll’s permalink configuration /:year/:month/:day/:title
has a small unusual thing, when reading the doc :
title
Title from the document’s filename. May be overridden via the document’s
slug
front matter.
So when setting /:year/:month/:day/:title/
in Hugo config.toml
the permalink
are not exactly what I hoped them to be, this actually the title from the
front matter of the article. I had to resort to the technic used in this blog
Convert Jekyll to Hugo Permalinks
to keep the same permalinks.
cd content/posts
for f in *.md;
do
base=`basename "$f" '.md' | cut -f 4- -d '-'`
gsed -i "s/title:/slug: $base\ntitle:/" "$f"
done
gsed
is gnu-sed from brew install gnu-sed
on MacOs. Because newlines
don’t work with the BSD sed
.
Adding custom pages
My Jekyll site had some other pages, those where located in the _pages
of my Jekyll site. In order to understand how the site works, I needed
to read content organization documentation.
The only thing I had to do is copy these files over the content
directory of the Hugo site.
/Users/bric3/private/bric3-hugo
├──archetypes
├──config.toml
├──content
│ ├──cool-stuff.md
│ ├──post
│ │ ├──2010-01-24-hello-world.md
│ │ ├──2010-02-09-a-propos-de-joda-time.md
│ │ ├──2010-02-12-une-fuite-memoire-beaucoup-de-reflection-et-pas-de-outofmemoryerror.md
│ │ ├──2010-02-16-les-collections-par-google-comment-sy-retrouver.md
│ │ └──37 unlisted
│ └──whoami.md
It’s even possible to do a layout like that.
/Users/bric3/private/bric3-hugo
├──archetypes
├──config.toml
├──content
│ ├──cool-stuff
│ │ └──index.md
│ ├──post
I preferred the easy way in regard of these page content. Also, I wanted
my post in the posts
folder instead of post
, I just had to rename
the folder and that was it.
Finally, in order to access the content I needed to add the menu entries, like that. I didn’t need to read the menu documentation for it to work, but there’s more stuff possible when going over it.
[menu]
[[menu.main]]
identifier = "post"
name = "Posts"
title = "All posts"
url = "/posts/"
weight = 1
# ...
[[menu.main]]
identifier = "whoami"
name = "Who Am I ?"
title = "About me"
weight = 4
url = "/whoami/"
# ...
Accents (diacritical marks) in some url
This will be appreciated for languages that have diacritical marks like
French. So defining the permalink with /:year/:month/:day/:title/
lead
to use the post title for the link, however some have accent that are
then url-encoded :
http://localhost:1313/2012/07/30/script-dinstallation-du-jdk-5-sur-macosx-lion-et-mountain-lion-mis-%C3%A0-jour/
I didn’t find it in the Hugo doc, but in the Hugo issue tracker, it’s possible
to add in the config.toml
, this setting is not part of any section.
removePathAccents = true
Will then make the urls as follows :
http://localhost:1313/2012/07/30/script-dinstallation-du-jdk-5-sur-macosx-lion-et-mountain-lion-mis-a-jour/
Migrate Jekyll related stuff
Migrate Jekyll liquid template to Hugo
-
{{ site.baseurl }}
for images, simply removed as website base url starts with/assets
too.
-
{% comment %} … {% endcomment %}
⇒ comments are tricky, if it’s a shortcake, this is part of the markdown generation and using html comment may work<!-- {{< shortcode >}} -→
. But for notes taken during articles, in the end I created my own shortcodedraftNotes
.{{ if .Site.Params.DisplayDraftNotes }} {{ .Inner | markdownify }} {{ end }}
-
{% if … %} {% endif %}
was used to comment stuff, it’s replaced bydraftNotes
shortcode. -
{% gist gist_id %}
⇒ -
{% raw %} {% endraw %}
there’s nothing to do here for me, this directive disables Jekyll processing for text having{{ … }}
which Jekyll interpreted as Jekyll template. -
{:.alternate}
⇒ used by kramdown to apply a CSS style, it can be removed
I had to read the shortcodes documentation and look at how to create my own shortcode.
Amazon links
They are just as other Jekyll liquid template :
{{ amazon_product_image_link | replace:'$asin$','0132350882' | replace:'$size$',img_size }}
These can be easily changed to a shortcode. Hugo doc for example showcase
the figure
shortcode:
{{< figure src="/media/spf13.jpg" title="Steve Francia" >}}
I’ve crafted my own simple shortcode for Amazon {{< amzn "B07XW76VHZ" >}}
:
<a href="https://www.amazon.com/exec/obidos/ASIN/{{ $itemId }}/" class="amazon-shortcode" target="\_blank">
<figure>
{{- if eq (len .Params) 1 -}}
<img src="https://images-na.ssl-images-amazon.com/images/P/{{ $itemId }}.jpg"/>
{{- else if eq (len .Params) 2 -}}
{{- $imageId := .Get 1 -}}
<img src="https://images-na.ssl-images-amazon.com/images/I/{{ $imageId }}.jpg"/>
{{- end -}}
</figure>
</a>
By the way I have found this blog post interesting to read to craft my own shortcodes.
Migrate inline HTML in the markdown content
The following HTML elements will be omitted in the rendered page
<div class="table-wrapper" markdown="block">
| Markdown table |
</div>
<!-- raw HTML omitted -->
<table>...</table>
<!-- raw HTML omitted -->
Hugo 0.69
uses Goldmark to render markdown, and it has a setting allow inline HTML
[markup]
defaultMarkdownHandler = "goldmark"
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true
But for safety, and self documentation, I’d prefer to migrate those to
shortcodes as well, like wrapTable
for this one.
<div class="table-wrapper">
{{ .Inner | markdownify }}
</div>
I encountered an issue however when the table itself has shortcodes. This
break table rendering. The only way to support that is to use/create
shortcodes for opening and for closing the div
in this cases.
Cleanup
The Hugo import command copied over a few Jekyll stuffs that are not anymore useful. So git gives me these files for example that could be removed. I included the favicon as well, as I wanted some refresh.
renamed: css/highlightjs.piperita.scss -> static/css/highlightjs.piperita.scss
renamed: css/jquery.mmenu.all.css -> static/css/jquery.mmenu.all.css
renamed: css/simplebar.css -> static/css/simplebar.css
renamed: css/style.scss -> static/css/style.scss
renamed: favicons/README.md -> static/favicons/README.md
renamed: favicons/android-chrome-144x144.png -> static/favicons/android-chrome-144x144.png
...
renamed: favicons/favicon.ico -> static/favicons/favicon.ico
renamed: js/jekyll-search.js -> static/js/jekyll-search.js
renamed: js/jquery.mmenu.min.all.js -> static/js/jquery.mmenu.min.all.js
renamed: search.json -> static/search.json
rm -rf static/{css,js,favicons,search.json}
However, the CNAME
file has been moved to static
folder, which is wrong,
let’s put it back at the root of the git repository.
renamed: CNAME -> static/CNAME
Remove unused Hugo themes
rm -rf themes/slick
Adapt my .gitignore
# Created by https://www.gitignore.io/api/hugo
# Edit at https://www.gitignore.io/?templates=hugo
### Hugo ###
# Generated files by hugo
/public/
/resources/_gen/
# Executable may be added to repository
hugo.exe
hugo.darwin
hugo.linux
# End of https://www.gitignore.io/api/hugo
Paginate post list page
I found this issue #18 on Hyde-hyde theme, however this issue referred to an old version of the theme which has since been updated.
One thing that is interesting and useful is how Hugo allows overriding
parts of a theme. The themes are located in the ./theme
folder, e.g.
/Users/bric3/private/bric3-hugo/themes/hyde-hyde
├──archetypes
│ └──default.md
├──assets
│ └──scss
│ ├──hyde-hyde …
│ ├──hyde-hyde.scss
│ └──4 unlisted
├──CHANGELOG.md
├──exampleSite
│ ├──config.toml
│ ├──content
│ │ ├──about.md
│ │ ├──portfolio …
│ │ └──posts …
│ ├──layouts
│ └──static
│ └──img …
├──images
│ ├──main.png
│ ├──mobile.png
│ ├──portfolio.png
│ ├──post.png
│ ├──screenshot.png
│ └──tn.png
├──layouts
│ ├──404.html
│ ├──_default
│ │ ├──baseof.html
│ │ ├──list.html
│ │ └──single.html
│ ├──about
│ │ └──single.html
│ ├──index.html
│ ├──partials
│ │ ├──footer …
│ │ ├──header …
│ │ └──9 unlisted
│ ├──portfolio
│ │ └──list.html
│ └──shortcodes
│ ├──fig.html
│ ├──kbd.html
│ └──3 unlisted
├──LICENSE.md
├──README.md
├──resources
│ └──_gen
│ └──assets …
├──static
│ ├──apple-touch-icon-144-precomposed.png
│ ├──css
│ │ ├──hugo-toc.css
│ │ ├──hugo-toc.css.map
│ │ ├──hyde-hyde.css
│ │ └──9 unlisted
│ ├──favicon.png
│ └──img
│ ├──hugo.png
│ ├──menu-close-dark.svg
│ ├──menu-close.svg
│ └──2 unlisted
└──theme.toml
In order to override parts of the theme it’s possible to copy the file in the root of the Hugo site (following the same directory structure). For post lists, I identified two files in the theme directory :
-
layouts/partials/page-list/content.html
-
layouts/partials/posts-list.html
These files need to be copied over the root of the Hugo site with the same relative path. And modify them as needed.
--- 1/themes/hyde-hyde/layouts/partials/page-list/content.html
+++ 2/layouts/partials/page-list/content.html
@@ -1,6 +1,4 @@
<span class="section__title">{{ .Title }}</span>
<ul class="posts">
- {{ with .Data.Pages }}
- {{ partial "posts-list.html" . }}
- {{ end }}
-</ul>
+ {{ partial "posts-list.html" . }}
+</ul>
--- 1/themes/hyde-hyde/layouts/partials/posts-list.html
+++ 2/layouts/partials/posts-list.html
@@ -1,6 +1,7 @@
-{{ range . }}
+{{ $paginator := .Paginate (where .Pages "Type" "in" "posts") }}
+{{ template "_internal/pagination.html" . }}
+<br/>
+{{ range $paginator.Pages }}
<li>
<a href="{{ .RelPermalink }}" {{if .Draft}}class="draft"{{end}}>{{ .Title }}</a>
{{if not .Date.IsZero}}
<time class="pull-right hidden-tablet">{{ .Date.Format (.Site.Params.dateformat | default "Jan 02 '06") }}</time>
@@ -8,3 +9,4 @@
</span>
</li>
{{ end }}
+<br/>
+{{ template "_internal/pagination.html" . }}
Here I’m using the Hugo internal template for pagination but one can
image using a custom template. The .Paginate
directive was taken
from pagination doc, however
the doc have a slight issue, the where query needs to be
where .Pages “Type” “in” “posts”
keyword.
However, I noted that other lists do not render anymore, for example
/tags
or /series
, this is because the file we modified affect all
list based page, search where the page-list/content.html
partial is
used raises the general list.html
located there
themes/hyde-hyde/layouts/_default/list.html
. Since I want the pagination
only for posts at this time, I just have to create a structure like
this in my root Hugo site.
/Users/bric3/private/bric3-hugo/layouts
├──index.html
├──partials
│ └──posts
│ └──content.html
└──posts
└──list.html
I created a posts
folder in the layouts
directory and in the layouts/partials
,
then I moved the file layouts/partials/page-list/content.html
to layouts/partials/posts/content.html
and merged the content
of layouts/partials/posts-list.html
replacing the Hugo function
{{ partial "posts-list.html" . }}
, and I removed this file as it breaks other
taxinomies. Finally, I had to create the layouts/posts/list.html
file, that
invokes {{ partial "posts/content.html" . }}
.
Tweak landing page number of posts
Here I needed to modify the index layout to only display the last X recent post.
--- 1/themes/hyde-hyde/layouts/index.html
+++ 2/layouts/index.html
@@ -4,7 +4,7 @@
{{ define "content" }}
<div class="post-list">
- {{ $paginator := .Paginate (where .Site.RegularPages "Type" "in" site.Params.mainSections) }}
+ {{ $paginator := .Paginate (first .Site.Params.landingLastPosts (where .Site.RegularPages "Type" "in" site.Params.mainSections)) }}
{{ range $paginator.Pages }}
{{ if .Draft }}
{{ .Scratch.Set "draftPage" true }}
This trick is not current as you need to wrap the query part with
the first
function (first .Site.Params.landingLastPosts <query>)
,
and I added landingLastPosts
in the params section.
[params]
landingLastPosts = 5
Comments with Disqus
EDIT: the comment system has been migrated from Disqus to Giscus.
So Hugo supports Disqus comments, but the thing is that site is generated by a theme and the theme may or may not handle comments as you wished, so it’s necessary to look at how it’s done in Hugo and in the theme depending on the requirements.
For my Jekyll site, my comments had to be migrated from a Wordpress engine, posts on Wordpress have different Disqus identifier that is now specified in the front matter, and this identifier was passed to Disqus script configuration. Here’s my Jekyll website relevant part:
<script type=“text/javascript”>
function disqus_config() {
this.experiment.enable_scroll_container = true;
this.page.url = “{{ site.cname }}{{ page.url }}”; // Replace PAGE_URL with your page’s canonical URL variable
this.page.identifier = ‘{% if page.disqus_identifier %}{{ page.disqus_identifier}}{% else %}{{ site.cname }}{{ page.url }}{% endif %}’; // Replace PAGE_IDENTIFIER with your page’s unique identifier variable
}
var disqus_shortname = "{{ site.disqus_account }}"; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function () {
var dsq = document.createElement('script');
dsq.type = 'text/javascript';
dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
This script was crafted manually to pass identifier coming from the old days when it was powered by Wordpress. Looking at the Hugo template for Disqus, I know there are other elements of the page configuration that are actually passed over to Disqus.
{{with .Params.disqus_identifier }}this.page.identifier = '{{ . }}';{{end}}
{{with .Params.disqus_title }}this.page.title = '{{ . }}';{{end}}
{{with .Params.disqus_url }}this.page.url = '{{ . | html }}';{{end}}
So that’s interesting but life isn’t that simple as theme may override as well
internal template by their own; the theme I chose uses a template that doesn’t
use the Hugo internal to initialise Disqus script, my them file is located at
layouts/partials/page-single/comment/disqus.html
. THa leaves me no choice but to
to override this partial comment template of the theme
layouts/partials/page-single/post-comment.html
, it this I merged the Hugo
internal template, and I tweaked the template of the script initialization
to behave the same as my old Jekyll site. The most important bit is here :
this.page.identifier = {{with .Params.disqus_identifier }}'{{ . }}'{{else}}{{ printf "'%s%s'" .Site.Params.disqusIdentifierBaseURL .RelPermalink | safeJS }}{{end}};
this.page.title = {{with .Params.disqus_title }}'{{ . }}'{{ else }}'{{ .Title }}'{{end}};
this.page.url = {{with .Params.disqus_url }}'{{ . | html }}'{{else}}{{ printf "'%s%s'" .Site.Params.disqusIdentifierBaseURL .RelPermalink | safeJS }}{{end}};
Also for reasons that I don’t understand, the .RelPermalink
/ .Permalink
Hugo functions escape the URL’s slashes with backslashes when the template
function is placed inside a single quotes of the JS script. The only
workaround was to use printf "'%s%s'"
then pipe to safeJS
function.
Then just in case the admin is available here
https://<you disqus short code>.disqus.com/admin/settings/general/
.
Tie together the permalink, and the Disqus content with the slug
for new posts
Now permalinks for posts are defined as /:year/:month/:day/:slug/
, the slug
is a special entry that is computed by Hugo or set in the front matter of the
content page. And the permalinks are used as Disqus identifiers. In order to have
stable permalinks and Disqus identifiers if changing the generation backend,
it’s better to write it down.
I name my posts with a date then a string that is likely the title of the post,
e.g. 2020-04-01-manage_dotfiles_with_chezmoi.md
. With the permalink structure
in mind I need my slug
as the part of the filename after the date.
So let’s use the Hugo archetypes that will allow us to create a new post.
It can be simply done by creating a file archetypes/posts.md
with a content like:
---
authors: ["brice.dutheil"]
date: "{{ .Date }}"
language: en
draft: true
tags: ["cool"]
slug: "{{ .Name | replaceRE "\\d{4}-\\d{2}-\\d{2}-(.*)" "$1" }}"
title: "{{ .Name | replaceRE "\\d{4}-\\d{2}-\\d{2}-(.*)" "$1" | title }}"
---
Example content
The slug
then becomes the part of the file without the date. Note that this archetype
file can be easily duplicated as an Asciidoc file by adding a posts.adoc
.
However, for now it’s necessary to write manually at the beginning of the filename the ISO-8601 date, meaning we have to write :
hugo new posts/2020-04-14-migrating-from-jekyll-to-hugo.md
Automate deployment on Github Pages
As I’m using Github Pages to host this site, and it only supports Jekyll based website for automatic site generation. This is nice to avoid only technical maintenance for me, but with Hugo this is a different story. I need to actually generate the website, then push it to a special branch. Let’s try to do that manually before going automatic.
Manual deployment of the static files
So that’s what I had hoped. Yet I got this message in the repository settings.
I tried to create an empty gh-pages
branch. Here’s some useful git command
by the way:
# create a new empty branch (from your current branch)
true | git mktree | xargs git commit-tree | xargs git branch gh-ages
git push --set-upstream origin gh-pages:gh-pages
But the settings page still insists that it should be done on master
, not quite
the same as mentioned in the
GitHub Pages doc.
I decided to drop this approach and removed the gh-pages
branch for now.
Finally, by trying things out, if there’s an index.html
file on master
, then
the branch files will be used to serve as static content. Following this clue,
removing all files in master but CNAME
, it is enough to publish Hugo generated
file from the ./public
folder to the master
branch.
Due to this Github Pages constraint the Hugo directory structure and site
files are in another branch like hugo-sources
that is configured as the
default branch of this repository.
Automate deployment
For that let’s use Github Actions, it’s possible to declare what needs to be done in a yaml file, and it appears the market place has everything I need to do that
-
The Hugo action that install Hugo and configures Hugo
Let’s configure the same version as the local one
❯ hugo version
Hugo Static Site Generator v0.69.0/extended darwin/amd64 BuildDate: unknown
Version 0.69.0
, and it is important to activate the extended
flag as well.
-
The GitHub Pages deployment action that will push the static file
The only thing that we need is a deployment key as documented here.
ssh-keygen -t rsa -b 4096 -C "$(git config user.email)" -f gh-pages -N ""
Going there, and add the public deployment key, check Allow write access :
cat gh-pages.pub | pbcopy
Then add the secret key here
named ACTIONS_DEPLOY_KEY
.
cat gh-pages | pbcopy
Don’t commit these files. But it may be useful to store them securely.
At the time I performed this migration
-
GitHub was building and deploying (the publication) from jekyll files on
master
branch -
GitHub could deploy (publish) HTMLs only from
master
branch
This was a requirement for user and organization page sites (<username>/<username>.github.io
)
to set the master
branch as the publishing branch as mentioned in the
actions-gh-pages@v2 Repository type - User and Organization section.
Meaning I kept the Hugo sources in another branch i.e. hugo-sources
and configured
accordingly le the publication branch.
This requirement does seem to be necessary anymore. Currently, I’m using what I just described, but in the future I may change the branch configuration. |
Note that the deployment actions seems to remove all files, but we need the
CNAME
file fortunately it’s possible to configure thecname
option.
This gives us the following configuration in .github/workflows/<name of the workflow>.yml
:
name: GitHub Pages
on:
push:
branches:
- hugo-sources
jobs:
build-deploy:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 0
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: '0.69.0'
extended: true
- name: Build
run: Hugo —minify
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
publish_branch: master
publish_dir: ./public
cname: blog.arkey.fr
After this file has been pushed, it’s possible to inspect what this action has been doing, for how long, etc, at the repository actions page