Skip to content

CI/CD 集成实战

源:GitHub Actions | GitLab CI

在 CI/CD 环境中高效运行 Gradle 构建,实现自动化测试、构建和发布。

CI/CD 基础概念

什么是 CI/CD

Continuous Integration (CI)

  • 自动构建
  • 自动测试
  • 快速反馈

Continuous Deployment (CD)

  • 自动发布
  • 版本管理
  • 部署流程

GitHub Actions

基本构建

.github/workflows/build.yml

yaml
name: Build  on:  push:  branches: [ main, develop ]  pull_request:  branches: [ main ]  jobs:  build:  runs-on: ubuntu-latest    steps:  - uses: actions/checkout@v3    - name: Setup JDK  uses: actions/setup-java@v3  with:  distribution: 'temurin'  java-version: '17'    - name: Setup Gradle  uses: gradle/gradle-build-action@v2    - name: Build  run: ./gradlew assembleDebug

优化构建速度

启用缓存

yaml
- name: Setup Gradle  uses: gradle/gradle-build-action@v2  with:  cache-read-only: ${{ github.ref != 'refs/heads/main' }}

配置构建缓存

yaml
- name: Build with Cache  run: ./gradlew build --build-cache --configuration-cache  env:  GRADLE_OPTS: -Dorg.gradle.caching=true

运行测试

yaml
- name: Run Unit Tests  run: ./gradlew test   - name: Upload Test Reports  if: always()  uses: actions/upload-artifact@v3  with:  name: test-reports  path: '**/build/reports/tests/**'

构建多个变体

yaml
strategy:  matrix:  variant: [debug, release]  steps:  - name: Build ${{ matrix.variant }}  run: ./gradlew assemble${{ matrix.variant }}

GitLab CI

基本配置

.gitlab-ci.yml

yaml
image: openjdk:17-jdk  variables:  GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.caching=true"  before_script:  - chmod +x gradlew  stages:  - build  - test  - deploy  build:  stage: build  script:  - ./gradlew assembleDebug  artifacts:  paths:  - app/build/outputs/apk/  expire_in: 1 week  test:  stage: test  script:  - ./gradlew test  artifacts:  reports:  junit: '**/build/test-results/test/TEST-*.xml'

缓存配置

yaml
cache:  key: ${CI_COMMIT_REF_SLUG}  paths:  - .gradle/caches  - .gradle/wrapper  - build

构建优化策略

Gradle Daemon

禁用 Daemon(CI 推荐):

yaml
env:  GRADLE_OPTS: -Dorg.gradle.daemon=false

并行构建

properties
# gradle.properties org.gradle.parallel=true org.gradle.workers.max=4

内存配置

yaml
env:  GRADLE_OPTS: -Xmx4g -XX:+UseParallelGC

构建缓存

本地缓存

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

远程缓存

kotlin
// settings.gradle.kts buildCache {  remote<HttpBuildCache> {  url = uri("https://cache.example.com/cache/")  isPush = System.getenv("CI") == "true"    credentials {  username = System.getenv("CACHE_USER")  password = System.getenv("CACHE_PASS")  }  } }

自动化测试

单元测试

yaml
- name: Run Unit Tests  run: ./gradlew test   - name: Publish Test Report  uses: mikepenz/action-junit-report@v3  if: always()  with:  report_paths: '**/build/test-results/test/TEST-*.xml'

UI 测试

yaml
- name: Run Instrumented Tests  uses: reactivecircus/android-emulator-runner@v2  with:  api-level: 33  script: ./gradlew connectedCheck

代码覆盖率

yaml
- name: Generate Coverage Report  run: ./gradlew jacocoTestReport   - name: Upload to Codecov  uses: codecov/codecov-action@v3  with:  files: '**/build/reports/jacoco/test/jacocoTestReport.xml'

自动发布

版本管理

使用 Git Tag

yaml
on:  push:  tags:  - 'v*'  jobs:  release:  runs-on: ubuntu-latest  steps:  - name: Get Version  id: version  run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT    - name: Build Release  run: ./gradlew assembleRelease -Pversion=${{ steps.version.outputs.VERSION }}

发布到 GitHub Releases

yaml
- name: Create Release  uses: softprops/action-gh-release@v1  with:  files: |  app/build/outputs/apk/release/*.apk  app/build/outputs/bundle/release/*.aab  env:  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

发布到 Maven

yaml
- name: Publish to Maven Central  run: ./gradlew publishReleasePublicationToOSSRHRepository  env:  OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}  OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}  SIGNING_KEY: ${{ secrets.SIGNING_KEY }}  SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}

实战案例

案例1:完整的 CI/CD 流程

yaml
name: CI/CD  on:  push:  branches: [ main, develop ]  tags:  - 'v*'  pull_request:  branches: [ main ]  jobs:  build:  runs-on: ubuntu-latest  steps:  - uses: actions/checkout@v3    - name: Setup JDK  uses: actions/setup-java@v3  with:  distribution: 'temurin'  java-version: '17'    - name: Setup Gradle  uses: gradle/gradle-build-action@v2    - name: Run Tests  run: ./gradlew test    - name: Build Debug APK  run: ./gradlew assembleDebug    - name: Upload APK  uses: actions/upload-artifact@v3  with:  name: app-debug  path: app/build/outputs/apk/debug/*.apk    test:  runs-on: ubuntu-latest  steps:  - uses: actions/checkout@v3    - name: Setup JDK  uses: actions/setup-java@v3  with:  distribution: 'temurin'  java-version: '17'    - name: Run UI Tests  uses: reactivecircus/android-emulator-runner@v2  with:  api-level: 33  script: ./gradlew connectedCheck    release:  if: startsWith(github.ref, 'refs/tags/v')  needs: [build, test]  runs-on: ubuntu-latest  steps:  - uses: actions/checkout@v3    - name: Setup JDK  uses: actions/setup-java@v3  with:  distribution: 'temurin'  java-version: '17'    - name: Build Release  run: ./gradlew assembleRelease bundleRelease    - name: Sign APK  uses: r0adkll/sign-android-release@v1  with:  releaseDirectory: app/build/outputs/apk/release  signingKeyBase64: ${{ secrets.SIGNING_KEY }}  alias: ${{ secrets.ALIAS }}  keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}  keyPassword: ${{ secrets.KEY_PASSWORD }}    - name: Create Release  uses: softprops/action-gh-release@v1  with:  files: |  app/build/outputs/apk/release/*.apk  app/build/outputs/bundle/release/*.aab

案例2:多环境部署

yaml
jobs:  deploy-dev:  if: github.ref == 'refs/heads/develop'  runs-on: ubuntu-latest  steps:  - name: Build Dev  run: ./gradlew assembleDevDebug    - name: Deploy to Firebase  uses: wzieba/Firebase-Distribution-Github-Action@v1  with:  appId: ${{ secrets.FIREBASE_APP_ID_DEV }}  token: ${{ secrets.FIREBASE_TOKEN }}  groups: dev-team  file: app/build/outputs/apk/dev/debug/app-dev-debug.apk    deploy-prod:  if: startsWith(github.ref, 'refs/tags/v')  runs-on: ubuntu-latest  steps:  - name: Build Prod  run: ./gradlew bundleRelease    - name: Deploy to Play Store  uses: r0adkll/upload-google-play@v1  with:  serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}  packageName: com.example.app  releaseFiles: app/build/outputs/bundle/release/app-release.aab  track: production

最佳实践

缓存策略

  • 使用 Gradle Build Action
  • 配置远程构建缓存
  • 仅主分支推送缓存

并行执行

yaml
strategy:  matrix:  task: [assembleDebug, test, lint]

失败快速反馈

yaml
strategy:  fail-fast: true

环境隔离

  • 使用 Secrets 管理凭证
  • 不同环境不同配置
  • 版本号自动化

构建优化

properties
org.gradle.daemon=false org.gradle.parallel=true org.gradle.caching=true org.gradle.configuration-cache=true

监控和通知

yaml
- name: Notify Slack  if: failure()  uses: 8398a7/action-slack@v3  with:  status: ${{ job.status }}  webhook_url: ${{ secrets.SLACK_WEBHOOK }}