In my previous post I talked about how to set up CI for your .NET MAUI Windows app in Azure DevOps. This post will focus on how you can do the same with GitHub Actions.
This post is heavily inspired by Gerald Versluis’s video on just this, along with this blog post from Microsoft on DevOps with .NET MAUI, so I highly recommend that you check those out. This is meant to be a written reference specifically for DevOps with .NET MAUI Windows.
Create your workflow
Create a new workflow under the Actions tab in your repository. Select the .NET workflow, which should be suggested to you (search it up if it isn’t suggested) and hit “Configure”.
Out of the box, the template will look like this:
The template now uses .NET 6 by default, so we don’t need to edit that anymore.
Select your VM image
The first thing we want to change is what kind of virtual image this action will run on. The template sets it to ubuntu-latest
, but we’ll want to change this to a Windows image. Let’s change it to windows-latest
:
runs-on: windows-latest
Install the MAUI workload
Next we’ll remove the dotnet restore
and dotnet test
commands. In place of the dotnet restore
command we’ll add a run
where we will install the .NET MAUI workload:
- name: Install MAUI workload
run: dotnet workload install maui
Build it
Finally we’ll modify the existing dotnet build
command to build the .NET MAUI app for Windows. We’ll have to make sure that the target framework matches the one that’s set in your csproj file:
- name: Build
run: dotnet build -c Release -f:net6.0-windows10.0.19041.0
Sign it
If you want to publish the resulting MSIX file to Windows Store, you can use this without having to sign your package. However, if you plan on sideloading your app, you’ll need to sign it. You can do this with a self-signed certificate (mostly used for testing) or a certificate issued by a trusted source. For instructions on how to create a self-signed certificate, follow this guide.
Tell me your secrets
Once you have your certificate, you’ll first have to create some Actions secrets. You can create these from the Settings tab in your repository and selecting Secrets -> Actions from the menu on the left. The first two we’ll create secrets for are the certificate password and the certificate thumbprint. You should be able to retrieve these after having created your certificate. We’ll name these WINDOWS_PFX_PASSWORD and WINDOWS_PFX_THUMBPRINT, respectively.
The last secret we need to add is the base64-encoded certificate. To encode our certificate, we can use the Windows utility certutil
to encode our certificate.
In the terminal on a local Windows computer, navigate to the folder where your certificate resides and use the following command:
certutil -encode .\mycert.pfx mycert.pfx.asc
To print out the newly encoded value, use this command:
cat .\mycert.pfx.asc
Copy the printed out content and create a new Actions secret. Paste the content in here and name the secret WINDOWS_PFX_FILE.
Decode PFX file
Since we’ve only uploaded the encoded PFX file, we’ll have to decode it during our action workflow in order to use it for signing. In our workflow, above the dotnet build
step, add this code:
- name: Decrypt PFX File
run: |
echo "${{ secrets.WINDOWS_PFX_FILE }}" > cert.pfx.asc
certutil -decode cert.pfx.asc cert.pfx
This will decode the content into a cert.pfx
that we can use further on in our workflow.
Install certificate
Next we’ll have to install the certificate to the Windows agent. We have to do this in a specific way – please refer to Gerald’s video for a full explanation on this.
- name: Add Cert to Store
run: certutil -user -q -p ${{ secrets.WINDOWS_PFX_PASSWORD }} -importpfx cert.pfx NoRoot
Build it (take 2)
Now we’ll have to edit the dotnet build
step to actually sign the package with the installed certificate. Edit the step to now look like this:
- name: Build
run: dotnet publish -c Release -f:net6.0-windows10.0.19041.0 /p:GenerateAppxPackageOnBuild=true /p:AppxPackageSigningEnabled=true /p:PackageCertificateThumbprint="${{ secrets.WINDOWS_PFX_THUMBPRINT }}"
The first thing to note is that we switched from build
to publish
. This is so that the step actually produces an MSIX file. Some MSBuild arguments are also passed here to make sure an Appx package is created and signed with the specified certificate thumbprint. The build agent will look for an installed certificate with the specified thumbprint and will use the certificate that way.
Done!
Your workflow should now have created and signed a distributable file for your .NET MAUI Windows application. To use the generated file in a further step, you can use the Upload Artifact action (actions/upload-artifact@v3.1.0).
Here’s a look at the final YAML file: