Introduction
Until now, I uploaded everything by hand for this blog. Of course, it would be easier to do this automatically, which is the topic of this post. The following features are important for me, and we will cover them all:
- Every week on Monday the content should be uploaded
- I want to start the pipeline in one click from the GitHub webpage or mobile app
- When the content of the content folder changes, it should be uploaded automatically
- Upload to a FTP-Server
Technology selection
There are great solutions which I could choose from e.g. Tarvis CI, GitHub Actions or CircleCI. Each of those are great and have individual features which are very well suited for different purposes.
As my build time is very small like two or three minutes I just went with GitHub Actions as there are 2000 Minutes/Month available. And the most important part is I wanted a free version. More importantly, I did not use GitHub Actions a lot - out of curiosity, it was very interesting to look into it.
GitHub Actions
GitHub Actions is the CI solution of GitHub, which is well documented. I will elaborate on how I configured the action, and in the end I will upload this file as a gist.
First, I had to create a .yml
file in the following folder: .github/workflows/{name}.yml
After this you can set a name for the workflow like this:
name: Deploy Hugo site to Pages
Now it is getting more interesting. The on
part is very important as it handles when the pipeline should run. For me, this is the place where I can achieve my goals.
- Only run the pipeline when something is pushed into the main-branch and the content of the content folder changes.
- Run every Monday at 12am
- Manually run the pipeline/workflow
on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]
paths:
- 'content/**'
# or normal schedule each monday at 0:00
schedule:
- cron: '0 0 * * MON'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
The schedule part can be any cronjob, as you would normally create one in Linux. It is a list, so you can even specify many cronjobs, e.g. you could run it on Monday and Tuesday etc.
The workflow_dispatch
enables that you can manually run the workflow in the actions tab.
There are many more options to handle the on
part, but to get more information, I would encourage you to read the documentation.
After this, you can tell the workflow which shell should be used. This is not a necessity, but it helps if you have a specific shell you like to use.
# Default to bash
defaults:
run:
shell: bash
The last part is the jobs
stage, which is the core of GitHub Actions. As I am working on ubuntu-latest
I want the build stage to run on the same system which you can specify with runs-on
. You can change this in every job as you like.
With env
you can handle environment variables for a specific pipeline, which for me was the HUGO_VERSION
.
jobs:
# Build job
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.114.0
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Install Dart Sass Embedded
run: sudo snap install dart-sass-embedded
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Build with Hugo
env:
# For maximum backward compatibility with Hugo modules
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: |
hugo \
--minify
- name: Sync files
uses: SamKirkland/[email protected]
with:
server: ftp.myserver.com
username: ftp_username
password: ${{ secrets.ftp_password }}
local-dir: ./public/
Each step will be displayed in the pipeline as it is easier to see what happens when and you can specify and split everything clearly.
The first step is to install the hugo-cli which is a simple wget
command. There, I use the env variable from before to explicitly use the correct Hugo version.
The second step is to install dart-sass
if you need this. This can possibly be removed if you do not sass/scss
. For completeness, I included it here.
After this, I checkout the repository with an already available GitHub Action actions/checkout@v3
. Without this checkout, you can not work with your repository. Normally, you’d use node
with sass
. That is why the next step is to install Node.
When all this prebuild stuff is done, we can finally build the static site with Hugo.
If the build finishes successfully, we can upload the public folder to the ftp server. Here I use the action SamKirkland/[email protected]
where you can specify a lot, but the minimal stuff is included in the last step.
Special thing is the password, as it should not be written in the file directly. Best practice is to go into the settings
in the GitHub repository. There you have to select the Secrets and variables
and under this topic select actions
. There you can create a new repository secret
.
The complete .yml
looks like this:
name: Deploy Hugo site to FTP
on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]
paths:
- 'content/**'
# or normal schedule each monday at 0:00
schedule:
- cron: '0 0 * * MON'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
# Default to bash
defaults:
run:
shell: bash
jobs:
# Build job
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.114.0
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Install Dart Sass Embedded
run: sudo snap install dart-sass-embedded
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Build with Hugo
env:
# For maximum backward compatibility with Hugo modules
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: |
hugo \
--minify
- name: Sync files
uses: SamKirkland/[email protected]
with:
server: ftp.myserver.com
username: ftp_username
password: ${{ secrets.ftp_password }}
local-dir: ./public/
Conclusion
As we have discovered, it is very easy to use GitHub actions, and it can easily be build and deployed. The expense to create all this is minimal, and I encourage everyone to simply try and use GitHub actions. As I build the pipeline, everything went smoothly and no errors occurred for me. If anything fails for you, just write a comment here or contact me directly, as I am more than happy to help you.