CD-ing My Blog With GitLab CI/CD


As someone who makes style changes to their blog almost every day and blogs twice every 4 years (typical), I simply cannot compromise on the delivery process of my blog. I need to able to build and deploy my changes as quickly as possible for the millions who are waiting for my insight.

I used to have a deploy script to build my pages and push them to a separate Git repository for the statically generated files only and that repo was served using GitHub Pages. So every time I made changes, I needed to run the script manually, from the terminal. I know, painful!!

Here’s a quick summary of my blog stack:

  • Hugo:

    I made the switch from Jekyll to Hugo at the time just because it’s written in Go and because I usually mistype Jykell. Currently looking for one that’s written in V-lang to migrate to. On the side, I got a hugo improvement in the build speed compared to Jekyll.

  • A Webpack project for building PostCSS and TypeScript:

    An overkill, I know. I should replace it with a simple React app with hooks and use the most redundant React features (which become the standard anyway). On a side note: I know Webpack will probably break if I update it :) :) :)

  • GitLab CI/CD:

    As a devOops/SRE/platform/YAML engineering enthusiast, I love GitLab CI/CD. It’s the poor man’s Jenkins. Jenkins feels like it came out in 2006 by Oracle and it requires Adobe Flash to use it. Nah, I’m kidding. Jenkins is the GOAT 🐐. Let’s get back to GitLab CI/CD, the main topic of this post: Great documentation. Clear dashboard. YAML (good: familiar, bad: YAML).

Now, to the juice:

  • 0: Have a blog

  • Make a repository on GitLab for your blog

  • Add a .gitlab-ci.yml:

    image: node:14.15.4
    pages:
      stage: deploy
      script:
      - ./build.sh
      artifacts:
        paths:
        - public/
      only:
      - master
    

    This looks straightforward. It calls a script build.sh and the build files will be cached under public/. This folder will be served to your GitLab Pages pages.

    I used image: node here to have npm available for my frontend/theme (Emphasis on the overkill part).

  • Add the build script:

    #!/bin/sh
    
    # If a command fails then the deploy stops
    set -e
    
    printf "\033[0;32mBuilding the website...\033[0m\n"
    
    yarn --cwd themes/dark-matter/
    yarn --cwd themes/dark-matter/ build
    ./hugo
    

    Also, straightforward. Install the theme dependencies and build the static JS and CSS files. Ah, I use yarn here because I like to show off.

    It’s worthy to note that I setup Webpack to output separate JS and CSS files based on what they’re needed for. For example:

    module.exports = {
      entry:  {
        index: path.resolve(__dirname, 'src', 'ts', 'index.ts'),
        comments: path.resolve(__dirname, 'src', 'ts', 'comments.ts')
      },
      output: {
        path: path.resolve(__dirname, 'static', 'assets'),
        filename: '[name].js'
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: "[name].css"
        })
      ],
    }
    

    index.js is loaded in all pages in hugo. But comments.js is only loaded in the comments partial (currently not working, thanks to GitHub for breaking their API). I don’t have a lot of JavaScript. I mainly use it to load some WebFonts and for the light-dark-theme toggle button above (although it changes with CSS and the system-wide theme anyway but I kept the manual toggle too).

    Next, hugo builds the pages. I downloaded and copied the hugo binary into my repository. It’s lazy and works well.

  • git commit -am "Add questionable changes" && git push origin master

  • If you’re lucky, your pages will be deployed

Now when I git push this post, it will be deployed automatically. And this is how you got here.

Have fun [not] blogging!