To build my site I use the static site generator Hugo. The process of editing the Markdown source files and afterwards building the site to deploy it to my website directory, is somewhat old-fashioned: Edit the files with Nextcloud, fire up my VPN, login with ssh and build the site with an alias to deploy it to my directory. Well…
So to address this issue, I gave the CI/CD system of GitHub Actions a try and want to share my experiences with you.
Goals
After the completion of this guide, you will be able to:
- Manage the basic functionality of a git repository on GitHub.
- Build your static Hugo site with GitHubs CI/CD tool: GitHub Actions.
- Automatically deploy your site onto your webserver via SSH (and optionally with WireGuard).
Prerequisites
Please take care of the following before you continue:
- GitHub account and the necessary credentials / tools to access it.
- Installed git tools on the machine you want to manage your git repository.
- Up and running sshd.
- Optionally: Configured Wireguard endpoint.
Set up GitHub related access
Setup access to the git repository
In order to access your git repository from your local machine, you need to setup a SSH access to GitHub.
First, lets create SSH keys for your user account. If you already have keys for your user, you can skip this step:
|
|
You can add a passphrase to make it more secure. The downside is, you have to enter the passphrase every time you use the key, which could be annoying. The decision is yours.
Now you need to export the public part of the key-pair and add it to GitHub in order to access any public repository on GitHub with git
.
|
|
Copy the content of your public key and add it here: https://github.com/settings/keys
Create access for GitHub Actions
The build and deploy process will be done by a small, temporary VM called github-runner. To deploy the generated website onto your webserver, it needs access to website directory. This will be done by SSH. I highly recommend a separate system user for that matter.
Create a user. For the purpose of this guide we will call her github
.
On FreeBSD this will be done with:
|
|
Make sure the user can access the specific website directory you want to deploy your built website to. Perhaps you have to adjust permission and / or add the user to groups accordingly.
The user needs a home directory for the ssh keys and a login shell for the scp command. Both necessities are the default options for adduser
.
Login as the newly created user ‘github’ and create a pair of ssh keys without a passphrase:
|
|
The private key will be added to the secret vault in GitHub Actions later. The public key has to be added to the authorized_keys of the user ‘github’ so the github-runner can login to your webserver via the pubkey authentication:
|
|
Setup GitHub repository
GitHub website
First you need to create an empty git repository on the GitHub website. For the purpose of this guide, the repository will be named ‘hugo-website’.
Once you have created the repository, you can copy its SSH link. The link can be found in the tab Code
within the dropdown (green) Code
. It has the format of:
|
|
Local repository
Login with your local user account and go to the source of your Hugo website (locally or wherever you are editing your site) to setup your local git repository:
|
|
You can add any existing files to the repository by typing:
|
|
Optionally, if you have files you don’t want to track, you can add them to .gitignore
:
|
|
Commit your changes with the commit message initial commit to the local repository:
|
|
Now you have to add your GitHub repository so you can push and pull your changes to it. The link can be found in on the GitHub website in your repository as mentioned in the previous section:
|
|
Now select your local repository to be the main branch:
|
|
And last but not least, push your local repository to GitHub. If you have created your SSH keys with a passphrase, you have to enter it every time you access your repository:
|
|
GitHub CI/CD with GitHub Actions
GitHub Actions will be responsible to build your Hugo site the same way you would do locally. Afterwards it will deploy it onto your webserver via SSH (scp). Since I can only access my server’s sshd from inside my VPN, I have added support to setup a Wireguard interface before deploying. This is purely optional for this guide.
There is a lot of sensitive information involved. To make sure it is not publicly available, we make use of GitHub’s encrypted secrets.
GitHub Actions are managed through so called workflows. They describe the job to do, when to be triggered and a hell lot more I still don’t know ;)
Once triggered (can be also manually) the workflow is picked up by a github-runner, a small temporary and basic VM. So everything you need, like settings or additional software, has to be taken care of in your workflow. When the job is finished, the VM is gone and the temporary data is lost.
Creating a workflow file
Every workflow is described in a separate yaml file located in your repository:
|
|
You can either create your workflow in GitHub’s frontend or locally and push it to GitHub’s repository afterwards. Lets create the file locally and edit it in your local repository:
|
|
Basic structure of a workflow
The basic layout is the following:
|
|
It consists of the name of your workflow. Be creative!
Followed by workflow triggers on. I want my workflow to run each time I push to my repository and also be able to start it manually.
Next are jobs defined. You could do a lot with different jobs which are, for example, dependent on another. Its my first time with CI/CD so I kept it stupidly simple with just one job deploy. The job also gets a name and which OS the workflow should run on (github-runner). You can basically select between Linux, Windows and MacOS and also different versions. You are also able to use your own runners, be it because of technical reasons or licensing concerns. None of those reasons apply to me at the moment.
The job consists of as many steps as you need (or want). Each step runs a set of commands in the VM. It is also possible to use pre-defined or user generated actions already available at GitHub.
Lets dive into it!
Checkout repository
The first step checks out the git repository (on GitHub of course) to the github-runner. There it is available as long as the workflows runs and therefore the VM is active. Each step gets a name and in this case, uses a predefined GitHub Action. Optionally, if you use the .GitInfo variable, you should fetch the complete history when doing a git checkout. Thanks goes to jjameson!
|
|
Optional: Install git
Hugo’s GitInfo variables, .GitInfo.AuthorDate
in particular, delivered wrong dates back once I have deployed my site. Locally everything worked as expected. Apparently some git config is needed: git config --global core.quotepath false
In order to make any changes to the git config on the github-runner, git has to be installed. So I added another step to do that:
|
|
Install Hugo
The next step will install and setup Hugo via apt-get
.
|
|
Now Hugo is installed and can build your site. This is also the first time we use a predefined GitHub variable: {{ github.workspace }}
which will point to our checked out repository.
In order to only deploy the built site later and have it not mixed into the repository, Hugo should deploy the site to the subfolder htdocs
.
|
|
Optional: Setup Wireguard
The next step is optional, but showcases another user generated action and the heavy use of GitHub encrypted secrets for sensitive data. Since my sshd is only accessible inside my VPN, I have to setup Wireguard inside the github-runner to deploy the site via SSH (scp) later.
|
|
The necessary secrets for Wireguard contain the following:
|
|
Encrypted GitHub Secrets
In order to use encrypted secrets in your workflow you have to add them in your repository on GitHub in the section Settings / Secrets and variables / Actions. They are encrypted after they have been added and only available to the github-runner executing the workflow. You can’t view the content of the secret and are only able to overwrite them afterwards, if you want to edit them. There is a lot more information found on GitHub encrypted secrets.
Before you can deploy your site via SSH, you need to install the SSH keys you created in the beginning of this guide on the github-runner. In order to do that, you have to create two encrypted secrets on GitHub:
PRIVATE_SSH_KEY
will contain the private key of the user github
you have created earlier. Copy the content of:
|
|
in a new secret and name it PRIVATE_SSH_KEY
. Afterwards you need to put your server’s public ssh key (where your website will be deployed) into the KNOWN_HOSTS
secret in order to make a trusted connection:
|
|
and past the content into a new secret. You have to add your server’s hostname or IP address in front of the key before saving the secret. It would look somethings similar to:
|
|
Setup SSH access to the webserver
Now the github-runner will setup the private ssh key and the known_hosts file so it can access your server via ssh.
|
|
Deploy site
Finally it is time to deploy your site onto your webserver. For this task scp
is used to copy the content of the built Hugo site in htdocs
to your HOSTNAME_OR_IP.
For the purpose of this guide, the location of the web directory for the Hugo site will be in /usr/local/www/hugo-site
. This, of course, can and probably will differ from your installation.
|
|
Source material
You can find my repository, including the workflow, on GitHub. Please feel free to check it out.
Conclusion
I can now edit the repository of my website with a decent editor (VSCode), push it to GitHub and automatically deploy it to my server. Yeah!
Although the simplicity of my site doesn’t really justify the use of CI/CD, it has been a fun experience setting up an automated task to deploy it. The whole experience has also triggered my interest in the fascinating topic of CI/CD. There is so much more to learn.
Besides that, I finally have my website version controlled and easily updated locally and the pushed to GitHub. So thats good too ;)
Have fun with your workflows and take care!