Automatically Push Changes To Your Live Site With GitHub Actions

Web Design Dev Ops All

Introduction

GitHub Actions makes it easy to automate developer workflows, for example building, testing and deploying your code. In this post I am going to automate the deployment of this website from my local development environment to the live production server.

Currently, to publish changes I need to open an FTP program, connect to my hosting server, then manually find and copy across the updated files. Although it doesn't sound too offensive, this can quickly become tedious and I am a keen advocate of living an easy life. Lets see how GitHub actions can help me maintain my tranquil state of mind...

Getting Started

Github Actions have the concept of "Workflows". Workflows are where we configure our automation. All workflows need to be stored in a .github/workflows directory in the root of the repository. For more information on workflow files see the GitHub action Docs.

To get started create the new directory structure and then create a .yml file to contain the workflow code.

mkdir -p .github/workflows 
cd .github/workflows
touch deployment-workflow.yml

Deployment Workflow

Now lets start configuring our GitHub Action.

To automate the copying of files to the remote server we are going to utilise a 3rd party GitHub action called RSync. This GitHub action deploys files from a folder in GitHub to a remote folder via rsync over ssh. Essentially, it logs into a remote server and syncs the files between two directories.

This is exactly what we need to automate those pesky manual deployment steps! Lets copy the example from RSync and go through what each part is doing.

name: Publish Site
on:
  push:
    branches:
      - master

jobs:
  rsync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: rsync
        uses: burnett01/rsync-deployments@4.0
        with:
          switches: -avzr --exclude=".git" --exclude=".github" --exclude "logs"
          path: /
          remote_path: ~/
          remote_host: ${{ secrets.HOST }}
          remote_user: ${{ secrets.HOST_USER }}
          remote_key: ${{ secrets.DEPLOY_KEY }}

The full GitHub action rsync example

First up, we have named the workflow. And also defined when we want our workflow to spring into action.

Here, Publish Site is only going to be triggered on a git push event to our git repositories master branch. If we push to any branch other than master this workflow will not be triggered. This is super handy, because we don't want every branch in our repository to be pushed to our live site.

A good way to think about your code is: master branch = production code.

name: Publish Site
on:
  push:
    branches:
      - master
      - github-actions

Next, we define the jobs that are going to run when push to master.

We are going to run the rsync job. Here we also define the runs-on parameter. The job is run in a fresh instance of virtual environment hosted on a GitHib server. Here "runs-on" specifies what environment to use. This job will be executing on a server running the latest version of Ubuntu.

jobs:
  rsync:
    runs-on: ubuntu-latest

After this, the "steps" are defined. Here, we are using an action called checkout. This checksout our GitHub repository and makes it accessible under the $GITHUB_WORKSPACE parameter. 

steps:
  - uses: actions/checkout@v1

In the next step we perform the rsync action. More detailed information about the confguration for this action can be found in the repository Docs however the configuration is pretty self explanatory.

- name: rsync
  uses: burnett01/rsync-deployments@4.0
  with:
    switches: -avzr
    path: /
    remote_path: ~/
    remote_host: ${{ secrets.HOST }}
    remote_user: ${{ secrets.HOST_USER }}
    remote_key: ${{ secrets.DEPLOY_KEY }}

Finally, we need to create some GitHub Secrets to contains the sensitive login credentials for our production server.

Setting up GitHub Secrets

I strongly recommend against adding senstive data directly to the workflow files as these will be stored in plain text. Instead, we should store the details in GitHub secrets and reference these in the workflow file like this.

remote_host: ${{ secrets.HOST }}
remote_user: ${{ secrets.HOST_USER }}
remote_key: ${{ secrets.DEPLOY_KEY }}

To create a GitHub secret go to your repository in the GitHub UI and click Settings -> Secrets -> Add a new secret. We need to create a secret called HOST which contains our remote servers hostname. A secret called HOST_USER which contains our username for the remote server. And a secret called DEPLOY_KEY which contains our private key which we use to log in to the remote server over SSH.

GitHub Secret

Creating an SSH Key 

If you do not have an SSH key you can follow the below method to create one. The SSH key is used by GitHub to connect to the production server.

To create an SSH key, open a terminal and run:

ssh-keygen -t rsa -b 4096

When prompted enter a location to save the key and press enter. You can also create passphrase to further secure the SSH key, but this is optional. Next, copy the public key to your remote server using scp:

scp mynewkey.pub username@remoteserverhostname

SSH into the remote server and add the public key to your authorized keys file:

ssh username@remoteserverhostname
cat ~/mynewkey.pub >> ~/.ssh/authorized_keys

Now your SSH key is all set up to use. You should be able to authenticate with the remote server using this ssh key, rather than having to enter password. To test it out try sshing with the new private key:

ssh -i mynewkey username@remoteserverhostname

If all goes well then head over to GitHub and create the DEPLOY_KEY secret with the private ssh key.

Deploying!

Now we're all set to test out our new automation! Lets create a TESTFILE and push it to master...

After pushing to master we will see the Workflow triggered in the GitHub UI. You can monitor the GitHub workflow by going to your repository and clicking the actions tab. You should see the Publish Site workflow status. Hopefully it has been triggered and completed succesfully.

And now TESTFILE has been copied to the remote server! No more manual copies and uploads. Our live site will always be an exact reflection of the code contained in the master branch in our git repository.


 

← Back to Blog

Writing blog posts is thirsty work! If you found this helpful feel free to buy me a beer...