Contents

Creating a static website

with task, hugo and GitLab Pages

Introduction

There are many tools to create static a website, and they all look pretty cool, but I decided to try Hugo.

The main idea is to have a simple git repository, with a GitLab CI/CD which will deploy the website everytime new contents are added.
Customizing the website is not the first goal, but it may come later.

In this article, I consider that you are familiar with GitLab and task.
If you are not, well I’m sorry because for now I don’t have any articles for them.
But I’m pretty sure you will find something on the net.

Pre-requisites

Before going anywhere, start by creating a new project on GitLab and clone it locally.
Now, let’s install Hugo by downloading the binary from the release page.
In my opinion, everything the CI/CD does, we should be able to do it locally, this is where task come in the game.
We are going to use to it to setup hugo, with the help of a bash script to make sure we download it only if it’s necessary:

# Taskfile.yml
version: '3'

env:
  HUGO_VERSION: "0.87.0"
  HUGO_EXTENDED: "true"
  HUGO_FILE_NAME: hugo_{{ if ne .HUGO_EXTENDED "false" }}extended_{{ end }}{{ .HUGO_VERSION }}_Linux-64bit.tar.gz
  HUGO_URL: "https://github.com/gohugoio/hugo/releases/download/v{{ .HUGO_VERSION }}/{{ .HUGO_FILE_NAME }}"
  HUGO_INSTALLATION_FOLDER: "./.bin"
  HUGO_BINARY: "{{ .HUGO_INSTALLATION_FOLDER }}/hugo"
  TMP_FOLDER: "./.tmp"

tasks:
  get-hugo:
    desc: Downloading Hugo
    cmds:
      - ./scripts/get-hugo.sh
# scripts/get-hugo.sh
#!/usr/bin/env bash
set -o errexit

HUGO_BINARY="${HUGO_INSTALLATION_FOLDER}/hugo"

echo "Checking if Hugo is already installed in ${HUGO_INSTALLATION_FOLDER}"

if command -v ${HUGO_BINARY}
then
    installed_version=$( ${HUGO_BINARY} version | grep ${HUGO_VERSION} )
    if [[ ${installed_version} != "" ]]
    then
        if [[ ${HUGO_EXTENDED} == "true" ]] && [[ $( echo ${installed_version} | grep "extended" ) != "" ]];
        then
            echo "Hugo is already installed, exiting"
            exit 0
        fi
    fi
    echo "Currently installed version does not match, installing..."
    echo "Wanted ${HUGO_VERSION} extended: ${HUGO_EXTENDED}"
    echo "Got: ${installed_version}"
else
    echo "Hugo is not installed, continuing..."
fi

mkdir -p ${HUGO_INSTALLATION_FOLDER}
mkdir -p ${TMP_FOLDER}
curl -L --output ${TMP_FOLDER}/hugo.tar.gz ${HUGO_URL}
tar -xzf ${TMP_FOLDER}/hugo.tar.gz -C ${TMP_FOLDER}
mv ${TMP_FOLDER}/hugo ${HUGO_INSTALLATION_FOLDER}
rm -Rf ${TMP_FOLDER}

Within the Taskfile.yml, we add a task called get-hugo and a few environment variables to easily configure the task.
This way, we change the version of Hugo, or its installation path quite easily.

The get-hugo.sh script will check the current installed version, and only download Hugo if it is not present, or if the version does not match.
The set -o errexit is a bash option that make sure the script fails if any command in it failed.
Now, if we run:

task get-hugo
task: [get-hugo] ./scripts/get-hugo.sh
Checking if Hugo is already installed in ./.bin
Hugo is not installed, continuing...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   645  100   645    0     0    644      0  0:00:01  0:00:01 --:--:--   644
100 15.9M  100 15.9M    0     0  10.5M      0  0:00:01  0:00:01 --:--:-- 10.5M

We can see that task runs the get-hugo.sh script, which detects that Hugo is not installed, and thus downloads it.

We can make sure it is working by running:

./.bin/hugo version
hugo v0.87.0-B0C541E4+extended linux/amd64 BuildDate=2021-08-03T10:57:28Z VendorInfo=gohugoio

We are using the extended version of Hugo because it is recommended by the theme we are going to use.
You can checkout the installation instruction if you want another alternative to install Hugo.

Initializing the website

It is time to initialize this new website by running the following command:

./.bin/hugo new site .
Congratulations! Your new Hugo site is created in /jnmoal/Development/jnmoal/blog.

Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder.
   Choose a theme from https://themes.gohugo.io/ or
   create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
   with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".

Visit https://gohugo.io/ for quickstart guide and full documentation.

Let’s see if it is working, we are going to add a new task to the Taskfile.yml that will locally serve the content of the website:

  serve:
    deps: [get-hugo]
    desc: Start a server to access the generated content
    cmds:
      - "{{ .HUGO_BINARY }} server --buildDrafts --watch"

In the task serve, we add a dependency to the task get-hugo, this way we are sure we are using the configured version.
The --buildDrafts option tells Hugo to include draft pages in the generated content, pretty cool when creating a new post.
The --watch option tells Hugo to watch and generates the website on any changes.

Let’s try this new task:

task serve
Start building sites …
hugo v0.87.0-B0C541E4 linux/amd64 BuildDate=2021-08-03T10:57:28Z VendorInfo=gohugoio
WARN 2021/08/23 01:32:40
...
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

When openning the website at the specified address, we see a blank page, which is normal since we didn’t add any content to the website.
But before writing anything, let’s add a new theme. I’ve picked up the LoveIt theme, let’s install it:

git submodule add https://github.com/dillonzq/LoveIt themes/LoveIt

When creating a new site, Hugo writes a config.toml file, but we are going to change it to config.yml (I’m more familiar with Yaml).
We need to tell Hugo to use the theme:

# config.yaml
theme: 'LoveIt'

params:
  # LoveIt theme version
  version: '0.2.X'

And now, if we reload the page, we can see the theme! Pretty cool!

LoveIt allows to configure a lot of things through the config.yaml file, let’s configure this home page:

# config.yaml
params:
  # LoveIt theme version
  version: '0.2.X'
  header:
    title:
      name: 'Jean-Nicolas Moal'
      typeit: false
  home:
    profile:
      gravatarEmail: 'jn.moal@gmail.com'
      title: 'Welcome to my technical blog'
      subtitle: "Let's talk about Kubernetes, Golang, GitLab, AWS or anything that I've found!"
      typeit: true
      disclaimer: 'My opinion is my own, but we can talk about it ;)'
      social: true
    
  social:
    Gitlab: 
      id: 'jn-moal'
      weight: 1
    Linkedin: 
      id: 'jeannicolasmoal'
      weight: 2

If we reload the page, we can see that Hugo and LoveIt added some content in the home page.
Feel free to tweak the configuration as you wish, this is your website after all.
We won’t create a post for now, the next step is to publish the website.

Publishing with GitLab Pages

There are many solutions tu publish a static website, we are going to use GitLab Pages.
This a fast and easy way to make your website available on the net.

GitLab Pages will create a default url which looks like this:

https://<user_name>.gitlab.io/<project_name>

So first, we need to configure Hugo so that it is aware of where the website will be:

# config.yaml 

baseURL: 'https://jn-moal.gitlab.io/blog'

This configuration will have only one effect locally, you’ll need to add /blog to the URL when testing your website.

Secondly, we are going to create a .gitlab-ci.yml file with the following content:

image: docker.io/fedora:34

variables:
  GIT_SUBMODULE_STRATEGY: recursive

.install_task: &install_task
- sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin

test:
  before_script:
    - *install_task
  script:
  - task build
  except:
  - main

pages:
  before_script:
    - *install_task
  script:
  - task build
  artifacts:
    paths:
    - public
  only:
    - main

The install_task only download and install task so that it is available in the CI.
Then, GitLab CI will use task to build the website, so let’s update it to add the build task:

  build:
    deps: [get-hugo]
    cmds:
      - "{{ .HUGO_BINARY }}"

We also add the dependency to the task get-hugo, for the same reason we did on the serve task.
In order to build the website, we simply run the hugo command, that’s it.
Thanks to pages, GitLab CI will publish the website with the content of the main branch.

Now if you commit on the main branch, after 20-30 seconds, you should see the website at https://<user_name>.gitlab.io/<project_name>.

This is how I created my website that your are actully reading!
The next step is to add some content to it, which is a long path ahead.

Thank you for reading this post, I hope you found it useful.
feel free to share it!