Since the source code of my website is kept on Github, using Github Actions to automate building and deploying my website seems to be a natural choice. Plus, it’s relatively simple to setup.
I’m using Hugo and Github provides a pretty good template for Hugo so it’s a good start. The only problem is that the template helps you deploy the website to Github Pages, but I need to deploy to my own server.
name:Deploy Hugo site to mywebsite.comon:# Runs on pushes targeting the default branchpush:branches:["master"]# Allows you to run this workflow manually from the Actions tabworkflow_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 bashdefaults:run:shell:bashjobs:# Build the website and deploy the generated static site to the serverbuild-and-deploy:runs-on:ubuntu-latestenv:HUGO_VERSION:0.112.5steps:- name:Install Hugo CLIrun:| 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 Sassrun:sudo snap install dart-sass- name:Checkoutuses:actions/checkout@v4with:submodules:recursive- name:Get short SHAid:short_sharun:echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT- name:Build with Hugoenv:# For maximum backward compatibility with Hugo modulesHUGO_ENVIRONMENT:productionHUGO_ENV:productionrun:hugo --minify- name:Deploy to mywebsite.comenv:SSH_PRIVATE_KEY:${{ secrets.MYWEBSITE_COM_KEY }}run:| mkdir ~/.ssh && echo "${SSH_PRIVATE_KEY}" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H mywebsite.com >> ~/.ssh/known_hostsDEPLOY_DIR=anakinfoxe.com_${{ steps.short_sha.outputs.SHA_SHORT }}_$(date +%Y%m%d%H%M%S)ssh deployer@mywebsite.com "mkdir -p /var/www/$DEPLOY_DIR"rsync -avz --delete public/ deployer@mywebsite.com:/var/www/$DEPLOY_DIRssh deployer@mywebsite.com "ln -snf /var/www/$DEPLOY_DIR /var/www/mywebsite.com"ssh deployer@mywebsite.com "find /var/www/ -maxdepth 1 -type d -name 'mywebsite.com_*' -mtime +180 -exec rm -rf {} \;"
Here is the main steps of the workflow:
branches: ["master"] defines the condition to trigger the workflow: a push event onto the master branch.
In jobs defines only one job: build-and-deploy which will generate static website and deploy it to the server. Most configs and steps were provided in the template except:
Get short SHA will be used to name the folder uploaded to the server
Deploy to mywebsite.com
Use the SSH private key to login to the server
Use rsync to recursively sync generated static website to the server
Create symbolic link to the website root directory
Remove previous uploads that are more than 180 days old
Instead of using a build job and a deployment job? Because that will require me to upload the generated static website to somewhere (for example, Github Pages) once the build is done, and then download it when the deployment job starts. I feel it’s not necessary for this simple project.
An user “deployer” will be used to login to the server and does all the deployment work. I don’t want to use any existing users on the server so I can properly manage its permission and capability.
Create the “deployer” user as I used in the workflow yaml, and create SSH folder
Don’t provide any passphrase. Otherwise it needs to be entered during the workflow. Two files will be generated under ~/.ssh:
github_actions_deployer: private key
github_actions_deployer.pub: public key
Copy the content of the public key into /home/deployer/.ssh/authorized_keys
On Github repository, go to “Settings” and in “Secrets and variables”, create a Repository Secret named MYWEBSITE_COM_KEY for Actions, with the content of private key
Add the user to the group that owns /var/www directory