Skip to main content

Automate hugo

·736 words·4 mins

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"));
Christoph Stoettner
Author
Christoph Stoettner
I work at Vegard IT GmbH as a senior consultant, focusing on collaboration software, Kubernetes, security, and automation. I primarily work with HCL Connections, WebSphere Application Server, Kubernetes, Ansible, Terraform, and Linux. My daily work occasionally leads to technical talks and blog articles, which I share here more or less regularly.

Related

New Blog Engine

·465 words·3 mins
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.

Asciidoctor for Professional Looking Documentation

·1613 words·8 mins
For GPN19 I prepared a second talk on Documentation with any Editor. The talk was based on a previous one from Froscon 13, but the pipeline tooling changed. This time there was a technical issue during the recording and so there are only the slides available, but you can still watch the video of the Froscon talk: Froscon 13: Documentation with any Editor