XuqmCoreSDK - 新增 userId / token(nonisolated) / deviceId 公开属性,对齐 BugCollectSDK / WebViewSDK 引用 XuqmWebViewSDK - XWebViewTypes:新增 jsBridgeName / onClose / onPageError / onProgressChanged / onNavigationChanged - XWebViewView:注册双 bridge(ReactNativeWebView 内部 + 自定义 jsBridgeName)、KVO progress、nav delegate - XWebViewStandardHandlers:@MainActor,injectedJavaScript 改 nonisolated,兼容 macOS - 新增 import XuqmCoreSDK XuqmBugCollectSDK - 全局 #if canImport(UIKit) 兼容 macOS 编译 - isReady 从 computed var 改为 func 消除歧义 - LogQueue 修正 LogStorage 为 enum 静态方法调用 XuqmUpdateSDK - getDeviceInfo() #if canImport(UIKit) 兼容 macOS XuqmLicenseSDK - ensureInitializedFromSDK() 用 DispatchQueue.main.sync 访问 @MainActor 属性 CI - 重写 Jenkinsfile:增加 SNAPSHOT/Release 模式,统一版本号管理,修正 git URL,对齐 Android 流水线 - 新增 version.properties:统一追踪 SDK_VERSION - 更新 .gitignore:移除 JS/Java 噪音,补 Swift/Xcode 规则 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
223 行
12 KiB
Groovy
223 行
12 KiB
Groovy
pipeline {
|
||
agent any // macOS agent (szyx.xuqinmin.com)
|
||
|
||
parameters {
|
||
// ── 发布模式 ─────────────────────────────────────────────────────────
|
||
booleanParam(name: 'SNAPSHOT', defaultValue: true,
|
||
description: '勾选=SNAPSHOT(推送到 main 分支,不升版本,不打 tag;应用通过 branch:"main" 引用);不勾选=正式 Release(升版本号、打 tag、推送)')
|
||
|
||
// ── 版本升级策略(仅 Release 时生效)───────────────────────────────
|
||
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)(SNAPSHOT 模式忽略)')
|
||
|
||
// ── 模块选择(标记本次改动范围,用于 changelog — 不影响版本号)──
|
||
booleanParam(name: 'MOD_CORE', defaultValue: false, description: '本次包含 XuqmCoreSDK 改动')
|
||
booleanParam(name: 'MOD_IM', defaultValue: false, description: '本次包含 XuqmImSDK 改动')
|
||
booleanParam(name: 'MOD_PUSH', defaultValue: false, description: '本次包含 XuqmPushSDK 改动')
|
||
booleanParam(name: 'MOD_UPDATE', defaultValue: false, description: '本次包含 XuqmUpdateSDK 改动')
|
||
booleanParam(name: 'MOD_LICENSE', defaultValue: false, description: '本次包含 XuqmLicenseSDK 改动')
|
||
booleanParam(name: 'MOD_FILE', defaultValue: false, description: '本次包含 XuqmFileSDK 改动')
|
||
booleanParam(name: 'MOD_WEBVIEW', defaultValue: false, description: '本次包含 XuqmWebViewSDK 改动')
|
||
booleanParam(name: 'MOD_BUGCOLLECT', defaultValue: false, description: '本次包含 XuqmBugCollectSDK 改动')
|
||
|
||
// ── 构建选项 ────────────────────────────────────────────────────────
|
||
booleanParam(name: 'RUN_TESTS', defaultValue: true,
|
||
description: '是否运行 swift test(在 macOS 上执行 SPM 测试)')
|
||
}
|
||
|
||
environment {
|
||
GITEA_REPO_URL = 'https://xuqinmin.com/xuqmGroup/XuqmGroup-iOSSDK.git'
|
||
GITEA_REPO_PATH = 'xuqmGroup/XuqmGroup-iOSSDK'
|
||
GITEA_API_BASE = 'https://xuqinmin.com/api/v1'
|
||
}
|
||
|
||
options {
|
||
timeout(time: 30, unit: 'MINUTES')
|
||
buildDiscarder(logRotator(numToKeepStr: '20'))
|
||
disableConcurrentBuilds()
|
||
}
|
||
|
||
stages {
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
stage('Checkout') {
|
||
steps {
|
||
checkout([
|
||
$class: 'GitSCM',
|
||
branches: [[name: 'main']],
|
||
extensions: [[$class: 'CleanBeforeCheckout']],
|
||
userRemoteConfigs: scm.userRemoteConfigs
|
||
])
|
||
script {
|
||
env.GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
|
||
|
||
def moduleChecks = [
|
||
'XuqmCoreSDK': params.MOD_CORE,
|
||
'XuqmImSDK': params.MOD_IM,
|
||
'XuqmPushSDK': params.MOD_PUSH,
|
||
'XuqmUpdateSDK': params.MOD_UPDATE,
|
||
'XuqmLicenseSDK': params.MOD_LICENSE,
|
||
'XuqmFileSDK': params.MOD_FILE,
|
||
'XuqmWebViewSDK': params.MOD_WEBVIEW,
|
||
'XuqmBugCollectSDK': params.MOD_BUGCOLLECT,
|
||
]
|
||
def changed = moduleChecks.findAll { k, v -> v }.collect { k, v -> k }
|
||
if (changed.isEmpty()) {
|
||
error("没有勾选任何模块,请至少标记一个有改动的模块")
|
||
}
|
||
env.CHANGED_MODULES = changed.join(', ')
|
||
echo "Changed modules: ${env.CHANGED_MODULES}"
|
||
}
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
stage('Resolve Version') {
|
||
steps {
|
||
script {
|
||
def propsText = readFile('version.properties')
|
||
def match = (propsText =~ /SDK_VERSION=([0-9][^\s#\r\n]*)/)
|
||
if (!match) { error("version.properties 中找不到 SDK_VERSION") }
|
||
def currentVer = match[0][1].replaceAll(/-SNAPSHOT$/, '')
|
||
|
||
def bumpVersion = { String ver ->
|
||
def parts = ver.tokenize('.')
|
||
while (parts.size() < 3) { parts.add('0') }
|
||
def major = parts[0].toInteger()
|
||
def minor = parts[1].toInteger()
|
||
def patch = parts[2].toInteger()
|
||
if (params.VERSION_BUMP == 'major') { major++; minor = 0; patch = 0 }
|
||
else if (params.VERSION_BUMP == 'minor') { minor++; patch = 0 }
|
||
else { patch++ }
|
||
return "${major}.${minor}.${patch}"
|
||
}
|
||
|
||
if (params.SNAPSHOT) {
|
||
// SNAPSHOT:version.properties 不变,展示下一版本的 SNAPSHOT 标签
|
||
def nextVer = bumpVersion(currentVer)
|
||
env.PUBLISH_VERSION = "${nextVer}-SNAPSHOT"
|
||
env.RELEASE_TAG = ''
|
||
echo "SNAPSHOT 模式 — 当前 Release: ${currentVer},本次标记: ${env.PUBLISH_VERSION}"
|
||
} else {
|
||
// Release:升级并写回
|
||
def newVer = bumpVersion(currentVer)
|
||
env.PUBLISH_VERSION = newVer
|
||
env.RELEASE_TAG = newVer
|
||
def newProps = propsText.replaceAll(/SDK_VERSION=[^\s#\r\n]+/, "SDK_VERSION=${newVer}")
|
||
writeFile(file: 'version.properties', text: newProps)
|
||
echo "Release 模式 — ${currentVer} → ${newVer}"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
stage('Build Info') {
|
||
steps {
|
||
script {
|
||
echo "================================================"
|
||
echo " iOS SDK — ${params.SNAPSHOT ? 'SNAPSHOT' : 'Release'}"
|
||
echo " 版本 : ${env.PUBLISH_VERSION}"
|
||
echo " Tag : ${env.RELEASE_TAG ?: '不打 tag'}"
|
||
echo " Commit : ${env.GIT_COMMIT_SHORT}"
|
||
echo " 改动模块 : ${env.CHANGED_MODULES}"
|
||
echo "================================================"
|
||
}
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
stage('Swift Build') {
|
||
steps {
|
||
sh 'swift build -c release 2>&1'
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
stage('Swift Test') {
|
||
when { expression { return params.RUN_TESTS } }
|
||
steps {
|
||
sh 'swift test 2>&1'
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// SNAPSHOT 分支:只推送代码到 main,不打 tag,不修改版本文件
|
||
stage('Publish SNAPSHOT') {
|
||
when { expression { return params.SNAPSHOT } }
|
||
steps {
|
||
withCredentials([string(credentialsId: 'GITEA_TOKEN', variable: 'TOKEN')]) {
|
||
sh """
|
||
git config user.email "jenkins@xuqm.com"
|
||
git config user.name "Jenkins CI"
|
||
git push https://xuqinmin12:\${TOKEN}@xuqinmin.com/xuqmGroup/XuqmGroup-iOSSDK.git HEAD:main
|
||
"""
|
||
}
|
||
script {
|
||
echo "SNAPSHOT 已推送到 main 分支 (${env.GIT_COMMIT_SHORT})"
|
||
echo "应用端 project.yml 使用: branch: \"main\""
|
||
echo "Xcode: File > Packages > Update to Latest Package Versions 获取最新代码"
|
||
}
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// Release 分支:升版本号 commit → 打 tag → 推送 commit + tag
|
||
stage('Commit & Tag Release') {
|
||
when { expression { return !params.SNAPSHOT } }
|
||
steps {
|
||
withCredentials([string(credentialsId: 'GITEA_TOKEN', variable: 'TOKEN')]) {
|
||
sh """
|
||
git config user.email "jenkins@xuqm.com"
|
||
git config user.name "Jenkins CI"
|
||
git add version.properties
|
||
git commit -m "ci: release ${env.RELEASE_TAG} [${env.CHANGED_MODULES}]"
|
||
git tag ${env.RELEASE_TAG}
|
||
git push https://xuqinmin12:\${TOKEN}@xuqinmin.com/xuqmGroup/XuqmGroup-iOSSDK.git HEAD:main
|
||
git push https://xuqinmin12:\${TOKEN}@xuqinmin.com/xuqmGroup/XuqmGroup-iOSSDK.git ${env.RELEASE_TAG}
|
||
"""
|
||
}
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// Release 分支:在 Gitea 上创建 Release 记录
|
||
stage('Create Gitea Release') {
|
||
when { expression { return !params.SNAPSHOT } }
|
||
steps {
|
||
withCredentials([string(credentialsId: 'GITEA_TOKEN', variable: 'TOKEN')]) {
|
||
script {
|
||
def moduleList = env.CHANGED_MODULES.split(', ').collect { "- ${it}" }.join('\\n')
|
||
def body = "## 改动模块\\n${moduleList}\\n\\n## 集成方式\\n\\`\\`\\`yaml\\npackages:\\n XuqmGroupSDK:\\n url: ${env.GITEA_REPO_URL}\\n from: \\"${env.RELEASE_TAG}\\"\\n\\`\\`\\`"
|
||
sh """
|
||
curl -sf -X POST "${GITEA_API_BASE}/repos/${GITEA_REPO_PATH}/releases" \\
|
||
-H "Authorization: token \${TOKEN}" \\
|
||
-H "Content-Type: application/json" \\
|
||
-d "{\\\"tag_name\\\":\\\"${env.RELEASE_TAG}\\\",\\\"name\\\":\\\"v${env.RELEASE_TAG}\\\",\\\"body\\\":\\\"${body}\\\",\\\"draft\\\":false,\\\"prerelease\\\":false}" \\
|
||
| python3 -c "import sys,json; d=json.load(sys.stdin); print('Release:', d.get('html_url','created'))"
|
||
"""
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
post {
|
||
success {
|
||
script {
|
||
if (params.SNAPSHOT) {
|
||
echo "✅ SNAPSHOT ${env.PUBLISH_VERSION} 构建并推送成功 (commit: ${env.GIT_COMMIT_SHORT})"
|
||
echo " 改动模块: ${env.CHANGED_MODULES}"
|
||
} else {
|
||
echo "✅ Release ${env.RELEASE_TAG} 发布成功"
|
||
echo " 改动模块: ${env.CHANGED_MODULES}"
|
||
echo " 应用端 project.yml 更新: from: \"${env.RELEASE_TAG}\""
|
||
}
|
||
}
|
||
}
|
||
failure {
|
||
echo "❌ iOS SDK 构建失败,请检查日志"
|
||
}
|
||
}
|
||
}
|