
com.tencent.devops.process.yaml.v2.parsers.template.YamlTemplate.kt Maven / Gradle / Ivy
/*
* Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* BK-CI 蓝鲸持续集成平台 is licensed under the MIT license.
*
* A copy of the MIT License is included in this file.
*
*
* Terms of the MIT License:
* ---------------------------------------------------
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
* the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.tencent.devops.process.yaml.v2.parsers.template
import com.fasterxml.jackson.core.JsonProcessingException
import com.tencent.devops.common.api.constant.CommonMessageCode.ERROR_YAML_FORMAT_EXCEPTION_LENGTH_LIMIT_EXCEEDED
import com.tencent.devops.common.web.utils.I18nUtil
import com.tencent.devops.process.yaml.v2.enums.TemplateType
import com.tencent.devops.process.yaml.v2.exception.YamlFormatException
import com.tencent.devops.process.yaml.v2.models.Extends
import com.tencent.devops.process.yaml.v2.models.GitNotices
import com.tencent.devops.process.yaml.v2.models.PreScriptBuildYaml
import com.tencent.devops.process.yaml.v2.models.PreTemplateScriptBuildYaml
import com.tencent.devops.process.yaml.v2.models.Repositories
import com.tencent.devops.process.yaml.v2.models.ResourcesPools
import com.tencent.devops.process.yaml.v2.models.TemplateInfo
import com.tencent.devops.process.yaml.v2.models.Variable
import com.tencent.devops.process.yaml.v2.models.format
import com.tencent.devops.process.yaml.v2.models.job.PreJob
import com.tencent.devops.process.yaml.v2.models.stage.PreStage
import com.tencent.devops.process.yaml.v2.models.step.PreStep
import com.tencent.devops.process.yaml.v2.parameter.Parameters
import com.tencent.devops.process.yaml.v2.parameter.ParametersTemplateNull
import com.tencent.devops.process.yaml.v2.parameter.PreParametersTemplate
import com.tencent.devops.process.yaml.v2.parsers.template.models.GetTemplateParam
import com.tencent.devops.process.yaml.v2.parsers.template.models.NoReplaceTemplate
import com.tencent.devops.process.yaml.v2.parsers.template.models.TemplateDeepTreeNode
import com.tencent.devops.process.yaml.v2.stageCheck.Gate
import com.tencent.devops.process.yaml.v2.stageCheck.GateTemplate
import com.tencent.devops.process.yaml.v2.stageCheck.PreStageCheck
import com.tencent.devops.process.yaml.v2.stageCheck.PreTemplateStageCheck
@Suppress("ALL")
class YamlTemplate(
val extraParameters: T,
// 当前文件
var filePath: String,
// 文件对象
var yamlObject: PreTemplateScriptBuildYaml?,
// 当前库信息
val nowRepo: Repositories?,
// 目标库信息(发起库没有库信息)
val repo: Repositories?,
// 额外所有引用的resource-pool,这样不会干扰替换逻辑
val resourcePoolMapExt: MutableMap? = null,
// 模板替换配置,针对某些替换功能提供可配置项
val conf: YamlTemplateConf = YamlTemplateConf(),
// 来自文件
private val fileFromPath: String? = null,
// 远程模板类型(用来校验远程打平的模板的格式)
private val resTemplateType: TemplateType? = null,
// 校验模板深度和广度树
private val rootDeepTree: TemplateDeepTreeNode = TemplateDeepTreeNode(
path = filePath,
parent = null,
children = mutableListOf()
),
// 获取模板文件函数,将模板替换过程与获取文件解耦,方便测试或链接其他代码库
val getTemplateMethod: (
param: GetTemplateParam
) -> String
) {
// 存储当前库的模板信息,减少重复获取 key: templatePath value: template
private val templateLib = TemplateLibrary(extraParameters, getTemplateMethod)
// 添加图防止模版的循环嵌套
private val templateGraph = TemplateGraph()
@Throws(
YamlFormatException::class,
JsonProcessingException::class,
StackOverflowError::class
)
fun replace(
parameters: Map? = null
): PreScriptBuildYaml {
// 针对远程库进行打平替换时,根文件没有被替换Parameters
val newYamlObject = if (repo != null) {
val template = parseTemplateParameters(
fromPath = fileFromPath ?: "",
path = filePath,
template = templateLib.getTemplate(
path = filePath,
templateType = resTemplateType,
nowRepo = nowRepo,
toRepo = repo
),
parameters = parameters,
deepTree = rootDeepTree
)
// 将根文件也保存在模板库中方便取出
templateLib.setTemplate(filePath, template)
YamlObjects.getObjectFromYaml(filePath, template)
} else {
templateLib.setTemplate(filePath, TemplateYamlMapper.toYaml(yamlObject!!))
yamlObject
}
val preYamlObject = with(newYamlObject!!) {
PreScriptBuildYaml(
version = version,
name = name,
label = label,
triggerOn = triggerOn,
resources = resources,
notices = notices,
concurrency = concurrency
)
}
if (newYamlObject.extends != null) {
replaceExtends(newYamlObject.extends, preYamlObject, rootDeepTree)
}
if (newYamlObject.variables != null) {
replaceVariables(newYamlObject.variables, preYamlObject, rootDeepTree)
}
if (newYamlObject.stages != null) {
replaceStages(newYamlObject.stages, preYamlObject, rootDeepTree)
}
if (newYamlObject.jobs != null) {
replaceJobs(newYamlObject.jobs, preYamlObject, rootDeepTree)
}
if (newYamlObject.steps != null) {
replaceSteps(newYamlObject.steps, preYamlObject, rootDeepTree)
}
if (newYamlObject.finally != null) {
replaceFinally(newYamlObject.finally!!, preYamlObject, rootDeepTree)
}
return preYamlObject
}
private fun replaceExtends(
extend: Extends,
preYamlObject: PreScriptBuildYaml,
deepTree: TemplateDeepTreeNode
) {
val toPath = extend.template
val parameters = extend.parameters
// 根据远程模板获取
val templateObject = replaceResAndParam(TemplateType.EXTEND, toPath, parameters, filePath, deepTree)
// 获取extends模板后filePath就为被替换的文件了
this.filePath = toPath
// 需要替换模板的的递归替换
if (templateObject[TemplateType.VARIABLE.content] != null) {
replaceVariables(
variables = YamlObjects.transValue(
file = filePath,
type = TemplateType.VARIABLE.text,
value = templateObject[TemplateType.VARIABLE.content]
),
preYamlObject = preYamlObject,
deepTree = deepTree
)
}
if (templateObject[TemplateType.STAGE.content] != null) {
replaceStages(
YamlObjects.transValue(filePath, TemplateType.STAGE.text, templateObject[TemplateType.STAGE.content]),
preYamlObject,
deepTree
)
}
if (templateObject[TemplateType.JOB.content] != null) {
replaceJobs(
YamlObjects.transValue(filePath, TemplateType.JOB.text, templateObject[TemplateType.JOB.content]),
preYamlObject,
deepTree
)
}
if (templateObject[TemplateType.STEP.content] != null) {
replaceSteps(
YamlObjects.transValue(filePath, TemplateType.STEP.text, templateObject[TemplateType.STEP.content]),
preYamlObject,
deepTree
)
}
if (templateObject[TemplateType.FINALLY.content] != null) {
replaceFinally(
finally = YamlObjects.transValue(
file = filePath,
type = TemplateType.FINALLY.text,
value = templateObject[TemplateType.FINALLY.content]
),
preYamlObject = preYamlObject,
deepTree = deepTree
)
}
// notices只用做一次模板替换没有嵌套模板
if (templateObject["notices"] != null) {
val notices = mutableListOf()
val temNotices =
YamlObjects.transValue>>(filePath, "notices", templateObject["notices"])
temNotices.forEach {
notices.add(YamlObjects.getNotice(filePath, it))
}
preYamlObject.notices = notices
}
// 将不用替换的直接传入
val newYaml =
YamlObjects.getObjectFromYaml(toPath, TemplateYamlMapper.toYaml(templateObject))
preYamlObject.label = newYaml.label
preYamlObject.resources = newYaml.resources
// 用户没写就用模板的名字
if (preYamlObject.name.isNullOrBlank()) {
preYamlObject.name = newYaml.name
}
if (preYamlObject.concurrency == null && newYaml.concurrency != null) {
preYamlObject.concurrency = newYaml.concurrency
}
}
private fun replaceVariables(
variables: Map,
preYamlObject: PreScriptBuildYaml,
deepTree: TemplateDeepTreeNode
) {
val variableMap = mutableMapOf()
variables.forEach { (key, value) ->
val newVariable = replaceVariableTemplate(mapOf(key to value), filePath, deepTree)
if (key == Constants.TEMPLATE_KEY) {
// 通过取交集判断除template关键字之外的ID是否重复
TemplateYamlUtil.checkDuplicateKey(
filePath = filePath,
keys = variables.keys,
newKeys = newVariable.keys
)
}
variableMap.putAll(newVariable)
}
preYamlObject.variables = variableMap
}
private fun replaceStages(
stages: List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy