Sunday, 17 September 2023

Publishing a Blazor WASM app to Github Pages

Introduction

When publishing a Blazor WASM app, and providing that there is no server element, all the files are static and (providing you avoid Github's prohibited uses), free.

The post is intended to save you all the pain I've just been through working it all out.  It does use some best practices around versioning and tagging, which you can choose to use or not.

If you choose not to use them, you can skip the next section.

Versions and Tags

The way I choose to version my projects is with NerdBank Gitversioning:
If you're not already using it, you DO want this.  It versions your code based on two things:
  • Your declared base version from a version.json file in your solution root
  • The "Git height" (the number of commits since the version.json files changed)
My version file looks like this:
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "0.1",
"publicReleaseRefSpec": [
"^refs/heads/main",
"^refs/heads/release/v\\d+(?:\\.\\d+)?$"
]
}

For this example, I'm versioning at v0.1.  Just replace the 0.1 in red above with the minor version that you're currently on.

Next, let's look at tagging.  You don't want to publish every time you do a commit.  Some people like to commit to a release branch and use that for their Continuous Integration / Continuous Deployment (CI/CD).  Me, I like to release when I tag.

So here's the Powershell that I use for automatic tagging based on the Git versioning.  Save the following as Tag.Ps1:

#Tag.ps1
$ErrorActionPreference = "Stop"

# This script will tag the current commit and push it to the origin

# Note: If nbgv (the Nerdbank Gitversioning executable) does not work then install it using either of the following
#dotnet tool install -g nbgv
#dotnet tool update -g nbgv

# Ensure to get latest changes on the branch to ensure we're at the correct git height
git pull

if (git status --porcelain) {
Write-Error "Git repo is not clean. Ensure any changes have been committed."
exit 1;
}

$nbgvVersion = nbgv get-version -v Version
$versionParts = $nbgvVersion.Split(".")
$versionString = $versionParts[0] + "." + $versionParts[1] + "." + $versionParts[2];
Write-Host("Tagging as " + $versionString);

git tag -a "$versionString" -m "Tagged version ${versionString}"

Write-Host("Pushing tag...");
git push origin $versionString

---
When this file doesn't run due to it not being signed, research how to do this yourself.  I'm not going to include that here, as this is a security issue and you need to 

Again, use this or not - entirely up to you.

Github action

Github is able to detect certain events and run workflows based on them.  These workflows are stored in your solution's ".github/workflows" folder as .yml files.  You can simply create that folder, create a file and Github will automatically detect and use it.

The following workflow detects a tag push for 0.1.* and runs.  It does everything needed to build your solution and publish it to Github pages:

name: Deploy Blazor WASM to GitHub Pages

on:
  push:
    tags: [0.1.*]

jobs:
    deploy-to-github-pages:
        runs-on: ubuntu-latest
        steps:

            # Uses GitHub's checkout action to checkout code form the main branch
            - uses: actions/checkout@v2
              with:
                fetch-depth: 0 # avoid shallow clone so nbgv can do its work.

            # Sets up .NET Core SDK 7.x
            - name: Setup .NET Core SDK
              uses: actions/setup-dotnet@v1
              with:
                  dotnet-version: 7.x
                  include-prerelease: false

            # Publishes Blazor project to the release-folder
            - name: Publish .NET Core Project
              run: dotnet publish PanoramicData.JsonMagic.Web/PanoramicData.JsonMagic.Web.csproj -c Release -o release --nologo

            # Copy index.html to 404.html to serve the same file when a file is not found
            - name: copy index.html to 404.html
              run: cp release/wwwroot/index.html release/wwwroot/404.html

            # Add .nojekyll file to tell GitHub pages to not treat this as a Jekyll project. (Allow files and folders starting with an underscore)
            - name: Add .nojekyll file
              run: touch release/wwwroot/.nojekyll

            # Deploy to GitHub pages
            - name: Deploy wwwroot to GitHub Pages
              uses: JamesIves/github-pages-deploy-action@releases/v3
              with:
                  ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
                  BASE_BRANCH: main # The branch the action should deploy from.
                  BRANCH: gh-pages # The branch the action should deploy to.
                  FOLDER: release/wwwroot
                  SINGLE_COMMIT: true

Note that James Ives' script takes the output from the main branch and publishes it onto the gh-pages branch.  In other words, one of your branches (which you will have to create) doesn't have your code on it, it has your output on it.

If you want to execute on every push, you can replace:

on:
  push:
    tags: [0.1.*]

...with (for example):

on:
  push:
    branches:
      - production

Github config

Great, so this will work, yes?  Well nearly.  The next thing you have to do is to attach the Github Page to your gh-pages branch.  Just set it up to look like this on Github (under Settings / Pages).  Note that the custom URL is optional.  If you don't have access to DNS, you can simply use the URL provided by Github, which will be in the form:
  • https://panoramicdata.github.io/PanoramicData.JsonMagic


Finally, you need to let the GitHub action write to the gh-pages branch.  

To do this, go to Settings -> Action -> General -> Workflow permissions and choose read and write permissions

Now, when you run Tag.ps1, the tag will trigger the GitHub Action, which will publish to your site!  You can track progress on the GitHub Actions page.

No comments:

Post a Comment