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 构建失败,请检查日志" } } }