Skip to content

CI/CD 配置

合理的 CI/CD 配置可以自动化 KMP 项目的构建、测试和发布流程,提升团队效率。

GitHub Actions 配置

基础工作流

yaml
# .github/workflows/ci.yml
name: CI

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

jobs:
  test:
    runs-on: macos-latest  # macOS 支持所有平台构建
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'
      
      - name: Setup Gradle
        uses: gradle/gradle-build-action@v3
      
      - name: Run tests
        run: ./gradlew allTests
      
      - name: Upload test results
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: test-results
          path: '**/build/test-results/**/*.xml'

多平台构建矩阵

yaml
# .github/workflows/build.yml
name: Build

on:
  push:
    tags:
      - 'v*'

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        include:
          - os: ubuntu-latest
            task: assembleRelease
          - os: macos-latest
            task: linkReleaseFrameworkIosArm64
          - os: windows-latest
            task: jvmJar
    
    runs-on: ${{ matrix.os }}
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'
      
      - name: Build
        run: ./gradlew ${{ matrix.task }}
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-${{ matrix.os }}
          path: |
            **/build/outputs/**/*.aar
            **/build/libs/**/*.jar
            **/build/bin/**/*.framework

缓存优化

yaml
- name: Cache Gradle packages
  uses: actions/cache@v4
  with:
    path: |
      ~/.gradle/caches
      ~/.gradle/wrapper
      ~/.konan
    key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
    restore-keys: |
      ${{ runner.os }}-gradle-

GitLab CI 配置

yaml
# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy

variables:
  GRADLE_USER_HOME: "$CI_PROJECT_DIR/.gradle"

cache:
  paths:
    - .gradle/wrapper
    - .gradle/caches

test:
  stage: test
  image: openjdk:17-jdk
  script:
    - ./gradlew test
  artifacts:
    reports:
      junit:
        - '**/build/test-results/test/**/TEST-*.xml'

build-android:
  stage: build
  image: openjdk:17-jdk
  script:
    - ./gradlew assembleRelease
  artifacts:
    paths:
      - app/build/outputs/apk/release/*.apk
  only:
    - tags

build-ios:
  stage: build
  tags:
    - macos
  script:
    - ./gradlew linkReleaseFrameworkIosArm64
  artifacts:
    paths:
      - shared/build/bin/iosArm64/releaseFramework/
  only:
    - tags

自动化测试

单元测试

yaml
test:
  runs-on: macos-latest
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-java@v4
      with:
        java-version: '17'
    
    - name: Run unit tests
      run: |
        ./gradlew :shared:testDebugUnitTest
        ./gradlew :shared:iosX64Test
    
    - name: Publish test report
      uses: mikepenz/action-junit-report@v4
      if: always()
      with:
        report_paths: '**/build/test-results/**/TEST-*.xml'

UI 测试 (Android)

yaml
android-ui-test:
  runs-on: macos-latest
  steps:
    - uses: actions/checkout@v4
    
    - name: Run instrumented tests
      uses: reactivecircus/android-emulator-runner@v2
      with:
        api-level: 29
        script: ./gradlew connectedDebugAndroidTest

版本发布自动化

语义化版本号

yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+'

jobs:
  release:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Extract version
        id: version
        run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
      
      - name: Build artifacts
        run: |
          ./gradlew assembleRelease
          ./gradlew linkReleaseFrameworkIosArm64
      
      - name: Create GitHub Release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            app/build/outputs/apk/release/*.apk
            shared/build/bin/iosArm64/releaseFramework/**
          body: |
            ## Version ${{ steps.version.outputs.VERSION }}
            
            ### Changes
            - See [CHANGELOG.md](CHANGELOG.md)

Maven 发布

yaml
publish:
  runs-on: macos-latest
  steps:
    - uses: actions/checkout@v4
    
    - name: Publish to Maven Central
      run: ./gradlew publishAllPublicationsToMavenCentralRepository
      env:
        ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_USERNAME }}
        ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_PASSWORD }}
        ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }}
        ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}

代码质量检查

Detekt 静态分析

yaml
code-quality:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    
    - name: Run Detekt
      run: ./gradlew detekt
    
    - name: Upload SARIF to GitHub
      uses: github/codeql-action/upload-sarif@v3
      with:
        sarif_file: build/reports/detekt/detekt.sarif

代码覆盖率

yaml
coverage:
  runs-on: macos-latest
  steps:
    - uses: actions/checkout@v4
    
    - name: Generate coverage report
      run: ./gradlew koverXmlReport
    
    - name: Upload to Codecov
      uses: codecov/codecov-action@v4
      with:
        files: ./build/reports/kover/report.xml

环境变量管理

使用 GitHub Secrets

yaml
steps:
  - name: Build with secrets
    run: ./gradlew assembleRelease
    env:
      API_KEY: ${{ secrets.API_KEY }}
      RELEASE_KEYSTORE: ${{ secrets.RELEASE_KEYSTORE }}
      KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}

Gradle 属性注入

kotlin
// build.gradle.kts
android {
    defaultConfig {
        buildConfigField("String", "API_KEY", "\"${System.getenv("API_KEY")}\"")
    }
}

通知集成

Slack 通知

yaml
- name: Notify Slack on failure
  if: failure()
  uses: slackapi/slack-github-action@v1
  with:
    payload: |
      {
        "text": "Build failed for ${{ github.repository }}",
        "blocks": [
          {
            "type": "section",
            "text": {
              "type": "mrkdwn",
              "text": "Build <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|#${{ github.run_number }}> failed"
            }
          }
        ]
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

最佳实践

✅ 实践 1:分离构建和发布

yaml
# 构建在 PR 时触发
on: [pull_request]

# 发布仅在标签推送时触发
on:
  push:
    tags:
      - 'v*'

✅ 实践 2:使用依赖缓存

减少构建时间:

yaml
cache:
  paths:
    - ~/.gradle/caches
    - ~/.gradle/wrapper
    - ~/.konan

✅ 实践 3:并行执行测试

yaml
test:
  strategy:
    matrix:
      test-suite: [unit, integration, ui]
  runs-on: macos-latest
  steps:
    - run: ./gradlew test${{ matrix.test-suite }}

✅ 实践 4:失败快速反馈

yaml
jobs:
  test:
    strategy:
      fail-fast: true  # 任何任务失败立即停止

通过自动化 CI/CD 流程,可以确保代码质量,加快交付速度,降低人工错误。