CI/CD 集成实战
在 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 }}