Open4

[.NET MAUI] GitHub Actionsで自動ビルド(Mac Catalyst編)

GomitaGomita

GitHub Actionsを利用すると、ソースをリモートリポジトリにpushしたタイミングとかでクラウド上の仮想マシンを使って自動ビルドすることができる。いわゆるCI(継続的インテグレーション)。
Microsoft Azureでもほぼ同じことができるはず。

参考となる記事

これが一番わかりやすくて参考になった。Part ⅡのiOS編、Part ⅢのAndroid編もあり。
https://thewissen.io/making-maui-cd-pipeline/

GomitaGomita

Apple Development証明書の準備

ストアでの公開などは抜きにして、ひとまずビルド〜発行までなら「Apple Development」の証明書があれば良い。
[.NET MAUI] iPhone実機デバッグをやっていれば、VSforMacの [ユーザー設定] > [公開処理中] > [Apple Developer のアカウント] > [詳細の表示...] と辿ると、証明書の一覧に [Apple Development: Created via API] が表示されているはず。これに対応する証明書のファイルをエクスポートする。

手順は、まず Apple Developerにログインし、[アカウント] > [プログラムのリソース] > [証明書] と辿る。すると、証明書の一覧に [Created via API] がいるので、これを選択して [Download] する。ダウンロードした [development.cer] をダブルクリックしてmacOSへインストールするとキーチェーンアクセスの [自分の証明書] に [Apple Development: Created via API (...)]が追加される。これを右クリックして [...を書き出す] して .p12ファイル形式で保管する。その際に設定したパスワードの値も大切に保管すること。

.p12ファイルの証明書はGitHub Actionsでpublishする際に必要となるが、GitHub上にそのままファイルを置くことはできないので、BASE64で文字列化してGItHubのsecretとして隠蔽する。BASE64文字列化はzshなら下記コマンドでクリップボードにコピーできる。

$ base64 -i xxx.p12 | pbcopy

GitHub sercretの追加

ここからはGitHub上での作業となる。.NET MAUIのソースを管理しているリポジトリの [Settings] > [Secrets and variables] > [Actions] と辿り、[New repository secret] で以下2つのsecretを追加する。

  • CERTIFICATES_P12 … .p12ファイルをBASE64文字列化した値
  • CERTIFICATES_P12_PASSWORD … 証明書を書き出した際のパスワード
GomitaGomita

GitHub Actions の作成

ここからはいよいよGitHub Actions の作成に取り掛かる。
リポジトリの [Actions] より[New workflow] > [set up a workflow yourself] と辿り、[CI_mac.yml] という名前でYAMLファイルを作成する。

CI_mac.yml
name: CI_mac

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  workflow_dispatch:

on:はActionsが実行されるタイミングで、mainブランチへのpushあるいはpull request、workflow_dispatch(手動実行)でのいずれかとする。

env:
  DOTNET_VERSION: 8.0.100
  TARGET_FRAMEWORK: net8.0-MacCatalyst
  PROJECT_FILE: xxx.csproj

あとで使用する環境変数を定義しておく。

jobs:
  Build_mac:
    runs-on: macos-13

ジョブにBuild_macと名付けてGitHubのmacOS 13のジョブランナーでジョブを実行する。2024年2月時点では macos-latest としても macOS 13 となるはず。

    steps:
      - name: Show Xcode version
        shell: bash
        run: xcodebuild -version

      - name: List Xcode installations
        run: sudo ls -1 /Applications | grep "Xcode"
  
      - name: Select Xcode 15.1
        run: sudo xcode-select -s /Applications/Xcode_15.1.app/Contents/Developer

ここからはジョブの個々のステップ。run: によりシェルスクリプトを実行する。2024年2月現時点ではmacOS 13ジョブランナーのXcodeのバージョンは15.0.1であるが、念のためローカルの開発環境と合わせるためXcode 15.1に変更する。

      - name: Checkout
        uses: actions/checkout@v4

uses:により既製のアクションactions/checkoutを使用し、ソースコードをリポジトリからcheckoutしてジョブランナー上に配置する。

      - name: Setup .NET SDK ${{ env.DOTNET_VERSION }}
        uses: actions/setup-dotnet@v2
        with:
          dotnet-version: '${{ env.DOTNET_VERSION }}'

      - name: List .NET info
        run: dotnet --info

2024年2月現時点ではmacOS 13ジョブランナーに.NET 8.0.100が入っているため無駄ではあるが actions/setup-dotnet を使って指定バージョンの.NETをセットアップすることができる。

      - name: Install .NET MAUI
        shell: bash
        run: |
          dotnet workload install maui

macOS 13ジョブランナーには.NET MAUIワークロードが入っていないためインストールする。アクションを実行するとわかるが、これに2〜3分かかる。

      - name: Restore packages
        run: |
          dotnet restore ./${{ env.PROJECT_FILE }}

      - name: Build
        run: |
          dotnet build ./${{ env.PROJECT_FILE }} -c Debug -f ${{ env.TARGET_FRAMEWORK }}

      - name: Test
        run: | 
          dotnet test ./${{ env.PROJECT_FILE }} --no-build --verbosity normal

プロジェクトのリストア、デバッグビルド、テストを実行。

      - name: Import certificates
        uses: apple-actions/import-codesign-certs@v2
        with: 
          p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
          p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}

次の公開(publish)時の署名に必要となる証明書をテンポラリなキーチェーンにインポートする。ここで事前準備しておいたsecretが使われる。証明書インポートせずにpublishしようとすると no valid code signing key で怒られる。

      - name: Publish
        run: |
          dotnet publish ./${{ env.PROJECT_FILE }} -c Release -f ${{ env.TARGET_FRAMEWORK }} -p:CreatePackage=false -p:EnableAssemblyILStripping=false

いよいよ発行。-p:CreatePackage=false で.pkg形式のインストーラーは生成せずに.app形式のままとする。-p:EnableAssemblyILStripping=falseはよくわからん。

      - name: Upload
        uses: actions/upload-artifact@v3
        with:
          name: artifact-mac
          path: |
            ./bin/Release/${{ env.TARGET_FRAMEWORK }}/maccatalyst-arm64/*.app

発行した.appファイルを成果物(artifact)としてあとで取り出せるようにジョブランナーからGitHubの公開ストレージにアップロードする。

GomitaGomita

ワークフローの実行

GitHubのWeb上でYAMLを編集して保存、またはローカルで編集してcommit〜pushするとワークフローが実行される。
Webブラウザ上でワークフロー実行状況がリアルタイムで出力されるさまは圧巻だ。無事ワークフロー実行が成功してワークフローのrun(実行結果)ページの artifact-mac のリンクから.appファイルをダウンロードしてローカルで実行したところ、「実行できません」というエラーとなった。

Mac Catalystであれば未署名のアプリも実行可能かと思ったが違ったか?公式サイトの解説をもう一度みてみる。
https://learn.microsoft.com/ja-jp/dotnet/maui/mac-catalyst/deployment/publish-unsigned?view=net-maui-8.0