Solution - Deploying Hugo to Azure Static Site
Deploying your Hugo site to an Azure Static Site from GitHub is pretty straightforward, Azure sets it up for you. But you’ll run into a problem when trying to use a later version of GoLang. Let’s run through the solution.
Initial GitHub Workflow
When you first setup your Azure Static Site you can setup the deployment by pointing it to a GitHub repository, after some authentication it’ll add a workflow file to your repo as well as some secrets.
The workflow file will look like this at first:
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- main
jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v3
with:
submodules: true
lfs: false
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: "upload"
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
app_location: "/" # App source code path
api_location: "" # Api source code path - optional
output_location: "public" # Built app content directory - optional
###### End of Repository/Build Configurations ######
You can view the secrets in your repository by going to settings at the top and finding Secrets and variables
on the left hand side.
To give it a high-level breakdown, the first step in the workflow checks out your code (including submodules, in case you’ve added your theme as a submodule).
There after it uses the Azure/static-web-apps-deploy@v1
action to deploy to Azure Static Web Apps. This really simplifies things by using Oryx to detect what kind of web app you’re installing and following implicit steps.
In our instance it would do the following:
- Install the required version of Golang.
- Install the required version NPM (if required) and runs
npm install
afterwards. - Install the required version of Hugo.
- Build the site for production with
hugo
- Publish the files found in
output_location
to the static site.
Pretty much all-inclusive for a single action.
Golang Version mismatches
If you install some of the latest themes for your Hugo site, especially if you do so by adding it as a Go module, you could run into the following error:
Error: Platform 'golang' version '1.22.5' is unsupported. Supported versions: 1.14.15, 1.15.15, 1.16.7, 1.17, 1.18.10, 1.18.8, 1.18, 1.19.3, 1.19.5, 1.19.7, 1.19, 1.14.15, 1.15.15, 1.16.7, 1.17, 1.18.10, 1.18.8, 1.18, 1.19.3, 1.19.5, 1.19.7, 1.19
This is an unfortunate shortcoming in Oryx. They haven’t updated it to the latest version in quite a while but the solution is simple. Head to your go.mod
file, it’ll look something like the below:
module github.com/your/repository
go 1.22
require github.com/hugo-toha/toha/v4 v4.5.0 // indirect
The easiest solution I have found for now was to change the to a supported version, though don’t include the patch version, stick to MAJOR.MINOR as below:
module github.com/your/repository
go 1.18
require github.com/hugo-toha/toha/v4 v4.5.0 // indirect
That should get your workflow passing that step.
Hugo Issues
Another issue I found was that in the latest themes we often see patterns that older versions of Hugo don’t recognise. You’d start seeing build errors like: Error: error building site: render: failed to render pages: render of "page" failed: ...
An example of this would be SITE.IsMultilingual
which was deprecated in v0.124.0 of Hugo and one should rather use hugo.IsMultilingual
. This would mean changing all the layout files of a theme to match an older version or, a much simpler solution, would be to simply force the workflow to use a later version of Hugo.
I couldn’t find a way to force Oryx to do this, no matter how I tried, so I decided it was time to simply update the workflow to simply build it myself.
Customising your Hugo install
Here’s the first step I added to the workflow:
- name: Install Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: '0.131.0' # replace this with your desired Hugo version
extended: true
This bit uses the peaceiris/actions-hugo@v2
action to install a specific version of Hugo to the runner. Make sure to specify extended: true
if your theme requires it, or you’ll run into further build issues.
Customising your NPM install
After this I realised that to complete the build myself, I’d need to do the same for NPM, a simple further step was added as below:
- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
This step should not be necessary if your theme doesn’t use any node packages. You can check if there’s a package.json
file in your project root, if there is, you need this step.
Build your website
Finally we take control of the build process by executing the following steps:
- name: Install NPM Modules
run: npm install
- name: Build Hugo site
run: hugo
This simply runs npm install
which will check for a package.json
file and install all necessary dependencies (like Font-Awesome). After that it builds your Hugo site.
I know, you could simply combine these into npm install && hugo
but I like to keep the steps separate for readability.
Modify the Oryx step to simply publish the resulting files
Once you’ve done all of the above, we need to tell Oryx to skip trying to build it (or we’ll simply run into the same issues) as well as pointing it to the right place to look for the production files. We do this by adding/adjusting the below variables to that step:
app_location: "/public"
skip_app_build: true
Final Workflow File
Finally, our workflow file looks like below:
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- main
jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v3
with:
submodules: true
lfs: false
- name: Install Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: '0.131.0' # replace this with your desired Hugo version
extended: true
- name: Install Node.js (for Azure Static Web Apps)
uses: actions/setup-node@v2
with:
node-version: '16' # Ensure Node.js is installed as Oryx requires it
- name: Install NPM Modules
run: npm install
- name: Build Hugo site
run: hugo # This will use the Hugo version installed by the previous step
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: "upload"
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
app_location: "/public" # App source code path
api_location: "" # Api source code path - optional
output_location: "public" # Built app content directory - optional
app_build_command: "hugo"
skip_app_build: true
###### End of Repository/Build Configurations ######
Conclusion
This should get you well on your way to publishing without further issue. In future I do want to add an additional step to actually install the latest version of Golang instead of simply forcing an older version but for now this will suffice.