Chris Tattum

Code & Design

Deploying Kirby CMS with GitHub Actions Pt 2

Development, Kirby / By Chris Tattum / October 31, 2024

Introduction

In Part 1, we set up the foundations so GitHub can access your website via SSH. We now complete the task and actually get the GitHub Action doing the work of pulling down the repository after it logs into your web server.

Setting up a GitHub Repository Deploy Key

We now need a third SSH key so that your web server can pull the git repo down. Although the GitHub Action can now SSH into your web server in order to issue a git pull command, your web server still needs to authenticate with GitHub so it can access the repository.

This is the reverse of the other two keys in that your web server needs access to your GitHub repository, so we need a private key stored on the web server and the public key stored as a Deploy Key on GitHub. Note: This only grants access to your repository, not your GitHub account.

First, we need to create the SSH Key directly on your web server. On your local machine, SSH into your web server with the following command at your terminal:-

ssh <friendly host>

Once connected to your web server, run the following command:-

cd ~/.ssh

followed by:-

ssh-keygen -t ed25519 -a 200 

This will create a default id_ed25519 and id_ed25519.pub SSH key pair. Through your GitHub repository, create a Github Deploy Key called “Staging” and copy the entire contents of the public key. (Remember, we only ever share public keys.). 

Initial clone of repo into website directory

We now need to clone your repo on our web server, replacing the existing website directory.

While still connected to your web server, go back to your home folder by typing:-

cd ~

Then navigate to your website directory. This varies from web hosting to hosting, but on ‌SiteGround it’s usually www/<website domain name>:-

cd www/<website domain name>

The website files are stored in the public_html directory, which we need to delete and recreate by cloning your GitHub repo. It’s really important that the permissions are correct for this folder, so before we remove this directory, we’ll check its current permissions by running the following command:-

stat -c "%a %n" ./public_html

It’s usually something like 755, but we need to ensure that we recreate it with the same permissions.

Then remove public_html:-

rm -rf public_html

Now clone the repo into the public_html directory:-

git clone <github repo name> public_html

Check the permissions of the public_html folder again by running:-

stat -c "%a %n" ./public_html

If the permissions match before, we’re done. If not, then restore them by running the following command (replacing 755 with whatever the original permissions were):-

chmod 755 ./public_html

Then cd into the public_html directory and install all the composer dependencies:-

composer install

Test the site – it should now work!

We can now set up the GitHub Action, so that any time you push code to the main branch, the GitHub action “runner” will spring to life, SSH into your web server, change to the correct directory, then issue a “git pull” command to get the latest changes.

Configuring the GitHub Action

First, we add a file in your repo project called git-deploy-staging.yml in “.github/workflows” with the GitHub Action runner commands. This file is already provided in the starter site project, located in the github_RENAME/workflows folder, so just rename the github_RENAME directory to .github, then push the change to your GitHub repository.

The runner needs to change to your website directory from earlier, e.g.

www/<website domain name>/public_html

Add this to another GitHub Action secret called STAGING_WEBSITE_DIR. The GitHub Action runner will then change to this directory before it issues the git pull command.

Note: The runner script was originally written by Benoit Blanchon, who wrote an excellent article explaining how this all works:-  https://blog.benoitblanchon.fr/github-action-run-ssh-commands/

The modified script is shown below:-

name: Git pull
on: [push, pull_request]
jobs:
  deploy:
    name: "Deploy to staging"
    runs-on: ubuntu-latest
    steps:
      - name: Configure SSH
        run: |
          mkdir -p ~/.ssh/
          echo "$SSH_KEY" > ~/.ssh/staging.key
          chmod 600 ~/.ssh/staging.key
          cat >>~/.ssh/config <<END
          Host staging
            HostName $SSH_HOST
            User $SSH_USER
            Port $SSH_PORT
            IdentityFile ~/.ssh/staging.key
            StrictHostKeyChecking no
          END
        env:
          SSH_KEY: ${{ secrets.SSH_KEY }}
          SSH_USER: ${{ secrets.STAGING_SSH_USER }}
          SSH_HOST: ${{ secrets.STAGING_SSH_HOST }}
          SSH_PORT: ${{ secrets.STAGING_SSH_PORT }}

      - name: Check out the source
        run: ssh staging "cd $WEBSITE_DIR && git checkout && git pull"
        env:
          WEBSITE_DIR: ${{ secrets.STAGING_WEBSITE_DIR }}

As GitHub creates a new virtual machine every time the Action runs, we need to recreate the private SSH key stored in the SSH_KEY secret we created earlier, every time the command runs. So the script creates a config file (similar to the one on your machine) so it uses the correct staging.key file, with the correct SSH host, user and port stored in your GitHub secrets.

The chmod command grants read/write permissions to the key to the GitHub runner user only. This is important because we’re using VMs on GitHub with a private key. It’s a good security practice to only grant the minimum permissions that are necessary.

Testing Git Deploy

Now all that’s setup, let’s try it out.

Make some changes to the “home.txt” file (located in the content folder) and then git push it to the main branch.

Within a few seconds, the GitHub Action will run, log into your web server, change to the website directory, then pull down the change you made to your repository.

If you navigate to the Actions tab in GitHub, you can see a log of everything that happens.

Summary

Congratulations, you now have an automatic git deploy workflow! Use feature branches as you would normally, and then merge into main and push when ready – the staging site will then update automatically.

I will provide a follow up post which explains how to add another GitHub action to update the live site whenever we tag the main branch with a release.

Leave the first comment