diff --git a/Jenkinsfile b/Jenkinsfile index 415d83b..35fcd2a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,20 +5,19 @@ pipeline { choice( name: 'SERVICE', choices: ['all', 'tenant-service', 'im-service', 'push-service', 'update-service', 'file-service', 'license-service', 'demo-service'], - description: '要构建的服务模块(all = 全部,包含所有微服务)' + description: '要构建的服务模块(all = 全部,每个服务独立版本号)' ) } environment { - ACR_REGISTRY = 'crpi-n44qjpuucgjt8e8c.cn-beijing.personal.cr.aliyuncs.com' - ACR_NAMESPACE = 'xuqmgroup' - ACR_USERNAME = 'xuqinmin12' - PROD_HOST = '106.54.23.149' - PROD_USER = 'ubuntu' - COMPOSE_FILE = '/opt/xuqm/deploy/compose.production.yaml' - VERSIONS_FILE = '/opt/xuqm/deploy/versions.json' + ACR_REGISTRY = 'crpi-n44qjpuucgjt8e8c.cn-beijing.personal.cr.aliyuncs.com' + ACR_NAMESPACE = 'xuqmgroup' + ACR_USERNAME = 'xuqinmin12' + PROD_HOST = '106.54.23.149' + PROD_USER = 'ubuntu' + COMPOSE_FILE = '/opt/xuqm/deploy/compose.production.yaml' + VERSIONS_FILE = '/opt/xuqm/deploy/versions.json' DOCKER_BUILDKIT = '1' - VERSION_FILE = 'VERSION' } options { @@ -39,16 +38,25 @@ pipeline { } } - stage('Resolve Version') { + stage('Resolve Versions') { steps { script { - def current = fileExists(env.VERSION_FILE) ? readFile(env.VERSION_FILE).trim() : '1.0.0' - def parts = current.tokenize('.') - while (parts.size() < 3) parts.add('0') - def newVer = "${parts[0]}.${parts[1]}.${parts[2].toInteger() + 1}" - env.SERVICE_VERSION = newVer - writeFile file: env.VERSION_FILE, text: newVer - echo "Server: ${current} → ${newVer}" + def allServices = ['tenant-service', 'im-service', 'push-service', 'update-service', 'file-service', 'license-service', 'demo-service'] + def targets = params.SERVICE == 'all' ? allServices : [params.SERVICE] + + // serviceVersions: Map + def serviceVersions = [:] + for (svc in targets) { + def vf = "VERSION.${svc}" + def current = fileExists(vf) ? readFile(vf).trim() : '1.0.0' + def parts = current.tokenize('.') + while (parts.size() < 3) parts.add('0') + def newVer = "${parts[0]}.${parts[1]}.${parts[2].toInteger() + 1}" + serviceVersions[svc] = newVer + writeFile file: vf, text: newVer + echo "${svc}: ${current} → ${newVer}" + } + env.SERVICE_VERSIONS_JSON = groovy.json.JsonOutput.toJson(serviceVersions) } } } @@ -57,20 +65,19 @@ pipeline { steps { withCredentials([string(credentialsId: 'ACR_PASSWORD', variable: 'ACR_PASS')]) { script { - def services = params.SERVICE == 'all' - ? ['tenant-service', 'im-service', 'push-service', 'update-service', 'file-service', 'license-service', 'demo-service'] - : [params.SERVICE] - - for (svc in services) { + def serviceVersions = new groovy.json.JsonSlurper().parseText(env.SERVICE_VERSIONS_JSON) + for (entry in serviceVersions) { + def svc = entry.key + def ver = entry.value def base = "${ACR_REGISTRY}/${ACR_NAMESPACE}/${svc}" - def versionedImage = "${base}:${env.SERVICE_VERSION}" + def versionedImage = "${base}:${ver}" def latestImage = "${base}:latest" - echo "Building ${svc} ${env.SERVICE_VERSION}..." + echo "Building ${svc}:${ver}..." bat """ docker login ${ACR_REGISTRY} -u ${ACR_USERNAME} -p %ACR_PASS% if %errorlevel% neq 0 exit /b 1 docker pull --platform=linux/amd64 ${latestImage} || echo Pull failed, will build fresh - docker build --platform=linux/amd64 --build-arg SERVICE_MODULE=${svc} --build-arg SERVICE_VERSION=${env.SERVICE_VERSION} --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${latestImage} -t ${versionedImage} -t ${latestImage} . + docker build --platform=linux/amd64 --build-arg SERVICE_MODULE=${svc} --build-arg SERVICE_VERSION=${ver} --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${latestImage} -t ${versionedImage} -t ${latestImage} . if %errorlevel% neq 0 exit /b 1 docker push ${versionedImage} if %errorlevel% neq 0 exit /b 1 @@ -90,11 +97,9 @@ pipeline { lock('prod-deploy') { withCredentials([sshUserPrivateKey(credentialsId: 'PROD_SSH_KEY', keyFileVariable: 'SSH_KEY')]) { script { - def services = params.SERVICE == 'all' - ? ['tenant-service', 'im-service', 'push-service', 'update-service', 'file-service', 'license-service', 'demo-service'] - : [params.SERVICE] - - for (svc in services) { + def serviceVersions = new groovy.json.JsonSlurper().parseText(env.SERVICE_VERSIONS_JSON) + for (entry in serviceVersions) { + def svc = entry.key def latestImage = "${ACR_REGISTRY}/${ACR_NAMESPACE}/${svc}:latest" echo "Deploying ${svc}..." retry(2) { @@ -104,23 +109,19 @@ pipeline { } } - // Update versions.json on server for deployed services - def deployedServices = params.SERVICE == 'all' - ? ['tenant-service', 'im-service', 'push-service', 'update-service', 'file-service', 'license-service', 'demo-service'] - : [params.SERVICE] + // 合并更新 versions.json(只改动本次构建涉及的服务,不覆盖其它服务或 web 条目) def releasedAt = new Date().format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone('UTC')) - def serviceEntries = deployedServices.collect { svc -> - "d.setdefault('services', {})['${svc}'] = {'version': '${env.SERVICE_VERSION}', 'changed': True}" + def serviceEntries = serviceVersions.collect { svc, ver -> + "d.setdefault('services', {})['${svc}'] = {'version': '${ver}'}" }.join('\n') def updateScript = """\ import json, os path = '${env.VERSIONS_FILE}' d = json.load(open(path)) if os.path.exists(path) else {} ${serviceEntries} -d['serviceVersion'] = '${env.SERVICE_VERSION}' d['releasedAt'] = '${releasedAt}' json.dump(d, open(path, 'w'), indent=2, ensure_ascii=False) -print('versions.json updated: ${params.SERVICE} = ${env.SERVICE_VERSION}') +print('versions.json updated') """.stripIndent() writeFile file: 'update_versions.py', text: updateScript bat """ @@ -133,14 +134,17 @@ print('versions.json updated: ${params.SERVICE} = ${env.SERVICE_VERSION}') } } - stage('Commit Version') { + stage('Commit Versions') { steps { script { + def serviceVersions = new groovy.json.JsonSlurper().parseText(env.SERVICE_VERSIONS_JSON) + def versionFiles = serviceVersions.keySet().collect { "VERSION.${it}" }.join(' ') + def summary = serviceVersions.collect { svc, ver -> "${svc}=${ver}" }.join(', ') bat """ git config user.email "jenkins@xuqm.com" git config user.name "Jenkins CI" - git add ${env.VERSION_FILE} - git diff --cached --quiet || git commit -m "ci: bump server to ${env.SERVICE_VERSION} [skip ci]" + git add ${versionFiles} + git diff --cached --quiet || git commit -m "ci: bump versions [${summary}] [skip ci]" git push origin HEAD:main """ } @@ -149,7 +153,13 @@ print('versions.json updated: ${params.SERVICE} = ${env.SERVICE_VERSION}') } post { - success { echo "✅ ${params.SERVICE} v${env.SERVICE_VERSION} 构建部署成功" } - failure { echo "❌ 构建失败,请检查日志" } + success { + script { + def serviceVersions = new groovy.json.JsonSlurper().parseText(env.SERVICE_VERSIONS_JSON ?: '{}') + def summary = serviceVersions.collect { svc, ver -> "${svc}:${ver}" }.join(', ') + echo "✅ 构建部署成功 — ${summary}" + } + } + failure { echo "❌ 构建失败,请检查日志" } } } diff --git a/VERSION.demo-service b/VERSION.demo-service new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION.demo-service @@ -0,0 +1 @@ +1.0.0 diff --git a/VERSION.file-service b/VERSION.file-service new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION.file-service @@ -0,0 +1 @@ +1.0.0 diff --git a/VERSION.im-service b/VERSION.im-service new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION.im-service @@ -0,0 +1 @@ +1.0.0 diff --git a/VERSION.license-service b/VERSION.license-service new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION.license-service @@ -0,0 +1 @@ +1.0.0 diff --git a/VERSION.push-service b/VERSION.push-service new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION.push-service @@ -0,0 +1 @@ +1.0.0 diff --git a/VERSION.tenant-service b/VERSION.tenant-service new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION.tenant-service @@ -0,0 +1 @@ +1.0.0 diff --git a/VERSION.update-service b/VERSION.update-service new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION.update-service @@ -0,0 +1 @@ +1.0.0