# 第十八章:持续集成与发布:CI/CD 自动化流水线
> **本章导读**:想象你经营着一家现代化的汽车工厂。当一辆汽车完成设计图纸后,它不会停留在纸上——原材料会自动进入流水线,机器人精准地完成焊接、喷涂、组装,最终一辆崭新的汽车驶出工厂大门。软件开发中的 CI/CD(持续集成与持续部署)流水线正是这样的存在:当你将代码推送到仓库的那一刻,一条无形的流水线开始运转,自动完成编译、测试、打包、签名,最终将可用的应用交付到用户手中。本章将带你构建这条从代码到产品的"数字流水线"。
---
## 🚚 18.1 跨平台交付的"最后公里"
当你完成了所有功能开发,测试也全部通过了,真正的挑战才刚刚开始。你手中的代码需要被转化为用户可以安装和运行的应用包,而这个转化过程因平台而异,各有各的复杂规则。
Android 平台要求你生成一个经过签名的 AAB(Android App Bundle)或 APK 文件。签名是 Android 安全模型的基石——只有经过开发者私钥签名的应用才能被安装和更新。iOS 平台的门槛更高,你不仅需要生成 IPA 文件,还需要处理苹果开发者证书、Provisioning Profile 等一系列身份验证材料。Windows 平台推荐使用 MSIX 格式,它提供了沙箱隔离和增量更新等现代特性。而 WebAssembly 相对简单,它只需要一组静态文件,但需要正确配置 Web 服务器的缓存策略和 MIME 类型。
如果依靠开发者手动在本地机器上完成这些打包工作,不仅耗时费力,而且极易出错。更糟糕的是,每次发布都需要重复相同的操作,任何疏忽都可能导致发布失败甚至上线有问题的版本。**CI/CD 的核心目标就是将这个脆弱的手动过程转化为一条可靠的全自动流水线**:开发者只需要推送代码,系统会自动完成编译、测试、签名和发布,整个过程可重复、可追溯、可审计。
> **第一性原理**:为什么我们需要 CI/CD?核心原因在于"消除人为因素的不确定性"。人类不擅长重复执行精确的操作——我们会疲劳、会分心、会遗漏步骤。但机器擅长这些,它们可以完美地执行相同的流程一千次、一万次而不出错。CI/CD 将发布这个"高风险操作"交给了"可靠的机器",让人类专注于创造性的工作。
---
## 🛠️ 18.2 选择你的流水线工具
在构建 CI/CD 流水线之前,你需要选择一个合适的流水线平台。市面上有很多选择,但在 Uno Platform 生态中,两个平台占据了绝对主流地位。
GitHub Actions 是与开源项目集成度最高的选择。如果你的项目托管在 GitHub 上,GitHub Actions 提供了无缝的体验——流水线配置就放在代码仓库的 `.github/workflows` 目录下,与代码一起版本管理。GitHub 为公开仓库提供免费的构建时间,对私有仓库也有每月免费的额度。它提供了 Windows、Linux 和 macOS 三种运行环境,能够满足 Uno Platform 跨平台构建的所有需求。
Azure DevOps 是企业用户的首选。它提供了更加丰富的项目管理功能,包括看板、代码仓库、测试计划、制品管理等一站式服务。对于需要复杂证书管理和审批流程的大型组织,Azure DevOps 的安全性和可管理性更胜一筹。它与 Azure 云服务的深度集成也是一个重要优势。
> **技术术语**:**CI**(Continuous Integration,持续集成)指的是频繁地将代码变更合并到主分支,并通过自动化构建和测试来验证每次变更。**CD** 可以指 Continuous Delivery(持续交付)或 Continuous Deployment(持续部署),前者意味着代码随时可以部署到生产环境(但需要手动触发),后者则意味着代码变更会自动部署到生产环境。在实践中,人们常用 CI/CD 来统称这套自动化流程。
---
## 🌐 18.3 WASM 自动化部署
WebAssembly 是所有平台中最容易自动化部署的一个。它的构建产物只是一组静态文件——HTML、JavaScript、WebAssembly 二进制文件和资源文件。这些文件可以部署到任何能够提供静态文件服务的平台,包括 GitHub Pages、Azure Static Web Apps、Netlify、Vercel 等。
### ⚙️ 18.3.1 构建完整的 GitHub Actions 工作流
让我们构建一个完整的 CI/CD 工作流,它会在每次推送到主分支时自动构建并部署 WASM 版本。
```yaml
# .github/workflows/deploy-wasm.yml
# 这个文件定义了 WebAssembly 版本的自动化构建和部署流程
name: 构建并部署 WebAssembly 版本
# 触发条件:推送到 main 分支,或者创建 Pull Request
on:
push:
branches: [ main ]
paths:
# 只有当以下路径的文件发生变化时才触发
- 'src/**'
- '.github/workflows/deploy-wasm.yml'
pull_request:
branches: [ main ]
# 允许手动触发(用于测试或重新部署)
workflow_dispatch:
# 设置环境变量,在所有步骤中可用
env:
DOTNET_VERSION: '8.0.x'
PROJECT_PATH: 'src/MyUnoApp.Wasm/MyUnoApp.Wasm.csproj'
PUBLISH_OUTPUT: './publish'
jobs:
build-and-deploy:
name: 构建并部署
runs-on: windows-latest
# 部署只在 main 分支的 push 时执行(不在 PR 时执行)
# 这样可以避免 PR 覆盖生产环境
steps:
# ==================== 步骤 1:检出代码 ====================
- name: 检出代码仓库
uses: actions/checkout@v4
with:
# fetch-depth: 0 获取完整的 git 历史
# 这对于某些需要版本信息的构建工具是必需的
fetch-depth: 0
# ==================== 步骤 2:设置 .NET SDK ====================
- name: 设置 .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
# 缓存 NuGet 包以加速后续构建
cache: true
cache-dependency-path: |
**/packages.lock.json
**/*.csproj
# ==================== 步骤 3:恢复 NuGet 依赖 ====================
- name: 恢复 NuGet 包
run: dotnet restore ${{ env.PROJECT_PATH }}
# ==================== 步骤 4:构建项目 ====================
- name: 构建项目
run: dotnet build ${{ env.PROJECT_PATH }} --configuration Release --no-restore
# ==================== 步骤 5:发布 WASM 版本 ====================
- name: 发布 WebAssembly 版本
run: |
dotnet publish ${{ env.PROJECT_PATH }} `
--configuration Release `
--no-build `
--output ${{ env.PUBLISH_OUTPUT }} `
-p:WasmShellILLinkerEnabled=true `
-p:PublishTrimmed=true `
-p:TrimMode=partial
# 上述参数说明:
# - WasmShellILLinkerEnabled=true: 启用 IL 裁剪
# - PublishTrimmed=true: 裁剪未使用的程序集
# - TrimMode=partial: 部分裁剪(避免过度裁剪导致反射失败)
# ==================== 步骤 6:(可选)运行测试 ====================
- name: 运行单元测试
run: dotnet test --configuration Release --no-build --verbosity normal
# ==================== 步骤 7:部署到 GitHub Pages ====================
# 只在 main 分支的 push 时执行部署
- name: 部署到 GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
# 使用内置的 GITHUB_TOKEN 进行身份验证
github_token: ${{ secrets.GITHUB_TOKEN }}
# 发布目录:dotnet publish 输出的 wwwroot 子目录
publish_dir: ${{ env.PUBLISH_OUTPUT }}/wwwroot
# 自定义域名(如果有)
# cname: myapp.example.com
# 清理旧文件,确保不会残留过期的文件
clean: true
# 添加 .nojekyll 文件,防止 Jekyll 处理
enable_jekyll: false
# ==================== 步骤 8:上传构建产物 ====================
# 即使部署失败,也可以下载构建产物进行调试
- name: 上传构建产物
if: always() # 即使前面步骤失败也执行
uses: actions/upload-artifact@v4
with:
name: wasm-build-output
path: ${{ env.PUBLISH_OUTPUT }}/wwwroot
retention-days: 7 # 保留 7 天
```
这个工作流展示了 CI/CD 的典型结构:首先检出代码,然后设置构建环境,接着恢复依赖、构建项目、发布输出,最后部署到目标平台。每个步骤都是独立的,可以单独调试和验证。这种模块化的设计让流水线易于理解和维护。
> **费曼技巧提问**:为什么 WASM 的部署比 Android 和 iOS 简单得多?关键在于"沙箱"的概念。WebAssembly 运行在浏览器的安全沙箱中,它不需要操作系统的显式批准,也不需要数字签名来证明身份。用户只需要打开一个 URL,浏览器就会自动加载和运行 WASM 应用。这种便利性的代价是 WASM 应用无法直接访问设备的底层功能——所有的硬件访问都必须通过浏览器提供的 JavaScript API。
---
## 🤖 18.4 Android 签名与发布
Android 应用的发布门槛虽然不如 iOS 那么高,但签名机制同样需要谨慎处理。每个 Android 应用都必须经过开发者的私钥签名,这个签名是应用身份的唯一证明——只有使用相同签名的更新才能覆盖安装旧版本的应用。
在 CI 环境中处理签名的核心挑战是:如何安全地存储和使用签名密钥。你不能把密钥文件直接放在代码仓库里,因为任何有权访问仓库的人都能获取你的签名密钥,冒充你发布恶意应用。正确的做法是将密钥存储在 CI 平台的密钥管理系统中,只在构建时动态注入。
```yaml
# .github/workflows/build-android.yml
# Android 版本的构建和签名工作流
name: 构建 Android 版本
on:
push:
branches: [ main ]
tags:
- 'v*' # 当推送版本标签时触发(如 v1.0.0)
env:
DOTNET_VERSION: '8.0.x'
PROJECT_PATH: 'src/MyUnoApp.Mobile/MyUnoApp.Mobile.csproj'
jobs:
build-android:
name: 构建 Android AAB
runs-on: windows-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 设置 .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
# ==================== 签名密钥处理 ====================
- name: 解码并还原签名密钥
env:
# 从 GitHub Secrets 读取 Base64 编码的密钥库文件
# 这些敏感信息不会出现在代码或日志中
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: |
# 将 Base64 字符串解码为二进制文件
# 这个文件只存在于本次构建的临时环境中
$bytes = [Convert]::FromBase64String($env:ANDROID_KEYSTORE_BASE64)
[IO.File]::WriteAllBytes("release.keystore", $bytes)
# ==================== 获取版本信息 ====================
- name: 从 Git 标签获取版本号
if: startsWith(github.ref, 'refs/tags/')
id: version
run: |
# 从标签引用中提取版本号
# refs/tags/v1.2.3 -> 1.2.3
$version = "${{ github.ref }}" -replace 'refs/tags/v', ''
echo "VERSION_NAME=$version" >> $env:GITHUB_OUTPUT
# 计算版本代码(用于 Play Store 的递增数字)
# 将 1.2.3 转换为 10203
$parts = $version.Split('.')
$versionCode = [int]$parts[0] * 10000 + [int]$parts[1] * 100 + [int]$parts[2]
echo "VERSION_CODE=$versionCode" >> $env:GITHUB_OUTPUT
# ==================== 构建 AAB ====================
- name: 构建 Android App Bundle
env:
# 从 Secrets 读取签名配置
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
# 构建发布版本的 AAB
dotnet publish ${{ env.PROJECT_PATH }} `
--configuration Release `
--framework net8.0-android `
-p:AndroidPackageFormat=aab `
-p:AndroidCreatePackagePerAbi=false `
-p:AndroidKeyStore=true `
-p:AndroidSigningKeyStore=release.keystore `
-p:AndroidSigningKeyPass="env:ANDROID_KEY_PASSWORD" `
-p:AndroidSigningKeyAlias="${{ secrets.ANDROID_KEY_ALIAS }}" `
-p:AndroidSigningStorePass="env:ANDROID_KEYSTORE_PASSWORD" `
-p:ApplicationVersion=${{ steps.version.outputs.VERSION_CODE }} `
-p:ApplicationDisplayVersion=${{ steps.version.outputs.VERSION_NAME }} `
--output ./output
# ==================== 上传构建产物 ====================
- name: 上传 AAB 文件
uses: actions/upload-artifact@v4
with:
name: android-aab
path: ./output/*.aab
retention-days: 30
# ==================== 部署到 Google Play ====================
# 只在发布标签时执行
- name: 部署到 Google Play 内部测试轨道
if: startsWith(github.ref, 'refs/tags/')
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.mycompany.myunoapp
releaseFiles: ./output/*.aab
track: internal # 部署到内部测试轨道
status: completed
```
> **第一性原理**:为什么 Android 需要签名机制?答案在于"信任链"。在开放的应用生态中,任何人都可以创建和分发 Android 应用。如果没有签名机制,恶意开发者可以创建一个与你应用同名的仿冒品,诱骗用户安装。签名证明了应用的来源身份,用户(和系统)可以确信应用确实来自你,而不是冒名顶替者。更重要的是,签名保证了更新的连续性——只有相同签名的应用才能覆盖安装,这防止了恶意软件"劫持"已安装应用的更新通道。
---
## 🍎 18.5 iOS 的挑战:证书与云端 Mac
iOS 应用的打包过程是所有平台中最复杂的,这主要源于苹果严格的安全模型。iOS 应用必须在 macOS 环境下构建,而且需要经过苹果开发者证书的签名才能安装到设备上。这些证书与开发者的 Apple ID 绑定,具有有效期限制,需要定期更新。
GitHub Actions 提供了 `macos-latest` 运行器,它是一台云端的 Mac 电脑,可以用来构建 iOS 应用。但证书的处理仍然是一个挑战——你需要将开发证书和 Provisioning Profile 安全地传输到云端 Mac,完成签名后再清理这些敏感文件。
```yaml
# .github/workflows/build-ios.yml
# iOS 版本的构建和签名工作流
name: 构建 iOS 版本
on:
push:
tags:
- 'v*'
env:
DOTNET_VERSION: '8.0.x'
PROJECT_PATH: 'src/MyUnoApp.Mobile/MyUnoApp.Mobile.csproj'
jobs:
build-ios:
name: 构建 iOS IPA
runs-on: macos-latest # macOS 运行器是构建 iOS 应用的必要条件
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 设置 .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
# ==================== 安装 Xcode ====================
- name: 选择 Xcode 版本
run: |
# 列出可用的 Xcode 版本
ls /Applications | grep Xcode
# 选择特定版本的 Xcode(确保与项目配置兼容)
sudo xcode-select -s /Applications/Xcode_15.0.app
# ==================== 证书处理 ====================
- name: 安装 Apple 开发者证书
env:
# 从 Secrets 读取 Base64 编码的 p12 证书文件
IOS_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE_BASE64 }}
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
# iOS 构建所需的钥匙串密码(可以是随机字符串)
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# 创建临时钥匙串
security create-keychain -p "$KEYCHAIN_PASSWORD" temp.keychain
security set-keychain-settings -l -u temp.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" temp.keychain
# 将证书导入钥匙串
echo $IOS_CERTIFICATE_BASE64 | base64 --decode > certificate.p12
security import certificate.p12 \
-k temp.keychain \
-P "$IOS_CERTIFICATE_PASSWORD" \
-T /usr/bin/codesign
# 设置钥匙串为默认
security list-keychains -s temp.keychain
security default-keychain -s temp.keychain
# ==================== 安装 Provisioning Profile ====================
- name: 安装 Provisioning Profile
env:
IOS_PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROVISION_PROFILE_BASE64 }}
run: |
# 创建 Provisioning Profile 目录
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
# 解码并安装 Profile
echo $IOS_PROVISION_PROFILE_BASE64 | base64 --decode > profile.mobileprovision
cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
# ==================== 构建 iOS 应用 ====================
- name: 构建 iOS IPA
run: |
# 恢复依赖
dotnet restore ${{ env.PROJECT_PATH }}
# 构建 iOS 版本
# -p:RuntimeIdentifier=ios-arm64 指定目标为 64 位 iOS 设备
# -p:ArchiveOnBuild=true 创建可用于分发的归档
dotnet publish ${{ env.PROJECT_PATH }} `
--configuration Release `
--framework net8.0-ios `
-p:RuntimeIdentifier=ios-arm64 `
-p:ArchiveOnBuild=true `
-p:CodesignEntitlements=Entitlements.plist `
-p:CodesignKey="Apple Development" `
-p:ApplicationDisplayVersion=${{ steps.version.outputs.VERSION_NAME }} `
--output ./output
# ==================== 导出 IPA ====================
- name: 导出 IPA 文件
run: |
# 使用 xcodebuild 导出 IPA
# 这一步需要一个 ExportOptions.plist 文件来配置导出选项
xcodebuild -exportArchive \
-archivePath ./output/MyUnoApp.Mobile.xcarchive \
-exportOptionsPlist ExportOptions.plist \
-exportPath ./ipa-output
# ==================== 上传构建产物 ====================
- name: 上传 IPA 文件
uses: actions/upload-artifact@v4
with:
name: ios-ipa
path: ./ipa-output/*.ipa
retention-days: 30
# ==================== 清理敏感信息 ====================
- name: 清理钥匙串和证书
if: always() # 确保即使构建失败也执行清理
run: |
security delete-keychain temp.keychain || true
rm -f certificate.p12 profile.mobileprovision
```
对于不希望折腾复杂脚本的开发者,**Microsoft App Center** 提供了一个更简单的选择。它是一个托管的 CI/CD 服务,专门针对移动应用优化。你只需要连接代码仓库、上传证书、选择构建配置,App Center 就会自动处理剩余的所有工作。它甚至可以直接将构建好的应用分发给测试用户。
> **费曼技巧提问**:为什么 iOS 的构建流程比其他平台复杂得多?这源于苹果"围墙花园"的设计哲学。苹果认为,通过严格控制应用的来源和签名,可以最大程度地保护用户安全。代价是开发者需要处理更多的配置工作。相比之下,Android 虽然也有签名机制,但对开发者更加开放——你可以使用自签名证书,不需要经过任何机构的审批。
---
## 🪟 18.6 Windows Store 与 MSIX
对于 Windows 平台,Uno Platform 生成的是标准的 WinUI 3 应用,可以使用 MSIX 格式进行打包和分发。MSIX 是 Windows 10/11 推荐的现代安装格式,它提供了沙箱隔离、增量更新、强制签名等安全特性。
```yaml
# .github/workflows/build-windows.yml
# Windows MSIX 的构建工作流
name: 构建 Windows MSIX
on:
push:
tags:
- 'v*'
env:
DOTNET_VERSION: '8.0.x'
PROJECT_PATH: 'src/MyUnoApp.Windows/MyUnoApp.Windows.csproj'
jobs:
build-windows:
name: 构建 MSIX 安装包
runs-on: windows-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 设置 .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
# ==================== 证书处理 ====================
- name: 解码并安装代码签名证书
env:
WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
run: |
# 解码证书文件
$bytes = [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_BASE64)
[IO.File]::WriteAllBytes("certificate.pfx", $bytes)
# 将证书导入到用户证书存储
$password = ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText
Import-PfxCertificate -FilePath certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password $password
# ==================== 构建 MSIX ====================
- name: 构建 Windows MSIX
run: |
dotnet publish ${{ env.PROJECT_PATH }} `
--configuration Release `
--framework net8.0-windows10.0.19041.0 `
-p:AppxPackageSigningEnabled=true `
-p:PackageCertificateThumbprint="${{ secrets.WINDOWS_CERT_THUMBPRINT }}" `
-p:Platform=x64 `
-p:GenerateAppxPackage=true `
--output ./output
# ==================== 上传 MSIX ====================
- name: 上传 MSIX 安装包
uses: actions/upload-artifact@v4
with:
name: windows-msix
path: ./output/*.msix
retention-days: 30
# ==================== 提交到 Microsoft Store ====================
# 需要配置 Microsoft Store 提交 API
- name: 提交到 Microsoft Store
if: startsWith(github.ref, 'refs/tags/')
run: |
# 使用 Store Broker 或 Microsoft Store 提交 API
# 这里需要额外的配置和脚本
echo "提交到 Microsoft Store..."
```
---
## 🔢 18.7 版本自动化
版本号管理是发布流程中容易被忽视但非常重要的环节。手动在项目文件中修改版本号不仅繁琐,而且容易出错——你可能会忘记更新某个平台配置文件,导致发布的应用版本号不一致。
推荐的做法是使用 Git 标签来驱动版本号。当你创建一个 `v1.2.3` 格式的 Git 标签时,CI 流水线会自动提取版本号并注入到所有平台的配置中。
```yaml
# 从 Git 标签计算版本号的通用步骤
- name: 计算版本信息
id: version
run: |
# 获取最近的 Git 标签
$tag = git describe --tags --abbrev=0 2>$null
if ($tag) {
# 从标签中提取版本号 (v1.2.3 -> 1.2.3)
$version = $tag -replace '^v', ''
# 解析版本组件
$parts = $version.Split('.')
$major = [int]$parts[0]
$minor = [int]$parts[1]
$patch = [int]$parts[2]
# 计算数字版本代码 (1.2.3 -> 10203)
$versionCode = $major * 10000 + $minor * 100 + $patch
echo "VERSION_NAME=$version" >> $env:GITHUB_OUTPUT
echo "VERSION_CODE=$versionCode" >> $env:GITHUB_OUTPUT
} else {
# 没有标签时使用默认值
echo "VERSION_NAME=0.0.1-dev" >> $env:GITHUB_OUTPUT
echo "VERSION_CODE=1" >> $env:GITHUB_OUTPUT
}
```
> **技术术语**:**语义化版本(Semantic Versioning,简称 SemVer)** 是一种版本号命名规范,格式为 `MAJOR.MINOR.PATCH`。MAJOR 版本号变更表示不兼容的 API 变更;MINOR 版本号变更表示向后兼容的功能新增;PATCH 版本号变更表示向后兼容的问题修复。这种规范让用户能够从版本号判断升级的风险和收益。
---
## 📝 本章小结
本章我们构建了一条完整的 CI/CD 自动化流水线,从代码到产品的"最后公里"。通过 GitHub Actions(或 Azure DevOps),我们实现了 WebAssembly 的自动部署、Android 的签名发布、iOS 的云端 Mac 构建、以及 Windows 的 MSIX 打包。这些自动化流程的建立,标志着你的应用从一个"个人作品"正式迈向了一个"商业产品"。
CI/CD 的真正价值不仅在于节省时间,更在于它为发布过程带来了确定性和可重复性。当你能够确信"每次发布都会执行相同的流程、产生相同的结果"时,你就有勇气更频繁地发布、更快地迭代。这种信心是敏捷开发的基础,也是持续交付高质量软件的关键。
在下一章中,我们将把前十八章学到的所有知识融会贯通,开始本卷的终极任务——**实战案例:构建一个跨平台云笔记应用**,让你在实践中检验和巩固所学的理论与技术。
---
> **动手实验**:
> 1. 为你的 Uno 项目创建一个 GitHub Actions 工作流,实现 WebAssembly 版本的自动构建和部署到 GitHub Pages。推送代码后,验证应用是否能够通过 URL 访问。
> 2. 为你的 Android 版本配置签名自动化。将签名密钥编码为 Base64,存储在 GitHub Secrets 中,在工作流中解码并用于签名。生成一个签名的 AAB 文件并验证签名信息。
> 3. 实现版本号自动化:创建一个 Git 标签 `v0.1.0`,观察 CI 流水线如何提取版本号并应用到构建产物中。修改标签为 `v0.2.0`,验证版本号更新是否生效。
登录后可参与表态
讨论回复
0 条回复还没有人回复,快来发表你的看法吧!