XuqmGroup-AndroidSDK/Jenkinsfile

211 行
9.4 KiB
Groovy

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

pipeline {
agent any
parameters {
// ── 版本升级策略 ─────────────────────────────────────────────────────
choice(name: 'VERSION_BUMP', choices: ['patch', 'minor', 'major'],
description: '版本升级策略: major(1.0.0→2.0.0), minor(1.0.0→1.1.0), patch(1.0.0→1.0.1)')
// 要构建并发布的模块(勾选即发布)
booleanParam(name: 'MOD_CORE', defaultValue: true, description: '发布 sdk-core')
booleanParam(name: 'MOD_IM', defaultValue: true, description: '发布 sdk-im')
booleanParam(name: 'MOD_PUSH', defaultValue: true, description: '发布 sdk-push')
booleanParam(name: 'MOD_UPDATE', defaultValue: true, description: '发布 sdk-update')
booleanParam(name: 'MOD_WEBVIEW', defaultValue: true, description: '发布 sdk-webview')
booleanParam(name: 'MOD_LICENSE', defaultValue: true, description: '发布 sdk-license')
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: '是否运行自动化测试')
booleanParam(name: 'PUBLISH', defaultValue: true, description: '是否发布到 Nexus')
}
environment {
NEXUS_CREDS = credentials('NEXUS_CREDS')
}
stages {
stage('Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'main']],
extensions: [[$class: 'CleanBeforeCheckout']],
userRemoteConfigs: scm.userRemoteConfigs
])
script {
// 根据复选框收集要发布的模块
def moduleChecks = [
'sdk-core': params.MOD_CORE,
'sdk-im': params.MOD_IM,
'sdk-push': params.MOD_PUSH,
'sdk-update': params.MOD_UPDATE,
'sdk-webview': params.MOD_WEBVIEW,
'sdk-license': params.MOD_LICENSE,
]
def resolved = moduleChecks.findAll { k, v -> v }.collect { k, v -> k }
if (resolved.isEmpty()) {
error("没有选择任何模块,请至少勾选一个")
}
env.PUBLISH_MODULES = resolved.join(',')
echo "Modules: ${env.PUBLISH_MODULES}"
}
}
}
stage('Resolve Versions') {
steps {
script {
// 从 gradle.properties 读取当前版本号
def currentVer = bat(
script: "@powershell -Command \"(Get-Content gradle.properties | Select-String '^PUBLISH_VERSION=').Line.Split('=')[1]\"",
returnStdout: true
).trim()
echo "Current PUBLISH_VERSION: ${currentVer}"
def parts = currentVer.tokenize('.')
while (parts.size() < 3) { parts.add('0') }
switch (params.VERSION_BUMP) {
case 'major':
parts[0] = (parts[0].toInteger() + 1).toString()
parts[1] = '0'
parts[2] = '0'
break
case 'minor':
parts[1] = (parts[1].toInteger() + 1).toString()
parts[2] = '0'
break
case 'patch':
default:
parts[2] = (parts[2].toInteger() + 1).toString()
break
}
def newVer = parts.join('.')
echo "Auto-bumped PUBLISH_VERSION: ${currentVer} → ${newVer}"
// 更新 gradle.properties
bat "powershell -Command \"(Get-Content gradle.properties) -replace '^PUBLISH_VERSION=.*', 'PUBLISH_VERSION=${newVer}' | Set-Content gradle.properties\""
// 用新版本号设置所有模块的版本参数
def modules = env.PUBLISH_MODULES.split(',').toList()
def versionMap = [:]
for (mod in modules) {
def propName = "SDK_${mod.replace('sdk-', '').toUpperCase()}_VERSION"
versionMap[propName] = newVer
}
env.VERSION_ARGS = versionMap
.collect { k, v -> "-P${k}=${v}" }
.join(' ')
echo "VERSION_ARGS: ${env.VERSION_ARGS}"
}
}
}
stage('Build AARs & APKs') {
steps {
script {
// 构建 sample-app测试用和各选中模块的 release
def modules = env.PUBLISH_MODULES.split(',').toList()
def moduleTasks = modules.collect { ":${it}:assembleRelease" }.join(' ')
def testTasks = ':sample-app:assembleDebug :sample-app:assembleDebugAndroidTest'
bat "gradlew.bat ${moduleTasks} ${testTasks} ${env.VERSION_ARGS} --no-daemon"
}
}
}
stage('Start Emulators') {
when { expression { return params.RUN_TESTS } }
steps {
bat '''
start /B emulator -avd Pixel_9_Pro -no-audio -no-boot-anim -no-snapshot-save -no-window
start /B emulator -avd Pixel_9_Pro_2 -no-audio -no-boot-anim -no-snapshot-save -no-window
adb wait-for-device
timeout /t 20 /nobreak >nul
adb devices
'''
}
}
stage('Install APKs') {
when { expression { return params.RUN_TESTS } }
steps {
bat '''
set APK=sample-app\\build\\outputs\\apk\\debug\\sample-app-debug.apk
set TEST_APK=sample-app\\build\\outputs\\apk\\androidTest\\debug\\sample-app-debug-androidTest.apk
for %%D in (emulator-5554 emulator-5556) do (
adb -s %%D install -r "%APK%"
adb -s %%D install -r "%TEST_APK%"
)
'''
}
}
stage('Single-Device Tests') {
when { expression { return params.RUN_TESTS } }
steps {
bat '''
set ALL_SINGLE=com.xuqm.sdk.sample.SdkIntegrationTest,com.xuqm.sdk.sample.PushSdkTest,com.xuqm.sdk.sample.NetworkResilienceTest
adb -s emulator-5554 shell am instrument -w -r -e class "%ALL_SINGLE%" "com.xuqm.demo.test/androidx.test.runner.AndroidJUnitRunner" > test-results-5554.txt
findstr /C:"FAILURES" test-results-5554.txt && exit 1 || exit 0
'''
}
}
stage('Cross-Device Tests') {
when { expression { return params.RUN_TESTS } }
parallel {
stage('Sender (5554)') {
steps {
bat '''
adb -s emulator-5554 shell am instrument -w -r -e class "com.xuqm.sdk.sample.CrossDeviceSenderTest" "com.xuqm.demo.test/androidx.test.runner.AndroidJUnitRunner" > cross-sender.txt
findstr /C:"FAILURES" cross-sender.txt && exit 1 || exit 0
'''
}
}
stage('Receiver (5556)') {
steps {
bat '''
adb -s emulator-5556 shell am instrument -w -r -e class "com.xuqm.sdk.sample.CrossDeviceReceiverTest" "com.xuqm.demo.test/androidx.test.runner.AndroidJUnitRunner" > cross-receiver.txt
findstr /C:"FAILURES" cross-receiver.txt && exit 1 || exit 0
'''
}
}
}
}
stage('Publish to Nexus') {
when { expression { return params.PUBLISH } }
steps {
script {
def modules = env.PUBLISH_MODULES.split(',').toList()
def publishTasks = modules.collect { ":${it}:publish" }.join(' ')
def credArgs = "-PNEXUS_USER=%NEXUS_CREDS_USR% -PNEXUS_PASSWORD=%NEXUS_CREDS_PSW%"
bat "gradlew.bat ${publishTasks} ${env.VERSION_ARGS} ${credArgs} --no-daemon"
}
}
}
}
post {
always {
script {
node {
bat 'adb emu kill || exit 0'
archiveArtifacts artifacts: 'test-results-*.txt, cross-*.txt', allowEmptyArchive: true
}
}
}
failure {
script {
node {
bat '''
adb -s emulator-5554 logcat -d -s XuqmImSDK:D XuqmPushSDK:W XuqmUpdateSDK:D > logcat-5554.txt || exit 0
adb -s emulator-5556 logcat -d -s XuqmImSDK:D XuqmPushSDK:W XuqmUpdateSDK:D > logcat-5556.txt || exit 0
'''
archiveArtifacts artifacts: 'logcat-*.txt', allowEmptyArchive: true
}
}
}
success { echo "Android SDK 构建成功" }
}
}