Sunday, 17 September 2023

Publishing a Blazor WASM app to Github Pages


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": "",
"version": "0.1",
"publicReleaseRefSpec": [

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:

$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

    tags: [0.1.*]

        runs-on: ubuntu-latest

            # Uses GitHub's checkout action to checkout code form the main branch
            - uses: actions/checkout@v2
                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
                  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
                  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:

    tags: [0.1.*]

...with (for example):

      - 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:

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.

Friday, 24 February 2023

LogicMonitor: How to detect what type of Batchscript mode you are in

Ah Chat GPT, how I wish you were correct.  Sadly, this does not work and it seems that there is no way to find out.

Wednesday, 2 November 2022

Craiyon is AMAZING

OK, so Craiyon (art AI) is stunning.  A beautiful creation.

Here are some screenshots that show how amazing this thing is:

Tuesday, 6 September 2022

LogicMonitor in containers

 If your LogicMonitor collector container fails to start with:

/usr/local/logicmonitor/agent/bin/logicmonitor-agent: 32: Syntax error : ")" unexpected

...allocate it more memory (e.g. from 2G to 4G).

You're welcome.  We, however, will not get the last 2 hours back.

Sunday, 31 July 2022

Recovering from a Failure / SMART ERROR RAID 1 situation

The situation

You have an DL380 or similar. Your first disk has failed.  Your second disk is in SMART ERROR.

What next?

The hardware

You will need:
  • a USB adapter for mounting your disk on Windows
  • the failed disks

The software

You will need
  • Windows 10/11
  • Windows Subsystem for Linux installed

The process

  1. Attach the disk to USB via the adapter
    • If the disk is Windows-compatible, you can use freeware disk recovery tools to go from here.  Do not follow the following steps.
    • If the disk is Linux-compatible, proceed to the next step
  2. Start Disk Manager to ensure that you are able to see the disk partition
  3. Start an elevated PowerShell prompt
  4. Type:
    • wmic diskdrive list brief
  5. Note the drive that you want to mount
  6. Type (e.g.)
    •  wsl --mount \\.\PHYSICALDRIVE2
  7. If you receive an error:
    • The disk \\.\PHYSICALDRIVE2 was attached from WSL2, but failed to mount (error code: -22). For more details, run 'dmesg' inside WSL2.
    • ... then you likely have a different file system to EXT4.
  8. Enter WSL with:
    • wsl --system
  9. Look at the dmesg output by typing:
    • dmesg
  10. Look for the following (or similar):
    • [224440.126973] EXT4-fs (sde): VFS: Can't find ext4 filesystem
    • [224440.127932] ERROR: Mount:2528: mount(/dev/sde, /share/PHYSICALDRIVE2, ext4, 0x0, ) failed
  11. Note the disk, in this case "sde"
  12. Make a data directories for each partition:
    • mkdir /data1
    • mkdir /data2
    • mkdir /data3
    • ...
  13. Mount each partition - the command will fail when there are no more partitions to mount:
    • mount -t xfs /dev/sde1 /data1
    • mount -t xfs /dev/sde2 /data2
    • mount -t xfs /dev/sde3 /data3
    • ...
  14. Now copy the data off to your home directory using the rsync command (cp has issues with large files)
    • Install rsync with:
      • yum install rsync
    • Have progress bars on your file copy with (e.g.):
      • rsync --progress /data1/110/disk.0 .
  15. When you are finished, remember to unmount the partitions:
    1. umount /data1
    2. umount /data2
    3. umount /data3
    4. ...
  16. You can now unplug the drive.

Wednesday, 8 June 2022

New York drivers: the most important people in the world

New York drivers are the most important people in the world.  I know this because I have had to isolate with COVID in a hotel room fourteen stories above Hell's Kitchen.

When someone displeases a driver in New York, they will hold their horn down for the whole time that they can still see the vehicle that has offended them.  This is often over a minute.

Fortunately, no-one else in the whole of New York can possibly matter, because this happens at all times of day and night. 

Thanks goodness that drivers have let everyone within four blocks in this densely packed city know that they have witnessed an example of bad driving, whilst being perfect themselves.

I look forward to leaving this nightmarish hellhole and only returning when it has been pedestrianised.

Sunday, 8 May 2022


Another Whole New World (Mars)

While "Earth 2" is out for repairs, I bought a temporary replacement machine on Amazon next day delivery.  It's smaller than Earth2, so it's called "Mars".


Choco is no longer the way to go, so let's try to get everything back in using winget....


winget install -e -h --accept-source-agreements Google.Chrome
winget install -e -h Microsoft.VisualStudio.2022.Professional
winget install -e -h Microsoft.SQLServer.2019.Express
winget install -e -h
winget install -e -h Microsoft.VisualStudioCode
winget install -e -h Ditto.Ditto
winget install -e -h Git.Git
winget install -e -h Mozilla.Firefox
winget install -e -h Github.GithubDesktop
winget install -e -h 7zip.7zip
winget install -e -h VideoLAN.VLC
winget install -e -h Notepad++.Notepad++
winget install -e -h Foxit.FoxitReader
winget install -e -h Zoom.Zoom
winget install -e -h Inkscape.Inkscape
winget install -e -h Docker.DockerDesktop
winget install -e -h ShareX.ShareX
winget install -e -h Audacity.Audacity
winget install -e -h Postman.Postman
winget install -e -h Microsoft.PowerToys
winget install -e -h WhatsApp.WhatsApp
winget install -e -h BlenderFoundation.Blender
winget install -e -h Discord.Discord
winget install -e -h KeePassXCTeam.KeePassXC
winget install -e -h PostgreSQL.pgAdmin
winget install -e -h Microsoft.SQLServerManagementStudio
winget install -e -h JanDeDobbeleer.OhMyPosh
winget install -e -h Microsoft.MouseWithoutBorders
REM SysInternals: Process Explorer etc.
winget install -e -h 9P7KNL5RWT25
REM Windows Terminal
winget install -e -h 9N0DX20HK701
winget install -e -h Microsoft.Teams
winget install -e -h Elgato.StreamDeck
winget install -e -h NathanBeals.WinSSH-Pageant
winget install -e -h Toggl.TogglDesktop
winget install -e -h Mirantis.Lens
winget install -e -h Canonical.Multipass
winget install -e -h 9NB2FLX7X7WG
winget install -e -h WinMerge.WinMerge

Other downloads

These are not covered by winget and should be installed using Google and old-school downloading.

Oh My Posh

Oh my posh provides a better Windows Terminal experience.
Follow installation instructions here:
  • Install CaskaydiaCove Nerd Font
In WT, go to settings and edit the JSON file.  Add the following to the Defaults:

       "defaults": {
            "font": {
                "face": "CaskaydiaCove Nerd Font"

Private key management

Set-Service -Name ssh-agent -StartupType Automatic
ssh-agent bash
ssh-add <keyname>.private.rsa

Docker desktop

To get Docker working:

First, carefully follow the instructions here:

Ensure there is enough memory for ElasticSearch etc.:

wsl -d docker-desktop
sysctl -w vm.max_map_count=262144

docker run --name rabbitmq -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=<username> -e RABBITMQ_DEFAULT_PASS=<password> -d bitnami/rabbitmq:latest
docker run --name postgres -e POSTGRES_PASSWORD=<mysecretpassword> -e PGDATA=/var/lib/postgresql/data/pgdata -v "C:/users/david/Postgres Data:/var/lib/postgresql/data" -d postgres

Visual Studio Extensions

Get ELK working:

Visual Studio Extensions

Add all the good Visual Studio Extensions:

  • Add New File (64-bit)
  • Bootstrap Snippet Pack
  • Case Converter 2022
  • EditorConfig Language Service
  • File Icons
  • Git Diff Margin
  • Inheritance Margin
  • Markdown Editor (64-bit)
  • Match Margin 2022
  • NamespaceFixer
  • Hgrok Extensions
  • Open Command Line
  • Solution Error Visualizer 2022
  • SwitchStartupProject for VS 2022
  • Time Stamp Margin 2022
  • Trailing Whitespace Visualizer
  • Viasfora
  • Visual Studio Spell Checker (VS2022 and Later)
  • Wix Toolset Visual Studio 2022 Extension


  • Visible white space
    • Enable with Ctrl R, Ctrl-W
    • Make less harsh on the eyes by moving the saturation to ~50% of its default value
  • Set Cascadia Code to be the default Font
  • Move the Solution Explorer back to the left hand side

Data transfer

    Data transfer will be fun - there's so much on my old PC, so I have that running at a second desk in case I need something in a hurry.

    HP Console