Jenkins_Pipeline_Library/vars/Pipeline_dev.groovy
2026-05-09 10:17:04 +08:00

320 lines
13 KiB
Groovy
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//=================================================================
// Byway Studios Inc. Confidential Information.
// Copyright Byway Studios Inc. All rights reserved
//=================================================================
import com.cloudbees.groovy.cps.NonCPS
import groovy.json.JsonOutput
import groovy.json.JsonSlurperClassic
@NonCPS
def getTriggerUser(buildCauses)
{
def triggerUser = "自动触发"
if (buildCauses && buildCauses.size() > 0 && buildCauses[0].userName) {
triggerUser = buildCauses[0].userName
}
return triggerUser
}
@NonCPS
def getFailedStage(build)
{
def failedStage = ""
if (build.currentResult == 'FAILURE') {
def stages = build.rawBuild.getAction(org.jenkinsci.plugins.workflow.job.views.FlowGraphAction)?.getNodes()
if (stages) {
for (stage in stages) {
if (stage.getError() != null) {
failedStage = "\n- 失败阶段: ${stage.getDisplayName()}"
break
}
}
}
}
return failedStage
}
@NonCPS
def getMainRepoChangeString(changeLogSets)
{
def changeString = ""
if (changeLogSets && changeLogSets.size() > 0) {
changeString = "\n\n提交记录:\n"
for (int i = 0; i < changeLogSets.size(); i++) {
def entries = changeLogSets[i].items
for (int j = 0; j < entries.length; j++) {
def entry = entries[j]
changeString += "- ${entry.commitId.take(7)} ${entry.msg} - ${entry.author}\n"
}
}
} else {
changeString = "\n\n提交记录: 无变更"
}
return changeString
}
def call(Map config)
{
pipeline
{
agent
{
label config.winbakeLabel
}
parameters
{
booleanParam(name: 'CleanBuild', defaultValue: false, description: 'Force Clean Build to Avoid Previous Build Cache')
booleanParam(name: 'DebugBuild', defaultValue: false, description: 'To Create Debug Build for Profiling')
booleanParam(name: 'CleanCache', defaultValue: false, description: 'Force Clean Script and Package Cache')
booleanParam(name: 'CleanLibrary', defaultValue: false, description: 'Force Clean the Whole Library Folder')
booleanParam(name: 'BuildNonTuyoo', defaultValue: false, description: 'Build the without_sdk APK package')
}
options
{
skipDefaultCheckout(true) // ★ 关键:禁止 Jenkins 自动 checkout library
}
stages
{
stage("Checkout Unity Project")
{
options
{
timeout(30)
}
steps
{
script {
// 配置 Git 支持长路径(解决 Windows 260 字符限制)
bat 'git config --global core.longpaths true'
}
script {
// 最小化处理checkout 前仅重置子模块,避免残留改动导致冲突
bat '''
if exist .gitmodules (
git submodule foreach --recursive git reset --hard
)
'''
}
checkout([
$class: 'GitSCM',
branches: [[name: '*/main']], // 分支名
userRemoteConfigs: [[
url: 'git@gitea.bywaystudios.com:pet_home/AplusB_Pet_nation.git',
credentialsId: '503eaa43-0676-40ac-81c0-d9c5cc8b4ff7'
]],
extensions: [
[$class: 'SubmoduleOption',
disableSubmodules: false,
parentCredentials: true,
recursiveSubmodules: true,
trackingSubmodules: false, // 关闭,避免冲突
reference: '',
timeout: 20,
threads: 1],
[$class: 'PruneStaleBranch']
]
])
}
}
stage("Build Unity Android")
{
options
{
timeout(60)
}
steps
{
BuildAndroid(config)
}
}
stage("AchiveArtifact")
{
options
{
timeout(10)
}
steps
{
archiveArtifact("Android_dev")
}
}
stage("Upload APK")
{
when
{
expression { config.uploadEnabled == true }
}
options
{
timeout(10)
}
steps
{
catchError(buildResult: 'UNSTABLE', stageResult: 'UNSTABLE')
{
script
{
env.APK_UPLOAD_STATUS = 'FAILED'
env.APK_UPLOAD_MESSAGE = ''
env.APK_UPLOAD_FILE = ''
env.APK_UPLOAD_VERSION = ''
env.APK_UPLOAD_PACKAGE_TYPE = ''
def packageType = config.uploadPackageType ?: (params.BuildNonTuyoo ? 'without_sdk' : 'with_sdk')
try {
def uploadResult = uploadApk([
publishSubFolder: 'Android_dev',
uploadEnv: 'dev',
packageType: packageType,
uploadUrl: config.uploadUrl,
uploadTokenCredentialsId: config.uploadTokenCredentialsId,
uploadVersion: config.uploadVersion
])
env.APK_UPLOAD_STATUS = 'SUCCESS'
env.APK_UPLOAD_MESSAGE = 'APK uploaded successfully.'
env.APK_UPLOAD_FILE = uploadResult.apkName
env.APK_UPLOAD_VERSION = uploadResult.version
env.APK_UPLOAD_PACKAGE_TYPE = uploadResult.packageType
} catch (Exception ex) {
env.APK_UPLOAD_MESSAGE = ex.message ?: 'APK upload failed.'
env.APK_UPLOAD_PACKAGE_TYPE = packageType
throw ex
}
}
}
}
}
}
post
{
always
{
script {
// 获取触发者
def triggerUser = getTriggerUser(currentBuild.getBuildCauses())
// 获取失败阶段(仅失败时)
def failedStage = getFailedStage(currentBuild)
// 主仓库提交记录与 prod / stable 保持一致
def changeString = getMainRepoChangeString(currentBuild.changeSets)
def uploadStatus = config.uploadEnabled == true ? '未执行' : '已跳过'
if (env.APK_UPLOAD_STATUS == 'SUCCESS') {
uploadStatus = env.APK_UPLOAD_VERSION ?
"成功: ${env.APK_UPLOAD_FILE} (${env.APK_UPLOAD_PACKAGE_TYPE}, version ${env.APK_UPLOAD_VERSION})" :
"成功: ${env.APK_UPLOAD_FILE} (${env.APK_UPLOAD_PACKAGE_TYPE})"
} else if (env.APK_UPLOAD_STATUS == 'FAILED') {
uploadStatus = env.APK_UPLOAD_PACKAGE_TYPE ?
"失败: ${env.APK_UPLOAD_PACKAGE_TYPE}, ${env.APK_UPLOAD_MESSAGE}" :
"失败: ${env.APK_UPLOAD_MESSAGE}"
}
// 收集子模块当前提交记录,仅显示本次有变化的子模块
def submoduleChangeString = ""
def submoduleStateFile = ".jenkins_submodule_state_dev.json"
def previousSubmoduleStates = [:]
def currentSubmoduleStates = [:]
if (fileExists(submoduleStateFile)) {
try {
previousSubmoduleStates = new JsonSlurperClassic().parseText(readFile(submoduleStateFile)) ?: [:]
} catch (Exception ignored) {
previousSubmoduleStates = [:]
}
}
if (fileExists('.git') && fileExists('.gitmodules')) {
try {
def submoduleStatusOutput = bat(
returnStdout: true,
script: '@echo off\r\ngit submodule status --recursive'
).trim()
def submoduleLines = submoduleStatusOutput ? submoduleStatusOutput.readLines().findAll { it?.trim() } : []
def submoduleSections = []
submoduleLines.each { rawLine ->
def line = rawLine.replace('\r', '').trim()
if (!line || line.length() < 42) {
return
}
def remainder = line.substring(1).trim()
def firstSpaceIndex = remainder.indexOf(' ')
if (firstSpaceIndex <= 0) {
return
}
def pathPart = remainder.substring(firstSpaceIndex + 1).trim()
def branchInfoIndex = pathPart.indexOf(' (')
def submodulePath = (branchInfoIndex >= 0 ? pathPart.substring(0, branchInfoIndex) : pathPart).trim()
if (!submodulePath) {
return
}
def currentSha = bat(
returnStdout: true,
script: "@echo off\r\ngit -C \"${submodulePath}\" rev-parse HEAD"
).trim()
def currentHeadMessage = bat(
returnStdout: true,
script: "@echo off\r\ngit -C \"${submodulePath}\" log -1 --pretty=format:\"%%h %%s - %%an\""
).trim()
currentSubmoduleStates[submodulePath] = [sha: currentSha]
if (currentSha) {
def previousSha = previousSubmoduleStates[submodulePath]?.sha
if (previousSha && previousSha != currentSha) {
submoduleSections << "- 子模块: ${submodulePath}\n- 当前提交: ${currentHeadMessage}"
}
}
}
if (submoduleSections.size() > 0) {
submoduleChangeString = "\n\n子模块提交记录:\n" + submoduleSections.join("\n")
}
writeFile(
file: submoduleStateFile,
text: JsonOutput.prettyPrint(JsonOutput.toJson(currentSubmoduleStates))
)
} catch (Exception ex) {
echo "子模块提交记录收集失败: ${ex.message}"
submoduleChangeString = "\n\n子模块提交记录: 收集失败"
}
}
def dingtalkText = [
"### ${currentBuild.currentResult}",
"\n构建信息:",
"- 项目: ${env.JOB_NAME}",
"- 构建号: #${env.BUILD_NUMBER}",
"- 分支: main",
"- 触发者: ${triggerUser}",
"- 耗时: ${currentBuild.durationString}${failedStage}",
"- APK 上传: ${uploadStatus}",
"\n链接:",
"- [下载链接](${env.BUILD_URL})",
changeString
]
if (submoduleChangeString) {
dingtalkText << submoduleChangeString
}
dingtalk(
robot:'dingtalk_unity_1',
type:'MARKDOWN',
title:"${currentBuild.currentResult} - dev #${env.BUILD_NUMBER}",
text: dingtalkText
)
}
}
}
}
}