Skip to content

Merge pull request #17 from skrysmanski/feature/hugo-0.125.0 #312

Merge pull request #17 from skrysmanski/feature/hugo-0.125.0

Merge pull request #17 from skrysmanski/feature/hugo-0.125.0 #312

#
# GitHub Actions workflow: Builds the site and uploads it to the target server.
#
# For more details on workflows, see README.md.
#
# See also: https://gohugo.io/hosting-and-deployment/hosting-on-github/
#
name: Build and Deploy
# When to run this workflow
#
# See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
# See: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on
#
# TIP: Don't use "schedule" triggers as this will cause the workflow to be disabled after 60 days of inactivity
# (and afterward the workflow must be manually reenabled).
on:
# Trigger the workflow on push to the main branch (deploy to production).
push:
branches:
- main
# NOTE: The preview branch is for tests without pull request (as GitHub Action sometimes work differently for pushes
# and pull requests).
- preview
# Trigger the workflow for any pull requests (deploy to preview, if "local" PR; don't deploy if PR from fork).
pull_request:
# Allow manual run of this workflow (https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow)
workflow_dispatch:
# Permissions for GITHUB_TOKEN for this workflow.
#
# See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
#
# NOTE: Because we run with minimal permissions, we use "@vX" (instead of "@hash") for non-GitHub steps below.
# Usually you would use "@hash" as a security measure to pin a specific version. However, since we run with
# minimal permissions here, malicious code can't do much harm (most likely). For more details, see:
# https://blog.gitguardian.com/github-actions-security-cheat-sheet/#use-specific-action-version-tags
permissions:
contents: read
env:
# The Hugo version to use: https://github.com/gohugoio/hugo/releases
# NOTE: It sometimes happens that new Hugo versions break the site. So I've decided to always
# "pin" the Hugo version (instead of using the latest version). This way, the Hugo version
# can be updated with a pull request where I can test the new version.
# IMPORTANT: Always use a pull request to update the Hugo version to be able to fix all problems
# before pushing the result to production!!!
HUGO_VERSION: 0.125.0
# Set "DEPLOY_STATE" to "production" if this workflow was triggered by a push to the main branch.
# Set "DEPLOY_STATE" to "preview" in any other case (i.e. pull requests).
DEPLOY_STAGE: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency
concurrency:
# Makes this workflow part of the "deploy" concurrency group. (Note that this text can be chosen arbitrarily.)
# NOTE: Unfortunately, we can't use "env.DEPLOY_STAGE" as "env." is not supported.
group: deploy-${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
# Do NOT cancel in-progress runs as we want to allow these deployments to complete.
cancel-in-progress: false
# NOTE: Jobs run in parallel by default.
# https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow
jobs:
build-and-deploy:
# Name the job
name: Build & Deploy
# Set the type of machine to run on
# See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
runs-on: ubuntu-latest
env:
# The directory in which Hugo stores the generated site.
# NOTE: '${{ runner.temp }}' isn't available here so we have to use '${{ github.workspace }}'.
PUBLISH_DIR: '${{ github.workspace }}/public/'
# The directory in which to store diffable files (i.e. the generated site which can be more easily diffed
# especially across branches).
DIFF_PUBLISH_DIR: '${{ github.workspace }}/public-diff/'
# Hugo build parameters used by all build steps.
HUGO_COMMON_BUILD_PARAMS: --printPathWarnings --logLevel info --panicOnWarning
steps:
###########################################################################
#
# Setup Steps
#
###########################################################################
- name: Install Hugo CLI
# This snippet is taken from: https://gohugo.io/hosting-and-deployment/hosting-on-github/
# NOTE: We can't use "snap" for this as it doesn't support installing old versions, if they're not provided by the package authors.
# Note, though, that this call is extremely fast (way faster than using snap), most likely because the source is also hosted
# by GitHub.
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
# See: https://github.com/marketplace/actions/setup-node-js-environment
- name: Setup NodeJS environment
uses: actions/setup-node@v4
with:
node-version: 'latest'
# See: https://github.com/marketplace/actions/checkout
- name: Clone Git repository
uses: actions/checkout@v4
with:
lfs: true
submodules: true
# IMPORTANT: Fetch the whole history. This is how Hugo determines the (publish) dates for the articles!!!
fetch-depth: 0
# Use this to dump the complete "github" context for the build log.
# - name: Dump GitHub context
# env:
# GITHUB_CONTEXT: ${{ toJson(github) }}
# run: echo "$GITHUB_CONTEXT"
- name: Download node modules (assets)
run: npm install
working-directory: themes/devlog-theme/assets
- name: Download node modules (utils)
run: npm install
working-directory: themes/devlog-theme/_utils
###########################################################################
#
# Build Steps
#
###########################################################################
- name: Build site (preview)
if: ${{ env.DEPLOY_STAGE == 'preview' }}
# NOTE: We don't use "--minify" here so that the output remains better readable/diffable.
run: hugo --destination '${{ env.PUBLISH_DIR }}' ${{ env.HUGO_COMMON_BUILD_PARAMS }} --buildDrafts --baseURL 'https://preview.manski.net'
env:
# Disable search engine crawling for the preview stage.
HUGO_PARAMS_robots: 'noindex,nofollow'
# For pull requests, the git branch name can't be determined by reading ".git/HEAD". So we have to specify it here explicitly.
# NOTE: This variable is empty if this workflow was not triggered by a pull request. This means that the default behavior
# remains in effect for the "preview" branch.
HUGO_PARAMS_gitBranch: ${{ github.head_ref }}
- name: Build site (production)
if: ${{ env.DEPLOY_STAGE == 'production' }}
run: hugo --destination '${{ env.PUBLISH_DIR }}' ${{ env.HUGO_COMMON_BUILD_PARAMS }}
- name: Beautify output
run: node beautify.js '${{ env.PUBLISH_DIR }}'
working-directory: themes/devlog-theme/_utils
- name: Build search index
run: npx pagefind --site '${{ env.PUBLISH_DIR }}'
working-directory: themes/devlog-theme/_utils
###########################################################################
#
# Archive Steps
#
###########################################################################
- name: Create diffable output
run: hugo --destination '${{ env.DIFF_PUBLISH_DIR }}' ${{ env.HUGO_COMMON_BUILD_PARAMS }} ${{ env.BUILD_DRAFTS_PARAM }} --baseURL 'https://fake.manski.net'
env:
BUILD_DRAFTS_PARAM: ${{ env.DEPLOY_STAGE == 'production' && '' || '--buildDrafts' }}
HUGO_PARAMS_robots: 'diffable-value'
HUGO_PARAMS_gitBranch: 'fake-branch'
HUGO_PARAMS_enableFingerprinting: 'false'
- name: Beautify diffable output
run: node beautify.js '${{ env.DIFF_PUBLISH_DIR }}'
working-directory: themes/devlog-theme/_utils
- name: Make branch name compatible with file names
id: file-branch-name
# Based on: https://stackoverflow.com/a/58035262/614177
# For syntax, see: https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
# Strips "feature/" from PR branch names. Also replaces '/' with '-'.
run: echo "${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" | sed 's#^feature/##' | sed 's#/#-#' | xargs printf "branch=%s\n" >> $GITHUB_OUTPUT
#
# Store generated files in a zip file in the workflow itself. This way outputs between two workflow runs can be compared,
# if necessary.
#
# See: https://github.com/marketplace/actions/upload-a-build-artifact
#
# NOTES:
# * Artifacts are retained only up to 90 days at the moment. See: https://github.com/orgs/community/discussions/107115
# * We keep the default compression level as in our tests increasing it to 9 (max) resulted in a zip that's only
# about 10 KB smaller (5.88 MB to 5.87 MB).
#
- name: Attach generated files to workflow run
uses: actions/upload-artifact@v4
with:
name: 'published-output__#${{ github.run_number }}__${{ steps.file-branch-name.outputs.branch }}'
path: ${{ env.PUBLISH_DIR }}
if-no-files-found: error
# NOTE: We attach the diffable files as separate(!) artifact which makes it easier to compare results (as the extracted
# folder can be compared directly).
- name: Attach generated files (diffable) to workflow run
uses: actions/upload-artifact@v4
with:
name: 'diffable-output__#${{ github.run_number }}__${{ steps.file-branch-name.outputs.branch }}'
path: ${{ env.DIFF_PUBLISH_DIR }}
if-no-files-found: error
###########################################################################
#
# Deploy Steps
#
###########################################################################
# See: https://github.com/marketplace/actions/ftp-deploy
- name: Deploy site
uses: SamKirkland/[email protected]
env:
HAS_ACCESS_TO_SECRETS: ${{ secrets.ftp_username }}
# Only run this step if the workflow has access to the repository's secret. Only pull requests
# from within the repository itself have access. Pull requests from forks don't have access.
# See below.
if: ${{ env.HAS_ACCESS_TO_SECRETS }}
with:
# See: https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions
# IMPORTANT: Secrets are NOT available for pull requests from forked repositories!!!
# Meaning: We don't need to fear that a malicious pull request will overwrite our web site.
# See: https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#using-secrets-in-a-workflow
server: ${{ secrets.ftp_server }}
username: ${{ secrets.ftp_username }}
password: ${{ secrets.ftp_password }}
# Use an encrypted FTP connection.
protocol: ftps
#log-level: verbose
# NOTE: This action actually compares file hashes to determine if a file needs to be uploaded.
local-dir: '${{ env.PUBLISH_DIR }}'
server-dir: ./www_${{ env.DEPLOY_STAGE }}/
state-name: ../sync-state-${{ env.DEPLOY_STAGE }}.json
# NOTE: By default, "exclude" contains "node_modules". We have to remove this exclude rule because
# we use this to ship fontawesome.
# For default, see: https://github.com/marketplace/actions/ftp-deploy#exclude-files
# NOTE: Unfortunately, you don't seem to be able to clear the exclude options because it then will simply
# use the default value again. So we keep some common sense value (even though we don't actually need to
# exclude anything).
exclude: |
**/.git*
**/.git*/**