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 }}