pipeline-model-architecture
npx skills add https://github.com/tencentblueking/bk-ci --skill pipeline-model-architecture
Agent 安装分布
Skill 文档
BK-CI æµæ°´çº¿æ ¸å¿æ¨¡å (Model) æ¶æè¯¦è§£
Skill æ¦è¿°
Skill åç§°: Pipeline Model Architecture
éç¨åºæ¯: çè§£åæä½ BK-CI æµæ°´çº¿çæ ¸å¿æ°æ®ç»æ
éè¦æ§: âââââ (æé«ä¼å
级)
ææ¡£çæ¬: 2.0
æåæ´æ°: 2024-12
Model æ¯æ´ä¸ª BK-CI æµæ°´çº¿ç³»ç»çæ ¸å¿æ°æ®æ¨¡åï¼å®ä¹äºæµæ°´çº¿å¨å é¨ç³»ç»ä¸ç宿´æ°æ®ç»æãæææµæ°´çº¿ç¸å ³çä¸å¡é»è¾ï¼å建ãç¼è¾ãæ§è¡ãè°åº¦ãçæ§ï¼é½å´ç»è¿ä¸ªæ¨¡åå±å¼ã
为ä»ä¹ Model 妿¤éè¦ï¼
- æ°æ®è½½ä½: Model æ¯æµæ°´çº¿é ç½®çå¯ä¸æ°æ®è½½ä½ï¼åç«¯ç¼æãå端åå¨ãæå»ºæ§è¡é½ä¾èµå®
- çæ¬æ§å¶: æ¯æ¬¡æµæ°´çº¿ä¿®æ¹é½ä¼çææ°çæ¬ç Model
- æå»ºå¿«ç §: æ¯æ¬¡æå»ºé½ä¼ä¿å Model å¿«ç §ï¼æ¯æåå²å溯åéè¯
- 跨系ç»éä¿¡: API æ¥å£ãå¾®æå¡é´éä¿¡é½ä»¥ Model ä¸ºæ ¸å¿æ°æ®ç»æ
- æ©å±åºç¡: æ°å¢æµæ°´çº¿åè½ï¼å¦æ°æä»¶ç±»åï¼å¿ é¡»çè§£ Model æ¶æ
ä¸ãModel æ¶ææ¦è§
1.1 æ ¸å¿å±æ¬¡ç»æ
Model éç¨åå±åµå¥çæ ç¶ç»æï¼å®æ´æè¿°äºä¸ä¸ªæµæ°´çº¿çç»ç»æ¹å¼ï¼
Model (æµæ°´çº¿)
âââ Stage[] (é¶æ®µéå)
âââ Container[] (容å¨/Jobéå)
âââ Element[] (æä»¶/ä»»å¡éå)
æ°æ®æä»¶ä½ç½®:
// æ ¸å¿æ¨¡åå®ä¹
src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/Model.kt
src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/container/Stage.kt
src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/container/Container.kt
src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/element/Element.kt
1.2 设计åå
- 夿æ§: Container å Element é½éç¨æ¥å£ + å®ç°ç±»çå¤æè®¾è®¡
- Jackson åºåå: 使ç¨
@JsonTypeInfoå@JsonSubTypes注解å®ç°å¤æ JSON åºåå - è¿è¡æ¶ä¸é´åæ°: å¾å¤å段ï¼å¦
statusãexecuteCountï¼æ 记为readOnly = trueï¼ä» å¨æå»ºè¿è¡æ¶ä½¿ç¨ - çæ¬å
¼å®¹æ§: å
å«
@Deprecatedåæ®µåtransformCompatibility()æ¹æ³å¤çå岿°æ®
äºãModel é¡¶å±ç»æè¯¦è§£
2.1 Model ç±»å®ä¹
data class Model(
var name: String, // æµæ°´çº¿åç§°
var desc: String?, // æµæ°´çº¿æè¿°
val stages: List<Stage>, // é¶æ®µéåï¼æ ¸å¿ï¼
var labels: List<String> = emptyList(), // æ ç¾ï¼å·²åºå¼ï¼
// 模æ¿ç¸å
³
val instanceFromTemplate: Boolean? = null, // æ¯å¦ä»æ¨¡æ¿å®ä¾å
var srcTemplateId: String? = null, // æºæ¨¡æ¿ID
var templateId: String? = null, // å½å模æ¿ID
var template: TemplateInstanceDescriptor?, // 模æ¿å®ä¾æè¿°ç¬¦
var overrideTemplateField: TemplateInstanceField?, // è¦ç模æ¿å段
// å
æ°æ®
var pipelineCreator: String? = null, // å建人
var latestVersion: Int = 0, // ææ°çæ¬å·
var tips: String? = null, // æç¤ºä¿¡æ¯
// äºä»¶åè§å¾
var events: Map<String, PipelineCallbackEvent>?, // æµæ°´çº¿åè°äºä»¶
var staticViews: List<String> = emptyList(), // éææµæ°´çº¿ç»
// è¿è¡æ¶æ°æ®
var timeCost: BuildRecordTimeCost? = null, // åé¡¹èæ¶ç»è®¡
val resources: Resources? = null // 模æ¿èµæº
)
2.2 æ ¸å¿æ¹æ³
2.2.1 è·å触å容å¨
@JsonIgnore
fun getTriggerContainer() = stages[0].containers[0] as TriggerContainer
说æ:
- 触å容卿°¸è¿å¨ç¬¬ä¸ä¸ª Stage ç第ä¸ä¸ª Container ä½ç½®
- è¿æ¯æµæ°´çº¿çå ¥å£ç¹ï¼å å«è§¦åå¨åæµæ°´çº¿åæ°
2.2.2 ç»è®¡ä»»å¡æ°é
fun taskCount(skipTaskClassType: Set<String> = emptySet()): Int {
var count = 0
stages.forEach { s ->
s.containers.forEach { c ->
c.elements.forEach { e ->
if (!skipTaskClassType.contains(e.getClassType())) {
count++
}
}
}
}
return count
}
2.2.3 å 餿å®ç±»ååå
fun removeElements(elementClassTypes: Set<String>): Model {
// éåææ StageãContainerãElement
// è¿æ»¤ææå® classType ç Element
// è¿åæ°ç Model å®ä¾
}
使ç¨åºæ¯: æ¨¡æ¿æ¸ çãæä»¶å¸è½½æ¶ç§»é¤ç¹å®ç±»åçä»»å¡
2.2.4 é»è®¤æ¨¡å工忹æ³
companion object {
fun defaultModel(
pipelineName: String = "",
userId: String? = null
): Model {
return Model(
name = pipelineName,
desc = "",
stages = listOf(
Stage(
id = "stage-1",
containers = listOf(
TriggerContainer(
id = "0",
name = "trigger",
elements = listOf(
ManualTriggerElement(
id = "T-1-1-1",
name = I18nUtil.getCodeLanMessage(
CommonMessageCode.BK_MANUAL_TRIGGER
)
)
)
)
)
)
),
pipelineCreator = userId
)
}
}
说æ: å建ä¸ä¸ªæå°åçæµæ°´çº¿æ¨¡åï¼åªå å«ä¸ä¸ªæå¨è§¦åå¨
ä¸ãStage (é¶æ®µ) 详解
3.1 Stage æ°æ®ç»æ
data class Stage(
val containers: List<Container> = listOf(), // 容å¨éåï¼æ ¸å¿ï¼
var id: String?, // ç³»ç»çæçIDï¼ä¸å¯ç¼è¾ï¼
var name: String? = "", // é¶æ®µåç§°
var stageIdForUser: String? = null, // ç¨æ·å¯ç¼è¾çID
// æµç¨æ§å¶
var stageControlOption: StageControlOption?, // æµç¨æ§å¶é项
var checkIn: StagePauseCheck? = null, // Stageåå
¥é
ç½®ï¼äººå·¥å®¡æ ¸ï¼
var checkOut: StagePauseCheck? = null, // Stageååºé
ç½®ï¼äººå·¥å®¡æ ¸ï¼
val finally: Boolean = false, // æ¯å¦ä¸ºFinallyStage
val fastKill: Boolean? = false, // 失败快éç»æ¢
// è¿è¡æ¶æ°æ®
var status: String? = null, // é¶æ®µç¶æ
var executeCount: Int? = null, // è¿è¡æ¬¡æ°
var canRetry: Boolean? = null, // æ¯å¦å¯éè¯
var timeCost: BuildRecordTimeCost? = null, // åé¡¹èæ¶
// å
¶ä»
val customBuildEnv: Map<String, String>?, // èªå®ä¹ç¯å¢åé
var tag: List<String>? = null, // é¶æ®µæ ç¾ï¼æ¾ç¤ºç¨ï¼
var template: TemplateDescriptor? = null // 模æ¿ä¿¡æ¯
)
3.2 Stage æ ¸å¿æ¦å¿µ
3.2.1 FinallyStage
val finally: Boolean = false
ç¹æ§:
- æ¯ä¸ª Model åªè½å å«ä¸ä¸ª FinallyStage
- å¿ é¡»å¤äºæåä½ç½®
- æ è®ºæµæ°´çº¿æåæå¤±è´¥é½ä¼æ§è¡ï¼ç±»ä¼¼ try-finallyï¼
- FinallyStage ä¸å¯éè¯ (
canRetry = false)
使ç¨åºæ¯:
- èµæºæ¸ ç
- éç¥åé
- æ¥å¿å½æ¡£
3.2.2 Stage åå ¥ååº (CheckIn/CheckOut)
var checkIn: StagePauseCheck? = null // é¶æ®µå¼å§åäººå·¥å®¡æ ¸
var checkOut: StagePauseCheck? = null // é¶æ®µç»æåäººå·¥å®¡æ ¸
StagePauseCheck å å«:
reviewGroups: å®¡æ ¸äººç»timeout: å®¡æ ¸è¶ æ¶æ¶é´reviewParams: å®¡æ ¸æ¶å¡«åçåæ°status: å®¡æ ¸ç¶æ
æµç¨:
Stage å¼å§ â checkIn å®¡æ ¸ â æ§è¡ Containers â checkOut å®¡æ ¸ â Stage ç»æ
3.3 Stage æ ¸å¿æ¹æ³
3.3.1 éç½®æå»ºé项
fun resetBuildOption(init: Boolean? = false) {
if (init == true) {
status = null
startEpoch = null
elapsed = null
}
checkIn?.fixReviewGroups(init == true)
checkOut?.fixReviewGroups(init == true)
// 妿é
ç½®äºæå¨è§¦å使²¡æcheckInï¼èªå¨å建
if (stageControlOption?.manualTrigger == true && checkIn == null) {
checkIn = StagePauseCheck.convertControlOption(stageControlOption!!)
}
if (finally) canRetry = false // FinallyStageç¦æ¢éè¯
}
3.3.2 è·å容å¨
fun getContainer(vmSeqId: String): Container? {
containers.forEach { container ->
return container.getContainerById(vmSeqId) ?: return@forEach
}
return null
}
3.3.3 Stage æ¯å¦å¯ç¨
fun stageEnabled(): Boolean {
return stageControlOption?.enable ?: true
}
åãContainer (容å¨/Job) 详解
4.1 Container æ¥å£å®ä¹
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type")
@JsonSubTypes(
JsonSubTypes.Type(value = TriggerContainer::class, name = TriggerContainer.classType),
JsonSubTypes.Type(value = NormalContainer::class, name = NormalContainer.classType),
JsonSubTypes.Type(value = VMBuildContainer::class, name = VMBuildContainer.classType),
JsonSubTypes.Type(value = JobTemplateContainer::class, name = JobTemplateContainer.classType)
)
interface Container {
var id: String? // åºåID
var name: String // 容å¨åç§°
var elements: List<Element> // ä»»å¡éåï¼æ ¸å¿ï¼
// è¿è¡æ¶ç¶æ
var status: String?
var startVMStatus: String? // æå»ºç¯å¢å¯å¨ç¶æ
var executeCount: Int? // è¿è¡æ¬¡æ°
var canRetry: Boolean? // æ¯å¦å¯éè¯
// å¯ä¸æ è¯
var containerId: String? // 容å¨å¯ä¸IDï¼åidï¼
var containerHashId: String? // 容å¨å
¨å±å¯ä¸HashID
var jobId: String? // ç¨æ·èªå®ä¹ID
// èæ¶ç»è®¡
var timeCost: BuildRecordTimeCost?
var startVMTaskSeq: Int? // 弿ºä»»å¡åºå·
// æ å¿ä½
var containPostTaskFlag: Boolean? // æ¯å¦å
å«postä»»å¡
val matrixGroupFlag: Boolean? // æ¯å¦ä¸ºæå»ºç©éµ
// æ½è±¡æ¹æ³
fun getClassType(): String
fun getContainerById(vmSeqId: String): Container?
fun containerEnabled(): Boolean
fun setContainerEnable(enable: Boolean)
fun resetBuildOption(executeCount: Int)
fun transformCompatibility()
fun genTaskParams(): MutableMap<String, Any>
fun copyElements(elements: List<Element>): Container
// ç©éµç¸å
³
fun retryFreshMatrixOption()
fun fetchGroupContainers(): List<Container>?
fun fetchMatrixContext(): Map<String, String>?
}
4.2 Container ä¸å¤§å®ç°ç±»
4.2.1 TriggerContainer (触å容å¨)
data class TriggerContainer(
override var id: String? = null,
override var name: String = "",
override var elements: List<Element> = listOf(), // 触åå¨éå
var params: List<BuildFormProperty> = listOf(), // æµæ°´çº¿åæ°
var templateParams: List<BuildFormProperty>?, // 模æ¿åæ°
var buildNo: BuildNo? = null, // æå»ºçæ¬å·è§å
// ... å
¶ä» Container æ¥å£å段
) : Container {
companion object {
const val classType = "trigger"
}
}
ç¹ç¹:
- æ°¸è¿å¨
stages[0].containers[0]ä½ç½® - å 嫿µæ°´çº¿çå ¨å±åæ°å®ä¹
- å å«è§¦åå¨ (ManualTriggerElement, TimerTriggerElement, CodeWebHookTriggerElement ç)
- åªæä¸ä¸ªï¼ä¸å¯å é¤
æµæ°´çº¿åæ° (BuildFormProperty):
data class BuildFormProperty(
var id: String, // åæ°ID
var name: String?, // åæ°åç§°
var required: Boolean, // æ¯å¦å¿
å¡«
var type: BuildFormPropertyType, // åæ°ç±»å (STRING, BOOLEAN, ENUM, SVN_TAGç)
var defaultValue: Any, // é»è®¤å¼
var value: Any? = null, // 䏿¬¡æå»ºåå¼
var options: List<BuildFormValue>?, // 䏿é项
var desc: String?, // æè¿°
// ç¹æ®å段
var asInstanceInput: Boolean? = null // æ§å¶å®ä¾å页é¢"å®ä¾å
¥å"æé®
)
4.2.2 VMBuildContainer (èææºæå»ºå®¹å¨)
data class VMBuildContainer(
override var id: String? = null,
override var name: String = "æå»ºç¯å¢",
override var elements: List<Element> = listOf(),
// æå»ºæºé
ç½®
val baseOS: VMBaseOS, // åºç¡æä½ç³»ç» (LINUX, WINDOWS, MACOS)
val vmNames: Set<String> = setOf(), // 颿å®VMåç§°å表
val dispatchType: DispatchType? = null, // æå»ºæºè°åº¦ç±»å
// ç¯å¢åé
val buildEnv: Map<String, String>?, // 容å¨å¯å¨æ¶ç¯å¢åé
val customEnv: List<NameAndValue>?, // Agentå¯å¨æ¶èªå®ä¹ç¯å¢åé
// ç¬¬ä¸æ¹æå»ºæº
val thirdPartyAgentId: String? = null,
val thirdPartyAgentEnvId: String? = null,
val thirdPartyWorkspace: String? = null,
// æµç¨æ§å¶
var jobControlOption: JobControlOption?, // Jobæ§å¶é项
var mutexGroup: MutexGroup?, // äºæ¥ç»
// æå»ºç©éµ
var matrixControlOption: MatrixControlOption?, // ç©éµé
ç½®
var groupContainers: MutableList<VMBuildContainer>?, // åè£åçå容å¨
var matrixGroupId: String?, // æå±ç©éµç»ID
var matrixContext: Map<String, String>?, // ç©éµä¸ä¸æ
var showBuildResource: Boolean? = false,
var enableExternal: Boolean? = false, // æ¯å¦è®¿é®å¤ç½
var nfsSwitch: Boolean? = null // NFSæè½½å¼å
³
) : Container
æå»ºæºè°åº¦ç±»å (DispatchType):
ThirdPartyAgentIDDispatchType: ç¬¬ä¸æ¹æå»ºæºThirdPartyAgentEnvDispatchType: ç¬¬ä¸æ¹ç¯å¢DockerDispatchType: Docker容å¨LocalDispatchType: æ¬å°è°åº¦
4.2.3 NormalContainer (æ ç¼è¯ç¯å¢å®¹å¨)
data class NormalContainer(
override var id: String? = null,
override var name: String = "",
override var elements: List<Element> = listOf(),
var jobControlOption: JobControlOption?,
var mutexGroup: MutexGroup?,
// æå»ºç©éµæ¯æ
var matrixControlOption: MatrixControlOption?,
var groupContainers: MutableList<NormalContainer>?,
var matrixGroupId: String?,
var matrixContext: Map<String, String>?,
// ... å
¶ä» Container æ¥å£å段
) : Container {
companion object {
const val classType = "normal"
}
}
ç¹ç¹:
- æ éå¯å¨æå»ºæº
- ç¨äºæ§è¡æ ç¼è¯ç¯å¢æä»¶ï¼å¦äººå·¥å®¡æ ¸ãè´¨é红线ãAPIè°ç¨çï¼
- è½»é级ï¼å¯å¨å¿«
4.3 JobControlOption (Jobæµç¨æ§å¶)
data class JobControlOption(
val enable: Boolean = true, // æ¯å¦å¯ç¨Job
val prepareTimeout: Int? = null, // åå¤ç¯å¢è¶
æ¶æ¶é´ï¼åéï¼
var timeout: Int? = 900, // æ§è¡è¶
æ¶æ¶é´ï¼åéï¼
var timeoutVar: String? = null, // è¶
æ¶æ¶é´åéï¼æ¯æè¡¨è¾¾å¼ï¼
// è¿è¡æ¡ä»¶
val runCondition: JobRunCondition = JobRunCondition.STAGE_RUNNING,
val customVariables: List<NameAndValue>?, // èªå®ä¹å鿡件
val customCondition: String? = null, // èªå®ä¹æ¡ä»¶è¡¨è¾¾å¼
// Jobä¾èµ
val dependOnType: DependOnType? = null, // ä¾èµç±»å
var dependOnId: List<String>? = null, // ä¾èµçJobIDå表
val dependOnName: String? = null,
var dependOnContainerId2JobIds: Map<String, String>?, // containerIdä¸jobIdæ å°
val continueWhenFailed: Boolean? = false, // 失败继ç»
// å¹¶åæ§å¶ï¼ç¬¬ä¸æ¹æå»ºæºï¼
val singleNodeConcurrency: Int? = null, // åèç¹å¹¶åéå¶
val allNodeConcurrency: Int? = null // ææèç¹å¹¶åéå¶
)
JobRunCondition æä¸¾:
STAGE_RUNNING: Stageè¿è¡ä¸ï¼é»è®¤ï¼CUSTOM_VARIABLE_MATCH: èªå®ä¹åéå¹é CUSTOM_CONDITION_MATCH: èªå®ä¹æ¡ä»¶å¹é
4.4 æå»ºç©éµ (Matrix)
æå»ºç©éµå 许ä¸ä¸ª Job æ ¹æ®åæ°ç»ååè£æå¤ä¸ªå¹¶è¡æ§è¡çå Jobã
data class MatrixControlOption(
var strategyStr: String?, // çç¥å符串
var includeCaseStr: String?, // å
å«ç¨ä¾
var excludeCaseStr: String?, // æé¤ç¨ä¾
var maxConcurrency: Int? = null, // æå¤§å¹¶åæ°
var totalCount: Int? = null, // æ»ä»»å¡æ°ï¼è¿è¡æ¶è®¡ç®ï¼
var finishCount: Int? = null // 已宿æ°ï¼è¿è¡æ¶è®¡ç®ï¼
)
ç©éµå段:
matrixGroupFlag: æ è¯å½å容卿¯å¦ä¸ºç©éµç¶å®¹å¨groupContainers: åè£åçå容å¨éåï¼ç¶å®¹å¨ç¹æï¼matrixGroupId: æå±ç©éµç»ç containerHashIdï¼å容å¨ç¹æï¼matrixContext: å½åå容å¨çåæ°ç»åï¼å容å¨ç¹æï¼
示ä¾:
matrix:
strategy:
os: [linux, windows]
version: [1.0, 2.0]
# ä¼çæ4个åJob: (linux,1.0), (linux,2.0), (windows,1.0), (windows,2.0)
äºãElement (æä»¶/ä»»å¡) 详解
5.1 Element æ½è±¡ç±»å®ä¹
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type")
@JsonSubTypes(
JsonSubTypes.Type(value = CodeGitElement::class, name = CodeGitElement.classType),
JsonSubTypes.Type(value = LinuxScriptElement::class, name = LinuxScriptElement.classType),
JsonSubTypes.Type(value = WindowsScriptElement::class, name = WindowsScriptElement.classType),
JsonSubTypes.Type(value = ManualTriggerElement::class, name = ManualTriggerElement.classType),
JsonSubTypes.Type(value = MarketBuildAtomElement::class, name = MarketBuildAtomElement.classType),
// ... æ´å¤åç±»å
)
abstract class Element(
open val name: String, // ä»»å¡åç§°
open var id: String? = null, // ä»»å¡ID
// è¿è¡æ¶ç¶æ
open var status: String? = null, // ç¶æ
open var executeCount: Int = 1, // æ§è¡æ¬¡æ°
open var canRetry: Boolean? = null, // æ¯å¦å¯éè¯
open var retryCount: Int? = null, // æ»éè¯æ¬¡æ°
open var retryCountManual: Int? = null, // æå¨éè¯æ¬¡æ°
open var retryCountAuto: Int? = null, // èªå¨éè¯æ¬¡æ°
open var canSkip: Boolean? = null, // æ¯å¦å¯è·³è¿
// æä»¶çæ¬
open var version: String = "1.*", // æä»¶çæ¬
open var originVersion: String? = null, // åå§çæ¬
// æµç¨æ§å¶
open var additionalOptions: ElementAdditionalOptions? = null, // éå é项
open var stepId: String? = null, // ç¨æ·èªå®ä¹IDï¼ç¨äºä¸ä¸æåéï¼
// ç¯å¢åé
open var customEnv: List<NameAndValue>? = null, // èªå®ä¹ç¯å¢åé
// é误信æ¯
open var errorType: String? = null,
open var errorCode: Int? = null,
open var errorMsg: String? = null,
// å
¶ä»
open var timeCost: BuildRecordTimeCost? = null,
open var progressRate: Double? = null, // è¿åº¦
var asyncStatus: String? = null
) {
abstract fun getClassType(): String
open fun getAtomCode() = getClassType()
open fun getTaskAtom(): String = ""
open fun genTaskParams(): MutableMap<String, Any> = JsonUtil.toMutableMap(this)
fun elementEnabled(): Boolean {
return additionalOptions?.enable ?: true
}
fun transformCompatibility() {
if (additionalOptions != null && additionalOptions!!.timeoutVar.isNullOrBlank()) {
additionalOptions!!.timeoutVar = additionalOptions!!.timeout.toString()
}
}
fun initStatus(rerun: Boolean = false): BuildStatus {
return if (!elementEnabled()) {
BuildStatus.SKIP
} else if (rerun) {
BuildStatus.QUEUE
} else if (status == BuildStatus.SKIP.name) {
BuildStatus.SKIP
} else {
BuildStatus.QUEUE
}
}
}
5.2 ElementAdditionalOptions (æä»¶çº§æµç¨æ§å¶)
data class ElementAdditionalOptions(
var enable: Boolean = true, // æ¯å¦å¯ç¨
var continueWhenFailed: Boolean = false, // 失败继ç»
val manualSkip: Boolean? = null, // æå¨è·³è¿æé®
// éè¯é
ç½®
var retryWhenFailed: Boolean = false, // 失败éè¯
var retryCount: Int = 0, // éè¯æ¬¡æ°
val manualRetry: Boolean = true, // å
许æå¨éè¯
// è¶
æ¶é
ç½®
var timeout: Long? = 100, // è¶
æ¶æ¶é´ï¼åéï¼
var timeoutVar: String? = null, // è¶
æ¶åé
// è¿è¡æ¡ä»¶
var runCondition: RunCondition? = null,
val customVariables: List<NameAndValue>?, // èªå®ä¹å鿡件
var customCondition: String? = "", // èªå®ä¹æ¡ä»¶è¡¨è¾¾å¼
// æåé
ç½®
var pauseBeforeExec: Boolean? = false, // æ§è¡åæå
var subscriptionPauseUser: String? = "", // 订é
æåéç¥ç¨æ·
// Postä»»å¡
var elementPostInfo: ElementPostInfo? = null // Postä¿¡æ¯
)
RunCondition æä¸¾:
enum class RunCondition {
PRE_TASK_SUCCESS, // ææåç½®æä»¶æå
PRE_TASK_FAILED_BUT_CANCEL, // å置失败ä¹è¿è¡ï¼é¤éåæ¶ï¼
PRE_TASK_FAILED_EVEN_CANCEL, // å置失败ä¹è¿è¡ï¼å³ä½¿åæ¶ï¼
PRE_TASK_FAILED_ONLY, // åªæå置失败æè¿è¡
CUSTOM_VARIABLE_MATCH, // èªå®ä¹åéå¹é
CUSTOM_VARIABLE_MATCH_NOT_RUN, // èªå®ä¹åéå¹é
æ¶ä¸è¿è¡
PARENT_TASK_CANCELED_OR_TIMEOUT, // ç¶ä»»å¡åæ¶æè¶
æ¶
PARENT_TASK_FINISH // ç¶ä»»å¡ç»æ
}
5.3 å¸¸è§ Element åç±»å
5.3.1 触åå¨ç±»
ManualTriggerElement: æå¨è§¦åTimerTriggerElement: 宿¶è§¦åCodeGitWebHookTriggerElement: Git WebHook触åCodeGitlabWebHookTriggerElement: GitLab WebHook触åRemoteTriggerElement: è¿ç¨è§¦å
5.3.2 ä»£ç æåç±»
CodeGitElement: Gitæä»£ç CodeGitlabElement: GitLabæä»£ç CodeSvnElement: SVNæä»£ç GithubElement: GitHubæä»£ç
5.3.3 èæ¬æ§è¡ç±»
LinuxScriptElement: Linux Shellèæ¬WindowsScriptElement: Windows Batch/PowerShellèæ¬
5.3.4 å¸åºæä»¶
MarketBuildAtomElement: ç åååºæä»¶ï¼æç¼è¯ç¯å¢ï¼MarketBuildLessAtomElement: ç åååºæä»¶ï¼æ ç¼è¯ç¯å¢ï¼
5.3.5 å ¶ä»
ManualReviewUserTaskElement: äººå·¥å®¡æ ¸SubPipelineCallElement: åæµæ°´çº¿è°ç¨QualityGateInElement: è´¨é红线ï¼åå ¥ï¼QualityGateOutElement: è´¨é红线ï¼ååºï¼
å ãModel æä¹ åä¸åºåå
6.1 å卿¹å¼
Model 卿°æ®åºä¸ä»¥ JSON å符串 å½¢å¼åå¨ï¼
// åºåå
val modelJson = JsonUtil.toJson(model, formatted = false)
// ååºåå
val model = JsonUtil.to(modelJson, Model::class.java)
6.2 åå¨ä½ç½®
6.2.1 æµæ°´çº¿çæ¬è¡¨ (T_PIPELINE_RESOURCE_VERSION)
CREATE TABLE `T_PIPELINE_RESOURCE_VERSION` (
`PROJECT_ID` varchar(64) NOT NULL,
`PIPELINE_ID` varchar(64) NOT NULL,
`VERSION` int(11) NOT NULL,
`MODEL` mediumtext, -- Model JSONå符串
`CREATOR` varchar(64),
`CREATE_TIME` datetime,
PRIMARY KEY (`PROJECT_ID`, `PIPELINE_ID`, `VERSION`)
)
ç¨é: å卿µæ°´çº¿æ¯ä¸ªçæ¬ç宿´ Model
6.2.2 æå»ºè®°å½è¡¨ (T_PIPELINE_BUILD_RECORD_MODEL)
CREATE TABLE `T_PIPELINE_BUILD_RECORD_MODEL` (
`BUILD_ID` varchar(34) NOT NULL,
`PROJECT_ID` varchar(64) NOT NULL,
`PIPELINE_ID` varchar(64) NOT NULL,
`EXECUTE_COUNT` int(11) NOT NULL DEFAULT 1,
`MODEL` mediumtext, -- æå»ºè¿è¡æ¶ç Model å¿«ç
§
PRIMARY KEY (`BUILD_ID`, `EXECUTE_COUNT`)
)
ç¨é:
- åå¨æå»ºè¿è¡æ¶ç Model å¿«ç §
- å å«è¿è¡æ¶ç¶æ (status, executeCount, timeCostç)
- æ¯ææå»ºè¯¦æ çå±ç¤ºåéè¯
6.3 Model è·åæµç¨
// 1. ä»æå»ºè®°å½è¡¨è·å
fun getRecordModel(
projectId: String,
pipelineId: String,
version: Int,
buildId: String,
executeCount: Int
): Model? {
// ä» T_PIPELINE_BUILD_RECORD_MODEL è·åè®°å½
val buildRecordModel = buildRecordModelDao.getRecord(
projectId = projectId,
pipelineId = pipelineId,
buildId = buildId,
executeCount = executeCount
)
// ä» T_PIPELINE_RESOURCE_VERSION è·ååºç¡æ¨¡å
val resourceStr = pipelineResourceVersionDao.getVersionModelString(
projectId = projectId,
pipelineId = pipelineId,
version = version
)
// åå¹¶åºç¡æ¨¡ååè¿è¡æ¶æ°æ®
val fullModel = JsonUtil.to(resourceStr, Model::class.java)
val recordMap = JsonUtil.toMap(buildRecordModel.model)
return mergeModel(fullModel, recordMap)
}
ä¸ãModel å¨ä¸å¡æµç¨ä¸çè§è²
7.1 æµæ°´çº¿å建æµç¨
// 1. ç¨æ·æäº¤ Model
fun create(userId: String, model: Model): String {
// 2. æ ¡éª Model
modelCheckPlugin.checkModelIntegrity(model)
// 3. çæ pipelineId
val pipelineId = UUIDUtil.generate()
// 4. åºåå Model
val modelJson = JsonUtil.toJson(model, formatted = false)
// 5. ä¿åå°æ°æ®åº
pipelineResourceVersionDao.create(
projectId = projectId,
pipelineId = pipelineId,
version = 1,
model = modelJson,
creator = userId
)
return pipelineId
}
7.2 æµæ°´çº¿å¯å¨æµç¨
fun buildModel(buildInfo: BuildInfo, executeCount: Int) {
// 1. è·åæµæ°´çº¿ææ°çæ¬ç Model
val model = getRecordModel(
projectId = buildInfo.projectId,
pipelineId = buildInfo.pipelineId,
version = buildInfo.version,
buildId = buildInfo.buildId,
executeCount = executeCount
)
// 2. åå§åè¿è¡æ¶ç¶æ
model.stages.forEach { stage ->
stage.resetBuildOption(init = true)
stage.containers.forEach { container ->
container.resetBuildOption(executeCount)
container.elements.forEach { element ->
element.status = element.initStatus().name
}
}
}
// 3. ä¿åæå»ºå¿«ç
§
buildRecordModelDao.create(
buildId = buildInfo.buildId,
projectId = buildInfo.projectId,
pipelineId = buildInfo.pipelineId,
executeCount = executeCount,
model = JsonUtil.toJson(model)
)
}
7.3 æå»ºæ§è¡å¼æ
// Stage è°åº¦
fun scheduleStage(buildId: String, stageId: String) {
val model = getRecordModel(buildId)
val stage = model.getStage(stageId)
if (!stage.stageEnabled()) {
// Stageæªå¯ç¨ï¼è·³è¿
return
}
// æ£æ¥åå
¥æ¡ä»¶
if (stage.checkIn != null) {
// çå¾
äººå·¥å®¡æ ¸
waitForReview(stage.checkIn)
}
// è°åº¦ Containers
stage.containers.forEach { container ->
scheduleContainer(buildId, container)
}
}
// Container è°åº¦
fun scheduleContainer(buildId: String, container: Container) {
when (container) {
is VMBuildContainer -> {
// 1. å¯å¨æå»ºæº
dispatchService.startVM(container)
// 2. æ§è¡ Elements
container.elements.forEach { executeElement(buildId, it) }
}
is NormalContainer -> {
// ç´æ¥æ§è¡ Elements
container.elements.forEach { executeElement(buildId, it) }
}
}
}
å «ãModel ççæ¬å ¼å®¹æ§å¤ç
8.1 å ¼å®¹æ§æ¹æ³
// Model å±çº§è°ç¨
model.stages.forEach { stage ->
stage.transformCompatibility() // Stage级å«
stage.containers.forEach { container ->
container.transformCompatibility() // Container级å«
container.elements.forEach { element ->
element.transformCompatibility() // Element级å«
}
}
}
8.2 å ¸åå ¼å®¹åºæ¯
8.2.1 è¶ æ¶æ¶é´å段è¿ç§»
// æ§çæ¬: timeout æ¯ Int ç±»å
// æ°çæ¬: timeoutVar æ¯ String ç±»åï¼æ¯æåé
fun transformCompatibility() {
if (additionalOptions != null && additionalOptions!!.timeoutVar.isNullOrBlank()) {
// è¿ç§»: å° timeout å¤å¶å° timeoutVar
additionalOptions!!.timeoutVar = additionalOptions!!.timeout.toString()
}
}
8.2.2 åºå¼å段å¤ç
@Deprecated("å³å°è¢«timeCost代æ¿")
var startEpoch: Long? = null
@Deprecated("å³å°è¢«timeCost代æ¿")
var elapsed: Long? = null
çç¥: ä¿çåºå¼åæ®µä½æ è®° @Deprecatedï¼æ°é»è¾ä½¿ç¨ timeCost
ä¹ãæä½³å®è·µ
9.1 å建 Model
// 使ç¨é»è®¤æ¨¡å
val model = Model.defaultModel(
pipelineName = "My Pipeline",
userId = "admin"
)
// æ·»å Stage
val buildStage = Stage(
id = "stage-2",
name = "Build Stage",
containers = listOf(
VMBuildContainer(
id = "1",
name = "Build Job",
baseOS = VMBaseOS.LINUX,
elements = listOf(
LinuxScriptElement(
id = "e-1",
name = "Compile",
script = "./build.sh"
)
)
)
)
)
model.stages.add(buildStage)
9.2 éå Model
fun traverseModel(model: Model, action: (Element) -> Unit) {
model.stages.forEach { stage ->
stage.containers.forEach { container ->
container.elements.forEach { element ->
action(element)
}
}
}
}
// 使ç¨
traverseModel(model) { element ->
println("Element: ${element.name}, Type: ${element.getClassType()}")
}
9.3 ä¿®æ¹ Model ç¶æ
// æ´æ° Element ç¶æ
fun updateElementStatus(model: Model, elementId: String, status: BuildStatus) {
traverseModel(model) { element ->
if (element.id == elementId) {
element.status = status.name
element.executeCount++
}
}
// ä¿åå°æ°æ®åº
updateModel(buildId, model)
}
9.4 ç»è®¡ä¿¡æ¯
// ç»è®¡æ»ä»»å¡æ°
val totalTasks = model.taskCount()
// ç»è®¡æå®ç±»åä»»å¡
val scriptTaskCount = model.stages.sumOf { stage ->
stage.containers.sumOf { container ->
container.elements.count {
it is LinuxScriptElement || it is WindowsScriptElement
}
}
}
// ç»è®¡ Stage æ°é
val stageCount = model.stages.size
// ç»è®¡ Job æ°é
val jobCount = model.stages.sumOf { it.containers.size }
åã常è§é®é¢ (FAQ)
Q1: ModelãStageãContainerãElement ç id åæ®µæä»ä¹åºå«ï¼
A:
- Stage.id: ç³»ç»çæï¼æ ¼å¼
stage-{seq}ï¼ä¸å¯ç¼è¾ - Stage.stageIdForUser: ç¨æ·å¯ç¼è¾çèªå®ä¹ID
- Container.id / containerId: åºåIDï¼æ ¼å¼ä¸ºæ°åå符串
"1","2"ç - Container.containerHashId: å ¨å±å¯ä¸HashIDï¼ç¨äºè·¨æå»ºç容å¨è¿½è¸ª
- Container.jobId: ç¨æ·èªå®ä¹IDï¼ç¨äº Job ä¾èµé ç½®
- Element.id: ä»»å¡IDï¼æ ¼å¼
{Stageåºå·}-{Containeråºå·}-{Elementåºå·}(å¦"2-1-3") - Element.stepId: ç¨æ·èªå®ä¹IDï¼ç¨äºä¸ä¸æåéå¼ç¨ (å¦
steps.myStep.status)
Q2: 为ä»ä¹å¾å¤å段æ 记为 var è䏿¯ valï¼
A: è¿è¡æ¶éè¦æ´æ°è¿äºå段çå¼ï¼å¦ statusãexecuteCountãtimeCostï¼ï¼æä»¥å¿
é¡»æ¯å¯åç (var)ã
Q3: resetBuildOption() æ¹æ³ä»ä¹æ¶åè°ç¨ï¼
A:
- éè¯æå»º:
init = falseï¼ä¿çé¨ååå²ç¶æ - æ°æå»º:
init = trueï¼æ¸ 空ææè¿è¡æ¶ç¶æ
Q4: TriggerContainer çåæ°å¦ä½ä¼ éå°æå»ºä¸ï¼
A:
val triggerContainer = model.getTriggerContainer()
val params = triggerContainer.params
// æå»ºå¯å¨æ¶ï¼å° params 转æ¢ä¸ºæå»ºåé
val variables = params.associate {
it.id to (it.value ?: it.defaultValue)
}
Q5: å¦ä½å¤æä¸ä¸ª Stage æ¯å¦ä¸º FinallyStageï¼
A:
if (stage.finally) {
// è¿æ¯ FinallyStageï¼æ è®ºæµæ°´çº¿æåæå¤±è´¥é½ä¼æ§è¡
}
Q6: æå»ºç©éµå¦ä½å±å¼ï¼
A:
// 1. ç¶å®¹å¨é
ç½® matrixControlOption
val parentContainer = VMBuildContainer(
matrixControlOption = MatrixControlOption(
strategyStr = """{"os": ["linux", "windows"], "version": ["1.0", "2.0"]}"""
)
)
// 2. å¼æè§£æçç¥å¹¶çæå容å¨
val childContainers = matrixService.expand(parentContainer)
// çæ: (linux,1.0), (linux,2.0), (windows,1.0), (windows,2.0)
// 3. 设置ç¶å®¹å¨å段
parentContainer.groupContainers = childContainers.toMutableList()
parentContainer.matrixGroupFlag = true
// 4. 设置å容å¨å段
childContainers.forEach { child ->
child.matrixGroupId = parentContainer.containerHashId
child.matrixContext = mapOf("os" to "linux", "version" to "1.0") // 示ä¾
}
Q7: Post ä»»å¡å¦ä½å®ç°ï¼
A: éè¿ ElementAdditionalOptions.elementPostInfo é
ç½®ï¼
val mainElement = LinuxScriptElement(
id = "e-1",
name = "Main Task",
script = "./build.sh"
)
val postElement = LinuxScriptElement(
id = "e-2",
name = "Cleanup",
script = "./cleanup.sh",
additionalOptions = ElementAdditionalOptions(
elementPostInfo = ElementPostInfo(
parentElementId = "e-1", // å
³è主任å¡
postCondition = "failure" // å¤±è´¥æ¶æ§è¡
)
)
)
// Postä»»å¡ä¼å¨ä¸»ä»»å¡æ§è¡å®æ¯åæ ¹æ®æ¡ä»¶æ§è¡
åä¸ãæ£æ¥æ¸ å
å¨æä½ Model æ¶ï¼è¯·ç¡®è®¤ä»¥ä¸äºé¡¹ï¼
- ç»æå®æ´æ§: Model è³å°å å«ä¸ä¸ª Stageï¼ç¬¬ä¸ä¸ª Stage å¿ é¡»å å« TriggerContainer
- ID å¯ä¸æ§: åä¸å±çº§ç
idåæ®µä¸è½éå¤ - FinallyStage å¯ä¸: æå¤åªè½æä¸ä¸ª FinallyStageï¼ä¸å¿ 须卿åä½ç½®
- 容å¨ç±»åå¹é : 第ä¸ä¸ª Stage ç第ä¸ä¸ª Container å¿ é¡»æ¯ TriggerContainer
- åºååå
¼å®¹: ä½¿ç¨ Jackson ç
@JsonTypeInfoç¡®ä¿å¤æåºååæ£ç¡® - è¿è¡æ¶å段: ç¼æé¶æ®µä¸è¦è®¾ç½®è¿è¡æ¶å段ï¼å¦
statusãexecuteCountï¼ - çæ¬å
¼å®¹: è°ç¨
transformCompatibility()å¤çå岿°æ® - Job ä¾èµ: æ£æ¥
JobControlOption.dependOnIdä¸ç JobID æ¯å¦åå¨ - æµç¨æ§å¶: 确认
enableåæ®µãrunConditionçæµç¨æ§å¶é项é ç½®æ£ç¡® - è¶
æ¶é
ç½®: ä¼å
使ç¨
timeoutVarèétimeoutï¼æ¯æåéè¡¨è¾¾å¼ - ç©éµé ç½®: ç©éµç¶å®¹å¨ä¸åºæå®é ä»»å¡ï¼ä»»å¡åºå¨å容å¨ä¸
- åæ°éªè¯: TriggerContainer ç
paramsåæ®µéªè¯requiredãvalueNotEmptyç约æ
åäºãç¸å ³æä»¶ç´¢å¼
æ ¸å¿æ¨¡åæä»¶
src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/
âââ Model.kt # Model é¡¶å±å®ä¹
âââ container/
â âââ Stage.kt # Stage å®ä¹
â âââ Container.kt # Container æ¥å£
â âââ TriggerContainer.kt # 触å容å¨
â âââ VMBuildContainer.kt # èææºæå»ºå®¹å¨
â âââ NormalContainer.kt # æ ç¼è¯ç¯å¢å®¹å¨
â âââ JobTemplateContainer.kt # Job模æ¿å®¹å¨
âââ pojo/
â âââ element/
â â âââ Element.kt # Element æ½è±¡ç±»
â â âââ ElementAdditionalOptions.kt # æä»¶æµç¨æ§å¶
â â âââ agent/ # æç¼è¯ç¯å¢æä»¶
â â âââ trigger/ # 触å卿件
â â âââ market/ # å¸åºæä»¶
â âââ BuildFormProperty.kt # æµæ°´çº¿åæ°å®ä¹
â âââ StagePauseCheck.kt # Stageåå
¥ååº
âââ option/
âââ JobControlOption.kt # Jobæµç¨æ§å¶
âââ StageControlOption.kt # Stageæµç¨æ§å¶
ä¸å¡é»è¾æä»¶
src/backend/ci/core/process/
âââ biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/
â âââ record/
â â âââ BaseBuildRecordService.kt # Modelè·ååä¿å
â â âââ PipelineBuildRecordService.kt # æµæ°´çº¿æå»ºè®°å½
â âââ PipelineBuildDetailService.kt # æå»ºè¯¦æ
æå¡
âââ biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/
â âââ BuildStartControl.kt # æå»ºå¯å¨æ§å¶
â âââ BuildEndControl.kt # æå»ºç»ææ§å¶
âââ biz-process/src/main/kotlin/com/tencent/devops/process/service/
âââ PipelineRepositoryService.kt # æµæ°´çº¿ä»åºæå¡
æ°æ®åºDAO
src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/
âââ PipelineResourceVersionDao.kt # æµæ°´çº¿çæ¬è¡¨
âââ PipelineBuildRecordModelDao.kt # æå»ºè®°å½è¡¨
åä¸ãè¾ å©æ°æ®ç»æè¯¦è§£
æ¬ç« 详ç»ä»ç» Model ä¸ä½¿ç¨çåç±»è¾ å©æ°æ®ç»æã
13.1 DispatchType (æå»ºæºè°åº¦ç±»å)
DispatchType æ¯ä¸ä¸ªæ½è±¡ç±»ï¼å®ä¹äºæå»ºæºçè°åº¦æ¹å¼ï¼
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "buildType")
@JsonSubTypes(
JsonSubTypes.Type(value = DockerDispatchType::class, name = "DOCKER"),
JsonSubTypes.Type(value = KubernetesDispatchType::class, name = "KUBERNETES"),
JsonSubTypes.Type(value = ThirdPartyAgentIDDispatchType::class, name = "THIRD_PARTY_AGENT_ID"),
JsonSubTypes.Type(value = ThirdPartyAgentEnvDispatchType::class, name = "THIRD_PARTY_AGENT_ENV"),
JsonSubTypes.Type(value = ThirdPartyDevCloudDispatchType::class, name = "THIRD_PARTY_DEVCLOUD")
)
abstract class DispatchType(
open var value: String, // è°åº¦å¼ï¼å¦éååãAgentIDçï¼
open val routeKeySuffix: DispatchRouteKeySuffix? // è·¯ç±é®åç¼
) {
// æ¿æ¢åé
fun replaceVariable(variables: Map<String, String>) {
value = EnvUtils.parseEnv(value, variables)
replaceField(variables)
}
// è·åæå»ºç±»å
abstract fun buildType(): BuildType
// æ¿æ¢èªå®ä¹å段
protected abstract fun replaceField(variables: Map<String, String>)
// ä¿å忏
çæ°æ®
abstract fun cleanDataBeforeSave()
}
è°åº¦ç±»å说æ:
| ç±»å | 说æ | 使ç¨åºæ¯ |
|---|---|---|
DOCKER |
Docker 容å¨è°åº¦ | å ¬å ±æå»ºæºãDocker éåæå»º |
KUBERNETES |
Kubernetes è°åº¦ | K8s é群æå»º |
THIRD_PARTY_AGENT_ID |
ç¬¬ä¸æ¹æå»ºæºï¼æå®IDï¼ | æå®ç¹å®æå»ºæº |
THIRD_PARTY_AGENT_ENV |
ç¬¬ä¸æ¹æå»ºæºï¼ç¯å¢ï¼ | ä»ç¯å¢æ± ä¸éæ©æå»ºæº |
THIRD_PARTY_DEVCLOUD |
äºå¼åæº | äºæ¡é¢/è¿ç¨å¼åç¯å¢ |
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/type/DispatchType.kt
13.2 MutexGroup (äºæ¥ç»)
äºæ¥ç»ç¨äºæ§å¶å䏿¶å»åªæä¸ä¸ª Job å¯ä»¥æ§è¡ï¼
data class MutexGroup(
val enable: Boolean, // æ¯å¦å¯ç¨
val mutexGroupName: String? = "", // äºæ¥ç»åç§°
val queueEnable: Boolean, // æ¯å¦å¯ç¨æé
var timeout: Int = 0, // æéçå¾
è¶
æ¶ï¼åéï¼ï¼0表示ä¸çå¾
ç´æ¥å¤±è´¥
var timeoutVar: String? = null, // è¶
æ¶åéï¼æ¯æè¡¨è¾¾å¼ï¼
val queue: Int = 0, // æééå大å°
var runtimeMutexGroup: String? = null, // è¿è¡æ¶å®é
äºæ¥éåç§°
var linkTip: String? = null // å ç¨éå®çä¿¡æ¯æç¤º
) {
// è·åè¿è¡æ¶äºæ¥ç»åç§°
fun fetchRuntimeMutexGroup() = runtimeMutexGroup ?: mutexGroupName ?: ""
// çæäºæ¥é Redis Key
fun genMutexLockKey(projectId: String): String {
val mutexGroupName = fetchRuntimeMutexGroup()
return "lock:container:mutex:$projectId:$mutexGroupName:lock"
}
// çææé Redis Key
fun genMutexQueueKey(projectId: String): String {
val mutexGroupName = fetchRuntimeMutexGroup()
return "lock:container:mutex:$projectId:$mutexGroupName:queue"
}
}
使ç¨åºæ¯:
- æ°æ®åºé¨ç½²ï¼å䏿¶å»åªè½æä¸ä¸ªé¨ç½²ä»»å¡
- èµæºç«äºï¼å¤æµæ°´çº¿å ±äº«åä¸èµæº
- é¡ºåºæ§è¡ï¼ç¡®ä¿ä»»å¡æé¡ºåºæ§è¡
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/container/MutexGroup.kt
13.3 MatrixControlOption (æå»ºç©éµé ç½®)
æå»ºç©éµå 许ä¸ä¸ª Job æ ¹æ®åæ°ç»ååè£æå¤ä¸ªå¹¶è¡å Jobï¼
data class MatrixControlOption(
val strategyStr: String? = null, // åè£çç¥ï¼JSON/YAMLæ ¼å¼ï¼
val includeCaseStr: String? = null, // é¢å¤å
å«çåæ°ç»å
val excludeCaseStr: String? = null, // æé¤çåæ°ç»å
val fastKill: Boolean? = false, // 失败快éç»æ¢æ´ä¸ªç©éµ
var maxConcurrency: Int? = 5, // æå¤§å¹¶åæ°
var customDispatchInfo: DispatchInfo?, // èªå®ä¹è°åº¦ä¿¡æ¯
var totalCount: Int? = null, // ç©éµæ»æ°éï¼è¿è¡æ¶è®¡ç®ï¼
var finishCount: Int? = null // 已宿æ°éï¼è¿è¡æ¶è®¡ç®ï¼
) {
companion object {
const val MATRIX_CASE_MAX_COUNT = 256 // ç©éµæå¤§ç»åæ°
}
// å°é
置转æ¢ä¸ºç©éµé
置对象
fun convertMatrixConfig(buildContext: Map<String, String>): MatrixConfig {
// è§£æ strategyStrï¼æ¯æ YAML å JSON æ ¼å¼ï¼
// è§£æ includeCaseStr å excludeCaseStr
// è¿å MatrixConfig 对象
}
}
ç©éµçç¥ç¤ºä¾:
# YAML æ ¼å¼
strategy:
os: [linux, windows, macos]
node: [14, 16, 18]
include:
- os: linux
node: 20
exclude:
- os: macos
node: 14
// JSON æ ¼å¼
{
"os": ["linux", "windows"],
"version": ["1.0", "2.0"]
}
ç©éµå±å¼æµç¨:
- è§£æ
strategyStrçæç¬å¡å°ç§¯ - æ·»å
includeCaseSträ¸çé¢å¤ç»å - ç§»é¤
excludeCaseSträ¸çæé¤ç»å - çæå容å¨å表
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/option/MatrixControlOption.kt
13.4 StagePauseCheck (Stage åå ¥ååºé ç½®)
Stage åå ¥ååºç¨äºå¨ Stage æ§è¡ååè¿è¡äººå·¥å®¡æ ¸æè´¨éçº¢çº¿æ£æ¥ï¼
data class StagePauseCheck(
var manualTrigger: Boolean? = false, // æ¯å¦äººå·¥è§¦å
var status: String? = null, // å®¡æ ¸ç¶æ
var reviewDesc: String? = null, // å®¡æ ¸è¯´æ
var reviewGroups: MutableList<StageReviewGroup>?, // å®¡æ ¸æµé
ç½®
var reviewParams: List<ManualReviewParam>?, // å®¡æ ¸åé
var timeout: Int? = 24, // å®¡æ ¸è¶
æ¶ï¼å°æ¶ï¼
var ruleIds: List<String>? = null, // è´¨é红线è§åID
var checkTimes: Int? = null, // è´¨éçº¢çº¿æ£æ¥æ¬¡æ°
var markdownContent: Boolean? = false, // æ¯å¦ Markdown æ ¼å¼
var notifyType: MutableList<String>?, // éç¥ç±»å
var notifyGroup: MutableList<String>? // ä¼ä¸å¾®ä¿¡ç¾¤ID
) {
// è·åå½åçå¾
å®¡æ ¸çç»
fun groupToReview(): StageReviewGroup? { ... }
// å¤æç¨æ·æ¯å¦å¨å®¡æ ¸äººååä¸
fun reviewerContains(userId: String): Boolean { ... }
// å®¡æ ¸éè¿/驳å
fun reviewGroup(
userId: String,
action: ManualReviewAction,
groupId: String?,
params: List<ManualReviewParam>?,
suggest: String?
): StageReviewGroup? { ... }
// åå§åå®¡æ ¸ç»ID
fun fixReviewGroups(init: Boolean) { ... }
// æ¿æ¢å®¡æ ¸äººåé
fun parseReviewVariables(variables: Map<String, String>, dialect: IPipelineDialect) { ... }
// éè¯æ¶éç½®ç¶æ
fun retryRefresh() { ... }
}
StageReviewGroup (å®¡æ ¸ç»):
data class StageReviewGroup(
var id: String? = null, // å®¡æ ¸ç»IDï¼åå°çæï¼
val name: String = "Flow 1", // å®¡æ ¸ç»åç§°
var reviewers: List<String> = listOf(), // å®¡æ ¸äººåå表
var groups: List<String> = listOf(), // å®¡æ ¸ç¨æ·ç»
var status: String? = null, // å®¡æ ¸ç»æï¼PROCESS/ABORTï¼
var operator: String? = null, // å®¡æ ¸æä½äºº
var reviewTime: Long? = null, // å®¡æ ¸æ¶é´
var suggest: String? = null, // å®¡æ ¸å»ºè®®
var params: List<ManualReviewParam>? // å®¡æ ¸ä¼ å
¥åé
)
å®¡æ ¸æµç¨:
Stage å¼å§
â
checkIn ä¸ä¸ºç©ºï¼ ââæ¯âââ çå¾
å®¡æ ¸
â â
å¦ å®¡æ ¸éè¿ï¼ ââå¦âââ ç»æ¢ Stage
â â
æ§è¡ Containers æ¯
â â
checkOut ä¸ä¸ºç©ºï¼ ââæ¯âââ çå¾
å®¡æ ¸
â â
å¦ å®¡æ ¸éè¿ï¼ ââå¦âââ æ 记失败
â â
Stage ç»æ æ¯
â
Stage ç»æ
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/StagePauseCheck.kt
13.5 BuildNo (æå»ºçæ¬å·)
BuildNo ç¨äºç®¡çæµæ°´çº¿çæå»ºçæ¬å·ï¼
data class BuildNo(
var buildNo: Int, // æå»ºå·åå§å¼
val buildNoType: BuildNoType, // æå»ºå·ç±»å
var required: Boolean? = false, // æ¯å¦å¿
å¡«
var currentBuildNo: Int? = null // å½åææ°å¼
)
enum class BuildNoType {
CONSISTENT, // åºå®å¼ï¼ä¸èªå¢ï¼
SUCCESS_BUILD_INCREMENT, // æåæå»ºåèªå¢
EVERY_BUILD_INCREMENT // æ¯æ¬¡æå»ºèªå¢
}
使ç¨åºæ¯:
CONSISTENT: çæ¬å·ç±ç¨æ·æå¨æå®SUCCESS_BUILD_INCREMENT: åªææå»ºæåæéå¢çæ¬å·EVERY_BUILD_INCREMENT: æ¯æ¬¡è§¦åæå»ºé½éå¢çæ¬å·
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/BuildNo.kt
13.6 BuildRecordTimeCost (èæ¶ç»è®¡)
BuildRecordTimeCost ç¨äºè®°å½åå±çº§çæ§è¡èæ¶ï¼
data class BuildRecordTimeCost(
var systemCost: Long = 0, // ç³»ç»èæ¶ï¼ç±æ»èæ¶åå»å
¶ä»å¾åºï¼
var executeCost: Long = 0, // æ§è¡èæ¶
var waitCost: Long = 0, // çå¾
èæ¶ï¼æé+äººå·¥å®¡æ ¸ï¼
var queueCost: Long = 0, // æéèæ¶ï¼å¹¶å/äºæ¥ç»ï¼
var totalCost: Long = 0 // æ»èæ¶ï¼ç»ææ¶é´-å¼å§æ¶é´ï¼
)
èæ¶è®¡ç®å ¬å¼:
totalCost = systemCost + executeCost + waitCost
waitCost >= queueCost // waitCost å
å« queueCost
åºç¨å±çº§:
- Model 级å«: æ´ä¸ªæµæ°´çº¿çèæ¶
- Stage 级å«: åä¸ªé¶æ®µçèæ¶
- Container 级å«: å个 Job çèæ¶
- Element 级å«: å个æä»¶çèæ¶
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/time/BuildRecordTimeCost.kt
13.7 ElementPostInfo (Post ä»»å¡ä¿¡æ¯)
ElementPostInfo ç¨äºé ç½®Post ä»»å¡ï¼å¨ä¸»ä»»å¡æ§è¡åæ§è¡çæ¸ çä»»å¡ï¼ï¼
data class ElementPostInfo(
val postEntryParam: String, // å
¥å£åæ°
val postCondition: String, // æ§è¡æ¡ä»¶ï¼always/success/failureï¼
var parentElementId: String, // ç¶å
ç´ ID
val parentElementName: String, // ç¶å
ç´ åç§°
val parentElementJobIndex: Int // ç¶å
ç´ å¨ Job ä¸çä½ç½®
)
æ§è¡æ¡ä»¶:
always: æ è®ºä¸»ä»»å¡æåæå¤±è´¥é½æ§è¡success: åªæä¸»ä»»å¡æåææ§è¡failure: åªæä¸»ä»»å¡å¤±è´¥ææ§è¡
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/element/ElementPostInfo.kt
ååãBuildStatus (æå»ºç¶æ) 详解
BuildStatus æ¯ä¸ä¸ªæ ¸å¿æä¸¾ï¼å®ä¹äºæå»ºè¿ç¨ä¸ææå¯è½çç¶æï¼
enum class BuildStatus(val statusName: String, val visible: Boolean) {
// æç»æ - æå
SUCCEED("succeed", true), // 0 æå
SKIP("skip", true), // 11 è·³è¿
REVIEW_PROCESSED("reviewProcessed", true), // 7 å®¡æ ¸éè¿
QUALITY_CHECK_PASS("qualityCheckPass", true),// 25 è´¨é红线éè¿
STAGE_SUCCESS("stageSuccess", true), // 22 Stageæåï¼äººå·¥åæ¶åï¼
// æç»æ - 失败
FAILED("failed", true), // 1 失败
CANCELED("canceled", true), // 2 åæ¶
TERMINATE("terminate", true), // 4 ç»æ¢
REVIEW_ABORT("reviewAbort", true), // 6 å®¡æ ¸é©³å
HEARTBEAT_TIMEOUT("heartbeatTimeout", true), // 8 å¿è·³è¶
æ¶
QUALITY_CHECK_FAIL("qualityCheckFail", true),// 12 è´¨é红线失败
QUEUE_TIMEOUT("queueTimeout", true), // 17 æéè¶
æ¶
EXEC_TIMEOUT("execTimeout", true), // 18 æ§è¡è¶
æ¶
QUOTA_FAILED("quotaFailed", true), // 23 é
é¢å¤±è´¥
// ä¸é´æ - è¿è¡ä¸
RUNNING("running", true), // 3 è¿è¡ä¸
REVIEWING("reviewing", true), // 5 å®¡æ ¸ä¸
PREPARE_ENV("prepareEnv", true), // 9 åå¤ç¯å¢ä¸
LOOP_WAITING("loopWaiting", true), // 14 轮循çå¾
ï¼äºæ¥ç»ï¼
CALL_WAITING("callWaiting", true), // 15 çå¾
åè°
PAUSE("pause", true), // 21 æåæ§è¡
DEPENDENT_WAITING("dependentWaiting", true), // 24 ä¾èµçå¾
QUALITY_CHECK_WAIT("qualityCheckWait", true),// 26 è´¨é红线çå¾
TRIGGER_REVIEWING("triggerReviewing", true), // 27 触åå¾
å®¡æ ¸
// åå§æ
QUEUE("queue", true), // 13 æé
QUEUE_CACHE("queueCache", true), // 19 éåå¾
å¤çï¼ç¬æï¼
RETRY("retry", true), // 20 éè¯
// ä¸å¯è§ç¶æ
UNEXEC("unexec", false), // 10 仿ªæ§è¡
TRY_FINALLY("tryFinally", false), // 16 åå°ç¶æ
UNKNOWN("unknown", false); // 99 æªç¥
// ç¶æå¤ææ¹æ³
fun isNeverRun(): Boolean = this == UNEXEC || this == TRIGGER_REVIEWING
fun isFinish(): Boolean = isFailure() || isSuccess() || isCancel()
fun isFailure(): Boolean = this == FAILED || isPassiveStop() || isTimeout()
fun isSuccess(): Boolean = this == SUCCEED || this == SKIP || this == REVIEW_PROCESSED
fun isCancel(): Boolean = this == CANCELED
fun isRunning(): Boolean = this == RUNNING || this == LOOP_WAITING || ...
fun isReadyToRun(): Boolean = this == QUEUE || this == QUEUE_CACHE || this == RETRY
fun isPause(): Boolean = this == PAUSE
fun isTimeout(): Boolean = this == QUEUE_TIMEOUT || this == EXEC_TIMEOUT || ...
}
ç¶æè½¬æ¢å¾:
âââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â
â¼ â
QUEUE âââ QUEUE_CACHE âââ PREPARE_ENV âââ RUNNING âââ SUCCEED â
â â â â â â
â â â â â¼ â
â â â â STAGE_SUCCESS â
â â â â â
â â â ââââ FAILED âââââââââââââââââ¤
â â â â â
â â â ââââ CANCELED âââââââââââââââ¤
â â â â â
â â â ââââ EXEC_TIMEOUT âââââââââââ¤
â â â â â
â â â ââââ PAUSE âââ RUNNING â
â â â â â
â â â ââââ REVIEWING âââ REVIEW_PROCESSED
â â â â â
â â â ââââ REVIEW_ABORT ââ¤
â â â â
â â ââââ HEARTBEAT_TIMEOUT âââââââââââââââââââââ¤
â â â
â ââââ DEPENDENT_WAITING âââ PREPARE_ENV â
â â
ââââ QUEUE_TIMEOUT âââââââââââââââââââââââââââââââââââââââââââââââââââââ
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/enums/BuildStatus.kt
åäºãElement åç±»å详解
15.1 ManualTriggerElement (æå¨è§¦å)
data class ManualTriggerElement(
override val name: String = "æå¨è§¦å",
override var id: String? = null,
override var status: String? = null,
override var stepId: String? = null,
var canElementSkip: Boolean? = false, // æ¯å¦å¯è·³è¿æä»¶
var useLatestParameters: Boolean? = false, // ä½¿ç¨æè¿ä¸æ¬¡åæ°
var buildMsg: String? = null // é»è®¤æå»ºä¿¡æ¯
) : Element(name, id, status) {
companion object {
const val classType = "manualTrigger"
}
// æ¯æçå¯å¨ç±»å
private val startTypeSet = setOf(
StartType.MANUAL.name, // æå¨è§¦å
StartType.SERVICE.name, // æå¡è°ç¨
StartType.PIPELINE.name // åæµæ°´çº¿è°ç¨
)
}
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/element/trigger/ManualTriggerElement.kt
15.2 TimerTriggerElement (宿¶è§¦å)
data class TimerTriggerElement(
override val name: String = "宿¶è§¦å",
override var id: String? = null,
override var status: String? = null,
override var version: String = "1.*",
override var stepId: String? = null,
@Deprecated("ä½¿ç¨ advanceExpression")
val expression: String? = null, // æ§ç Cron 表达å¼
val newExpression: List<String>? = null, // æ°ç Cron 表达å¼å表
val advanceExpression: List<String>? = null, // é«çº§è¡¨è¾¾å¼ï¼æ¯æåéï¼
val noScm: Boolean? = false, // ä»£ç æªæ´æ°ä¸è§¦å
val branches: List<String>? = null, // æå®åæ¯
val repositoryType: TriggerRepositoryType?, // 代ç åºç±»å
val repoHashId: String? = null, // 代ç åº HashId
val repoName: String? = null, // 代ç åºå«å
val startParams: String? = null // å¯å¨åæ° JSON
) : Element(name, id, status) {
companion object {
const val classType = "timerTrigger"
}
// è½¬æ¢ Cron 表达å¼ï¼Unix â Quartzï¼
fun convertExpressions(params: Map<String, String>): Set<String> { ... }
// è§£æå¯å¨åæ°
fun convertStartParams(): Map<String, String>? { ... }
}
Cron è¡¨è¾¾å¼æ ¼å¼:
- Unix æ ¼å¼:
0 0 * * *(å æ¶ æ¥ æ å¨) - Quartz æ ¼å¼:
0 0 0 * * ?(ç§ å æ¶ æ¥ æ å¨)
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/element/trigger/TimerTriggerElement.kt
15.3 LinuxScriptElement (Linux èæ¬)
data class LinuxScriptElement(
override val name: String = "æ§è¡Linuxèæ¬",
override var id: String? = null,
override var status: String? = null,
override var stepId: String? = null,
override var customEnv: List<NameAndValue>? = null,
val errorFAQUrl: String? = null, // FAQ 龿¥
val scriptType: BuildScriptType, // èæ¬ç±»åï¼SHELLï¼
val script: String, // èæ¬å
容
val continueNoneZero: Boolean?, // é0éåºç ç»§ç»æ§è¡
val enableArchiveFile: Boolean? = false, // å¯ç¨å¤±è´¥å½æ¡£
val archiveFile: String? = null, // 彿¡£æä»¶è·¯å¾
override var additionalOptions: ElementAdditionalOptions? = null
) : Element(name, id, status, additionalOptions = additionalOptions) {
companion object {
const val classType = "linuxScript"
}
// çæä»»å¡åæ°ï¼URL ç¼ç èæ¬ï¼
override fun genTaskParams(): MutableMap<String, Any> {
val mutableMap = super.genTaskParams()
mutableMap["script"] = URLEncoder.encode(script, "UTF-8")
return mutableMap
}
}
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/element/agent/LinuxScriptElement.kt
15.4 MarketBuildAtomElement (ç åååºæä»¶)
data class MarketBuildAtomElement(
override val name: String = "ä»»å¡åç§°ç±ç¨æ·èªå·±å¡«å",
override var id: String? = null,
override var status: String? = null,
var atomCode: String = "", // æä»¶å¯ä¸æ è¯
override var version: String = "1.*", // æä»¶çæ¬
override var stepId: String? = null,
override var customEnv: List<NameAndValue>? = null,
var data: Map<String, Any> = mapOf(), // æä»¶åæ°æ°æ®
override var additionalOptions: ElementAdditionalOptions? = null
) : Element(name, id, status, additionalOptions = additionalOptions) {
companion object {
const val classType = "marketBuild"
}
override fun getAtomCode(): String = atomCode
// 转æ¢ä¸º YAML æ ¼å¼
override fun transferYaml(defaultValue: JSONObject?): PreStep {
val input = data["input"] as Map<String, Any>? ?: emptyMap()
return PreStep(
name = name,
id = stepId,
uses = "${getAtomCode()}@$version",
namespace = data["namespace"]?.toString()?.ifBlank { null },
with = TransferUtil.simplifyParams(defaultValue, input).ifEmpty { null }
)
}
}
data åæ®µç»æ:
data = mapOf(
"input" to mapOf( // æä»¶è¾å
¥åæ°
"param1" to "value1",
"param2" to "value2"
),
"output" to mapOf( // æä»¶è¾åºåæ°
"result" to "string"
),
"namespace" to "myNamespace" // å½å空é´
)
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/element/market/MarketBuildAtomElement.kt
åå ãBuildFormPropertyType (åæ°ç±»å) 详解
BuildFormPropertyType å®ä¹äºæµæ°´çº¿åæ°çææå¯ç¨ç±»åï¼
enum class BuildFormPropertyType(val value: String) {
STRING("string"), // å符串
TEXTAREA("textarea"), // å¤è¡ææ¬
ENUM("enum"), // æä¸¾ï¼ä¸æåéï¼
DATE("date"), // æ¥æ
LONG("long"), // é¿æ´å
BOOLEAN("boolean"), // å¸å°å¼
SVN_TAG("svn_tag"), // SVN Tag
GIT_REF("git_ref"), // Git å¼ç¨ï¼åæ¯/Tagï¼
REPO_REF("repo_ref"), // 代ç åºå¼ç¨
MULTIPLE("multiple"), // å¤é
CODE_LIB("code_lib"), // 代ç åº
CONTAINER_TYPE("container_type"), // æå»ºæºç±»å
ARTIFACTORY("artifactory"), // çæ¬ä»åº
SUB_PIPELINE("sub_pipeline"), // åæµæ°´çº¿
CUSTOM_FILE("custom_file"), // èªå®ä¹ä»åºæä»¶
PASSWORD("password"), // å¯ç ï¼å å¯åå¨ï¼
TEMPORARY("do not storage") // 临æ¶åæ°ï¼ä¸åå¨ï¼
}
åç±»å说æ:
| ç±»å | å端ç»ä»¶ | 说æ |
|---|---|---|
STRING |
åè¡è¾å ¥æ¡ | æ®éåç¬¦ä¸²åæ° |
TEXTAREA |
å¤è¡è¾å ¥æ¡ | é¿ææ¬åæ° |
ENUM |
ä¸æéæ©æ¡ | éé
å options åæ®µ |
BOOLEAN |
å¼å ³/å¤éæ¡ | true/false |
GIT_REF |
åæ¯éæ©å¨ | ä»ä»£ç åºè·å忝å表 |
CODE_LIB |
代ç åºéæ©å¨ | 鿩项ç®ä¸ç代ç åº |
SUB_PIPELINE |
æµæ°´çº¿éæ©å¨ | 鿩项ç®ä¸çæµæ°´çº¿ |
PASSWORD |
å¯ç è¾å ¥æ¡ | å¼ä¼å å¯åå¨ |
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/enums/BuildFormPropertyType.kt
åä¸ãVMBaseOS (æä½ç³»ç»ç±»å)
VMBaseOS å®ä¹äºæå»ºæºæ¯æçæä½ç³»ç»ç±»åï¼
enum class VMBaseOS {
MACOS, // macOS ç³»ç»
LINUX, // Linux ç³»ç»
WINDOWS, // Windows ç³»ç»
ALL // ææç³»ç»ï¼ç¨äºæ ç¼è¯ç¯å¢ï¼
}
使ç¨åºæ¯:
VMBuildContainer.baseOS: æå® Job è¿è¡çæä½ç³»ç»- æå»ºæºè°åº¦æ¶æ ¹æ®
baseOSéæ©åéçæå»ºæº
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/enums/VMBaseOS.kt
åå «ãè¿è¡æ¡ä»¶æä¸¾è¯¦è§£
18.1 JobRunCondition (Job è¿è¡æ¡ä»¶)
enum class JobRunCondition {
STAGE_RUNNING, // å½å Stage å¼å§è¿è¡æ¶ï¼é»è®¤ï¼
CUSTOM_VARIABLE_MATCH, // èªå®ä¹åéå
¨é¨æ»¡è¶³æ¶è¿è¡
CUSTOM_VARIABLE_MATCH_NOT_RUN, // èªå®ä¹åéå
¨é¨æ»¡è¶³æ¶ä¸è¿è¡
CUSTOM_CONDITION_MATCH, // 满足èªå®ä¹æ¡ä»¶è¡¨è¾¾å¼æ¶è¿è¡
PREVIOUS_STAGE_SUCCESS, // 䏿¸¸ Stage æåæ¶
PREVIOUS_STAGE_FAILED, // 䏿¸¸ Stage 失败æ¶
PREVIOUS_STAGE_CANCEL // 䏿¸¸ Stage åæ¶æ¶
}
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/enums/JobRunCondition.kt
18.2 StageRunCondition (Stage è¿è¡æ¡ä»¶)
enum class StageRunCondition {
AFTER_LAST_FINISHED, // ä¸ä¸ªé¶æ®µæ§è¡ç»æï¼é»è®¤ï¼
CUSTOM_VARIABLE_MATCH, // èªå®ä¹åéå
¨é¨æ»¡è¶³æ¶è¿è¡
CUSTOM_VARIABLE_MATCH_NOT_RUN, // èªå®ä¹åéå
¨é¨æ»¡è¶³æ¶ä¸è¿è¡
CUSTOM_CONDITION_MATCH // 满足èªå®ä¹æ¡ä»¶è¡¨è¾¾å¼æ¶è¿è¡
}
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/enums/StageRunCondition.kt
18.3 RunCondition (Element è¿è¡æ¡ä»¶)
enum class RunCondition {
PRE_TASK_SUCCESS, // ææåç½®æä»¶è¿è¡æåæ¶
PRE_TASK_FAILED_BUT_CANCEL, // å置失败ä¹è¿è¡ï¼é¤éè¢«åæ¶ï¼
PRE_TASK_FAILED_EVEN_CANCEL, // å置失败ä¹è¿è¡ï¼å³ä½¿è¢«åæ¶ï¼
PRE_TASK_FAILED_ONLY, // åªæå置失败æè¿è¡
CUSTOM_VARIABLE_MATCH, // èªå®ä¹åéå
¨é¨æ»¡è¶³æ¶è¿è¡
CUSTOM_VARIABLE_MATCH_NOT_RUN, // èªå®ä¹åéå
¨é¨æ»¡è¶³æ¶ä¸è¿è¡
PARENT_TASK_CANCELED_OR_TIMEOUT, // ç¶ä»»å¡åæ¶æè¶
æ¶æ¶è¿è¡
PARENT_TASK_FINISH // ç¶ä»»å¡ç»æå°±è¿è¡
}
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/element/ElementAdditionalOptions.kt
åä¹ãModel æ ¡éªæºå¶
19.1 ModelCheckPlugin æ¥å£
ModelCheckPlugin æ¯ Model æ ¡éªçæ ¸å¿æ©å±ç¹ï¼
interface ModelCheckPlugin {
// æ£æ¥ Model 宿´æ§ï¼è¿åå
ç´ æ°é
fun checkModelIntegrity(
model: Model,
projectId: String?,
userId: String,
isTemplate: Boolean = false,
oauthUser: String? = null,
pipelineDialect: IPipelineDialect? = null,
pipelineId: String = ""
): Int
// æ£æ¥ Setting 宿´æ§
fun checkSettingIntegrity(setting: PipelineSetting, projectId: String?)
// æ¸
ç Model
fun clearUpModel(model: Model)
// å é¤ Element åçå¤ç
fun beforeDeleteElementInExistsModel(
existModel: Model,
sourceModel: Model? = null,
param: BeforeDeleteParam
)
// æ£æ¥ Element è¶
æ¶é
ç½®
fun checkElementTimeoutVar(container: Container, element: Element, contextMap: Map<String, String>)
// æ£æ¥äºæ¥ç»é
ç½®
fun checkMutexGroup(container: Container, contextMap: Map<String, String>)
// æ£æ¥ Job è¿è¡æ¡ä»¶
fun checkJobCondition(container: Container, finallyStage: Boolean, contextMap: Map<String, String>)
}
æä»¶ä½ç½®: src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/extend/ModelCheckPlugin.kt
19.2 DefaultModelCheckPlugin å®ç°
DefaultModelCheckPlugin æ¯é»è®¤çæ ¡éªå®ç°ï¼
open class DefaultModelCheckPlugin(
open val client: Client,
open val pipelineCommonSettingConfig: PipelineCommonSettingConfig,
open val stageCommonSettingConfig: StageCommonSettingConfig,
open val jobCommonSettingConfig: JobCommonSettingConfig,
open val taskCommonSettingConfig: TaskCommonSettingConfig,
open val elementBizPluginServices: List<IElementBizPluginService>
) : ModelCheckPlugin {
override fun checkModelIntegrity(model: Model, ...): Int {
var metaSize = 0
// 1. æ£æ¥æµæ°´çº¿åç§°
PipelineUtils.checkPipelineName(model.name, maxSize)
// 2. æ£æ¥æµæ°´çº¿æè¿°é¿åº¦
PipelineUtils.checkPipelineDescLength(model.desc, maxSize)
// 3. æ£æ¥ Model JSON 大å°
val modelSize = JsonUtil.toJson(model).length
if (modelSize > maxModelSize) {
throw ErrorCodeException(ERROR_PIPELINE_MODEL_TOO_LARGE)
}
// 4. æ£æ¥ Stage æ°é
if (stages.size > maxStageNum) {
throw ErrorCodeException(ERROR_PIPELINE_MODEL_COMPONENT_NUM_TOO_LARGE)
}
// 5. æ£æ¥è§¦å容å¨
checkTriggerContainer(trigger)
// 6. éåæ£æ¥æ¯ä¸ª Stage
model.stages.forEachIndexed { index, stage ->
// æ£æ¥ Container æ°é
// æ£æ¥ FinallyStage ä½ç½®
// æ£æ¥å®¡æ ¸ç»é
ç½®
// æ£æ¥è¿è¡æ¡ä»¶
// æ£æ¥ Element
metaSize += stage.checkJob(...)
}
return metaSize
}
}
æ ¡éªé¡¹æ¸ å:
- æµæ°´çº¿åç§°é¿åº¦ï¼é»è®¤æå¤§ 64 å符ï¼
- æµæ°´çº¿æè¿°é¿åº¦ï¼é»è®¤æå¤§ 100 å符ï¼
- Model JSON 大å°ï¼é»è®¤æå¤§ 4MBï¼
- Stage æ°éï¼é»è®¤æå¤§ 20 个ï¼
- æ¯ä¸ª Stage ä¸ç Job æ°éï¼é»è®¤æå¤§ 20 个ï¼
- æ¯ä¸ª Job ä¸ç Element æ°éï¼é»è®¤æå¤§ 50 个ï¼
- FinallyStage å¿ é¡»å¨æåä½ç½®
- 触å容å¨å¿ é¡»åå¨ä¸å¨ç¬¬ä¸ä¸ªä½ç½®
- è¶ æ¶æ¶é´é ç½®åæ³æ§
- äºæ¥ç»é ç½®åæ³æ§
- è¿è¡æ¡ä»¶è¡¨è¾¾å¼é¿åº¦
æä»¶ä½ç½®: src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/extend/DefaultModelCheckPlugin.kt
äºåãElement ç®å½ç»æ
Element åç±»æåè½åç±»åæ¾å¨ä¸åç®å½ï¼
src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/element/
âââ Element.kt # æ½è±¡åºç±»
âââ ElementAdditionalOptions.kt # éå é项
âââ ElementBaseInfo.kt # åºç¡ä¿¡æ¯
âââ ElementPostInfo.kt # Post ä»»å¡ä¿¡æ¯
âââ ElementProp.kt # 屿§
âââ EmptyElement.kt # 空å
ç´ ï¼é»è®¤ï¼
âââ StepTemplateElement.kt # Step 模æ¿
âââ SubPipelineCallElement.kt # åæµæ°´çº¿è°ç¨
â
âââ agent/ # æç¼è¯ç¯å¢æä»¶
â âââ CodeGitElement.kt # Git æä»£ç
â âââ CodeGitlabElement.kt # GitLab æä»£ç
â âââ CodeSvnElement.kt # SVN æä»£ç
â âââ GithubElement.kt # GitHub æä»£ç
â âââ LinuxScriptElement.kt # Linux èæ¬
â âââ WindowsScriptElement.kt # Windows èæ¬
â âââ ManualReviewUserTaskElement.kt # äººå·¥å®¡æ ¸
â
âââ trigger/ # 触åå¨
â âââ ManualTriggerElement.kt # æå¨è§¦å
â âââ TimerTriggerElement.kt # 宿¶è§¦å
â âââ RemoteTriggerElement.kt # è¿ç¨è§¦å
â âââ CodeGitWebHookTriggerElement.kt # Git WebHook
â âââ CodeGitlabWebHookTriggerElement.kt # GitLab WebHook
â âââ CodeGithubWebHookTriggerElement.kt # GitHub WebHook
â âââ CodeSVNWebHookTriggerElement.kt # SVN WebHook
â âââ CodeTGitWebHookTriggerElement.kt # TGit WebHook
â âââ CodeP4WebHookTriggerElement.kt # P4 WebHook
â âââ WebHookTriggerElement.kt # WebHook åºç±»
â
âââ market/ # ç åååºæä»¶
â âââ MarketBuildAtomElement.kt # æç¼è¯ç¯å¢æä»¶
â âââ MarketBuildLessAtomElement.kt # æ ç¼è¯ç¯å¢æä»¶
â âââ MarketCheckImageElement.kt # é忣æ¥
â âââ AtomBuildArchiveElement.kt # æå»ºå½æ¡£
â
âââ quality/ # è´¨é红线
â âââ QualityGateInElement.kt # åå
¥è´¨é红线
â âââ QualityGateOutElement.kt # ååºè´¨é红线
â
âââ matrix/ # æå»ºç©éµ
â âââ MatrixStatusElement.kt # ç©éµç¶æå
ç´
â
âââ atom/ # ååç¸å
³
âââ BeforeDeleteParam.kt # å é¤ååæ°
âââ ElementBatchCheckParam.kt # æ¹éæ£æ¥åæ°
âââ ElementCheckResult.kt # æ£æ¥ç»æ
âââ ElementHolder.kt # å
ç´ ææè
âââ ManualReviewParam.kt # äººå·¥å®¡æ ¸åæ°
âââ ManualReviewParamPair.kt # å®¡æ ¸åæ°å¯¹
âââ ManualReviewParamType.kt # å®¡æ ¸åæ°ç±»å
âââ SubPipelineType.kt # åæµæ°´çº¿ç±»å
äºåä¸ã宿´ JSON 示ä¾
21.1 æå°å Model
{
"name": "My Pipeline",
"desc": "A simple pipeline",
"stages": [
{
"@type": "stage",
"id": "stage-1",
"name": "Trigger Stage",
"containers": [
{
"@type": "trigger",
"id": "0",
"name": "trigger",
"elements": [
{
"@type": "manualTrigger",
"id": "T-1-1-1",
"name": "æå¨è§¦å"
}
],
"params": []
}
]
}
]
}
21.2 宿´ Model 示ä¾
{
"name": "Full Pipeline Example",
"desc": "A complete pipeline with all features",
"stages": [
{
"@type": "stage",
"id": "stage-1",
"name": "Trigger",
"containers": [
{
"@type": "trigger",
"id": "0",
"name": "trigger",
"elements": [
{
"@type": "manualTrigger",
"id": "T-1-1-1",
"name": "æå¨è§¦å",
"canElementSkip": false,
"useLatestParameters": false
},
{
"@type": "timerTrigger",
"id": "T-1-1-2",
"name": "宿¶è§¦å",
"advanceExpression": ["0 0 8 * * ?"],
"noScm": false
}
],
"params": [
{
"id": "version",
"name": "çæ¬å·",
"type": "STRING",
"required": true,
"defaultValue": "1.0.0",
"desc": "åå¸çæ¬å·"
},
{
"id": "env",
"name": "ç¯å¢",
"type": "ENUM",
"required": true,
"defaultValue": "dev",
"options": [
{"key": "dev", "value": "å¼åç¯å¢"},
{"key": "test", "value": "æµè¯ç¯å¢"},
{"key": "prod", "value": "ç产ç¯å¢"}
]
}
],
"buildNo": {
"buildNo": 1,
"buildNoType": "EVERY_BUILD_INCREMENT",
"required": false
}
}
]
},
{
"@type": "stage",
"id": "stage-2",
"name": "Build",
"stageControlOption": {
"enable": true,
"runCondition": "AFTER_LAST_FINISHED"
},
"containers": [
{
"@type": "vmBuild",
"id": "1",
"name": "Build Job",
"baseOS": "LINUX",
"dispatchType": {
"buildType": "DOCKER",
"value": "bkci/ci:latest"
},
"jobControlOption": {
"enable": true,
"timeout": 60,
"runCondition": "STAGE_RUNNING"
},
"elements": [
{
"@type": "linuxScript",
"id": "e-2-1-1",
"name": "ç¼è¯",
"scriptType": "SHELL",
"script": "#!/bin/bash\necho 'Building...'\nmake build",
"continueNoneZero": false,
"additionalOptions": {
"enable": true,
"timeout": 30,
"retryWhenFailed": true,
"retryCount": 2
}
},
{
"@type": "marketBuild",
"id": "e-2-1-2",
"name": "ä¸ä¼ å¶å",
"atomCode": "uploadArtifact",
"version": "1.*",
"data": {
"input": {
"filePath": "./build/output/*",
"destPath": "/artifacts/"
}
}
}
]
}
]
},
{
"@type": "stage",
"id": "stage-3",
"name": "Deploy",
"checkIn": {
"manualTrigger": true,
"reviewGroups": [
{
"name": "å®¡æ ¸ç»",
"reviewers": ["admin", "reviewer"]
}
],
"timeout": 24,
"reviewDesc": "请确认æ¯å¦é¨ç½²å° ${env} ç¯å¢"
},
"containers": [
{
"@type": "normal",
"id": "2",
"name": "Deploy Job",
"elements": [
{
"@type": "marketBuildLess",
"id": "e-3-1-1",
"name": "é¨ç½²",
"atomCode": "deploy",
"version": "1.*"
}
]
}
]
},
{
"@type": "stage",
"id": "stage-4",
"name": "Finally",
"finally": true,
"containers": [
{
"@type": "normal",
"id": "3",
"name": "Cleanup",
"elements": [
{
"@type": "marketBuildLess",
"id": "e-4-1-1",
"name": "åééç¥",
"atomCode": "sendNotify",
"version": "1.*"
}
]
}
]
}
],
"pipelineCreator": "admin"
}
æ»ç»
Model æ¯ BK-CI çæ ¸å¿æ°æ®ç»æï¼çè§£ Model ç屿¬¡å ³ç³»ãåæ®µå«ä¹åçå½å¨ææ¯å¼åæµæ°´çº¿åè½çåºç¡ã
æ ¸å¿è¦ç¹:
- åå±ç»æ: Model â Stage â Container â Element
- å¤æè®¾è®¡: Container å Element ä½¿ç¨æ¥å£ + å®ç°ç±»
- è¿è¡æ¶å段: å¾å¤å段åªå¨æå»ºè¿è¡æ¶ææï¼ç¼ææ¶ä¸è¦è®¾ç½®
- çæ¬å
¼å®¹: éè¿
transformCompatibility()å¤çå岿°æ® - JSON åå¨: Model 以 JSON å符串åå¨å¨æ°æ®åºä¸
- å¿«ç §æºå¶: æ¯æ¬¡æå»ºä¼ä¿å Model å¿«ç §å°æå»ºè®°å½è¡¨
- æ ¡éªæºå¶: éè¿
ModelCheckPluginè¿è¡å®æ´æ§æ ¡éª - ç¶æç®¡ç: éè¿
BuildStatusæä¸¾ç®¡çæå»ºç¶æ
ææ¡ Model 模ååï¼ä½ å°è½å¤ï¼
- çè§£æµæ°´çº¿ç宿´æ°æ®ç»æ
- å¼åæµæ°´çº¿ç¼æåè½
- æ©å±æ°ç Container æ Element ç±»å
- å®ç°æµæ°´çº¿å¯¼å ¥å¯¼åº
- è¿è¡ Model å±é¢çæ ¡éªåä¼å
- çè§£æå»ºæ§è¡å¼æçå·¥ä½åç
- ææ¥æµæ°´çº¿æ§è¡é®é¢
äºåäºãModel æä¹ å详解
22.1 æ°æ®åºè¡¨ç»æ
22.1.1 æµæ°´çº¿çæ¬è¡¨ (T_PIPELINE_RESOURCE_VERSION)
å卿µæ°´çº¿çæ¯ä¸ªçæ¬ç宿´ Modelï¼
CREATE TABLE `T_PIPELINE_RESOURCE_VERSION` (
`PROJECT_ID` varchar(64) NOT NULL COMMENT '项ç®ID',
`PIPELINE_ID` varchar(64) NOT NULL COMMENT 'æµæ°´çº¿ID',
`VERSION` int(11) NOT NULL COMMENT 'çæ¬å·',
`VERSION_NAME` varchar(64) DEFAULT NULL COMMENT 'çæ¬åç§°',
`MODEL` mediumtext COMMENT 'Model JSONå符串',
`YAML` mediumtext COMMENT 'YAML é
ç½®',
`YAML_VERSION` varchar(34) DEFAULT NULL COMMENT 'YAML çæ¬',
`CREATOR` varchar(64) NOT NULL COMMENT 'å建人',
`UPDATER` varchar(64) DEFAULT NULL COMMENT 'æ´æ°äºº',
`CREATE_TIME` datetime NOT NULL COMMENT 'å建æ¶é´',
`VERSION_NUM` int(11) DEFAULT NULL COMMENT 'å¤§çæ¬å·',
`PIPELINE_VERSION` int(11) DEFAULT NULL COMMENT 'ç¼æçæ¬',
`TRIGGER_VERSION` int(11) DEFAULT NULL COMMENT '触åå¨çæ¬',
`SETTING_VERSION` int(11) DEFAULT NULL COMMENT 'è®¾ç½®çæ¬',
`STATUS` varchar(16) DEFAULT NULL COMMENT 'çæ¬ç¶æ',
`BRANCH_ACTION` varchar(32) DEFAULT NULL COMMENT '忝å¨ä½',
`DESCRIPTION` text COMMENT 'çæ¬æè¿°',
`BASE_VERSION` int(11) DEFAULT NULL COMMENT 'åºç¡çæ¬',
`REFER_FLAG` bit(1) DEFAULT NULL COMMENT 'å¼ç¨æ å¿',
`RELEASE_TIME` datetime DEFAULT NULL COMMENT 'å叿¶é´',
PRIMARY KEY (`PROJECT_ID`, `PIPELINE_ID`, `VERSION`),
KEY `idx_status` (`STATUS`),
KEY `idx_release_time` (`RELEASE_TIME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='æµæ°´çº¿èµæºçæ¬è¡¨';
å ³é®å段说æï¼
| åæ®µ | 说æ |
|---|---|
VERSION |
çæ¬å·ï¼æ¯æ¬¡ä¿åéå¢ |
VERSION_NAME |
ç¨æ·å¯è§ççæ¬åç§° |
MODEL |
Model ç JSON åºååå符串 |
YAML |
PAC 模å¼ä¸ç YAML é ç½® |
STATUS |
çæ¬ç¶æï¼RELEASED/COMMITTING/BRANCH/DELETEï¼ |
BASE_VERSION |
åæ¯çæ¬çåºç¡çæ¬å· |
22.1.2 æå»ºè®°å½æ¨¡å表 (T_PIPELINE_BUILD_RECORD_MODEL)
åå¨æå»ºè¿è¡æ¶ç Model ç¶æï¼
CREATE TABLE `T_PIPELINE_BUILD_RECORD_MODEL` (
`BUILD_ID` varchar(34) NOT NULL COMMENT 'æå»ºID',
`PROJECT_ID` varchar(64) NOT NULL COMMENT '项ç®ID',
`PIPELINE_ID` varchar(64) NOT NULL COMMENT 'æµæ°´çº¿ID',
`RESOURCE_VERSION` int(11) NOT NULL COMMENT 'èµæºçæ¬',
`BUILD_NUM` int(11) DEFAULT NULL COMMENT 'æå»ºå·',
`EXECUTE_COUNT` int(11) NOT NULL DEFAULT '1' COMMENT 'æ§è¡æ¬¡æ°',
`START_USER` varchar(64) DEFAULT NULL COMMENT 'å¯å¨ç¨æ·',
`START_TYPE` varchar(32) DEFAULT NULL COMMENT 'å¯å¨ç±»å',
`MODEL_VAR` mediumtext COMMENT '模ååéï¼è¿è¡æ¶ç¶æï¼',
`STATUS` varchar(32) DEFAULT NULL COMMENT 'æå»ºç¶æ',
`START_TIME` datetime DEFAULT NULL COMMENT 'å¼å§æ¶é´',
`END_TIME` datetime DEFAULT NULL COMMENT 'ç»ææ¶é´',
`ERROR_INFO` text COMMENT 'é误信æ¯',
`CANCEL_USER` varchar(64) DEFAULT NULL COMMENT 'åæ¶ç¨æ·',
`TIMESTAMPS` text COMMENT 'æ¶é´æ³è®°å½',
PRIMARY KEY (`BUILD_ID`, `EXECUTE_COUNT`),
KEY `idx_project_pipeline` (`PROJECT_ID`, `PIPELINE_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='æµæ°´çº¿æå»ºè®°å½æ¨¡å表';
å ³é®å段说æï¼
| åæ®µ | 说æ |
|---|---|
RESOURCE_VERSION |
å ³èçæµæ°´çº¿çæ¬ |
EXECUTE_COUNT |
æ§è¡æ¬¡æ°ï¼éè¯æ¶éå¢ï¼ |
MODEL_VAR |
è¿è¡æ¶åéåç¶æç JSON |
STATUS |
å½åæå»ºç¶æ |
TIMESTAMPS |
åé¶æ®µæ¶é´æ³è®°å½ |
22.2 DAO å±å®ç°
22.2.1 PipelineResourceVersionDao
@Repository
class PipelineResourceVersionDao {
// å建æ°çæ¬
fun create(
dslContext: DSLContext,
userId: String,
projectId: String,
pipelineId: String,
version: Int,
versionName: String,
model: Model,
baseVersion: Int?,
yamlStr: String?,
yamlVersion: String?,
versionNum: Int?,
pipelineVersion: Int?,
triggerVersion: Int?,
settingVersion: Int?,
versionStatus: VersionStatus?,
branchAction: BranchVersionAction?,
description: String?
): TPipelineResourceVersionRecord? {
with(T_PIPELINE_RESOURCE_VERSION) {
val modelStr = JsonUtil.toJson(model, formatted = false)
val createTime = LocalDateTime.now()
return dslContext.insertInto(this)
.set(PROJECT_ID, projectId)
.set(PIPELINE_ID, pipelineId)
.set(VERSION, version)
.set(VERSION_NAME, versionName)
.set(MODEL, modelStr)
// ... å
¶ä»å段
.onDuplicateKeyUpdate()
.set(MODEL, modelStr)
// ... æ´æ°å段
.returning()
.fetchOne()
}
}
// è·åæå®çæ¬ç Model å符串
fun getVersionModelString(
dslContext: DSLContext,
projectId: String,
pipelineId: String,
version: Int?,
includeDraft: Boolean? = null
): String? {
return with(T_PIPELINE_RESOURCE_VERSION) {
val where = dslContext.select(MODEL)
.from(this)
.where(PIPELINE_ID.eq(pipelineId).and(PROJECT_ID.eq(projectId)))
if (version != null) {
where.and(VERSION.eq(version))
} else {
if (includeDraft != true) where.and(
(STATUS.ne(VersionStatus.COMMITTING.name)
.and(STATUS.ne(VersionStatus.DELETE.name)))
.or(STATUS.isNull)
)
where.orderBy(VERSION.desc()).limit(1)
}
where.fetchOne()?.value1()
}
}
}
æä»¶ä½ç½®: src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/dao/PipelineResourceVersionDao.kt
22.2.2 BuildRecordModelDao
@Repository
class BuildRecordModelDao {
// å建æå»ºè®°å½
fun createRecord(dslContext: DSLContext, record: BuildRecordModel) {
with(TPipelineBuildRecordModel.T_PIPELINE_BUILD_RECORD_MODEL) {
dslContext.insertInto(this)
.set(BUILD_ID, record.buildId)
.set(PROJECT_ID, record.projectId)
.set(PIPELINE_ID, record.pipelineId)
.set(RESOURCE_VERSION, record.resourceVersion)
.set(BUILD_NUM, record.buildNum)
.set(EXECUTE_COUNT, record.executeCount)
.set(START_USER, record.startUser)
.set(START_TYPE, record.startType)
.set(MODEL_VAR, JsonUtil.toJson(record.modelVar, false))
.set(STATUS, record.status)
.set(ERROR_INFO, record.errorInfoList?.let { JsonUtil.toJson(it, false) })
.set(CANCEL_USER, record.cancelUser)
.set(TIMESTAMPS, JsonUtil.toJson(record.timestamps, false))
.execute()
}
}
// æ´æ°æå»ºè®°å½
fun updateRecord(
dslContext: DSLContext,
projectId: String,
pipelineId: String,
buildId: String,
executeCount: Int,
buildStatus: BuildStatus?,
modelVar: Map<String, Any>,
startTime: LocalDateTime?,
endTime: LocalDateTime?,
errorInfoList: List<ErrorInfo>?,
cancelUser: String?,
timestamps: Map<BuildTimestampType, BuildRecordTimeStamp>?
) {
with(TPipelineBuildRecordModel.T_PIPELINE_BUILD_RECORD_MODEL) {
val update = dslContext.update(this)
.set(MODEL_VAR, JsonUtil.toJson(modelVar, false))
buildStatus?.let { update.set(STATUS, buildStatus.name) }
cancelUser?.let { update.set(CANCEL_USER, cancelUser) }
startTime?.let { update.set(START_TIME, startTime) }
endTime?.let { update.set(END_TIME, endTime) }
timestamps?.let {
update.set(TIMESTAMPS, JsonUtil.toJson(timestamps, false))
}
errorInfoList?.let {
update.set(ERROR_INFO, JsonUtil.toJson(errorInfoList, false))
}
update.where(
BUILD_ID.eq(buildId)
.and(PROJECT_ID.eq(projectId))
.and(PIPELINE_ID.eq(pipelineId))
.and(EXECUTE_COUNT.eq(executeCount))
).execute()
}
}
// è·åæå»ºè®°å½
fun getRecord(
dslContext: DSLContext,
projectId: String,
pipelineId: String,
buildId: String,
executeCount: Int
): BuildRecordModel? {
with(TPipelineBuildRecordModel.T_PIPELINE_BUILD_RECORD_MODEL) {
return dslContext.selectFrom(this)
.where(
BUILD_ID.eq(buildId)
.and(PROJECT_ID.eq(projectId))
.and(PIPELINE_ID.eq(pipelineId))
.and(EXECUTE_COUNT.eq(executeCount))
).fetchAny(mapper)
}
}
}
æä»¶ä½ç½®: src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/record/BuildRecordModelDao.kt
äºåä¸ãService å±ä¸å¡é»è¾
23.1 PipelineRepositoryService
PipelineRepositoryService æ¯ Model æä¹ åçæ ¸å¿æå¡ï¼
@Service
class PipelineRepositoryService @Autowired constructor(
private val pipelineResourceVersionDao: PipelineResourceVersionDao,
private val pipelineResourceDao: PipelineResourceDao,
private val modelCheckPlugin: ModelCheckPlugin,
// ... å
¶ä»ä¾èµ
) {
/**
* è·åæµæ°´çº¿ Model
*/
fun getModel(projectId: String, pipelineId: String): Model? {
return pipelineResourceDao.getLatestVersionModelString(
dslContext = dslContext,
projectId = projectId,
pipelineId = pipelineId
)?.let { str2model(it, pipelineId) }
}
/**
* åå§åå¹¶æ£æ¥ Model åæ³æ§
*/
fun initModel(
model: Model,
projectId: String,
pipelineId: String,
userId: String,
create: Boolean = true,
versionStatus: VersionStatus? = VersionStatus.RELEASED,
channelCode: ChannelCode,
yamlInfo: PipelineYamlVo? = null,
pipelineDialect: IPipelineDialect? = null
): List<PipelineModelTask> {
// 1. æ£æ¥ Model 宿´æ§
val metaSize = modelCheckPlugin.checkModelIntegrity(
model = model,
projectId = projectId,
userId = userId,
oauthUser = getPipelineOauthUser(projectId, pipelineId),
pipelineDialect = pipelineDialect,
pipelineId = pipelineId
)
// 2. å»é ID
val distinctIdSet = HashSet<String>(metaSize, 1F)
val jobIdDuplicateChecker = ModelIdDuplicateChecker()
// 3. åå§å ID
val modelTasks = ArrayList<PipelineModelTask>(metaSize)
val containerSeqId = AtomicInteger(0)
// 4. éååå§åæ¯ä¸ª Stage
model.stages.forEachIndexed { index, s ->
s.id = VMUtils.genStageId(index + 1)
s.resetBuildOption(true)
s.timeCost = null
if (index == 0) {
// åå§å触å容å¨
initTriggerContainer(
stage = s,
containerSeqId = containerSeqId,
projectId = projectId,
pipelineId = pipelineId,
model = model,
userId = userId,
modelTasks = modelTasks,
channelCode = channelCode,
create = create,
distIds = distinctIdSet,
versionStatus = versionStatus,
yamlInfo = yamlInfo,
jobIdDuplicateChecker = jobIdDuplicateChecker
)
} else {
// åå§åå
¶ä»å®¹å¨
initOtherContainer(
stage = s,
projectId = projectId,
containerSeqId = containerSeqId,
userId = userId,
pipelineId = pipelineId,
model = model,
modelTasks = modelTasks,
channelCode = channelCode,
create = create,
distIds = distinctIdSet,
versionStatus = versionStatus,
yamlInfo = yamlInfo,
stageIndex = index,
jobIdDuplicateChecker = jobIdDuplicateChecker
)
}
}
// 5. æ£æ¥ Job ID æ¯å¦éå¤
if (jobIdDuplicateChecker.duplicateIdSet.isNotEmpty()) {
throw ErrorCodeException(
errorCode = ProcessMessageCode.ERROR_JOB_ID_DUPLICATE,
params = arrayOf(jobIdDuplicateChecker.duplicateIdSet.joinToString(","))
)
}
return modelTasks
}
/**
* JSON å符串转 Model
*/
private fun str2model(modelString: String, pipelineId: String): Model {
return try {
JsonUtil.to(modelString, Model::class.java)
} catch (e: Exception) {
logger.error("[$pipelineId] str2model failed", e)
throw e
}
}
}
æä»¶ä½ç½®: src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRepositoryService.kt
23.2 PipelineBuildDetailService
PipelineBuildDetailService è´è´£æå»ºè¯¦æ ç Model 管çï¼
@Service
class PipelineBuildDetailService @Autowired constructor(
private val buildRecordModelDao: BuildRecordModelDao,
private val pipelineResourceVersionDao: PipelineResourceVersionDao,
// ... å
¶ä»ä¾èµ
) {
/**
* æ´æ°æå»º Model
*/
fun updateModel(projectId: String, buildId: String, model: Model) {
// å° Model ç¶ææ´æ°å°æå»ºè®°å½è¡¨
val modelVar = extractModelVar(model)
buildRecordModelDao.updateRecord(
dslContext = dslContext,
projectId = projectId,
pipelineId = model.pipelineId,
buildId = buildId,
executeCount = model.executeCount,
buildStatus = null,
modelVar = modelVar,
startTime = null,
endTime = null,
errorInfoList = null,
cancelUser = null,
timestamps = null
)
}
/**
* ä» Model 䏿ååé
*/
private fun extractModelVar(model: Model): Map<String, Any> {
val modelVar = mutableMapOf<String, Any>()
// æå Stage ç¶æ
model.stages.forEach { stage ->
modelVar["stage_${stage.id}_status"] = stage.status ?: ""
// æå Container ç¶æ
stage.containers.forEach { container ->
modelVar["container_${container.id}_status"] = container.status ?: ""
// æå Element ç¶æ
container.elements.forEach { element ->
modelVar["element_${element.id}_status"] = element.status ?: ""
}
}
}
return modelVar
}
}
æä»¶ä½ç½®: src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineBuildDetailService.kt
äºååãæå»ºæ§è¡å¼æä¸ç Model
24.1 BuildStartControl
BuildStartControl è´è´£æå»ºå¯å¨æ¶ç Model å¤çï¼
@Service
class BuildStartControl @Autowired constructor(
private val buildDetailService: PipelineBuildDetailService,
private val pipelineContainerService: PipelineContainerService,
private val taskRecordService: TaskRecordService,
// ... å
¶ä»ä¾èµ
) {
/**
* æ´æ° Modelï¼æå»ºå¯å¨æ¶ï¼
*/
private fun updateModel(model: Model, buildInfo: BuildInfo, taskId: String, executeCount: Int) {
val now = LocalDateTime.now()
val stage = model.stages[0]
val container = stage.containers[0]
// 1. æ´æ°è§¦åå¨ Element ç¶æ
run lit@{
container.name = ContainerUtils.getClearedQueueContainerName(container.name)
container.elements.forEach {
if (it.id == taskId) {
// æ´æ° Container ç¶æ
pipelineContainerService.updateContainerStatus(
projectId = buildInfo.projectId,
buildId = buildInfo.buildId,
stageId = stage.id!!,
containerId = container.id!!,
startTime = now,
endTime = now,
buildStatus = BuildStatus.SUCCEED
)
// æ´æ° Task ç¶æ
taskRecordService.updateTaskStatus(
projectId = buildInfo.projectId,
pipelineId = buildInfo.pipelineId,
buildId = buildInfo.buildId,
taskId = taskId,
buildStatus = BuildStatus.SUCCEED,
executeCount = executeCount,
operation = "updateTriggerElement#$taskId"
)
it.status = BuildStatus.SUCCEED.name
return@lit
}
}
}
// 2. æ´æ° Stage ç¶æ
pipelineStageService.updateStageStatus(
projectId = buildInfo.projectId,
buildId = buildInfo.buildId,
stageId = stage.id!!,
buildStatus = BuildStatus.SUCCEED,
checkIn = stage.checkIn,
checkOut = stage.checkOut
)
// 3. æ´æ° Model è®°å½
pipelineRecordService.updateModelRecord(
projectId = buildInfo.projectId,
pipelineId = buildInfo.pipelineId,
buildId = buildInfo.buildId,
executeCount = executeCount,
buildStatus = null,
modelVar = mutableMapOf(),
timestamps = mapOf(
BuildTimestampType.BUILD_CONCURRENCY_QUEUE to
BuildRecordTimeStamp(null, LocalDateTime.now().timestampmilli())
),
startTime = LocalDateTime.now(),
endTime = null
)
// 4. 计ç®èæ¶å¹¶æ´æ°
val nowMills = now.timestampmilli()
val stageElapsed = max(0, nowMills - buildInfo.queueTime)
stage.elapsed = stageElapsed
stage.status = BuildStatus.SUCCEED.name
// 5. æ´æ° Container è¿è¡æ¶ç¶æ
container.status = BuildStatus.SUCCEED.name
container.startEpoch = nowMills
container.systemElapsed = stage.elapsed
container.elementElapsed = 0
container.executeCount = executeCount
container.startVMStatus = BuildStatus.SUCCEED.name
// 6. æä¹
å Model æ´æ°
buildDetailService.updateModel(
projectId = buildInfo.projectId,
buildId = buildInfo.buildId,
model = model
)
}
}
æä»¶ä½ç½®: src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildStartControl.kt
24.2 Model ç¶ææ´æ°æµç¨
æå»ºå¯å¨
â
ä» T_PIPELINE_RESOURCE_VERSION è·å Model
â
å建 T_PIPELINE_BUILD_RECORD_MODEL è®°å½
â
åå§åææ Stage/Container/Element ç¶æä¸º QUEUE
â
æ§è¡ç¬¬ä¸ä¸ª Stage
â
æ´æ° Stage ç¶æä¸º RUNNING
â
è°åº¦ Container
â
æ´æ° Container ç¶æä¸º PREPARE_ENV â RUNNING
â
æ§è¡ Element
â
æ´æ° Element ç¶æä¸º RUNNING â SUCCEED/FAILED
â
Container å®æï¼æ´æ°ç¶æ
â
Stage å®æï¼æ´æ°ç¶æ
â
ææ Stage 宿
â
æ´æ° Model æç»ç¶æ
â
æå»ºç»æ
äºåäºãModel çæ¬ç®¡ç
25.1 çæ¬ç¶æ (VersionStatus)
enum class VersionStatus {
RELEASED, // å·²åå¸ï¼æ£å¼çæ¬ï¼
COMMITTING, // æäº¤ä¸ï¼è稿ï¼
BRANCH, // åæ¯çæ¬
DELETE // å·²å é¤
}
25.2 çæ¬å·è§å
BK-CI 使ç¨å¤ç»´çæ¬å·ç³»ç»ï¼
data class PipelineVersionSimple(
val version: Int, // å
é¨çæ¬å·ï¼èªå¢ï¼
val versionName: String, // ç¨æ·å¯è§çæ¬å
val versionNum: Int?, // å¤§çæ¬å·
val pipelineVersion: Int?, // ç¼æçæ¬
val triggerVersion: Int?, // 触åå¨çæ¬
val settingVersion: Int? // è®¾ç½®çæ¬
)
çæ¬å·ç¤ºä¾ï¼
version = 15ï¼å é¨çæ¬å·versionName = "V1.5"ï¼ç¨æ·å¯è§çæ¬versionNum = 1ï¼å¤§çæ¬pipelineVersion = 5ï¼ç¼æä¿®æ¹æ¬¡æ°triggerVersion = 2ï¼è§¦åå¨ä¿®æ¹æ¬¡æ°settingVersion = 3ï¼è®¾ç½®ä¿®æ¹æ¬¡æ°
25.3 åæ¯çæ¬ (BranchVersionAction)
enum class BranchVersionAction {
ACTIVE, // æ´»è·åæ¯
INACTIVE, // éæ´»è·åæ¯
MERGED // å·²åå¹¶
}
åæ¯çæ¬ç¨éï¼
- æ¯æå¤äººåä½å¼å
- æ¯æ PAC (Pipeline as Code) 模å¼
- æ¯æçæ¬åæ»
äºåå ãModel ä¸ YAML 转æ¢
26.1 Model 转 YAML
// Model 转æ¢ä¸º YAML æ ¼å¼
fun Model.toYaml(): String {
val preModel = PreScriptBuildYaml(
version = "v2.0",
name = this.name,
stages = this.stages.mapIndexed { index, stage ->
if (index == 0) {
// 触åå¨ Stage 转æ¢ä¸º on é
ç½®
null
} else {
stage.toPreStage()
}
}.filterNotNull()
)
return YamlUtil.toYaml(preModel)
}
// Stage 转æ¢
fun Stage.toPreStage(): PreStage {
return PreStage(
name = this.name,
jobs = this.containers.map { it.toPreJob() }
)
}
// Container 转æ¢
fun Container.toPreJob(): PreJob {
return PreJob(
name = this.name,
runsOn = when (this) {
is VMBuildContainer -> this.dispatchType?.toRunsOn()
else -> null
},
steps = this.elements.map { it.toPreStep() }
)
}
// Element 转æ¢
fun Element.toPreStep(): PreStep {
return PreStep(
name = this.name,
id = this.stepId,
uses = "${this.getAtomCode()}@${this.version}",
with = this.genTaskParams()
)
}
26.2 YAML 转 Model
// YAML 转æ¢ä¸º Model
fun PreScriptBuildYaml.toModel(): Model {
return Model(
name = this.name ?: "Pipeline",
desc = "",
stages = listOf(
// 触åå¨ Stage
Stage(
id = "stage-1",
containers = listOf(
TriggerContainer(
id = "0",
name = "trigger",
elements = this.on?.toTriggerElements() ?: listOf(
ManualTriggerElement()
)
)
)
)
) + this.stages.mapIndexed { index, preStage ->
preStage.toStage(index + 2)
}
)
}
æä»¶ä½ç½®: src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineTransferYamlService.kt
äºåä¸ãModel æ©å±æå
27.1 æ°å¢ Element ç±»å
æ¥éª¤ï¼
- å®ä¹ Element ç±»ï¼
// å¨ common-pipeline 模å
data class MyCustomElement(
override val name: String = "æçèªå®ä¹æä»¶",
override var id: String? = null,
override var status: String? = null,
override var stepId: String? = null,
// èªå®ä¹å段
val myParam1: String,
val myParam2: Int = 0,
override var additionalOptions: ElementAdditionalOptions? = null
) : Element(name, id, status, additionalOptions = additionalOptions) {
companion object {
const val classType = "myCustomElement"
}
override fun getClassType() = classType
override fun getAtomCode() = "myCustomAtom"
}
- 注åå° JsonSubTypesï¼
// å¨ Element.kt ç @JsonSubTypes 䏿·»å
@JsonSubTypes.Type(value = MyCustomElement::class, name = MyCustomElement.classType)
- å®ç°æä»¶æ§è¡é»è¾ï¼å¨ Worker 端ï¼
27.2 æ°å¢ Container ç±»å
æ¥éª¤ï¼
- å®ä¹ Container ç±»ï¼
data class MyCustomContainer(
override var id: String? = null,
override var name: String = "",
override var elements: List<Element> = listOf(),
// èªå®ä¹å段
val customConfig: MyConfig?,
// ... å
¶ä» Container æ¥å£å段
) : Container {
companion object {
const val classType = "myCustom"
}
override fun getClassType() = classType
// å®ç°å
¶ä»æ¥å£æ¹æ³
}
- 注åå° JsonSubTypesï¼
// å¨ Container.kt ç @JsonSubTypes 䏿·»å
@JsonSubTypes.Type(value = MyCustomContainer::class, name = MyCustomContainer.classType)
- å®ç°è°åº¦é»è¾ï¼å¨ Dispatch æå¡ä¸ï¼
äºåå «ãè°è¯ä¸ææ¥
28.1 常ç¨è°è¯æ¹æ³
æå° Model JSONï¼
val modelJson = JsonUtil.toJson(model, formatted = true)
logger.info("Model: $modelJson")
æ£æ¥ Model ç»æï¼
fun debugModel(model: Model) {
println("Pipeline: ${model.name}")
model.stages.forEachIndexed { sIndex, stage ->
println(" Stage[$sIndex]: ${stage.id} - ${stage.name} - ${stage.status}")
stage.containers.forEachIndexed { cIndex, container ->
println(" Container[$cIndex]: ${container.id} - ${container.name} - ${container.status}")
container.elements.forEachIndexed { eIndex, element ->
println(" Element[$eIndex]: ${element.id} - ${element.name} - ${element.status}")
}
}
}
}
28.2 常è§é®é¢ææ¥
é®é¢1ï¼Model ååºåå失败
åå ï¼Element æ Container ç @type åæ®µä¸å¹é
ææ¥ï¼æ£æ¥ JSON ä¸ç @type 弿¯å¦å¨ @JsonSubTypes 䏿³¨å
é®é¢2ï¼æå»ºç¶æä¸æ´æ°
åå ï¼Model æ´æ°åæªæä¹
å
ææ¥ï¼æ£æ¥ buildDetailService.updateModel() æ¯å¦è¢«è°ç¨
é®é¢3ï¼çæ¬å·ä¸éå¢
åå ï¼çæ¬ç¶æä¸æ¯ RELEASED
ææ¥ï¼æ£æ¥ versionStatus åæ°
äºåä¹ãæ§è½ä¼å建议
29.1 Model åºååä¼å
// 使ç¨éæ ¼å¼å JSONï¼åå°ç©ºæ ¼åæ¢è¡ï¼
val modelJson = JsonUtil.toJson(model, formatted = false)
// 使ç¨å缩åå¨ï¼å¯¹äºå¤§å Modelï¼
val compressedModel = GZIPUtils.compress(modelJson)
29.2 æ¥è¯¢ä¼å
// åªè·åéè¦çåæ®µ
fun getModelStatus(buildId: String): String? {
return buildRecordModelDao.getStatus(dslContext, buildId)
}
// æ¹éæ¥è¯¢
fun batchGetModels(buildIds: List<String>): Map<String, Model> {
return buildRecordModelDao.batchGet(dslContext, buildIds)
}
29.3 ç¼åçç¥
// ä½¿ç¨ Redis ç¼åçç¹ Model
@Cacheable(cacheNames = ["model"], key = "#pipelineId + ':' + #version")
fun getModel(pipelineId: String, version: Int): Model? {
return pipelineResourceVersionDao.getVersionModel(dslContext, pipelineId, version)
}
ä¸åãéå½ï¼å®æ´ç±»å¾
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Model â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â + name: String â
â + desc: String? â
â + stages: List<Stage> â
â + pipelineCreator: String? â
â + latestVersion: Int â
â + timeCost: BuildRecordTimeCost? â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â + getTriggerContainer(): TriggerContainer â
â + taskCount(): Int â
â + removeElements(types: Set<String>): Model â
â + defaultModel(name: String, userId: String?): Model â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â
â 1:N
â¼
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Stage â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â + id: String? â
â + name: String? â
â + containers: List<Container> â
â + stageControlOption: StageControlOption? â
â + checkIn: StagePauseCheck? â
â + checkOut: StagePauseCheck? â
â + finally: Boolean â
â + status: String? â
â + timeCost: BuildRecordTimeCost? â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â + resetBuildOption(init: Boolean?) â
â + getContainer(vmSeqId: String): Container? â
â + stageEnabled(): Boolean â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â
â 1:N
â¼
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â <<interface>> â
â Container â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â + id: String? â
â + name: String â
â + elements: List<Element> â
â + status: String? â
â + executeCount: Int? â
â + containerHashId: String? â
â + jobId: String? â
â + timeCost: BuildRecordTimeCost? â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â + getClassType(): String â
â + containerEnabled(): Boolean â
â + resetBuildOption(executeCount: Int) â
â + transformCompatibility() â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â² â² â²
â â â
âââââââââââ´ââââââââââ ââââââââââ´âââââââââ âââââââââââ´ââââââââââ
â TriggerContainer â â VMBuildContainerâ â NormalContainer â
ââââââââââââââââââââ⤠ââââââââââââââââââ⤠âââââââââââââââââââââ¤
â + params: List â â + baseOS â â + jobControlOptionâ
â + buildNo â â + dispatchType â â + mutexGroup â
â + templateParams â â + jobControlOpt â â + matrixControl â
âââââââââââââââââââââ â + mutexGroup â âââââââââââââââââââââ
â + matrixControl â
â + groupContainersâ
âââââââââââââââââââ
â
â 1:N
â¼
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â <<abstract>> â
â Element â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â + name: String â
â + id: String? â
â + status: String? â
â + version: String â
â + stepId: String? â
â + additionalOptions: ElementAdditionalOptions? â
â + timeCost: BuildRecordTimeCost? â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â + getClassType(): String â
â + getAtomCode(): String â
â + elementEnabled(): Boolean â
â + transformCompatibility() â
â + initStatus(rerun: Boolean): BuildStatus â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â² â² â²
â â â
âââââââââââ´ââââââââââ ââââââââââ´âââââââââ âââââââââââ´ââââââââââ
âManualTriggerElementâ âLinuxScriptElementâ âMarketBuildAtomElementâ
ââââââââââââââââââââ⤠ââââââââââââââââââ⤠âââââââââââââââââââââââ¤
â + canElementSkip â â + scriptType â â + atomCode â
â + useLatestParams â â + script â â + data â
â + buildMsg â â + continueNone â â â
âââââââââââââââââââââ âââââââââââââââââââ âââââââââââââââââââââââ
ææ¡£çæ¬: 2.1
æåæ´æ°: 2024-12
ç»´æ¤è
: BK-CI Team