Intro

This blog is built using Hugo and Github actions, and hosted on Azure Static Web Apps. These are all super cool, but until now my focus was on content, not the tech. I’ve been meaning to set up a preview environment and finally I spent a couple of hours to get it working.

Branching Bad

It’s easy when it’s just you working on a code base like a personal blog. Commit on main, preview in prod, fix it up and repeat. But really, there’s no excuse for not working on at least a dev branch, and deploying that to a preview environment for proofreading. Azure Static Web Apps basically gives you this out of the box.

Preview Environments

There are a few different ways of configuring a preview env. By default, a pull request will trigger a deploy to a temp url, but you can also configure branch and named environments. All that mucking about with PRs seems overkill for a single author blog site, so I’m going to go with the branch approach. I’ll create a dev branch, and configure github to deploy that branch to a preview environment.

So my very basic workflow is:

  1. Create new posts on a feature branch (I’m just going to use a branch called dev for now)
  2. Push this branch to github
  3. Review the post in the preview environment
  4. Merge into main and push (or I could do a PR on github to achieve the same thing)

Hugo. Oh Hugo.

Hugo needs to know the site base url, handily via the baseUrl config entry. There’s plenty of conflicting information online on how Hugo should treat the baseUrl. Although the docs are pretty clear that it should be set to the full base url for the site, there’s a lot of discussion on how it should work if left empty or set to '/'. After much mucking about, and for my theme, it most def needs to be set to the actual url. When running locally Hugo will ignore the setting and although it does still need to be set to something valid - "" doesn’t work.

So my build/deploy is going to need to update the config.yml site settings to reflect the correct baseUrl of the temporary preview env. This should be easy, after all Hugo has a commandline switch to do this: -b url. Should.

Onyx. Hmm.

Static web apps creates a workflow yml as part of creating the site using Onyx to build. Here’s my OG workflow. (Yes, my generated site name is Agreeable Desert. Not sure any desert is that agreeable, although plenty of desserts are, but I digress.)

name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened, closed]
    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@v2
        with:
          submodules: true
      - 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_AGREEABLE_DESERT_08FFE2B1E }}
          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 ######

  close_pull_request_job:
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    runs-on: ubuntu-latest
    name: Close Pull Request Job
    steps:
      - name: Close Pull Request
        id: closepullrequest
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AGREEABLE_DESERT_08FFE2B1E }}
          action: "close"

There are additional build commands available such as app_build_command but the doco does say this is for node apps only. I thought I’d give it a go and add app_build_command: hugo -b someurl but the docs don’t lie. This command gets ignored when Onyx detects a Hugo site.

Update the config, old skool style

After much trial and nothing but error, I decided to drop back to old skool editing of the config.yml file:

  1. Get the branch name, derive the correct base url
  2. Update config.yml
  3. Build the site as normal

The format of the url for a branch preview is https://defaulthostname-branch.location.azurestaticapps.net/ For a production build, the url is the custom domain. The default host and location, and prod url can be added a vars to the workflow for us to retrieve. We can then simply sed these into the config.yml at build time.

I set the baseUrl in the checked in config.yml to "https://localhost:1313/" just to give me something known to regex on, and it’s a string that doesn’t break Hugo when running locally.

Get the branch

I’m not using PRs, I simple push my dev branch to github. After test I merge into main and then push that. I know, sketchy but simple. So I can just use the github.ref_name variable to get the branch name.

There’s no if with an else in github actions, so two steps are needed - one for main, and one for any other branch.

- name: Get URL from Branch - Main
    if: github.ref_name == 'main'
    run: echo "BASEURL=${{ vars.PRODUCTION_URL }}" >> $GITHUB_ENV
- name: Get URL from Branch - Other
    if: github.ref_name != 'main'
    run: echo "BASEURL=https://${{ vars.DEFAULT_HOST_NAME }}-${{ github.ref_name }}.${{ vars.LOCATION }}.azurestaticapps.net/" >> $GITHUB_ENV

Edit the Config

Next we can checkout the branch, update the config, and build the site as normal.

- name: Checkout
    uses: actions/checkout@v2
    with:
        submodules: true
- name: Replace baseURL
    run: sed -i 's|http://localhost:1313/|${{ env.BASEURL }}|' config.yml

Complete yml

Here’s my complete workflow. I’ve added my main and dev branches to the on push and pull request triggers. I’ve also added production_branch: "main" to the Build And Deploy step as per the docs.

name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
      - main
      - dev
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
      - main
      - dev
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:
      - name: Get URL from Branch - Main
        if: github.ref_name == 'main'
        run: echo "BASEURL=${{ vars.PRODUCTION_URL }}" >> $GITHUB_ENV
      - name: Get URL from Branch - Other
        if: github.ref_name != 'main'
        run: echo "BASEURL=https://${{ vars.DEFAULT_HOST_NAME }}-${{ github.ref_name }}.${{ vars.LOCATION }}.azurestaticapps.net/" >> $GITHUB_ENV
      - name: Checkout
        uses: actions/checkout@v2
        with:
          submodules: true
      - name: Replace baseURL
        run: sed -i 's|http://localhost:1313/|${{ env.BASEURL }}|' config.yml
      - name: Build And Deploy
        id: builddeploy
        uses: Azure/static-web-apps-deploy@v1
        with:
          production_branch: "main"
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AGREEABLE_DESERT_08FFE2B1E }}
          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 ######

  close_pull_request_job:
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    runs-on: ubuntu-latest
    name: Close Pull Request Job
    steps:
      - name: Close Pull Request
        id: closepullrequest
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AGREEABLE_DESERT_08FFE2B1E }}
          action: "close"

ToDo: FixMe

Still todo - the preview env is public. I should update this to not be. It would also be nice to move to PRs and hook into the close event to delete the preview env, but for now, I just delete it in the portal after I’ve tested it.

And that’s quite enough devopsy stuff for me. I’m going back to writing something easy like DAX.