Automate hugo

by Christoph Stoettner
Read in about 4 min · (words)

Fountain pen and a notebook

Photo by Aaron Burden | Unsplash

Mid 2018 I switched my blog from Wordpress to Hugo . Main reason was performance and that I can use Asciidoctor to write the posts.

What happened the last 18 months? I stayed with the theme I selected 2018, but I tweaked it a little bit. So I added lunr to implement searching, changed all scripts and fonts from CDN to local (privacy and tracking), updated Bootstrap 3 to 4.

Working with Bootstrap was quite fun, I haven’t done a lot of HTML or CSS the last years, but the grid and css classes from Bootstrap are working without checking each change on all browsers and are responsive.

Daily journal

After reading The Unicorn Project I started writing a daily journal with things I learned or figured out during the day. I tried this several times the last years, but never was happy with the result. This time I just use the same setup as my blog (Hugo, Theme and Asciidoctor), but deploy it to a local Docker container, which is started with podman.

Gitlab CI/CD

Hugo generates static html pages from Asciidoctor or Markdown sources. The first months I just ran the hugo binary on my local machine and did a rsync of the generated html to my blog and the container.

Updating Hugo, or reinstall my Notebook sometimes delayed a blog post. So I decided to automated the process through Gitlab CI/CD.

.gitmodules

I added the theme (which is stored as a seperate git repository) as a submodule (git add submodule …​) to the blog content. .gitmodules can handle complete repository URL, or relative ones. The relative URL are easier to checkout in the pipeline, so I changed the file to

.gitmodules

[submodule "themes/stoeps-theme"]
    path: themes/stoeps-theme
    url: ../stoeps-theme.git 
  • Edit Url and replace with the relative path (I have the theme in the same git group)

Creating .gitlab-ci.yml

variables:
  GIT_SUBMODULE_STRATEGY: recursive   

stages:                               
  - build
  - deploy

build:
  stage: build
  image: "node:alpine"
  before_script:
    - 'which curl || ( apk update && apk add curl)'  
    - 'which gem || (apk add ruby)'
    - curl -L https://github.com/gohugoio/hugo/releases/download/v0.61.0/hugo_0.61.0_Linux-64bit.tar.gz | tar -xz && mv hugo /usr/local/bin/hugo
    - npm install
    - gem install asciidoctor
  script:
    - npx gulp build                  
  only:                               
    refs:
      - master
    variables:
      - $BUILDHTML
    changes:
      - content/**/*
  artifacts:
    paths:
      - docs                          
    expire_in: 2 hours

deploy:
  image: "debian:buster"
  stage: deploy
  before_script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - 'which rsync || ( apt-get update -y && apt-get install rsync -y )'
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | base64 --decode | tr -d '\r' | ssh-add - > /dev/null  
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - ssh-keyscan -t rsa home26617100.1and1-data.host >> ~/.ssh/known_hosts
  script:
    - rsync -az docs/ p7594620@home26617100.1and1-data.host:/kunden/homepages/13/d26617100/htdocs/stoeps.de --delete
  only:
    refs:
      - master
    variables:
      - $DEPLOYBLOG
    changes:
      - content/**/*
  • Clone submodules with the main repository

  • Two stages, one builds html, one deploys the code

  • Check if curl is included in the container, if not install it

  • Run build with Gulp (see below)

  • Stage runs only for master-Branch, if variable BUILDHTML is set and changed files are in the content-tree

  • Keep directory docs as artifacts for the next stage

  • I created a seperate SSH key just for the synchronisation, it is stored as a variable in the Gitlab project

Store SSH-Key as variable

Create Key

ssh-keygen

base64 encode the key and copy to clipboard

base64 -i ~/.ssh/id_gitlab |tr -d '\n' | xclip

Store as variable

Gitlab Crontab setting

Syntax is equal to Linux cron, so the setting of this image will run the pipeline at 23:55 UTC every day. Other option could be hourly at 5 minutes past the full hour: 5 * * * *

I added a scheduler which runs hourly and sets the variables BUILDHTML and DEPLYBLOG, so I can check-in code to gitlab multiple times and the posts will only be build on a scheduled basis.

Just set the variables to true.

Gitlab Scheduler variables

Gulp

The article Continuously Deploy a Hugo Site with GitLab CI added Gulp to minify the built HTML. Cool idea and saves me some megabytes. I added css-minify and build the hugo page with:

npx gulp build

gulpfile.js

var gulp: require("gulp");
var htmlmin: require("gulp-htmlmin");
var cssmin: require("gulp-cssmin");
var shell: require("gulp-shell");

gulp.task("hugo-build", shell.task(["hugo"]));

gulp.task("minify-html", () => {
    return gulp.src(["docs/**/*.html"])
    .pipe(htmlmin({
        collapseWhitespace: true,
        minifyCSS: true,
        minifyJS: true,
        removeComments: true,
        useShortDoctype: true,
    }))
    .pipe(gulp.dest("./docs"));
});

gulp.task('minify-css', () => {
    return gulp.src(["docs/**/*.css"])
    .pipe(cssmin())
    .pipe(gulp.dest("./docs"));
});

gulp.task("build", gulp.series("hugo-build", "minify-html", "minify-css"));
Author
Add a comment
Error
There was an error sending your comment, please try again.
Thank you!
Your comment has been submitted and will be published once it has been approved.

Your email address will not be published. Required fields are marked with *

Suggested Reading
Aaron Burden: Fountain pen and a notebook

I switched my blog to Hugo the last days. After nearly 12 years with WordPress , I needed something new. Why did I drop WordPress, one of the most used blog engines in the world?

Most used means always most interesting for bad guys. Dynamic pages are slower and can contain more vulnerabilities than static pages (which Hugo generates). Hugo supports git, so I have version control in my posts and design. I can start a small web server locally and test the posts: hugo server -D and the most convenient thing: I can use VIM for editing.

Read in about 3 min
Aaron Burden: Fountain pen and a notebook

Here for example embedding a video to a blog post. Prerequist is that you’ve no fear to change some html source.

Read in about 1 min
Aaron Burden: Fountain pen and a notebook
Last week a new blog editor for offline blogging has announced IBM Connections blogs support. Blogsy is a nice tool and costs about 4€ in apple app store. My first tests are not very good, because Blogsy 3.3 has problems with multiple Blogs in IBM Connections. Only one day later, after some comments in Blogsy Blog, a new version can be downloaded from Appstore. Blogsy 3.3.1 can handle multiple Connections blogs, but the App crashes right after uploading a new post. I hope they will make a updated version soon. I enjoy offline blog editors very much, because i can prepare articles on train and upload them later after i found a free WLAN.
Read in about 1 min