Deployment with GitHub Actions

January 21, 2025

So to make my life easier and automate the process of updating this very website you are currently on, I have decided to look into GitHub Actions. The base idea behind the process was to have an automated build job, that runs the Jekyll build script (see Jekyll article for more information). The automatic build job was to be followed by a manual deploy job, which send the built files to my hosting provider via SFTP.

First I had to learn a bit about GitHub actions. As it turns out, the general approach is pretty simple. All you need is a .github/workflows directory at the root of your project. In this directory, you can create YAML files that describe the various actions to be executed in your repository.

In my case, I wanted a build job that takes the current state of the main branch and builds the _site directory from it. Initially I was thinking about having the job push the built file into a separate branch. After tinkering around with the idea for a while, struggling to get the script going, then realizing I had added the _site directory to my .gitignore file, so the script couldn’t detect any changes to push. When I finally ended up facing a permission denied error, I’ve decided to divert from this idea. The script was now to generate an artifact, which I could manually download and upload into the file server of the hosting provider for this website.

The final script for this job looked like this:

However… Since you’ve read the title of this article, you know I couldn’t stop there. Once I had the artifact in my hands, and had to manually upload and extract it onto the file server… I realized I’d be too lazy to do that. So the natural next step was to have another GitHub Action, which essentially somehow moves the files over to the file server.

The hosting I’m using is provided by Namecheap and the server that it runs on can be accessed through cPanel. For security reasons the ftp:// protocol isn’t exposed (good!). According to their documentation, there’s 2 approaches to setting up an FTP client - FTPes or SFTP. I tried getting an SFTP workflow to trigger correctly but I was faced with error after error. I ended up deciding on using rsync over ssh whenever the main branch gets updated. Here’s what the final workflow file looks like. I’m likely going to add more steps in the future, depending on how the website develops but for now I am pretty happy with it.

name: Build and Deploy Jekyll Site

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    # Checkout the branch
    - name: Checkout code
      uses: actions/checkout@v3
    # Set up Ruby environment
    - name: Set up Ruby
      uses: ruby/setup-ruby@v1
      with:
        ruby-version: '3.3.5' # Specify the Ruby version required by Jekyll
    # Install Jekyll and dependencies
    - name: Install dependencies
      run: |
        gem install bundler
        bundle install
    # Build the Jekyll site
    - name: Build Jekyll site
      run: bundle exec jekyll build
    # Upload the _site directory as an artifact
    - name: Upload _site artifact
      uses: actions/upload-artifact@v4
      with:
        name: jekyll-site
        path: _site

  deploy:
    name: deploy
    needs: build
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v4
    - name: Download built site artifact
      uses: actions/download-artifact@v4
      with:
        name: jekyll-site
        path: _site
    # Deploy to SFTP server using SSH
    - name: rsync deployments
      uses: burnett01/rsync-deployments@7.0.2
      with:
        switches: -avzr --delete
        path: "_site/"
        remote_path: "~/public_html/"
        remote_host: $
        remote_port: $
        remote_user: $
        remote_key: $