CI/CD 与自动化发布

Flutter 自动化流水线:GitHub Actions、Codemagic、Fastlane 配置,代码签名、自动化测试、多渠道分发。

开篇:为什么 CI/CD 对 Flutter 至关重要

移动端应用的分发流程远比 Web 复杂:iOS 需要证书、Provisioning Profile、App Store Connect 审核;Android 需要密钥库、Play Store 签名(bundle signing)。手动执行这些步骤不仅耗时且容易出错,尤其在团队协作中,“我本地能跑但 CI 挂了"是最常见也最头痛的问题。

建立一条完整的 CI/CD 流水线,让每次代码提交自动触发代码检查、测试、构建和分发,是 Flutter 项目走向专业化的必经之路。


一、方案选型

方案托管价格特点
GitHub ActionsGitHub免费额度/按量与代码仓库深度集成,社区生态丰富
Codemagic独立 SaaS免费 500min/月Flutter/Dart 原生优化,M1 Mac 构建
Bitrise独立 SaaS免费额度移动优先,工作流可视化编辑
CircleCI独立 SaaS按量高度可配置,Docker 支持好

一句话总结:大多数团队从 GitHub Actions 起步(零额外成本),对构建速度要求高的团队可升级到 Codemagic 的 M1 构建.


二、GitHub Actions 工作流

# .github/workflows/flutter_ci.yml
name: Flutter CI/CD

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  # 阶段 1: 代码分析与测试
  analyze-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          channel: 'stable'
          cache: true
      
      - name: Get dependencies
        run: flutter pub get
      
      - name: Generate code
        run: dart run build_runner build --delete-conflicting-outputs
      
      - name: Verify formatting
        run: dart format --output=none --set-exit-if-changed .
      
      - name: Analyze project source
        run: flutter analyze --fatal-infos
      
      - name: Run tests with coverage
        run: flutter test --coverage
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: coverage/lcov.info

  # 阶段 2: Android 构建
  build-android:
    needs: analyze-and-test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
      
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          channel: 'stable'
          cache: true
      
      - name: Decode keystore
        run: |
          echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/keystore.jks
      
      - name: Build APK
        run: flutter build apk --release
        env:
          KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
          KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
      
      - name: Build App Bundle
        run: flutter build appbundle --release
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: android-builds
          path: |
            build/app/outputs/flutter-apk/*
            build/app/outputs/bundle/release/*
  
  # 阶段 3: iOS 构建
  build-ios:
    needs: analyze-and-test
    runs-on: macos-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          channel: 'stable'
          cache: true
      
      - name: Install dependencies
        run: flutter pub get
      
      - name: Setup certificates
        env:
          P12_CERTIFICATE_BASE64: ${{ secrets.P12_CERTIFICATE_BASE64 }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
        run: |
          echo "$P12_CERTIFICATE_BASE64" | base64 --decode > certificate.p12
          security create-keychain -p "password" build.keychain
          security import certificate.p12 -t agg -k build.keychain -P "$P12_PASSWORD" -A
      
      - name: Build iOS
        run: flutter build ios --release --no-codesign
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: ios-build
          path: build/ios/iphoneos/*.app

  # 阶段 4: Web 构建
  build-web:
    needs: analyze-and-test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
      
      - run: flutter pub get
      - run: flutter build web --release
      
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}

一句话总结:GitHub Actions 通过 YAML 定义工作流,将代码检查、测试、多平台构建串联为自动化流水线,是 Flutter 项目 CI/CD 的入门首选。


三、Fastlane 自动化分发

# android/fastlane/Fastfile
default_platform(:android)

platform :android do
  desc "Deploy to Play Store Internal"
  lane :internal do
    flutter_command(command: "build appbundle --release")
    upload_to_play_store(
      track: 'internal',
      aab: '../build/app/outputs/bundle/release/app-release.aab',
      json_key_data: ENV['PLAY_STORE_JSON_KEY'],
    )
  end
  
  desc "Deploy to Firebase App Distribution"
  lane :beta do
    flutter_command(command: "build apk --release")
    firebase_app_distribution(
      app: ENV['FIREBASE_APP_ID'],
      groups: "testers",
      apk_path: "../build/app/outputs/flutter-apk/app-release.apk",
      service_credentials_file: "firebase-service-account.json",
    )
  end
end

一句话总结:Fastlane 封装了各大平台的分发 API,将原本需要 Web 控制台操作的多步流程简化为一条命令。


FAQ

Q1: 代码签名证书如何安全存储?

  • GitHub Secrets:适合大多数场景
  • 1Password/HashiCorp Vault:企业级密钥管理
  • 切勿将密钥提交到版本控制

Q2: iOS 构建为什么比 Android 慢?

iOS 编译需要在 macOS 上运行 Xcode,GitHub Actions 的 macOS runner 比 Linux runner 慢且贵。Codemagic 提供专用 M1 构建机,速度提升 2-3 倍。

Q3: Web 构建产物如何部署?

Flutter Web 输出为静态文件,可部署到任何静态托管:Vercel、Netlify、Firebase Hosting、GitHub Pages、AWS S3 + CloudFront。


相关阅读

  • https://plumephp.com/flutter-testing/ — 测试策略与自动化
  • https://plumephp.com/flutter-web-desktop/ — Web 与桌面端构建配置

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「Flutter」更多文章

  1. Widget 体系与布局系统
  2. Flutter 状态管理全解析
  3. Flutter 测试策略与自动化