How I automated the deployment of my blog using GitHub Actions

ยท 1156 words ยท 6 minute read

Actions feature on GitHub is really powerful tool. I used it to automate the deployment of my blog. Here’s how I did it.

Please take into account it’s not a tutorial per se.

Automation of what? ๐Ÿ”—

Currently to update articles or anything on this website I have to make the change locally, build a new HTML using hugo, copy the result from public/ directory and paste it onto the remote server over SSH or other file transfer protocol. It’s a bit cumbersome to do so. Then why can’t we automate it?

The perfect workflow would look like this:

  1. Make a local change and test it
  2. Create a git commit
  3. Push the changes to the remote repository
  4. Magic happens…
  5. The site is publicly updated!

The mysterious fourth step actually is the most important one. In this step the site is checked for errors and built using GitHub CI/CD and the resulting files are copied to my server which is hosting them.

Why not use GitHub Pages? ๐Ÿ”—

Wait. Do we need to copy those files to a remote server? Can’t we just use the GitHub Pages? Pages allows us to host static websites on GitHub itself using our repository as data source and it’s free. So…

Of course we can.

I decided to use my own hosting for a few not so important reasons. You can use GitHub Pages if you want to! If you’re really curious why do I chose coping the files to a remote server here are the reasons:

  1. It’s fun and it’s an great opportunity to learn something new.
  2. No need to use Google Analytics hence no cookies.
  3. Greater control over the data, server settings and everything else.
  4. I just have to start using those servers of mine…

Setting up the server ๐Ÿ”—

For the server part I just created a new user in the system, set some permissions and generated an SSH key for him to give it to the GitHub workflow which will upload the files on his behalf.

Creating a new user ๐Ÿ”—

I started with creating a new user with useradd -m <username>, adding him to the www-data group which is the default group used by the HTTP Server on UNIX using usermod -aG www-data <username> and it was basically done.

Eventually you may also need to set proper permissions for your data directory so your user can modify it.

Generating an SSH Key ๐Ÿ”—

After switching to the new user’s shell I created a new SSH Key pair by running ssh-keygen. Next I had to append the ~/.ssh/authorized_keys file with our new public part of the key to allow users with it to connect. It can be done with cat .ssh/<key-name>.pub >> ~/.ssh/authorized_keys.

On the other host we can check if we can connect without problems by getting the private key and establishing a connection. If everything works, great.

Spilling some secrets ๐Ÿ”—

Our workflows on GitHub have to know a few things to be able to copy the files over the network, among others:

  • Where? ( HOST, PORT, PATH )
  • Who? ( USER, SSH_KEY )

We can provide those secrets to the workflows by using something called … Secrets and variables in our GitHub’s repository settings. It’s pretty straightforward so I won’t dwell on this.

GitHub secrets list GitHub secrets list

As you can see I added a variable to all of those things I mentioned earlier. Now we can use them in our scripts and nobody will see their uncovered values.

Setting up new workflows ๐Ÿ”—

Workflows configurations are just files put in the .github/workflows/ directory from root of our repository. GitHub will automatically recognize them and execute the content if the conditions of the execution specified in them are met.

Check for errors action ๐Ÿ”—

First workflow will only compile our website and see if it won’t explode compile time.

 1# ...
 2on: push # run on every push to the repository
 3
 4jobs:
 5  build:
 6    # ...
 7    steps:
 8      - uses: actions/checkout@v3 # get the source code
 9        with:
10          submodules: true # true because, we use them for themes
11          # ...
12
13      - name: Setup Hugo # guess what it does
14        uses: peaceiris/actions-hugo@v2
15        with:
16          hugo-version: 'latest' # use latest version of Hugo
17
18      - name: Build # let's see if it explodes
19        run: hugo --minify

See full version of this file on GitHub

Now on every push no matters to which branch we push this action should run and tell us if our site is building without any errors.

Deploy action ๐Ÿ”—

Second workflow will build and deploy the finished website to the internet by using rsync action and transferring the files to our remote server. As you can see we’re also using here the variables which we set earlier in the repository settings.

 1# ...
 2on:
 3  push:
 4    branches:  # this time we are running the job
 5      - master # only when the changes are pushed
 6               # to the master branch
 7
 8      # ...
 9      - name: Build
10        env:
11          HUGO_ENV: production # it's not necessary but just in case
12        run: hugo --minify
13
14      - name: Deploy # here's where the magic happens
15        uses: burnett01/[email protected]
16        with:
17          switches: -avzr --delete
18          path: public/
19          remote_path: ${{ secrets.DEPLOY_PATH }}
20          remote_host: ${{ secrets.DEPLOY_HOST }}
21          remote_port: ${{ secrets.DEPLOY_PORT }}
22          remote_user: ${{ secrets.DEPLOY_USER }}
23          remote_key: ${{ secrets.DEPLOY_KEY }}

See full version of this file on GitHub

And it’s done! Whenever we push anything to the master branch it will be built and pushed onto our server. Right into specified by us directory. What’s next? In my case I’m just serving those file through nginx. That’s how you can read this article right now ;)

Final words ๐Ÿ”—

I really like this kind of workflow (11th usage of this word). It’s dead simple to work with, you have advantages of using git version control system and you’re even allowing other people to write their articles for your website by opening a pull request or just maybe fix your typos (assuming your repository is public).

Check as well something called Branch Protection Rules. By using it you can block direct pushes to the master branch and make sure that pushed code (e.g. from pull request) will generate static files without any problems by seeing result of our Build job.

I’m also aware this article is a bit chaotic but you have to forgive me for that. Maybe you have noticed (for sure you did) that I didn’t use a translator for the whole text so the Engrish can be strong. Anyway I hope it will improve with time and number of written articles.

Any suggestions? Questions? Feel free to leave them in comments below, I’ll be glad to read them!

Useful resources ๐Ÿ”—

comments powered by Disqus