Azure Static WebApps is a recent service provided by Microsoft to host static files, alongside optional backend Azure Functions. A free tier is available that allows the application to be deployed quickly while integrating it with PRs validations.

These features make this service a good candidate for hosting WebAssembly-based apps, which don’t need any much server-side dynamic content.

There is however one feature missing, currently there is no proper way to handle application upgrades, as hosted files may become out of sync, or just plain removed when a new version of the app is deployed. This can in turn create random application failures for users who are in between versions.

Uno Platform, the Uno.Wasm.Bootstrap and the Azure DevOps Artifacts Multi-Download projects are able to help when releasing the application through Azure DevOps.

This set of scripts is used to deploy nuget.info, the NuGet Package Explorer for WebAssembly.

 

The Uno Application packaging

An Uno Platform application is built around the creation of an hash-based packaging, making it easy to hint that a CDN can cache files provided by the app indefinitely, except for the index.html file.

The index.html file is generally set to be cached for a short period of time. It contains references to the hash-based URLs and once the index.html is updated, all the assets for the newer version of the app get downloaded, ignoring any previously downloaded assets.

The problem here is that if the new version of your application is only published using the latest bits of the application’s files, any browser, CDN or proxy that may have partially cached the application, will likely fail to serve the files for any previous versions of the app.

As for any good caching problem, even if your http headers specify short caching hints, browsers, CDNs and proxies may decide otherwise. This means you cannot count on having a “short window” of time where the app may behave incorrectly when starting up.

Using the Azure DevOps Artifacts multi-download tool

The solution to this problem is to ensure that previous versions of the app’s resources are published alongside the newest bits. In practice, this means that the index.html file is always served to be latest, but the package files are all present to accommodate for clients that are running previous versions of the app.

Using Azure devOps, we can tag the builds that have been previously deployed. When a new version is published, download the artifacts of those previously tagged builds to create a cache friendly Azure Static WebApp deployment.

 

Let’s dive into an Azure DevOps release pipeline :

First, you’ll need to setup for Azure Static WebApp’s tooling:

- bash: |
   # Set this variable as the WebApps Oryx task is hard coded to read it
   echo '##vso[task.setvariable variable=BUILD_SOURCESDIRECTORY]$(System.DefaultWorkingDirectory)'

  displayName: 'Set build directory variable'

 

Then we’ll download all the artifacts from previous bits and place those in the artifacts folder, where the current release files were already downloaded (CI/WebAssembly, in this case):
- powershell: |
   ## Update this to filter builds based on a specific tag
   $buildTag="wasm-ci"
   $downloadLimit="4"
   $appArtifactsPath="$(System.DefaultWorkingDirectory)/CI/WebAssembly"

   ## Downloads previous artifacts for tagged builds
   dotnet tool install --tool-path . dotnet-azdo-artifacts-multidownload
   
   echo "SourceBranch:$(Build.SourceBranch) DefinitionName:$(Build.DefinitionName) TeamProject:$(System.TeamProject) TeamFoundationCollectionUri:$(System.TeamFoundationCollectionUri) BuildId:$(Build.BuildId)"

   # Download previous assets
   ./azdo-artifacts-multidownload --pat=$(System.AccessToken) --source-branch="$(Build.SourceBranch)" --artifact-name="WebAssembly" --definition-name="$(Build.DefinitionName)" --project-name="$(System.TeamProject)" --server-uri="$(System.TeamFoundationCollectionUri)" --current-build="$(Build.BuildId)" --run-limit="$downloadLimit" --tags=$buildTag --output-path=./downloads   

   # Copy the Uno Platform app Package folders contents to the working directory 
   # to allow for previous versions continue functioning properly
   cd ./downloads
   Get-ChildItem -Directory -Recurse | Where-Object { $_.Name -match 'Package_*' } | Copy-Item -Verbose -Force -Recurse -Destination "$appArtifactsPath"

  displayName: 'Download assets for previous tagged builds'

 

Once we’re done, we can run the Azure static webapp deployment:
- task: AzureStaticWebApp@0
  displayName: 'Deploy to Azure Static WebApps'
  inputs:
    app_location: CI/WebAssembly
    api_location: CI/YourAzureFunction
    skip_app_build: true
    azure_static_web_apps_api_token: '$(StaticAppsDeploymentToken_Variable)'

 

And finally, we can tag the newly published build to any subsequent one can find it:
- task: colinsalmcorner.colinsalmcorner-buildtasks.tag-build-task.tagBuildOrRelease@0
  displayName: 'Tag Build'
  inputs:
    tags: 'wasm-ci'

 

The tags can be changed per pipeline to enable for production and staging environments.

Also note that Azure Static WebApps does not document explicitly the publication to specific branches. If you need multiple non-PR environments, you will like need to create multiple Static WebApp instances in Azure.