profile picture

Automating Blog Deployment with GitHub Actions

March 12, 2021 - ci devops beginner

Okay! Day 2 of the new blogging platform, and I've solved the automated deployment problem using GitHub Actions.

GitHub Actions to Test Python

I worked on a small Python project a little while ago and wanted to get CI running with as little effort as possible. I'd heard good things about GitHub Actions (my friend Michael Heap is even writing a book about them).

That project's CI issues were solved by following the instructions. I copied the following YAML into .github/worflows/main.yml and I was more-or-less done.

name: Python package

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [2.7, 3.5, 3.6, 3.7, 3.8]

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install flake8 pytest
        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
    - name: Lint with flake8
      run: |
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Test with pytest
      run: |
        pytest

Porting the Knowledge to Deployment

What I wanted to do this time was quite similar. I wanted to build the site's static pages, and then, if on the master branch copy the files to my personal server, using SCP.

I found an existing Zola deploy action that builds a Zola site, and then deploys to GitHub Pages, but that wasn't quite what I wanted. Nevertheless, it seemed like a good starting point, so I forked it, and then started hacking on it.

And then I had an epiphany! I already had steps that checked out the code and built it into a static site. All I needed was another small, specific step to SCP to my server. A quick search on GitHub's Action Marketplace showed up the perfect action, appleboy/scp-action.

So, now I have the following workflow:

name: Build and deploy

on:
  push:
    branches:
      - master
  pull_request:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout'
        uses: actions/checkout@master
      - name: 'Build only' 
        uses: shalzz/zola-deploy-action@master
        env:
          BUILD_ONLY: true
  deploy:
    if: github.ref == 'refs/heads/master'
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout'
        uses: actions/checkout@master
      - name: 'Build Site' 
        uses: shalzz/zola-deploy-action@master
        env:
          BUILD_ONLY: true
      - name: Deploy Site
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.KEY }}
          source: "public"
          target: "/var/lib/websites/judy.co.uk"
          strip_components: 1

It'll build on any push to master, or any new PR. If the commit is to master, then it'll also deploy to my server using SCP. Using a small set of focused actions (Unix philosophy, anyone?) has led me to something much more simple and maintainable than maintaining my own "build and deploy and ..." action.

One thing I've noticed is that it's building the Docker images each time, before spinning up the containers. I haven't found a way to speed things up by getting around this, but if you know, please contact me and let me know!

I have to say that it's nice when a task that shouldn't be hard turns out to not actually be hard. Now I've reduced the friction for publishing to my blog, so if you come here in 2025 and this is the last post then there's nobody else to blame.

I think it's time for a Negroni.