Hugo with CircleCI

Hugo

I’ve finally gotten around to redoing my personal site. I had high hopes of making my own web platform using indieweb standards, but have finally faced reality and decided that with all my other hobbies that was just too ambitious. So instead I’ve chosen a static site generator so that I do not have to worry about databases or many other dependencies.

The new site is running hugo with the casper-two theme.

I will cover a few details of setting up the site with a focus on deploying to a custom server. These details assume that you already have hugo installed locally.

Setup a new site

hugo new site YOURSITENAME

This creates a new hugo site found in the directory YOURSITENAME.

Initialize new site as a git repository and commit initial hugo build.

cd YOURSITENAME
git init
git add
git commit -m "Initial commit of hugo site"

From here we need to add a theme. There are plenty to choose from see hugo themes and choose a theme of your liking. I chose casper-two as indicated above and added that to my codebase via the following;

git submodule add https://github.com/eueung/hugo-casper-two.git themes/casper-two/

This sets up casper-two as a git submodule which can be updated independently of my local code.

From here I followed the instructions for casper-two to set appropriate variables and configuration settings in my config.toml file. Mine looks like the following;

baseurl         = "/"
theme           = "casper-two"
languageCode    = "en-US"
disqusShortname = ""
paginate        = 6
#SectionPagesMenu = "main"

[params]
  title       = "Jeff Graham"
  subtitle    = "Random Ramblings"

  cover       = "images/cover.jpg"
  description = "Here is a description of your site."
  metaDescription = ""
  googleAnalytics = ""
  customCSS = []
  RSSLink = ""

  twitterName = "jgraham909"
  githubName = "jgraham909"

  logo = ""
  orgName = ""
  orgWebsite = ""
  orgDescription = ""

  author = "Jeff Graham"
  authorAvatar = "images/me.jpg"
  authorLocation = ""
  authorWebsite = ""
  authorDescription = ""

  pageNotFoundTitle = "404 - Page not found"

  #casper or caspertwo
  singleViewStyle = "casper"

[permalinks]
  post = "/:slug/"

[[menu.main]]
  name = "Home"
  url = "/"
  weight = 200


For the casper-two theme static files are served from the static directory. So create that directory and add a favicon.ico and any images you wish to use.

Circle CI (securely)

After that portion I wanted to set this up so that I could push new posts to github and have those automatically deployed to my site on a custom server running nginx. After pushing the repository to github we can now step over to circleCI setup.

.circleci/config.yml

version: 2
jobs:
  build:
    docker:
      - image: felicianotech/docker-hugo:0.33
    working_directory: ~/jgraham909.com
    steps:
      - checkout
      - run:
          name: "Initialize git submodules"
          command: git submodule init
      - run:
          name: "Update submodles"
          command: git submodule update
      - run:
          name: "Run Hugo"
          command: HUGO_ENV=production hugo -v -s ~/jgraham909.com
      - run:
          name: "Test Website"
          command: htmlproofer ~/jgraham909.com/public --allow-hash-href --check-html --empty-alt-ignore --disable-external
      - run:
          name: "Add HostKeys"
          command: echo $HOSTKEY1 >> ~/.ssh/known_hosts && echo $HOSTKEY2 >> ~/.ssh/known_hosts && echo $HOSTKEY3 >> ~/.ssh/known_hosts
      - run:
          name: "Deploy"
          command: rsync -avz -e "ssh -i ~/.ssh/id_rsa" --delete ~/jgraham909.com/public/ jgraham909@jgraham909.com:/var/www/jgraham909/

But there are two things we need to configure first. We don’t want to have to provide a password to deploy and CircleCI does not have our server in the known hosts.

Add a new user and set ssh keys

After logging into your server create a new user account with an appropriately secure password.

adduser USERNAME

Once you have created the new user logout as your privileged user and login as the newly created account with the password you set. At this point we need to do two things: generate ssh keys, and set authorized ssh keys.

We do that via the following commands

ssh-keygen
cp .ssh/id_rsa.pub .ssh/authorized_keys

The first step ssh-keygen generates new ssh keys for this user do not set a passphrase for this portion so that we can authenticate without needing to supply a password. The second step adds this new public key to the authorized_keys file which will allow us to login using this ssh key.

This has a corresponding step in circle CI we need to add the newly created .ssh/id_rsa (note this is the private key) to our Circle CI page’s “ssh permissions” section.

Add our server’s ssh key to circleCI

ssh-keyscan HOSTNAME will list out your server ssh key signatures for each of these entries I added a corresponding HOSTKEYX environment variable in Circle CI where X started with 1 and was incremented for each entry from ssh-keyscan. Ignore the lines that start with #.

During deploy we add all of these keys (3 in my case) to our ~/.ssh/known_hosts file. This will avoid CircleCI from hanging during deploy when encountering a server with a host key not already in its known_hosts file.

echo $HOSTKEY1 >> ~/.ssh/known_hosts && echo $HOSTKEY2 >> ~/.ssh/known_hosts && echo $HOSTKEY3 >> ~/.ssh/known_hosts

Deploy

Our final step in the CircleCI process is the actual deploy stage.

rsync -avz -e "ssh -i ~/.ssh/id_rsa" --delete ~/jgraham909.com/public/ USERNAME@SERVERNAME:/var/www/jgraham909/

In this process we deploy using rsync via ssh. In our case hugo built the site files in ~jgraham/public and that is what we deploy to our server to the directory /var/www/jgraham909.

nginx config

Earlier I mentioned that I have nging running on this server. In addition to creating a directory at /var/www/jgraham909 and chowning that to the user we previously created we need to set nginx to serve those files. In my case the server is running ubuntu so we edit /etc/nginx/sites-enabled/default and add an entry to map the appropriate incoming domain to the appropriate directory in our case that looks like the following;

server {
  listen 80;
  server_name jgraham909.com;
  root /var/www/jgraham909;
  index index.html;

I’m sure I’ve missed some details if you have questions hit me up on twitter.