What’s a Static Site Generator
Hugo is an awesome static site generator (SSG). What’s a static site generator? What’s even a static site? Well a static site is basically a site without a database behind it. When a web request comes in, instead of the web server making a database query to populate some HTML templates and build HTML files to return, the web server just serves some pre-built HTML files that are already sitting on the web server.
This turns out to have a lot of benefits. But I’ll pretty much avoid going into all that and just skip to the steps to create a website on Hugo and then host it (for free) on Gitlab-pages.
Getting started.
The Hugo documentation is particularly good. Almost everything I lay out below is taken from the quickstart section or the host on Gitlab section.
If you don’t have Hugo installed, you can install it with
$ snap install hugo --channel=extended
Create a new (empty) site and git repo:
~/$ hugo new site modelIdiot
~/$ cd modelIdiot
~/modelIdiot$ git init
We now have the (empty) shell of a site built with Hugo:
~/modelIdiot$ tree -I themes
.
├── archetypes
│ └── default.md
├── config.toml
├── content
├── data
├── layouts
├── static
├── themes
Installing a Theme
Next you’ll want to install a theme. You can browse & choose a theme from the themes gallery. Originally I chose Hyde, but now I’m changing it to hyde-hyde, so that’s what we’ll use here. All the theme sites will give you a git clone
and/or git submodule add
command to download/install the theme to your Hugo site.
If you go the git clone
route, you should probably hose any .git*
files in the downloaded theme subdirectory to prevent any weirdness with the parent/project git directory.
If you want to monitor the theme’s repo for potential updates to pull into your theme in the future, you should go the git submodule route. But then if you want to make any changes of your own to any of the files in the theme/subdir, you have to deal with the wackness of git submodules.
There’s probably a use case for git submodules where the benefits are worth the moderate annoyance of dealing with them, but (at least for me) this is not it; I’d rather just basically vendor my theme directory. .git gets the hose again.
~/modelIdiot$ git clone https://github.com/htr3n/hyde-hyde.git themes/hyde-hyde
~/modelIdiot$ ls -a themes/hyde-hyde/
. archetypes CHANGELOG.md .git images LICENSE.md resources theme.toml
.. assets exampleSite .gitmodules layouts README.md static
~/modelIdiot$ rm -rf themes/hyde-hyde/.git*
Starting from the Theme’s Example Site
Now the official Hugo & theme docs suggest updating the base directory’s config.toml
to include your themename (in my case adding theme = "hyde-hyde"
) and then adding content pages. I diverge slightly; I prefer to first copy over all the files from a theme’s exampleSite/
subdir– including content/page structure and config
file. Then I swap in my own content into that structure. After all, I chose this theme because I liked the example site it linked to.
~/modelIdiot$ mv themes/hyde-hyde/exampleSite/* .
So we’ve now significantly expanded our site’s config.toml
file from what came with hugo new site [sitename]
out of the box:
baseURL = "http://example.org/"
languageCode = "en-us"
title = "My New Hugo Site"
to:
## Basic Configuration
baseurl = "https://example.com/"
languageCode = "en"
title = "Title"
theme = "hyde-hyde"
## Hugo Built-in Features
# disqusShortname = "your-disqus-shortname"
# googleAnalytics = "your-google-analytics-id"
# enableRobotsTXT = true
# summarylength = 30
#paginate = 5
## Site Settings
[params]
author = "Author"
title = "Title"
# description = "..."
authorimage = "/img/hugo.png"
dateformat = "Jan 2, 2006"
# sidebar, copyright & license
copyright = "htr3n"
license = "CC BY-SA 4.0"
licenseURL = "https://creativecommons.org/licenses/by-sa/4.0"
showBuiltWith = true
# https://highlightjs.org
highlightjs = true
highlightjsstyle = "github"
# please choose either GraphComment or Disqus or Utterances
#GraphCommentId = "..."
#UtterancesRepo = "..." # https://utteranc.es/
#UtterancesIssueTerm = "..." # pathname, url, title, og:title
#UtterancesTheme = "..." # github-light or github-dark
# Table of contents
#toc = none, "hugo", or "tocbot"
## Social Accounts
[params.social]
github = "<username>"
instagram = "<username>"
xing = "<username>"
linkedin = "<username>"
twitter = "<username>"
facebook = "<username>"
stackoverflow = "<username>"
telegram = "<username>"
email = "your-email@example.com"
# gravatar = "your-email@example.com"
## Main Menu
[[menu.main]]
name = "Posts"
weight = 100
identifier = "posts"
url = "/posts/"
[[menu.main]]
name = "Portfolio"
identifier = "portfolio"
weight = 200
url = "/portfolio/"
[[menu.main]]
name = "About"
identifier = "about"
weight = 300
url = "/about/"
In addition, we now have some site content (ignoring the theme subdir because it’s so huge):
~/modelIdiot$ tree -I themes
.
├── archetypes
│ └── default.md
├── config.toml
├── content
│ ├── about.md
│ ├── portfolio
│ │ ├── dera.md
│ │ ├── dera.png
│ │ ├── hyde-hyde.md
│ │ ├── hyde-hyde.png
│ │ ├── _index.md
│ │ ├── laramod.md
│ │ └── laramod.png
│ └── posts
│ ├── creating-a-new-theme.md
│ ├── goisforlovers.md
│ ├── hugoisforlovers.md
│ └── migrate-from-jekyll.md
├── data
├── layouts
└── static
└── img
└── hugo.png
However, you’ll notice there are no html files. Hugo is a templating engine; we write pages in markdown, specify configuration parameters and layouts. Then the hugo
command builds the html files which will compose our actual site, and stores them in a public/
subdirectory. Since that means everything outside of public/
is our actual source code that generates what’s in public/
, we’ll drop public/
in a .gitignore file to keep it out of our version-controlled code repo.
~/modelIdiot$ echo "public/" >> .gitignore
~/modelIdiot$ hugo
Output:
| EN
-------------------+-----
Pages | 47
Paginator pages | 0
Non-page files | 3
Static files | 20
Processed images | 0
Aliases | 1
Sitemaps | 1
Cleaned | 0
Total in 118 ms
Before swapping in our own content (and customizing our config.toml), we can first deploy this site locally to make sure it looks as expected and we haven’t messed anything up yet. It should pretty much match the hyde-hyde theme’s example site online:
~/modelIdiot$ hugo serve
Clicking on the resulting localhost link for our locally built site, and comparing to the theme’s example site we see:
Customizing our site
So they’re not exactly the same, but we’ve got a locally built site that looks pretty good to me. Time to start swapping in our own content.
First steps: Editing the site’s config
The first thing I’ll customize in a new Hugo site is generally to update the site’s config file (config.toml
). As a rule of thumb, this will control plenty of things shown on the homepage - e.g. title, logo image, social links, license, etc. These are often pretty self-explanatory.
After a couple quick updates to the title
and authorimage
fields in config.toml
, our site now looks like this:
The authorimage
field in the toml was originally set to /img/hugo.png
, which referenced an image located at static/img/hugo.png
. So before updating that path for my logo image, I dropped my own logo image in static/img
as well.
Then I:
- removed the
[[menu.main]]
block forname= "Portfolio"
since I don’t have a portfolio section here. - swapped in my own usernames for stackoverflow/gitlab/github/linkedin, so those icons would link to my own accounts.
- Note: for stackoverflow, replacing “username” with my username “MaxPower” led to a dead-link. Using my user-id (1870832) correctly linked to my account, but to the ‘activity’ tab. To link to the ‘profile’ tab as I wanted, the final line I used in my
config.toml
was:stackoverflow = "1870832/max-power?tab=profile"
. I found that string by just manually navigating to my stackoverflow profile, and then taking the URL, without thehttps://stackoverflow.com/users/
at the front.
- Note: for stackoverflow, replacing “username” with my username “MaxPower” led to a dead-link. Using my user-id (1870832) correctly linked to my account, but to the ‘activity’ tab. To link to the ‘profile’ tab as I wanted, the final line I used in my
Swapping in our own content
We can take a look at the content section of the example site we’re starting with:
~/modelIdiot$ tree content
content
├── about.md
├── portfolio
│ ├── dera.md
│ ├── dera.png
│ ├── hyde-hyde.md
│ ├── hyde-hyde.png
│ ├── _index.md
│ ├── laramod.md
│ └── laramod.png
└── posts
├── creating-a-new-theme.md
├── goisforlovers.md
├── hugoisforlovers.md
├── Launching a Hugo Site for Free on Gitlab Pages.md
└── migrate-from-jekyll.md
Now I’ll just delete (rm -rf
) the portfolio
subdirectory, update the about.md
with my own content, and swap out the posts in content/posts
with my own markdown blog posts.
Two quick notes:
- While Hugo’s input is markdown files, generally for a code-heavy post, I’ll draft the blog post as a jupyter notebook and then
nbconvert
it to a markdown file. - A post in Hugo needs some metadata up top, which Hugo calls “front matter.” So after
nbconvert
ing a jupyter notebook (or drafting a fresh markdown file), we have to add the front matter metadata before building the site with Hugo.
We can see an example of this front matter in one of the posts that came with the example site:
~/modelIdiot$ head content/posts/creating-a-new-theme.md
---
author: "Michael Henderson"
date: 2014-09-28
linktitle: Creating a New Theme
title: Creating a New Theme
categories: [ "Development", "hugo" ]
tags: ["hugo", "theme", "html", "css"]
weight: 10
---
These metadata fields are accessible as page-specific variables by the Hugo templating code (e.g. the layout files that come with any theme) to organize how pages on the site are organized.
There are many different types of metadata you can include in your Hugo front-matter, for more info see the applicable documentation page
After replacing the example site content files with my own, here’s what my content directory looks like now
~/modelIdiot$ tree content
content
├── about.md
└── posts
├── blogging_from_jup_notebooks.md
├── Exploring the MNIST Digits Dataset.md
├── Launching a Hugo Site for Free on Gitlab Pages.md
├── Multiprocessing with Pandas.md
├── Pandas Dont Apply _ Vectorize.md
└── Pandas_View_vs_Copy.md
Now all that’s left to do is…
Deploying the site to Gitlab Pages
Deploying a Hugo site via gitlab pages is incredibly easy>
- add this very short and sweet yaml file to your site’s root folder
- commit and push some changes on your master branch to your gitlab repo