Building .NET MAUI apps with GitHub Actions

Building .NET MAUI apps with GitHub Actions

ยท

7 min read

Writing a GitHub Actions workflow to build a cross platform .NET MAUI app is more complicated than the average .NET app. First you have to build all the platforms you want to support, taking into consideration the runners they can be built on. Then, you have the fact that .NET MAUI is still in preview to complicate things. In this article I demonstrate a workflow that builds a cross platform .NET MAUI app for Android, iOS, MacCatalyst and Windows on GitHub, explaining each step in the process.

โš  Warning! Out of Date Content Ahead! โš 
Since the GA release of .NET MAUI the build process has changed and the workarounds required below are no longer needed. This article has been superceeded by my new article Build .NET MAUI apps with GitHub Actions.

Preamble

We need a .NET MAUI app for our workflow to build so I will be using my demo app Maui Beach. Because .NET MAUI is in preview we will be using preview versions of .NET and MSBuild so our app must be compatible with the latest preview version of .NET MAUI. Currently this is Preview 11, I explain how to install Preview 11 and update your app in my article Upgrading to .NET MAUI Preview 11.

This is a CI build workflow without any testing, signing or deployment steps and will build our .NET MAUI app for Android, iOS, MacCatalyst and Windows in separate jobs that will run in parallel. Each build job will take 4 - 7 minutes to run and we will be using Windows and MacOS runners which are charged at an increased rate so be aware this workflow can really eat into your Actions minutes.

Set up the Workflow

The first part of our CI build is standard stuff for GitHub Actions - set the action name, what events it responds to and any environment variables.

name: CI Build

on:
  push:
    branches: [ master ]
    paths-ignore:
      - '**/*.md'
      - '**/*.gitignore'
      - '**/*.gitattributes'
  pull_request:
    branches: [ master ]
  workflow_dispatch:
permissions:
  contents: read

env:
  DOTNET_NOLOGO: true                     # Disable the .NET logo
  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable the .NET first time experience
  DOTNET_CLI_TELEMETRY_OPTOUT: true       # Disable sending .NET CLI telemetry

As you can see our workflow will respond to push and pull request events on the master branch and has a manual workflow_dispatch trigger. I've also included a little trick I learned recently, to prevent unnecessary builds I've excluded the push event running when the only changes involve markdown files, .gitignore or .gitattributes.

The environment variables tell the .NET CLI not to show the logo, to skip the first time experience and disable telemetry. This should help improve build times.

Android Build Job

  build-android:
    runs-on: windows-2022
    name: Android Build
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup .NET 6
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 6.0.x
          include-prerelease: true

      - uses: actions/setup-java@v2
        with:
          distribution: 'microsoft'
          java-version: '11'

      - name: Install MAUI Workloads
        run: |
          dotnet workload install android --ignore-failed-sources
          dotnet workload install maui --ignore-failed-sources

      - name: Restore Dependencies
        run: dotnet restore src/MauiBeach/MauiBeach.csproj

      - name: Build MAUI Android
        run: dotnet build src/MauiBeach/MauiBeach.csproj -c Release -f net6.0-android --no-restore

      - name: Upload Android Artifact
        uses: actions/upload-artifact@v2.3.1
        with:
          name: android-ci-build
          path: src/MauiBeach/bin/Release/net6.0-android/*Signed.a*

Building a .NET MAUI app for Android can be done on MacOS or Windows so our build will run on Windows since that uses less minutes. Building .NET 6 apps requires Visual Studio 2022 so we must use a windows-2022 runner.

We start by checking out the code as normal and then install .NET 6. Because MAUI is still in preview we need to install the latest pre-release version of .NET 6.

Building a .NET MAUI app for Android requires JDK 11 so that is our next step. I've picked the Microsoft distribution of JDK 11.

The .NET MAUI workloads are not included in GitHub's windows-2022 runner yet so we need to install them too. To build an Android app we need the Android and MAUI workloads.

Now we can get to the build part of the workflow - restore dependencies and build the app. We can use a standard dotnet build command to build our app for Android. The important option here is -f net6.0-android to set the framework we want to build. I like my CI builds to be release builds so I've set the option -c Release as well as --no-restore so we only restore our dependencies once.

Finally we upload our build artifacts, I've chosen to upload both AAB and APK packages.

Windows Build Job

  build-windows:
    runs-on: windows-2022
    name: Windows Build
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup .NET 6
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 6.0.x
          include-prerelease: true

      - name: Setup MSBuild
        uses: microsoft/setup-msbuild@v1.1
        with:
          vs-prerelease: true

      - name: Install MAUI Workloads
        run: |
          dotnet workload install maui --ignore-failed-sources

      - name: Restore Dependencies
        run: dotnet restore src/MauiBeach/MauiBeach.csproj

      - name: Build MAUI Windows
        run: msbuild src/MauiBeach/MauiBeach.csproj -r -p:Configuration=Release -p:RestorePackages=false -p:TargetFramework=net6.0-windows10.0.19041 /p:GenerateAppxPackageOnBuild=true

      - name: Upload Windows Artifact
        uses: actions/upload-artifact@v2.3.1
        with:
          name: windows-ci-build
          path: src/MauiBeach/bin/Release/net6.0-windows*/**/MauiBeach*.msix

Building a .NET app for Windows requires Windows and .NET 6 requires Visual Studio 2022 so we will use a windows-2022 runner for our Windows build job as well.

Again we start by checking out the code and install the latest pre-release version of .NET 6.

Using dotnet build to build a .NET MAUI app for Windows doesn't work with the current previews of .NET MAUI so for this job we will need to use MSBuild. We need the latest pre-release version of MSBuild so that is our next step.

The .NET MAUI workloads are not included in GitHub's windows-2022 runner yet so we need to install them too. To build a Windows app we only need the MAUI workload.

With our build environment set up we can now restore our dependencies and build our app. I want MSBuild to create a release build and not to restore dependencies itself. The important options in the MSBuild command are the framework -p:TargetFramework=net6.0-windows10.0.19041 and the instruction to create an app package /p:GenerateAppxPackageOnBuild=true.

Finally we upload the app package as a build artifact.

iOS Build Job

  build-ios:
    runs-on: macos-11
    name: iOS Build
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup .NET 6
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 6.0.x
          include-prerelease: true

      - name: Install MAUI Workloads
        run: |
          dotnet workload install ios --ignore-failed-sources
          dotnet workload install maui --ignore-failed-sources

      - name: Restore Dependencies
        run: dotnet restore src/MauiBeach/MauiBeach.csproj

      - name: Build MAUI iOS
        run: dotnet build src/MauiBeach/MauiBeach.csproj -c Release -f net6.0-ios --no-restore

      - name: Upload iOS Artifact
        uses: actions/upload-artifact@v2.3.1
        with:
          name: ios-ci-build
          path: src/MauiBeach/bin/Release/net6.0-ios/**/*.app/

Apple requires that all iOS apps are built on a Mac and .NET 6 requires Visual Studio 2022 so we need to use a macos-11 runner for our iOS build job.

Again we start by checking out the code and install the latest pre-release version of .NET 6.

The .NET MAUI workloads are not included in GitHub's macos-11 runner yet so we need to install them too. To build an iOS app we need the iOS and MAUI workloads.

Now we can restore our dependencies and build our app. Like the Android build job we can use a standard dotnet build command to build our app for iOS. Again I want dotnet build to create a release build and not to restore dependencies. The important option here is -f net6.0-ios to set the framework we want to build.

Finally we upload the *.app folder as a build artifact.

MacCatalyst Build Job

  build-mac:
    runs-on: macos-11
    name: MacCatalyst Build
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup .NET 6
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 6.0.x
          include-prerelease: true

      - name: Install MAUI Workloads
        run: |
          dotnet workload install maccatalyst --ignore-failed-sources
          dotnet workload install maui --ignore-failed-sources

      - name: Restore Dependencies
        run: dotnet restore src/MauiBeach/MauiBeach.csproj

      - name: Build MAUI MacCatalyst
        run: dotnet build src/MauiBeach/MauiBeach.csproj -c Release -f net6.0-maccatalyst --no-restore

      - name: Upload MacCatalyst Artifact
        uses: actions/upload-artifact@v2.3.1
        with:
          name: macos-ci-build
          path: src/MauiBeach/bin/Release/net6.0-maccatalyst/**/*.app/

Our MacCatalyst build job is very similar to our iOS build job. Using a macos-11 runner we check out our code and install the latest pre-release version of .NET 6. To build a MacCatalyst app we need the MacCatalyst and MAUI workloads so they are installed next.

Again we can use a standard dotnet build command to build our app for MacCatalyst. For this job the framework option is -f net6.0-maccatalyst.

The last step is to upload the *.app folder as a build artifact.

And Finally...

The complete workflow can be found in my Maui Beach repo:
๐Ÿ‘ฉโ€๐Ÿ’ป .github/workflows/ci-build.yml

And, you can see the workflow run here. ๐Ÿฅณ๐ŸŽ‰

ย 

ย 

Cover image includes a vector created by brgfx from www.freepik.com.

Did you find this article valuable?

Support Dave Murray by becoming a sponsor. Any amount is appreciated!

ย