Setting up Github Actions for Deploying Pelican

December 18, 2019


Github actions allow me to implement continuous delivery for this blog. Each time I merge to master, jimkubicek.com is built and deployed.

First, this action should run anytime the master branch is updated.

name: Build and Deploy

on:
  push:
    branches: [master]

Now we need to create our deploy steps. Let’s use a Ubuntu container with the latest commit in our branch checked out.

jobs:
  deploy:
    name: Deploy to jimkubicek.com
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1

Install Python 3.8 (or whatever version you’re using):

    - name: Set up Python 3.8
      uses: actions/setup-python@v1
      with:
        python-version: 3.8

Upgrade pip and install the latest version of the Pelican static site generator and the Invoke task runner:

    - name: Install Pelican
      run: |
        python -m pip install --upgrade pip
        pip install invoke pelican[Markdown]

In order for SSH to work correctly, we’ll need to make sure that our public and private keys are created and placed in the correct location. On your local machine, let’s generate a valid keypair.

ssh-keygen -t rsa -f -b 4096 ./github_actions -C github_actions

Now, let’s copy the public key up to our server.

ssh-copy-id root@example.com -i github_actions

Now copy the private key into the pasteboard and add it to your github secrets. This action uses a secret with the name PRIVATE_KEY. Paste the contents of your clipboard into this secret.

cat github_actions | pbcopy

Now, in our action file, we’ve got to read this secret and get it setup on disk. The env command extracts the secret out and stores it into an environment variable. Make sure that the .ssh directory exists, and save that private key into the id_rsa file. SSH requires that the key has restricted access, so set the access to 400. Finally, add our IP address to the list of known and approved hosts. The downside of this is that if your server is MITM’d you’ll continue to just blindly trust it. I’m not sure of a way around this, if you know, let me know.

Here’s the final SSH setup stage:

    - name: Setup the SSH key
      env:
        PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
      run: |
        mkdir $HOME/.ssh
        echo "$PRIVATE_KEY" >> $HOME/.ssh/id_rsa
        chmod 400 $HOME/.ssh/id_rsa
        ssh-keyscan -t rsa <SERVER_IP> >> $HOME/.ssh/known_hosts

Finally, use Invoke to upload to our server.

    - name: Push to example.com
      run: inv publish

« | Home | »