CI/CD Pipeline for Hugo in AWS

October 2, 2018

Automatically deploy hugo updates using AWS CodeBuild

I recently set up this blog and I’m using the awesome GoHugo as a static site generator. I decided early on I wanted to host in S3 and front that with CloudFront, because its going to be really low cost to run a site like this and secondly it just scales without me spending lots of time managing infrastructure. I’ll be doing a few posts and videos on Hugo and AWS over the next few months as I build up some serverless functions round it. The first task I wanted to complete though was to build a CI/CD Pipeline for deployment, yet again I’ve started simple and will probably add to this as the blog grows.

I’m going to use AWS CodeCommit as my git repository, theres no reason why you can’t swap this in for Github or BitBucket easily enough. Then I’m going to use CodePipeline to monitor that repo and trigger a build using CodeBuild on a new commit. In fact if you are reading this post its going to be the first article to get published in this way! So lets look what this is going to look like with a little diagram.

Deployment Pipeline

I won’t cover how to set up S3 as website endpoint and then front that with CloudFront in this article as its been documented lots of times, one thing to note however I’ve opted to force all HTTP traffic to HTTPS and I issued my certificate for this using Cert Manager. It was super simple and just required me to add some DNS entries to validate.

Setting up and Linking it all together

First I created a new git repository in CodeCommit, in my case its called digilution-site. This is where I store the root config of my Hugo site, such as the config.toml file. Its a really simple setup, simply hit create new repository from the CodeCommit console and enter a name then you’re ready to push code to it.

CodeCommit Setup

Its then a simple case of running git add . followed by a git commit and a git push to send your code to the repository.

CodeCommit Repository

If you haven’t used CodeCommit before I recommend setting up a SSH key for committing. You can find details about that here: https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-ssh-unixes.html

Next we are going to build the CodeDeploy script and setup before we use CodePipeline to plump them together. Head on over to the CodeBuild console and create a new project.

Give your CodeBuild a sensible name and then lets start configuring it. When you select your source you need to point it to your CodeCommit repository, this should be populated in the drop down list. Now for the build environment I selected Ubuntu and the base image. I only needed base as the commands to do the deployment are bash commands so we don’t need anything special here. The build is going to use a file in your git repo called buildspec.yml (we’ll create this later) I haven’t enabled any cache (yet) as currently my site is pretty small, but I may revisit this later on.

I’ve also configured the build logs to go to a group and stream name in CloudWatch, this may be useful when i’m doing debug later on.

One very important thing to note is that I’ve assigned a new IAM Service role to this project. We’ll add some permissions to that shortly so note the name of the policy you create!

CodeBuild Setup

Right lets add those S3 permissions so our code build can push our site out. Head to the IAM console and find your new role, add a custom policy to the permissions. The policy you want should look like the following (make sure to edit the YOUR-BUCKET variable):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                        "s3:GetBucketLocation",
                        "s3:ListAllMyBuckets"
                      ],
            "Resource": "arn:aws:s3:::*"
        },
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::YOUR-BUCKET",
                "arn:aws:s3:::YOUR-BUCKET/*"
            ]
        }
    ]
}

So thats code build created configured, we now need to give the build some instructions. These are going to be:

In order to do this we need to create a new file in the root of your git repo. This file is called buildspec.yml so go ahead and create the file (yet again update YOUR BUCKET):

version: 0.2

env:
  variables:
    s3_output: "YOUR BUCKET"
    hugo_version: "0.49"

phases:
  install:
    commands:
      - wget "https://github.com/gohugoio/hugo/releases/download/v${hugo_version}/hugo_${hugo_version}_Linux-64bit.deb"
      - sudo dpkg -i hugo_${hugo_version}_Linux-64bit.deb
    finally:
      - hugo version
  build:
    commands:
      - hugo
      - cd public && aws s3 sync . s3://${s3_output} --delete --acl public-read
    finally:
      - echo "Script finished running"

Now before you commit this we are going to use CodePipeline as some glue to connect CodeCommit and CodeBuild together. So head back to your browser and open up the CodePipeline console and create a new pipeline. You’ll walk through a wizard to set up the pipeline.

Now lets head back to your git repo. Add the buildspec.yml and commit and push your repo. This should now trigger a build of your site which ultimately gets pushed to S3 Whoop!!! Now every time you push to the repo you’ll update your site and not have to take any manual steps again. A successful build will look like the following.

Code Pipeline Success

If you run into problems thats where your cloud watch logs come in handy!

Future enhancements

So I won’t rest here, I’m looking to add new features pretty soon. Firstly I’m going to get some logic in place to interact with CloudFront to create invalidations if needed when content is updated. Secondly I’m going to play about with CodeBuilds Cache and see if it speeds up build times. I’ll report back when I’ve done, but for now enjoy!