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:
- Create new posts on a feature branch (I’m just going to use a branch called
dev
for now) - Push this branch to github
- Review the post in the preview environment
- 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:
- Get the branch name, derive the correct base url
- Update
config.yml
- 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.