如何为 .NET 7 Maui (Android) 配置 GitLab CI/CD

ibe*_*dev 9 docker gitlab-ci .net-core maui

我有点震惊 Linux 不支持构建 .NET MAUI 应用程序,除非它们是

android                    .NET SDK Workload for building Android applications.
macos                      .NET SDK Workload for building macOS applications.  
maui-android               .NET MAUI SDK for Android                           
maui-tizen                 .NET MAUI SDK for Tizen                             
maui-windows               .NET MAUI SDK for Windows                           
runtimes-windows           Windows Runtime Packs                               
runtimes-windows-net6      Windows Runtime Packs                               
wasm-experimental          .NET WebAssembly experimental tooling               
wasm-tools                 .NET WebAssembly build tools                        
wasm-tools-net6            .NET WebAssembly build tools for net6.0  
Run Code Online (Sandbox Code Playgroud)

无论如何,我想知道 .NET Maui 开发人员如何在 GitLab 中实现 CI/CD 自动化。 是否有像 GitHub 或 Azure Pipelines 中那样的内置机制?还是必须手动配置?我可以看一个例子吗?

这是我到目前为止一直在尝试的,mcr.microsoft.com/dotnet/sdk:7.0不幸的是,考虑到基础映像将无法用于某些平台。

我创建了一个自定义 Docker 映像。我们称之为 dotnet-maui

FROM mcr.microsoft.com/dotnet/sdk:7.0

# Install workload maui
RUN dotnet workload search
RUN dotnet workload install android maui-android --ignore-failed-sources
Run Code Online (Sandbox Code Playgroud)

还有更好的选择吗?也许是使用支持 MAUI 的 MacO 或 Windows 的图像?

现在我使用该映像作为我的 MAUI 应用程序的 GitLab 基础映像。专门针对仅针对 android 的一个,但我仍然无法编译..

dotnet build -f:net7.0-android -c:Release
Run Code Online (Sandbox Code Playgroud)

抛出错误

 dotnet build -f:net7.0-android -c:Release
MSBuild version 17.4.0+18d5aef85 for .NET
  Determining projects to restore...
  Restored /builds/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj (in 16.86 sec).
/usr/share/dotnet/packs/Microsoft.Android.Sdk.Linux/33.0.4/tools/Xamarin.Android.Tooling.targets(70,5): error XA5300: The Android SDK directory could not be found. Check that the Android SDK Manager in Visual Studio shows a valid installation. To use a custom SDK path for a command line build, set the 'AndroidSdkDirectory' MSBuild property to the custom path. [/builds/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj::TargetFramework=net7.0-android]
Build FAILED.
/usr/share/dotnet/packs/Microsoft.Android.Sdk.Linux/33.0.4/tools/Xamarin.Android.Tooling.targets(70,5): error XA5300: The Android SDK directory could not be found. Check that the Android SDK Manager in Visual Studio shows a valid installation. To use a custom SDK path for a command line build, set the 'AndroidSdkDirectory' MSBuild property to the custom path. [/builds/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj::TargetFramework=net7.0-android]
    0 Warning(s)
    1 Error(s)
Time Elapsed 00:00:00.58
Cleaning up project directory and file based variables 00:01
ERROR: Job failed: exit code 1
Run Code Online (Sandbox Code Playgroud)

我应该如何安装和配置这个Android SDK?

PS:错误显示有关 Visual Studio(这是一个图形用户界面 IDE)的详细信息这一事实有点令人担忧。


更新1 (2023-01-02)

我尝试使用 CLI 在 Ubuntu 22.04 中构建,但遇到了问题。我们的目标是在 Docker 镜像中实现所有这些自动化。

首先我安装 maui-android

dotnet workload install maui-android --ignore-failed-sources
Run Code Online (Sandbox Code Playgroud)

然后我安装Android SDK。2个选项:

sudo apt update && sudo apt install -y android-sdk
Run Code Online (Sandbox Code Playgroud)

或者

sudo snap install androidsdk
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,以下尝试构建都会失败

dotnet build -f net7.0-android /p:AndroidSdkDirectory=/usr/lib/android-sdk
Run Code Online (Sandbox Code Playgroud)

出现有关未安装 API 级别 33 的错误

MSBuild version 17.4.0+18d5aef85 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
/usr/share/dotnet/packs/Microsoft.Android.Sdk.Linux/33.0.4/tools/Xamarin.Android.Tooling.targets(100,5): error XA5207: Could not find android.jar for API level 33. This means the Android SDK platform for API level 33 is not installed. Either install it in the Android SDK Manager (Tools > Open Android SDK Manager...), or change the Xamarin.Android project to target an API version that is installed. (/usr/lib/android-sdk/platforms/android-33/android.jar missing.) [/media/diegosasw/data/src/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj::TargetFramework=net7.0-android]
Run Code Online (Sandbox Code Playgroud)

我尝试显式安装 API 33

yes | androidsdk "platform-tools" "platforms;android-33"
Run Code Online (Sandbox Code Playgroud)

这会创建一个~/AndroidSDK/platforms/android-33里面有一个罐子的

dotnet build -f net7.0-android /p:AndroidSdkDirectory=/home/diegosasw/AndroidSDK/platforms/android-33
Run Code Online (Sandbox Code Playgroud)

也失败了

MSBuild version 17.4.0+18d5aef85 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
/usr/share/dotnet/packs/Microsoft.Android.Sdk.Linux/33.0.4/tools/Xamarin.Android.Tooling.targets(70,5): error XA5300: The Android SDK directory could not be found. Check that the Android SDK Manager in Visual Studio shows a valid installation. To use a custom SDK path for a command line build, set the 'AndroidSdkDirectory' MSBuild property to the custom path. [/media/diegosasw/data/src/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj::TargetFramework=net7.0-android]
Run Code Online (Sandbox Code Playgroud)

我希望有一个可以在 Linux Ubuntu 本地和 GitLab CI/CD 中使用的工作示例


更新2 (2023-01-02)

我在本地找到了一些工作,但仍然无法找到 GitLab CI/CD 的良好解决方案(基础镜像太大会导致问题)

我有以下 *.csproj

<Project Sdk="Microsoft.NET.Sdk.Razor">

    <PropertyGroup>
        <TargetFrameworks>net7.0-android</TargetFrameworks>
        <OutputType>Exe</OutputType>
        <RootNamespace>MySample</RootNamespace>
        <UseMaui>true</UseMaui>
        <SingleProject>true</SingleProject>
        <ImplicitUsings>enable</ImplicitUsings>
        <EnableDefaultCssItems>false</EnableDefaultCssItems>

        <!-- Display name -->
        <ApplicationTitle>MySample</ApplicationTitle>

        <!-- App Identifier -->
        <ApplicationId>com.roundev.mysample</ApplicationId>
        <ApplicationIdGuid>840AB3E3-0460-46FE-BDEA-94EEBB053DE2</ApplicationIdGuid>

        <!-- Versions -->
        <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
        <ApplicationVersion>1</ApplicationVersion>

        <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
    </PropertyGroup>

    <PropertyGroup Condition="$(TargetFramework.Contains('-android')) and '$(Configuration)' == 'Release'">
        <AndroidKeyStore>True</AndroidKeyStore>
        <AndroidSigningKeyStore>myapp.keystore</AndroidSigningKeyStore>
        <AndroidSigningKeyAlias>myalias</AndroidSigningKeyAlias>
        <AndroidSigningKeyPass></AndroidSigningKeyPass>
        <AndroidSigningStorePass></AndroidSigningStorePass>
    </PropertyGroup>


    <ItemGroup>
        <!-- App Icon -->
        <MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />

        <!-- Splash Screen -->
        <MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />

        <!-- Images -->
        <MauiImage Include="Resources\Images\*" />
        <MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />

        <!-- Custom Fonts -->
        <MauiFont Include="Resources\Fonts\*" />

        <!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
        <MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
    </ItemGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
    </ItemGroup>

</Project>
Run Code Online (Sandbox Code Playgroud)

在本地主机(Ubuntu 22.04)

在 localhost,我设法通过以下步骤编译和发布签名的 APK/AAB:

安卓软件开发工具包 SDK工具

假设 SDK .NET 7 已安装。

  1. 在 SDK .NET 之上安装工作负载 maui-android
    dotnet workload install maui-android --ignore-failed-sources
    
    Run Code Online (Sandbox Code Playgroud)
  2. 通过安装 Android Studio 以及 Android SDK 构建工具,通过 Jetbrains Toolbox 安装了 Android SDK(Api 级别 33)。一切都在下面/home/diegosasw/Android/Sdk
  3. 创建密钥库
    keytool -genkey -v -keystore myapp.keystore -alias myalias -keyalg RSA -keysize 2048 -validity 36500
    
    Run Code Online (Sandbox Code Playgroud) 并输入密码和详细信息。生成的 myapp.keystore,我将其放置在与*.csproj(理想情况下不应进行版本控制)相同的级别
  4. 编译
    dotnet build -f net7.0-android /p:AndroidSdkDirectory=/home/diegosasw/Android/Sdk
    
    Run Code Online (Sandbox Code Playgroud)
  5. 发布
    dotnet publish -f:net7.0-android -c:Release -o dist /p:AndroidSigningKeyPass=<the_password> /p:AndroidSigningStorePass=<the_password> /p:AndroidSdkDirectory=/home/diegosasw/Android/Sdk
    
    Run Code Online (Sandbox Code Playgroud)dist文件夹将包含 AAB、签名的 AAB 和签名的 APK。

CI/CD 自动化的过程必须类似,但需要自动化 Android SDK 安装以及其他工具,

在 GitLab CI/CD

  1. 首先,我使用以下内容创建一个图像Dockerfile。它非常重,可能可以参数化,并大大改进。

    ARG REPO=mcr.microsoft.com/dotnet/aspnet
    FROM $REPO:7.0.1-jammy-amd64 AS platform
    
    ENV \
        # Unset ASPNETCORE_URLS from aspnet base image
        ASPNETCORE_URLS= \
        # Do not generate certificate
        DOTNET_GENERATE_ASPNET_CERTIFICATE=false \
        # Do not show first run text
        DOTNET_NOLOGO=true \
        # SDK version
        DOTNET_SDK_VERSION=7.0.101 \
        # Enable correct mode for dotnet watch (only mode supported in a container)
        DOTNET_USE_POLLING_FILE_WATCHER=true \
        # Skip extraction of XML docs - generally not useful within an image/container - helps performance
        NUGET_XMLDOC_MODE=skip \
        # PowerShell telemetry for docker image usage
        POWERSHELL_DISTRIBUTION_CHANNEL=PSDocker-DotnetSDK-Ubuntu-22.04
    
    RUN apt-get update \
        && apt-get install -y --no-install-recommends \
            curl \
            git \
            wget \
        && rm -rf /var/lib/apt/lists/*
    
    # Install .NET SDK
    RUN curl -fSL --output dotnet.tar.gz https://dotnetcli.azureedge.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz \
        && dotnet_sha512='cf289ad0e661c38dcda7f415b3078a224e8347528448429d62c0f354ee951f4e7bef9cceaf3db02fb52b5dd7be987b7a4327ca33fb9239b667dc1c41c678095c' \
        && echo "$dotnet_sha512  dotnet.tar.gz" | sha512sum -c - \
        && mkdir -p /usr/share/dotnet \
        && tar -oxzf dotnet.tar.gz -C /usr/share/dotnet ./packs ./sdk ./sdk-manifests ./templates ./LICENSE.txt ./ThirdPartyNotices.txt \
        && rm dotnet.tar.gz \
        # Trigger first run experience by running arbitrary cmd
        && dotnet help
    
    # Install PowerShell global tool
    RUN powershell_version=7.3.0 \
        && curl -fSL --output PowerShell.Linux.x64.$powershell_version.nupkg https://pwshtool.blob.core.windows.net/tool/$powershell_version/PowerShell.Linux.x64.$powershell_version.nupkg \
        && powershell_sha512='c4a72142e2bfae0c2a64a662f1baa27940f1db8a09384c90843163e339581d8d41824145fb9f79c680f9b7906043365e870d48d751ab8809c15a590f47562ae6' \
        && echo "$powershell_sha512  PowerShell.Linux.x64.$powershell_version.nupkg" | sha512sum -c - \
        && mkdir -p /usr/share/powershell \
        && dotnet tool install --add-source / --tool-path /usr/share/powershell --version $powershell_version PowerShell.Linux.x64 \
        && dotnet nuget locals all --clear \
        && rm PowerShell.Linux.x64.$powershell_version.nupkg \
        && ln -s /usr/share/powershell/pwsh /usr/bin/pwsh \
        && chmod 755 /usr/share/powershell/pwsh \
        # To reduce image size, remove the copy nupkg that nuget keeps.
        && find /usr/share/powershell -print | grep -i '.*[.]nupkg$' | xargs rm
    
    # JAVA
    RUN apt-get update && \
        apt-get install -y openjdk-11-jdk && \
        rm -rf /var/lib/apt/lists/*
    
    ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/
    
    # Install workload maui
    RUN dotnet workload install maui-android --ignore-failed-sources
    
    # Utils
    RUN apt-get update && apt-get install -y \
        unzip \
        jq \
        bzip2 \
        libzip4 \
        libzip-dev && \
        rm -rf /var/lib/apt/lists/*
    
    # Install Android SDK
    RUN mkdir -p /usr/lib/android-sdk/cmdline-tools/latest && \
        curl -k "https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip" -o commandlinetools-linux.zip && \
        unzip -q commandlinetools-linux.zip -d /usr/lib/android-sdk/tmp && \
        mv  /usr/lib/android-sdk/tmp/cmdline-tools/* /usr/lib/android-sdk/cmdline-tools/latest && \
        rm -rf /usr/lib/android-sdk/tmp/ && \
        rm commandlinetools-linux.zip 
    
    ENV ANDROID_SDK_ROOT=/usr/lib/android-sdk
    ENV PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$PATH
    
    RUN yes | sdkmanager --licenses && \
        sdkmanager "platform-tools" && \
        sdkmanager "ndk-bundle" && \
        sdkmanager "build-tools;33.0.0" "platforms;android-33"
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在 GitLab 的存储库中,我创建了一个安全文件并上传myapp.keystore(并将其从文件系统中删除)。

  3. 在 GitLab 的存储库中,我KEYSTORE_PASSWORD使用密码值创建一个 CI/CD 环境变量

  4. 这个想法是有一个.gitlab-ci.yml类似的

    image: registry.gitlab.com/your_image_generated_out_of_Dockerfile:latest
    
    variables:
      PUBLISH_OUTPUT_DIR: dist
      ANDROID_SIGNING_KEY_PASS: $KEYSTORE_PASSWORD
      ANDROID_SIGNING_STORE_PASS: $KEYSTORE_PASSWORD
    
    stages:
      - build
      - publish
      - delivery
    
    build:
      stage: build
      script:
        - dotnet build -f net7.0-android /p:AndroidSdkDirectory=/usr/lib/android-sdk 
      artifacts:
        paths:
          - test
        expire_in: 8 hour
      rules:
        - if: '$CI_COMMIT_TAG == null'
    
    publish_android:
      stage: publish
      variables:
        SECURE_FILES_DOWNLOAD_PATH: './'
      before_script:
        - APP_VERSION=$(cat ./version/semver)
        - curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
      script:
        - dotnet publish 
          -f:net7.0-android 
          -c:Release 
          -o $PUBLISH_OUTPUT_DIR 
          /p:AndroidSigningKeyPass=$ANDROID_SIGNING_KEY_PASS 
          /p:AndroidSigningStorePass=$ANDROID_SIGNING_STORE_PASS 
          /p:AndroidSdkDirectory=/usr/lib/android-sdk 
          -p:Version=$APP_VERSION
      allow_failure: false
      artifacts:
        paths: 
          - $PUBLISH_OUTPUT_DIR/
        expire_in: 8 hour
      rules:
        - if: $CI_COMMIT_BRANCH == "main"
          when: on_success
        - if: '$CI_COMMIT_TAG == null'   
    
    Run Code Online (Sandbox Code Playgroud)

    但不幸的是,我什至无法生成用作基础映像的 Docker 映像,因为由于我尝试构建的映像大小巨大,所以空间不足

问题仍然是:

  • 有没有任何简单/好的方法(工作示例)来使用 GitLab CI/CD 构建和发布 AAB/APK 以及针对 Android 的 .NET 7 Maui?
  • 是否有任何官方 Docker 映像可供我在 GitLab CI 上直接用于我的dotnet builddotnet publish.NET 7 Maui 项目(带有 .NET 7 SDK、Android SDK、maui-android 工作负载、JDK 和其他所需工具的映像)?

ibe*_*dev 10

我让它工作了,并在“官方”Microsoft SDK .NET 7 之上构建了一个较小的自定义映像,而无需添加 Android NDK(我认为不需要)。

这是我的泊坞窗图像

FROM mcr.microsoft.com/dotnet/sdk:7.0
MAINTAINER roundev

# JAVA
RUN apt-get update && \
    apt-get install -y openjdk-11-jdk && \
    rm -rf /var/lib/apt/lists/*

ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/

# Install workload maui
RUN dotnet workload install maui-android --ignore-failed-sources

# Utils
RUN apt-get update && apt-get install -y \
    unzip && \
    rm -rf /var/lib/apt/lists/*

# Install Android SDK
RUN mkdir -p /usr/lib/android-sdk/cmdline-tools/latest && \
    curl -k "https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip" -o commandlinetools-linux.zip && \
    unzip -q commandlinetools-linux.zip -d /usr/lib/android-sdk/tmp && \
    mv  /usr/lib/android-sdk/tmp/cmdline-tools/* /usr/lib/android-sdk/cmdline-tools/latest && \
    rm -rf /usr/lib/android-sdk/tmp/ && \
    rm commandlinetools-linux.zip 

ENV ANDROID_SDK_ROOT=/usr/lib/android-sdk
ENV PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$PATH

RUN yes | sdkmanager --licenses && \
    sdkmanager "platform-tools" && \
    #sdkmanager "ndk-bundle" && \ # I Don't think this was needed at all
    sdkmanager "build-tools;33.0.0" "platforms;android-33"
Run Code Online (Sandbox Code Playgroud)

这个生成并存储在 GitLab 容器注册表中,“仅”需要 2.5GB。

所以现在管道已经成功使用这个 .gitlab-ci.yml

image: registry.gitlab.com/roundev/devops/dotnet-maui:latest

variables:
  PUBLISH_OUTPUT_DIR: dist
  ANDROID_SIGNING_KEY_PASS: $KEYSTORE_PASSWORD
  ANDROID_SIGNING_STORE_PASS: $KEYSTORE_PASSWORD

stages:
  - build
  - publish
  - delivery

build:
  stage: build
  script:
    - dotnet build -f net7.0-android /p:AndroidSdkDirectory=/usr/lib/android-sdk 
  rules:
    - if: '$CI_COMMIT_TAG == null'

publish_android:
  stage: publish
  variables:
    SECURE_FILES_DOWNLOAD_PATH: './'
  before_script:
    - curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
  script:
    - dotnet publish 
      -f:net7.0-android 
      -c:Release 
      -o $PUBLISH_OUTPUT_DIR 
      /p:AndroidSigningKeyPass=$ANDROID_SIGNING_KEY_PASS 
      /p:AndroidSigningStorePass=$ANDROID_SIGNING_STORE_PASS 
      /p:AndroidSdkDirectory=/usr/lib/android-sdk
  allow_failure: false
  artifacts:
    paths: 
      - $PUBLISH_OUTPUT_DIR/
    expire_in: 8 hour
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: on_success
    - if: '$CI_COMMIT_TAG == null'
Run Code Online (Sandbox Code Playgroud)

请注意,它使用一个安全文件,我在其中上传我的myapp.keystore CI/CD 变量KEYSTORE_PASSWORD以及签名密钥传递和存储传递(在我的情况下是相同的)。