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:

  1. Install the required version of Golang.
  2. Install the required version NPM (if required) and runs npm install afterwards.
  3. Install the required version of Hugo.
  4. Build the site for production with hugo
  5. 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.