v4 提交新的模型,但是模型不能运行,如果要运行,把ci-process卸载pom

This commit is contained in:
mian-bin@hotmail.com 2025-03-27 20:03:27 +08:00
parent a9820840f4
commit 42759b8f40
59 changed files with 3006 additions and 155 deletions

File diff suppressed because one or more lines are too long

2
.idea/compiler.xml generated
View File

@ -14,8 +14,8 @@
<entry name="$MAVEN_REPOSITORY$/org/mapstruct/mapstruct/1.6.2/mapstruct-1.6.2.jar" />
</processorPath>
<module name="ci-scm-repository" />
<module name="spring-boot-starter-protection" />
<module name="pipeline-ci-process-api" />
<module name="spring-boot-starter-protection" />
<module name="spring-boot-starter-monitor" />
<module name="module-system-biz" />
<module name="ci-quality" />

View File

@ -19,12 +19,12 @@ import java.util.ArrayList;
import java.util.List;
/**
* JSON 工具类
* JSON 工具类 ,经量用hutool的
*
* @author mianbin
*/
@Slf4j
public class JsonUtils {
public class JsonUtils extends JSONUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
@ -104,7 +104,7 @@ public class JsonUtils {
* 使用 {@link #parseObject(String, Class)} @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下
* 如果 text 没有 class 属性则会报错此时使用这个方法可以解决
*
* @param text 字符串
* @param text 字符串
* @param clazz 类型
* @return 对象
*/
@ -139,7 +139,7 @@ public class JsonUtils {
/**
* 解析 JSON 字符串成指定类型的对象如果解析失败则返回 null
*
* @param text 字符串
* @param text 字符串
* @param typeReference 类型引用
* @return 指定类型的对象
*/

View File

@ -25,7 +25,14 @@ public interface IModelTemplate {
@Schema(title = "变量", required = true)
Map<String, String> getVariables();
/**
* 判读是否来自于模板
*
* @return true 是来自于模板|false 不是来自于模板
*/
default boolean fromTemplate() {
return getTemplate() != null && !getTemplate().isEmpty();
String template = getTemplate();
return template != null && !template.isEmpty();
}
}

View File

@ -1,5 +1,6 @@
package cd.casic.ci.common.pipeline.container;
import cd.casic.ci.common.pipeline.type.agent.AgentType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;

View File

@ -1,12 +1,16 @@
package cd.casic.ci.common.pipeline.container;
import cd.casic.ci.common.pipeline.IModelTemplate;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import cd.casic.ci.common.pipeline.pojo.time.BuildRecordTimeCost;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Getter;
import lombok.experimental.SuperBuilder;
import java.util.List;
import java.util.Map;
@ -20,8 +24,8 @@ import java.util.Map;
* @FilenameContainer
* @descriptionTodo
*/
@Data
@SuperBuilder
@Schema(title = "流水线模型-多态基类")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type")
@JsonSubTypes({
@ -29,50 +33,65 @@ import java.util.Map;
@JsonSubTypes.Type(value = NormalContainer.class, name = NormalContainer.classType),
@JsonSubTypes.Type(value = VMBuildContainer.class, name = VMBuildContainer.classType)
})
public interface Container extends IModelTemplate {
String getId();
void setId(String id);
String getName();
void setName(String name);
List<Element> getElements();
void setElements(List<Element> elements);
String getStatus();
void setStatus(String status);
Long getStartEpoch();
void setStartEpoch(Long startEpoch);
Long getSystemElapsed();
void setSystemElapsed(Long systemElapsed);
Long getElementElapsed();
void setElementElapsed(Long elementElapsed);
Boolean getCanRetry();
void setCanRetry(Boolean canRetry);
String getContainerId();
void setContainerId(String containerId);
String getContainerHashId();
void setContainerHashId(String containerHashId);
String getStartVMStatus();
void setStartVMStatus(String startVMStatus);
Integer getExecuteCount();
void setExecuteCount(Integer executeCount);
String getJobId();
void setJobId(String jobId);
Boolean getContainPostTaskFlag();
void setContainPostTaskFlag(Boolean containPostTaskFlag);
Boolean getMatrixGroupFlag();
void setMatrixGroupFlag(Boolean matrixGroupFlag);
BuildRecordTimeCost getTimeCost();
void setTimeCost(BuildRecordTimeCost timeCost);
Integer getStartVMTaskSeq();
void setStartVMTaskSeq(Integer startVMTaskSeq);
@Getter
@AllArgsConstructor
public abstract class Container implements IModelTemplate {
private String id;
private String name;
private List<Element> elements;
private String status;
@Deprecated
private Long startEpoch;
@Deprecated
private Long systemElapsed;
@Deprecated
private Long elementElapsed;
private Boolean canRetry;
private String containerId;
private String containerHashId;
private String startVMStatus;
private Integer executeCount;
private String jobId;
private Boolean containPostTaskFlag;
private Boolean matrixGroupFlag;
private BuildRecordTimeCost timeCost;
private Integer startVMTaskSeq;
void resetBuildOption(int executeCount);
void transformCompatibility();
Map<String, Object> genTaskParams();
String getClassType();
Container getContainerById(String vmSeqId);
void retryFreshMatrixOption();
List<Container> fetchGroupContainers();
Map<String, String> fetchMatrixContext();
Boolean containerEnabled();
void setContainerEnable(Boolean enable);
public void resetBuildOption(int executeCount) {
this.status = null;
this.timeCost = null;
this.startEpoch = null;
this.elementElapsed = null;
this.systemElapsed = null;
this.startVMStatus = null;
this.executeCount = executeCount;
}
public void transformCompatibility() {
if (elements != null) {
elements.forEach(Element::transformCompatibility);
}
}
public Map<String, Object> genTaskParams() {
Map<String, Object> configParams = JSONUtil.parseObj(this).toBean(Map.class);
if (elements != null && !elements.isEmpty()) {
configParams.put("elements", List.of());
}
return configParams;
}
public abstract String getClassType();
public abstract Container getContainerById(String vmSeqId);
public abstract void retryFreshMatrixOption();
public abstract List<Container> fetchGroupContainers();
public abstract Map<String, String> fetchMatrixContext();
public abstract boolean containerEnabled();
public abstract void setContainerEnable(boolean enable);
}

View File

@ -1,5 +1,10 @@
package cd.casic.ci.common.pipeline.container;
import cd.casic.ci.common.pipeline.NameAndValue;
import cd.casic.ci.common.pipeline.option.JobControlOption;
import cd.casic.ci.common.pipeline.option.MatrixControlOption;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import cd.casic.ci.common.pipeline.pojo.time.BuildRecordTimeCost;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -130,6 +135,11 @@ public class NormalContainer implements Container {
jobControlOption = jobControlOption != null ? jobControlOption.copy(enable) : new JobControlOption(enable);
}
@Override
public void resetBuildOption(int executeCount) {
}
@Override
public void transformCompatibility() {
if (jobControlOption != null && jobControlOption.getTimeoutVar() == null || jobControlOption.getTimeoutVar().isBlank()) {
@ -139,4 +149,24 @@ public class NormalContainer implements Container {
mutexGroup.setTimeoutVar(String.valueOf(mutexGroup.getTimeout()));
}
}
@Override
public Map<String, Object> genTaskParams() {
return null;
}
@Override
public String getTemplate() {
return null;
}
@Override
public String getRef() {
return null;
}
@Override
public Map<String, String> getVariables() {
return null;
}
}

View File

@ -1,6 +1,9 @@
package cd.casic.ci.common.pipeline.container;
import cd.casic.ci.common.pipeline.IModelTemplate;
import cd.casic.ci.common.pipeline.option.StageControlOption;
import cd.casic.ci.common.pipeline.pojo.StagePauseCheck;
import cd.casic.ci.common.pipeline.pojo.time.BuildRecordTimeCost;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -17,89 +20,103 @@ import java.util.Map;
* @FilenameStage
* @descriptionTodo
*/
public class Stage {
@Data
@NoArgsConstructor
@Schema(title = "流水线模型-阶段")
public class Stage implements IModelTemplate {
@Schema(title = "容器集合", required = true)
private List<Container> containers;
@Schema(title = "阶段ID (系统标识,用户不可编辑)", required = false)
private String id;
@Schema(title = "阶段名称", required = true)
private String name;
@Schema(title = "阶段ID (用户可编辑)", required = false)
private String stageIdForUser;
@Schema(title = "阶段标签", required = false, readOnly = true)
private List<String> tag;
@Schema(title = "阶段状态", required = false, readOnly = true)
private String status;
@Schema(title = "阶段启动时间", required = false, readOnly = true)
@Deprecated
private Long startEpoch;
@Schema(title = "容器运行时间", required = false, readOnly = true)
@Deprecated
private Long elapsed;
@Schema(title = "用户自定义环境变量", required = false)
private Map<String, String> customBuildEnv;
@Schema(title = "是否启用容器失败快速终止阶段", required = false)
private Boolean fastKill;
@Schema(title = "标识是否为FinallyStage每个Model只能包含一个FinallyStage并且处于最后位置", required = false)
private Boolean finally;
@Schema(title = "当前Stage是否能重试", required = false)
private Boolean canRetry;
@Schema(title = "流程控制选项", required = true)
private StageControlOption stageControlOption;
@Schema(title = "stage准入配置", required = false)
private StagePauseCheck checkIn;
@Schema(title = "stage准出配置", required = false)
private StagePauseCheck checkOut;
@Schema(title = "步骤运行次数", required = false, readOnly = true)
private Integer executeCount;
@Schema(title = "各项耗时", required = true)
private BuildRecordTimeCost timeCost;
@Data
@NoArgsConstructor
@Schema(title = "流水线模型-阶段")
public class Stage implements IModelTemplate {
@Schema(title = "容器集合", required = true)
private List<Container> containers;
@Schema(title = "阶段ID (系统标识,用户不可编辑)", required = false)
private String id;
@Schema(title = "阶段名称", required = true)
private String name;
@Schema(title = "阶段ID (用户可编辑)", required = false)
private String stageIdForUser;
@Schema(title = "阶段标签", required = false, readOnly = true)
private List<String> tag;
@Schema(title = "阶段状态", required = false, readOnly = true)
private String status;
@Schema(title = "阶段启动时间", required = false, readOnly = true)
@Deprecated
private Long startEpoch;
@Schema(title = "容器运行时间", required = false, readOnly = true)
@Deprecated
private Long elapsed;
@Schema(title = "用户自定义环境变量", required = false)
private Map<String, String> customBuildEnv;
@Schema(title = "是否启用容器失败快速终止阶段", required = false)
private Boolean fastKill;
@Schema(title = "标识是否为FinallyStage每个Model只能包含一个FinallyStage并且处于最后位置", required = false)
private Boolean finallyStage;
@Schema(title = "当前Stage是否能重试", required = false)
private Boolean canRetry;
@Schema(title = "流程控制选项", required = true)
private StageControlOption stageControlOption;
@Schema(title = "stage准入配置", required = false)
private StagePauseCheck checkIn;
@Schema(title = "stage准出配置", required = false)
private StagePauseCheck checkOut;
@Schema(title = "步骤运行次数", required = false, readOnly = true)
private Integer executeCount;
@Schema(title = "各项耗时", required = true)
private BuildRecordTimeCost timeCost;
public void resetBuildOption(Boolean init) {
if (init != null && init) {
status = null;
startEpoch = null;
elapsed = null;
}
if (checkIn != null) {
checkIn.fixReviewGroups(init != null && init);
}
if (checkOut != null) {
checkOut.fixReviewGroups(init != null && init);
}
if (stageControlOption != null && stageControlOption.getManualTrigger() && checkIn == null) {
checkIn = StagePauseCheck.convertControlOption(stageControlOption);
}
if (finally !=null && finally){
canRetry = false;
}
public void resetBuildOption(Boolean init) {
if (init != null && init) {
status = null;
startEpoch = null;
elapsed = null;
}
public Container getContainer(String vmSeqId) {
if (containers != null) {
for (Container container : containers) {
Container foundContainer = container.getContainerById(vmSeqId);
if (foundContainer != null) {
return foundContainer;
}
}
}
return null;
if (checkIn != null) {
checkIn.fixReviewGroups(init != null && init);
}
public void transformCompatibility() {
if (containers != null) {
for (Container container : containers) {
container.transformCompatibility();
}
}
if (checkOut != null) {
checkOut.fixReviewGroups(init != null && init);
}
public Boolean stageEnabled() {
return stageControlOption != null ? stageControlOption.getEnable() : true;
if (stageControlOption != null && stageControlOption.getManualTrigger() && checkIn == null) {
checkIn = StagePauseCheck.convertControlOption(stageControlOption);
}
if (finallyStage !=null && finallyStage){
canRetry = false;
}
}
public Container getContainer(String vmSeqId) {
if (containers != null) {
for (Container container : containers) {
Container foundContainer = container.getContainerById(vmSeqId);
if (foundContainer != null) {
return foundContainer;
}
}
}
return null;
}
public void transformCompatibility() {
if (containers != null) {
for (Container container : containers) {
container.transformCompatibility();
}
}
}
public Boolean stageEnabled() {
return stageControlOption != null ? stageControlOption.getEnable() : true;
}
@Override
public String getTemplate() {
return null;
}
@Override
public String getRef() {
return null;
}
@Override
public Map<String, String> getVariables() {
return null;
}
}

View File

@ -1,5 +1,12 @@
package cd.casic.ci.common.pipeline.container;
import cd.casic.ci.common.pipeline.NameAndValue;
import cd.casic.ci.common.pipeline.enums.VMBaseOS;
import cd.casic.ci.common.pipeline.option.JobControlOption;
import cd.casic.ci.common.pipeline.option.MatrixControlOption;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import cd.casic.ci.common.pipeline.pojo.time.BuildRecordTimeCost;
import cd.casic.ci.common.pipeline.type.DispatchType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -154,7 +161,7 @@ public class VMBuildContainer implements Container {
@Override
public void setContainerEnable(Boolean enable) {
jobControlOption = jobControlOption != null ? jobControlOption.copy(enable) : new JobControlOption(enable);
jobControlOption = jobControlOption != null ? jobControlOption.setEnable(enable) : new JobControlOption(enable);
}
@Override

View File

@ -0,0 +1,32 @@
package cd.casic.ci.common.pipeline.dialect;
import lombok.Getter;
import lombok.Setter;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.dialect
* @Projectops-pro
* @nameClassicPipelineDialect
* @Date2025/03/27 10:36
* @FilenameClassicPipelineDialect
* @descriptionTodo
*/
@Getter
@Setter
public class ClassicPipelineDialect implements IPipelineDialect {
@Override
public String getPipelineDialectType() {
return PipelineDialectType.CLASSIC.name();
}
@Override
public boolean supportUseExpression() {
return false;
}
@Override
public boolean supportChineseVarName() {
return true;
}
}

View File

@ -0,0 +1,32 @@
package cd.casic.ci.common.pipeline.dialect;
import lombok.Getter;
import lombok.Setter;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.dialect
* @Projectops-pro
* @nameConstrainedPipelineDialect
* @Date2025/03/27 10:36
* @FilenameConstrainedPipelineDialect
* @descriptionTodo
*/
@Getter
@Setter
public class ConstrainedPipelineDialect implements IPipelineDialect {
@Override
public String getPipelineDialectType() {
return PipelineDialectType.CONSTRAINED.name();
}
@Override
public boolean supportUseExpression() {
return true;
}
@Override
public boolean supportChineseVarName() {
return false;
}
}

View File

@ -0,0 +1,24 @@
package cd.casic.ci.common.pipeline.dialect;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.dialect
* @Projectops-pro
* @nameIPipelineDialect
* @Date2025/03/27 10:35
* @FilenameIPipelineDialect
* @descriptionTodo
*/
public interface IPipelineDialect {
String getPipelineDialectType();
/**
* 1. 仅支持双花括号避免出现 bash 脚本变量在执行前被系统赋值的问题
* 2. 流程控制选项插件入参Job设置等流水线配置中均可使用函数
*/
boolean supportUseExpression();
/**
* 是否支持中文变量名
*/
boolean supportChineseVarName();
}

View File

@ -0,0 +1,24 @@
package cd.casic.ci.common.pipeline.dialect;
import lombok.Getter;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.dialect
* @Projectops-pro
* @namePipelineDialectType
* @Date2025/03/27 10:36
* @FilenamePipelineDialectType
* @descriptionTodo
*/
@Getter
public enum PipelineDialectType {
CLASSIC(new ClassicPipelineDialect()),
CONSTRAINED(new ConstrainedPipelineDialect());
private final IPipelineDialect dialect;
PipelineDialectType(IPipelineDialect dialect) {
this.dialect = dialect;
}
}

View File

@ -0,0 +1,36 @@
package cd.casic.ci.common.pipeline.dialect;
import cd.casic.ci.common.pipeline.enums.ChannelCode;
import lombok.NoArgsConstructor;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.dialect
* @Projectops-pro
* @namePipelineDialectUtil
* @Date2025/03/27 10:36
* @FilenamePipelineDialectUtil
* @descriptionTodo
*/
@NoArgsConstructor
public class PipelineDialectUtil {
public static IPipelineDialect getPipelineDialect(String pipelineDialectType) {
return pipelineDialectType != null ? PipelineDialectType.valueOf(pipelineDialectType).getDialect() : PipelineDialectType.CLASSIC.getDialect();
}
public static IPipelineDialect getPipelineDialect(Boolean inheritedDialect, String projectDialect, String pipelineDialect) {
return getPipelineDialectType(inheritedDialect, projectDialect, pipelineDialect).getDialect();
}
public static PipelineDialectType getPipelineDialectType(Boolean inheritedDialect, String projectDialect, String pipelineDialect) {
if (inheritedDialect == null || inheritedDialect) {
if (projectDialect != null) {
return PipelineDialectType.valueOf(projectDialect);
}
} else if (pipelineDialect != null) {
return PipelineDialectType.valueOf(pipelineDialect);
}
return PipelineDialectType.CLASSIC;
}
}

View File

@ -1,5 +1,6 @@
package cd.casic.ci.common.pipeline.enums;
import cd.casic.ci.common.pipeline.pojo.element.trigger.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,83 @@
package cd.casic.ci.common.pipeline.extend;
import cd.casic.ci.common.pipeline.Model;
import cd.casic.ci.common.pipeline.container.Container;
import cd.casic.ci.common.pipeline.dialect.IPipelineDialect;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import cd.casic.ci.common.pipeline.pojo.element.atom.BeforeDeleteParam;
import cd.casic.ci.common.pipeline.pojo.setting.PipelineSetting;
import java.util.Map;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.extend
* @Projectops-pro
* @nameModelCheckPlugin
* @Date2025/03/27 10:39
* @FilenameModelCheckPlugin
* @descriptionTodo
*/
public interface ModelCheckPlugin {
/**
* 检查[model]编排的完整性并返回[JobSize + ElementSize = MetaSize]所有元素数量
*
* @param userId 操作人
* @param oauthUser 当前流水线权限代持人
* @param pipelineDialect 流水线方言,只有新增/编辑流水线或模版时才需要传入
* @throws ErrorCodeException 检查失败或异常
*/
int checkModelIntegrity(
Model model,
String projectId,
String userId,
boolean isTemplate,
String oauthUser,
IPipelineDialect pipelineDialect,
String pipelineId
) throws ErrorCodeException;
/**
* 检查[setting]配置的完整性
*
* @throws ErrorCodeException 检查失败或异常
*/
void checkSettingIntegrity(PipelineSetting setting, String projectId) throws ErrorCodeException;
/**
* 清理Model--不删除里面的Element内的逻辑
*/
void clearUpModel(Model model);
/**
* 在删除element前做的一些处理
* 对比sourceModel并清理model中与之不同的Element
*
* @param existModel 目标Model要清理的Model)
* @param sourceModel 源要比较的Model
*/
void beforeDeleteElementInExistsModel(
Model existModel,
Model sourceModel,
BeforeDeleteParam param
);
/**
* 检查[container]下的[element]插件的超时配置是否合法
* 如果使用了变量则从变量表[contextMap]进行替换如果值不符合则抛出异常[ErrorCodeException]
*/
void checkElementTimeoutVar(Container container, Element element, Map<String, String> contextMap) throws ErrorCodeException;
/**
* 检查[container]下互斥组配置是否合法
* 如果使用了变量则从变量表[contextMap]进行替换如果值不符合则抛出异常[ErrorCodeException]
*/
void checkMutexGroup(Container container, Map<String, String> contextMap) throws ErrorCodeException;
/**
* 检查是否是[finallyStage]Stage的Job[container][JobControlOption]配置是否合法
* 如果使用了变量则从变量表[contextMap]进行替换如果值不符合则抛出异常[ErrorCodeException]
*/
void checkJobCondition(Container container, boolean finallyStage, Map<String, String> contextMap) throws ErrorCodeException;
}

View File

@ -1,6 +1,16 @@
package cd.casic.ci.common.pipeline.pojo;
import cd.casic.ci.common.pipeline.EnvReplacementParser;
import cd.casic.ci.common.pipeline.dialect.IPipelineDialect;
import cd.casic.ci.common.pipeline.enums.BuildStatus;
import cd.casic.ci.common.pipeline.enums.ManualReviewAction;
import cd.casic.ci.common.pipeline.option.StageControlOption;
import cd.casic.ci.common.pipeline.pojo.element.atom.ManualReviewParam;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
@ -20,6 +30,7 @@ import java.util.stream.Collectors;
* @descriptionTodo
*/
@Data
@Builder
@Accessors(chain = true)
@Schema(title = "stage准入准出配置模型")
public class StagePauseCheck {
@ -192,11 +203,12 @@ public class StagePauseCheck {
}
public static StagePauseCheck convertControlOption(StageControlOption stageControlOption) {
return new StagePauseCheck()
.setManualTrigger(stageControlOption.getManualTrigger())
.setStatus(stageControlOption.getTriggered() ? BuildStatus.REVIEW_PROCESSED.name() : null)
.setReviewGroups(List.of(new StageReviewGroup()
.setId(UUIDUtil.generate())
.setId(IdUtil.fastUUID())
.setReviewers(stageControlOption.getTriggerUsers())
.setStatus(stageControlOption.getTriggered() ? ManualReviewAction.PROCESS.name() : null)
.setParams(stageControlOption.getReviewParams())))

View File

@ -4,9 +4,19 @@ import cd.casic.ci.common.pipeline.IModelTemplate;
import cd.casic.ci.common.pipeline.NameAndValue;
import cd.casic.ci.common.pipeline.enums.BuildStatus;
import cd.casic.ci.common.pipeline.enums.StartType;
import cd.casic.ci.common.pipeline.pojo.element.agent.*;
import cd.casic.ci.common.pipeline.pojo.element.market.MarketBuildAtomElement;
import cd.casic.ci.common.pipeline.pojo.element.market.MarketBuildLessAtomElement;
import cd.casic.ci.common.pipeline.pojo.element.market.MarketCheckImageElement;
import cd.casic.ci.common.pipeline.pojo.element.matrix.MatrixStatusElement;
import cd.casic.ci.common.pipeline.pojo.element.quality.QualityGateInElement;
import cd.casic.ci.common.pipeline.pojo.element.quality.QualityGateOutElement;
import cd.casic.ci.common.pipeline.pojo.element.trigger.*;
import cd.casic.ci.common.pipeline.pojo.time.BuildRecordTimeCost;
import cd.casic.ci.common.pipeline.pojo.transfer.PreStep;
import cd.casic.ci.common.pipeline.utils.ElementUtils;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.Schema;
@ -121,7 +131,7 @@ public abstract class Element implements IModelTemplate {
}
public Map<String, Object> genTaskParams() {
return JsonUtil.toMutableMap(this);
return JSONUtil.parseObj(this).toBean(Map.class);
}
public void cleanUp() {

View File

@ -1,6 +1,7 @@
package cd.casic.ci.common.pipeline.pojo.element;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
@ -13,6 +14,7 @@ import lombok.Data;
* @descriptionTodo
*/
@Data
@AllArgsConstructor
@Schema(title = "插件属性")
public class ElementProp {
private String name;

View File

@ -1,4 +1,4 @@
package cd.casic.ci.common.pipeline.pojo.market;
package cd.casic.ci.common.pipeline.pojo.element.market;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -1,4 +1,4 @@
package cd.casic.ci.common.pipeline.pojo.market;
package cd.casic.ci.common.pipeline.pojo.element.market;
import cd.casic.ci.common.pipeline.NameAndValue;
import cd.casic.ci.common.pipeline.pojo.element.Element;

View File

@ -1,4 +1,4 @@
package cd.casic.ci.common.pipeline.pojo.market;
package cd.casic.ci.common.pipeline.pojo.element.market;
import cd.casic.ci.common.pipeline.NameAndValue;
import cd.casic.ci.common.pipeline.pojo.element.Element;

View File

@ -1,4 +1,4 @@
package cd.casic.ci.common.pipeline.pojo.market;
package cd.casic.ci.common.pipeline.pojo.element.market;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -1,4 +1,4 @@
package cd.casic.ci.common.pipeline.pojo.matrix;
package cd.casic.ci.common.pipeline.pojo.element.matrix;
import cd.casic.ci.common.pipeline.NameAndValue;
import cd.casic.ci.common.pipeline.pojo.element.Element;

View File

@ -1,4 +1,4 @@
package cd.casic.ci.common.pipeline.pojo.quality;
package cd.casic.ci.common.pipeline.pojo.element.quality;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -1,4 +1,4 @@
package cd.casic.ci.common.pipeline.pojo.quality;
package cd.casic.ci.common.pipeline.pojo.element.quality;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -0,0 +1,225 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.api.enums.TriggerRepositoryType;
import cd.casic.ci.common.pipeline.enums.StartType;
import cd.casic.ci.common.pipeline.pojo.element.ElementProp;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.CodeEventType;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.PathFilterType;
import cd.casic.ci.common.pipeline.utils.TriggerElementPropUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameCodeGitWebHookTriggerElement
* @Date2025/03/27 10:20
* @FilenameCodeGitWebHookTriggerElement
* @descriptionTodo
*/
@Accessors(chain = true)
@Schema(title = "Git事件触发", description = CodeGitWebHookTriggerElement.classType)
public class CodeGitWebHookTriggerElement extends WebHookTriggerElement {
public static final String classType = "codeGitWebHookTrigger";
public static final String MERGE_ACTION_OPEN = "open";
public static final String MERGE_ACTION_CLOSE = "close";
public static final String MERGE_ACTION_REOPEN = "reopen";
public static final String MERGE_ACTION_PUSH_UPDATE = "push-update";
public static final String MERGE_ACTION_MERGE = "merge";
public static final String PUSH_ACTION_CREATE_BRANCH = "new-branch";
public static final String PUSH_ACTION_PUSH_FILE = "push-file";
@Schema(title = "任务名称", required = true)
private String name = "Git事件触发";
@Schema(title = "id", required = false)
private String id;
@Schema(title = "状态", required = false)
private String status;
@Schema(title = "仓库ID", required = true)
private String repositoryHashId;
@Schema(title = "分支名称", required = false)
private String branchName;
@Schema(title = "用于排除的分支名", required = false)
private String excludeBranchName;
@Schema(title = "路径过滤类型", required = true)
private PathFilterType pathFilterType = PathFilterType.NamePrefixFilter;
@Schema(title = "用于包含的路径", required = false)
private String includePaths;
@Schema(title = "用于排除的路径", required = false)
private String excludePaths;
@Schema(title = "用户白名单", required = false)
private List<String> includeUsers;
@Schema(title = "用于排除的user id", required = false)
private List<String> excludeUsers;
@Schema(title = "事件类型", required = false)
private CodeEventType eventType;
@Schema(title = "是否为block", required = false)
private Boolean block;
@Schema(title = "新版的git原子的类型")
private TriggerRepositoryType repositoryType;
@Schema(title = "新版的git代码库名")
private String repositoryName;
@Schema(title = "tag名称", required = false)
private String tagName;
@Schema(title = "用于排除的tag名称", required = false)
private String excludeTagName;
@Schema(title = "tag从哪条分支创建", required = false)
private String fromBranches;
@Schema(title = "用于排除的源分支名称", required = false)
private String excludeSourceBranchName;
@Schema(title = "用于包含的源分支名称", required = false)
private String includeSourceBranchName;
@Schema(title = "webhook队列", required = false)
private Boolean webhookQueue = false;
@Schema(title = "code review 状态", required = false)
private List<String> includeCrState;
@Schema(title = "code review 类型", required = false)
private List<String> includeCrTypes;
@Schema(title = "code note comment", required = false)
private String includeNoteComment;
@Schema(title = "code note 类型", required = false)
private List<String> includeNoteTypes;
@Schema(title = "是否启用回写")
private Boolean enableCheck = true;
@Schema(title = "issue事件action")
private List<String> includeIssueAction;
@Schema(title = "mr事件action")
private List<String> includeMrAction;
@Schema(title = "push事件action")
private List<String> includePushAction;
@Schema(title = "是否启用第三方过滤")
private Boolean enableThirdFilter = false;
@Schema(title = "第三方应用地址")
private String thirdUrl;
@Schema(title = "第三方应用鉴权token")
private String thirdSecretToken;
@Schema(title = "跳过WIP")
private Boolean skipWip = false;
public CodeGitWebHookTriggerElement(String name, String id, String status) {
super(name, id, status);
}
@Override
public String getClassType() {
return classType;
}
@Override
public String findFirstTaskIdByStartType(StartType startType) {
return StartType.WEB_HOOK.name().equals(startType.name()) ? this.id : super.findFirstTaskIdByStartType(startType);
}
@Override
public List<ElementProp> triggerCondition() {
List<ElementProp> props;
switch (eventType) {
case PUSH:
props = List.of(
TriggerElementPropUtils.vuexInput("action", joinToString(includePushAction)),
TriggerElementPropUtils.vuexInput("branchName", branchName),
TriggerElementPropUtils.vuexInput("excludeBranchName", excludeBranchName),
TriggerElementPropUtils.vuexInput("includePaths", includePaths),
TriggerElementPropUtils.vuexInput("excludePaths", excludePaths),
TriggerElementPropUtils.staffInput("includeUsers", includeUsers),
TriggerElementPropUtils.staffInput("excludeUsers", excludeUsers)
);
break;
case MERGE_REQUEST:
props = List.of(
TriggerElementPropUtils.vuexInput("action", joinToString(includeMrAction)),
TriggerElementPropUtils.selector("skip-wip", List.of((skipWip != null ? skipWip.toString() : "false"))),
TriggerElementPropUtils.vuexInput("branchName", branchName),
TriggerElementPropUtils.vuexInput("excludeBranchName", excludeBranchName),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", includeSourceBranchName),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", includeSourceBranchName),
TriggerElementPropUtils.vuexInput("includePaths", includePaths),
TriggerElementPropUtils.vuexInput("excludePaths", excludePaths),
TriggerElementPropUtils.staffInput("includeUsers", includeUsers),
TriggerElementPropUtils.staffInput("excludeUsers", excludeUsers)
);
break;
case MERGE_REQUEST_ACCEPT:
props = List.of(
TriggerElementPropUtils.vuexInput("action", "merge"),
TriggerElementPropUtils.vuexInput("branchName", branchName),
TriggerElementPropUtils.vuexInput("excludeBranchName", excludeBranchName),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", includeSourceBranchName),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", includeSourceBranchName),
TriggerElementPropUtils.vuexInput("includePaths", includePaths),
TriggerElementPropUtils.vuexInput("excludePaths", excludePaths),
TriggerElementPropUtils.staffInput("includeUsers", includeUsers),
TriggerElementPropUtils.staffInput("excludeUsers", excludeUsers)
);
break;
case TAG_PUSH:
props = List.of(
TriggerElementPropUtils.vuexInput("tagName", tagName),
TriggerElementPropUtils.vuexInput("excludeTagName", excludeTagName),
TriggerElementPropUtils.vuexInput("fromBranches", fromBranches),
TriggerElementPropUtils.staffInput("includeUsers", includeUsers),
TriggerElementPropUtils.staffInput("excludeUsers", excludeUsers)
);
break;
case REVIEW:
props = List.of(
TriggerElementPropUtils.selector("includeCrState", includeCrState),
TriggerElementPropUtils.selector("includeCrTypes", includeCrTypes)
);
break;
case ISSUES:
props = List.of(
TriggerElementPropUtils.selector("includeIssueAction", includeIssueAction)
);
break;
case NOTE:
props = List.of(
TriggerElementPropUtils.selector("includeNoteTypes", includeNoteTypes),
TriggerElementPropUtils.vuexInput("includeNoteComment", includeNoteComment)
);
break;
default:
props = List.of();
}
return props.stream().filter(java.util.Objects::nonNull).toList();
}
}

View File

@ -0,0 +1,134 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.api.enums.TriggerRepositoryType;
import cd.casic.ci.common.pipeline.enums.StartType;
import cd.casic.ci.common.pipeline.pojo.element.ElementProp;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.CodeEventType;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.PathFilterType;
import cd.casic.ci.common.pipeline.utils.TriggerElementPropUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameCodeGithubWebHookTriggerElement
* @Date2025/03/27 9:42
* @FilenameCodeGithubWebHookTriggerElement
* @descriptionTodo
*/
@Accessors(chain = true)
@Schema(title = "Github事件触发", description = CodeGithubWebHookTriggerElement.classType)
public class CodeGithubWebHookTriggerElement extends WebHookTriggerElement {
public static final String classType = "codeGithubWebHookTrigger";
public static final String MERGE_ACTION_OPEN = "open";
public static final String MERGE_ACTION_CLOSE = "close";
public static final String MERGE_ACTION_REOPEN = "reopen";
public static final String MERGE_ACTION_PUSH_UPDATE = "push-update";
public static final String MERGE_ACTION_MERGE = "merge";
@Schema(title = "任务名称", required = true)
private String name = "GitHub事件触发";
@Schema(title = "id", required = false)
private String id;
@Schema(title = "状态", required = false)
private String status;
@Schema(title = "仓库ID", required = true)
private String repositoryHashId;
@Schema(title = "分支名称", required = false)
private String branchName;
@Schema(title = "用于排除的分支名称", required = false)
private String excludeBranchName;
@Schema(title = "路径过滤类型", required = true)
private PathFilterType pathFilterType = PathFilterType.NamePrefixFilter;
@Schema(title = "用于包含的路径", required = false)
private String includePaths;
@Schema(title = "用于排除的路径", required = false)
private String excludePaths;
@Schema(title = "用户白名单", required = false)
private List<String> includeUsers;
@Schema(title = "用于排除的user id", required = false)
private String excludeUsers;
@Schema(title = "tag名称", required = false)
private String tagName;
@Schema(title = "用于排除的tag名称", required = false)
private String excludeTagName;
@Schema(title = "tag从哪条分支创建", required = false)
private String fromBranches;
@Schema(title = "用于排除的源分支名称", required = false)
private String excludeSourceBranchName;
@Schema(title = "用于包含的源分支名称", required = false)
private String includeSourceBranchName;
@Schema(title = "webhook队列", required = false)
private Boolean webhookQueue = false;
@Schema(title = "code review 类型", required = false)
private List<String> includeCrTypes;
@Schema(title = "是否启用回写")
private Boolean enableCheck = true;
@Schema(title = "push事件action")
private List<String> includePushAction;
@Schema(title = "是否启用第三方过滤")
private Boolean enableThirdFilter = false;
@Schema(title = "事件类型", required = false)
private CodeEventType eventType;
@Schema(title = "新版的github原子的类型")
private TriggerRepositoryType repositoryType;
@Schema(title = "新版的github代码库名")
private String repositoryName;
@Schema(title = "code review 状态", required = false)
private List<String> includeCrState;
@Schema(title = "code note comment", required = false)
private String includeNoteComment;
@Schema(title = "code note 类型", required = false)
private List<String> includeNoteTypes;
@Schema(title = "issue事件action")
private List<String> includeIssueAction;
@Schema(title = "pull request事件action")
private List<String> includeMrAction = List.of(MERGE_ACTION_OPEN, MERGE_ACTION_REOPEN, MERGE_ACTION_PUSH_UPDATE);
public CodeGithubWebHookTriggerElement(String name, String id, String status) {
super(name, id, status);
}
@Override
public String getClassType() {
return classType;
}
@Override
public String findFirstTaskIdByStartType(StartType startType) {
return StartType.WEB_HOOK.name().equals(startType.name()) ? this.id : super.findFirstTaskIdByStartType(startType);
}
}

View File

@ -0,0 +1,164 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.api.enums.TriggerRepositoryType;
import cd.casic.ci.common.pipeline.enums.StartType;
import cd.casic.ci.common.pipeline.pojo.element.ElementProp;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.CodeEventType;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.PathFilterType;
import cd.casic.ci.common.pipeline.utils.TriggerElementPropUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameCodeGitlabWebHookTriggerElement
* @Date2025/03/27 10:22
* @FilenameCodeGitlabWebHookTriggerElement
* @descriptionTodo
*/
@Accessors(chain = true)
@Schema(title = "GitLab仓库代码提交触发", description = CodeGitlabWebHookTriggerElement.classType)
public class CodeGitlabWebHookTriggerElement extends WebHookTriggerElement {
public static final String classType = "codeGitlabWebHookTrigger";
@Schema(title = "任务名称", required = true)
private String name = "Gitlab变更触发";
@Schema(title = "id", required = false)
private String id;
@Schema(title = "状态", required = false)
private String status;
@Schema(title = "仓库ID", required = true)
private String repositoryHashId;
@Schema(title = "分支名称", required = false)
private String branchName;
@Schema(title = "新版的gitlab原子的类型")
private TriggerRepositoryType repositoryType;
@Schema(title = "新版的gitlab代码库名")
private String repositoryName;
@Schema(title = "事件类型", required = false)
private CodeEventType eventType = CodeEventType.PUSH;
@Schema(title = "用于排除的分支名", required = false)
private String excludeBranchName;
@Schema(title = "路径过滤类型", required = true)
private PathFilterType pathFilterType = PathFilterType.NamePrefixFilter;
@Schema(title = "用于包含的路径", required = false)
private String includePaths;
@Schema(title = "用于排除的路径", required = false)
private String excludePaths;
@Schema(title = "用于包含的user id", required = false)
private List<String> includeUsers;
@Schema(title = "用于排除的user id", required = false)
private List<String> excludeUsers;
@Schema(title = "是否为block", required = false)
private Boolean block;
@Schema(title = "tag名称", required = false)
private String tagName;
@Schema(title = "用于排除的tag名称", required = false)
private String excludeTagName;
@Schema(title = "用于排除的源分支名称", required = false)
private String excludeSourceBranchName;
@Schema(title = "用于包含的源分支名称", required = false)
private String includeSourceBranchName;
@Schema(title = "用于包含的提交信息", required = false)
private String includeCommitMsg;
@Schema(title = "用于排除的提交信息", required = false)
private String excludeCommitMsg;
@Schema(title = "push事件action")
private List<String> includePushAction;
@Schema(title = "mr事件action")
private List<String> includeMrAction;
public CodeGitlabWebHookTriggerElement(String name, String id, String status) {
super(name, id, status);
}
@Override
public String getClassType() {
return classType;
}
@Override
public String findFirstTaskIdByStartType(StartType startType) {
return StartType.WEB_HOOK.name().equals(startType.name()) ? this.id : super.findFirstTaskIdByStartType(startType);
}
@Override
public List<ElementProp> triggerCondition() {
List<ElementProp> props;
switch (eventType) {
case PUSH:
props = List.of(
TriggerElementPropUtils.vuexInput("action", joinToString(includePushAction)),
TriggerElementPropUtils.vuexInput("branchName", branchName),
TriggerElementPropUtils.vuexInput("excludeBranchName", excludeBranchName),
TriggerElementPropUtils.vuexInput("includePaths", includePaths),
TriggerElementPropUtils.vuexInput("excludePaths", excludePaths),
TriggerElementPropUtils.staffInput("includeUsers", includeUsers),
TriggerElementPropUtils.staffInput("excludeUsers", excludeUsers)
);
break;
case MERGE_REQUEST:
props = List.of(
TriggerElementPropUtils.vuexInput("action", joinToString(includeMrAction)),
TriggerElementPropUtils.vuexInput("branchName", branchName),
TriggerElementPropUtils.vuexInput("excludeBranchName", excludeBranchName),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", includeSourceBranchName),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", includeSourceBranchName),
TriggerElementPropUtils.vuexInput("includePaths", includePaths),
TriggerElementPropUtils.vuexInput("excludePaths", excludePaths),
TriggerElementPropUtils.staffInput("includeUsers", includeUsers),
TriggerElementPropUtils.staffInput("excludeUsers", excludeUsers)
);
break;
case MERGE_REQUEST_ACCEPT:
props = List.of(
TriggerElementPropUtils.vuexInput("action", "merge"),
TriggerElementPropUtils.vuexInput("branchName", branchName),
TriggerElementPropUtils.vuexInput("excludeBranchName", excludeBranchName),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", includeSourceBranchName),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", includeSourceBranchName),
TriggerElementPropUtils.vuexInput("includePaths", includePaths),
TriggerElementPropUtils.vuexInput("excludePaths", excludePaths),
TriggerElementPropUtils.staffInput("includeUsers", includeUsers),
TriggerElementPropUtils.staffInput("excludeUsers", excludeUsers)
);
break;
case TAG_PUSH:
props = List.of(
TriggerElementPropUtils.vuexInput("tagName", tagName),
TriggerElementPropUtils.vuexInput("excludeTagName", excludeTagName)
);
break;
default:
props = List.of();
}
return props.stream().filter(java.util.Objects::nonNull).toList();
}
}

View File

@ -0,0 +1,100 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.api.enums.TriggerRepositoryType;
import cd.casic.ci.common.pipeline.enums.StartType;
import cd.casic.ci.common.pipeline.pojo.element.ElementProp;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.CodeEventType;
import cd.casic.ci.common.pipeline.utils.TriggerElementPropUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameCodeP4WebHookTriggerElement
* @Date2025/03/27 10:20
* @FilenameCodeP4WebHookTriggerElement
* @descriptionTodo
*/
@Data
@Accessors(chain = true)
@Schema(title = "p4事件触发", description = CodeP4WebHookTriggerElement.classType)
public class CodeP4WebHookTriggerElement extends WebHookTriggerElement {
public static final String classType = "codeP4WebHookTrigger";
@Schema(title = "任务名称", required = true)
private String name = "P4事件触发";
@Schema(title = "id", required = false)
private String id;
@Schema(title = "状态", required = false)
private String status;
@Schema(title = "数据", required = true)
private CodeP4WebHookTriggerData data;
@Override
public String getClassType() {
return classType;
}
@Override
public String findFirstTaskIdByStartType(StartType startType) {
return StartType.WEB_HOOK.name().equals(startType.name()) ? this.id : super.findFirstTaskIdByStartType(startType);
}
@Override
public List<ElementProp> triggerCondition() {
CodeP4WebHookTriggerInput input = data.getInput();
List<ElementProp> props;
switch (input.getEventType()) {
case CHANGE_COMMIT:
case CHANGE_SUBMIT:
case CHANGE_CONTENT:
case SHELVE_COMMIT:
case SHELVE_SUBMIT:
case SHELVE_DELETE:
props = List.of(
TriggerElementPropUtils.vuexInput("includePaths", input.getIncludePaths()),
TriggerElementPropUtils.vuexInput("excludePaths", input.getExcludePaths())
);
break;
default:
props = List.of();
}
return props.stream().filter(java.util.Objects::nonNull).toList();
}
}
@Data
@Accessors(chain = true)
class CodeP4WebHookTriggerData {
private CodeP4WebHookTriggerInput input;
}
@Data
@Accessors(chain = true)
class CodeP4WebHookTriggerInput {
@Schema(title = "仓库ID", required = true)
private String repositoryHashId;
@Schema(title = "新版的git原子的类型")
private TriggerRepositoryType repositoryType;
@Schema(title = "新版的git代码库名")
private String repositoryName;
@Schema(title = "用于包含的路径", required = false)
private String includePaths;
@Schema(title = "用于排除的路径", required = false)
private String excludePaths;
@Schema(title = "事件类型", required = false)
private CodeEventType eventType;
}

View File

@ -0,0 +1,81 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.api.enums.TriggerRepositoryType;
import cd.casic.ci.common.pipeline.enums.StartType;
import cd.casic.ci.common.pipeline.pojo.element.ElementProp;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.PathFilterType;
import cd.casic.ci.common.pipeline.utils.TriggerElementPropUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameCodeSVNWebHookTriggerElement
* @Date2025/03/27 10:09
* @FilenameCodeSVNWebHookTriggerElement
* @descriptionTodo
*/
@Data
@Accessors(chain = true)
@Schema(title = "SVN仓库代码提交触发", description = CodeSVNWebHookTriggerElement.classType)
public class CodeSVNWebHookTriggerElement extends WebHookTriggerElement {
public static final String classType = "codeSVNWebHookTrigger";
@Schema(title = "任务名称", required = true)
private String name = "SVN事件触发";
@Schema(title = "id", required = false)
private String id;
@Schema(title = "状态", required = false)
private String status;
@Schema(title = "仓库ID", required = true)
private String repositoryHashId;
@Schema(title = "路径过滤类型", required = true)
private PathFilterType pathFilterType = PathFilterType.NamePrefixFilter;
@Schema(title = "相对路径", required = true)
private String relativePath;
@Schema(title = "排除的路径", required = false)
private String excludePaths;
@Schema(title = "用户黑名单", required = false)
private List<String> excludeUsers;
@Schema(title = "用户白名单", required = false)
private List<String> includeUsers;
@Schema(title = "新版的svn原子的类型")
private TriggerRepositoryType repositoryType;
@Schema(title = "新版的svn代码库名")
private String repositoryName;
@Override
public String getClassType() {
return classType;
}
@Override
public String findFirstTaskIdByStartType(StartType startType) {
return StartType.WEB_HOOK.name().equals(startType.name()) ? this.id : super.findFirstTaskIdByStartType(startType);
}
@Override
public List<ElementProp> triggerCondition() {
return List.of(
TriggerElementPropUtils.vuexInput("relativePath", relativePath),
TriggerElementPropUtils.vuexInput("excludePaths", excludePaths),
TriggerElementPropUtils.staffInput("includeUsers", includeUsers),
TriggerElementPropUtils.staffInput("excludeUsers", excludeUsers)
).stream().filter(java.util.Objects::nonNull).toList();
}
}

View File

@ -0,0 +1,222 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.api.enums.TriggerRepositoryType;
import cd.casic.ci.common.pipeline.enums.StartType;
import cd.casic.ci.common.pipeline.pojo.element.ElementProp;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.CodeEventType;
import cd.casic.ci.common.pipeline.pojo.element.trigger.enums.PathFilterType;
import cd.casic.ci.common.pipeline.utils.TriggerElementPropUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameCodeTGitWebHookTriggerElement
* @Date2025/03/27 10:05
* @FilenameCodeTGitWebHookTriggerElement
* @descriptionTodo
*/
@Accessors(chain = true)
@Schema(title = "TGit事件触发", description = CodeTGitWebHookTriggerElement.classType)
public class CodeTGitWebHookTriggerElement extends WebHookTriggerElement {
public static final String classType = "codeTGitWebHookTrigger";
@Schema(title = "任务名称", required = true)
private String name = "TGit事件触发";
@Schema(title = "id", required = false)
private String id;
@Schema(title = "状态", required = false)
private String status;
@Schema(title = "数据", required = true)
private CodeTGitWebHookTriggerData data;
public CodeTGitWebHookTriggerElement(String name, String id, String status) {
super(name, id, status);
}
@Override
public String getClassType() {
return classType;
}
@Override
public String findFirstTaskIdByStartType(StartType startType) {
return StartType.WEB_HOOK.name().equals(startType.name()) ? this.id : super.findFirstTaskIdByStartType(startType);
}
@Override
public List<ElementProp> triggerCondition() {
CodeTGitWebHookTriggerInput input = data.getInput();
List<ElementProp> props;
switch (input.getEventType()) {
case PUSH:
props = List.of(
TriggerElementPropUtils.vuexInput("action", joinToString(input.getIncludePushAction())),
TriggerElementPropUtils.vuexInput("branchName", input.getBranchName()),
TriggerElementPropUtils.vuexInput("excludeBranchName", input.getExcludeBranchName()),
TriggerElementPropUtils.vuexInput("includePaths", input.getIncludePaths()),
TriggerElementPropUtils.vuexInput("excludePaths", input.getExcludePaths()),
TriggerElementPropUtils.staffInput("includeUsers", input.getIncludeUsers()),
TriggerElementPropUtils.staffInput("excludeUsers", input.getExcludeUsers())
);
break;
case MERGE_REQUEST:
props = List.of(
TriggerElementPropUtils.vuexInput("action", joinToString(input.getIncludeMrAction())),
TriggerElementPropUtils.selector("skip-wip", List.of((input.getSkipWip() != null ? input.getSkipWip().toString() : "false"))),
TriggerElementPropUtils.vuexInput("branchName", input.getBranchName()),
TriggerElementPropUtils.vuexInput("excludeBranchName", input.getExcludeBranchName()),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", input.getIncludeSourceBranchName()),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", input.getIncludeSourceBranchName()),
TriggerElementPropUtils.vuexInput("includePaths", input.getIncludePaths()),
TriggerElementPropUtils.vuexInput("excludePaths", input.getExcludePaths()),
TriggerElementPropUtils.staffInput("includeUsers", input.getIncludeUsers()),
TriggerElementPropUtils.staffInput("excludeUsers", input.getExcludeUsers())
);
break;
case MERGE_REQUEST_ACCEPT:
props = List.of(
TriggerElementPropUtils.vuexInput("action", "merge"),
TriggerElementPropUtils.vuexInput("branchName", input.getBranchName()),
TriggerElementPropUtils.vuexInput("excludeBranchName", input.getExcludeBranchName()),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", input.getIncludeSourceBranchName()),
TriggerElementPropUtils.vuexInput("includeSourceBranchName", input.getIncludeSourceBranchName()),
TriggerElementPropUtils.vuexInput("includePaths", input.getIncludePaths()),
TriggerElementPropUtils.vuexInput("excludePaths", input.getExcludePaths()),
TriggerElementPropUtils.staffInput("includeUsers", input.getIncludeUsers()),
TriggerElementPropUtils.staffInput("excludeUsers", input.getExcludeUsers())
);
break;
case TAG_PUSH:
props = List.of(
TriggerElementPropUtils.vuexInput("tagName", input.getTagName()),
TriggerElementPropUtils.vuexInput("excludeTagName", input.getExcludeTagName()),
TriggerElementPropUtils.vuexInput("fromBranches", input.getFromBranches()),
TriggerElementPropUtils.staffInput("includeUsers", input.getIncludeUsers()),
TriggerElementPropUtils.staffInput("excludeUsers", input.getExcludeUsers())
);
break;
case REVIEW:
props = List.of(
TriggerElementPropUtils.selector("includeCrState", input.getIncludeCrState())
);
break;
case ISSUES:
props = List.of(
TriggerElementPropUtils.selector("includeIssueAction", input.getIncludeIssueAction())
);
break;
case NOTE:
props = List.of(
TriggerElementPropUtils.selector("includeNoteTypes", input.getIncludeNoteTypes()),
TriggerElementPropUtils.vuexInput("includeNoteComment", input.getIncludeNoteComment())
);
break;
default:
props = List.of();
}
return props.stream().filter(java.util.Objects::nonNull).toList();
}
}
@Data
@Accessors(chain = true)
class CodeTGitWebHookTriggerData {
private CodeTGitWebHookTriggerInput input;
}
@Data
@Accessors(chain = true)
class CodeTGitWebHookTriggerInput {
@Schema(title = "仓库ID", required = true)
private String repositoryHashId;
@Schema(title = "分支名称", required = false)
private String branchName;
@Schema(title = "用于排除的分支名", required = false)
private String excludeBranchName;
@Schema(title = "路径过滤类型", required = true)
private PathFilterType pathFilterType = PathFilterType.NamePrefixFilter;
@Schema(title = "用于包含的路径", required = false)
private String includePaths;
@Schema(title = "用于排除的路径", required = false)
private String excludePaths;
@Schema(title = "用户白名单", required = false)
private List<String> includeUsers;
@Schema(title = "用于排除的user id", required = false)
private List<String> excludeUsers;
@Schema(title = "事件类型", required = false)
private CodeEventType eventType;
@Schema(title = "是否为block", required = false)
private Boolean block;
@Schema(title = "新版的git原子的类型")
private TriggerRepositoryType repositoryType;
@Schema(title = "新版的git代码库名")
private String repositoryName;
@Schema(title = "tag名称", required = false)
private String tagName;
@Schema(title = "用于排除的tag名称", required = false)
private String excludeTagName;
@Schema(title = "tag从哪条分支创建", required = false)
private String fromBranches;
@Schema(title = "用于排除的源分支名称", required = false)
private String excludeSourceBranchName;
@Schema(title = "用于包含的源分支名称", required = false)
private String includeSourceBranchName;
@Schema(title = "webhook队列", required = false)
private Boolean webhookQueue = false;
@Schema(title = "code review 状态", required = false)
private List<String> includeCrState;
@Schema(title = "code review 类型", required = false)
private List<String> includeCrTypes;
@Schema(title = "code note comment", required = false)
private String includeNoteComment;
@Schema(title = "code note 类型", required = false)
private List<String> includeNoteTypes;
@Schema(title = "是否启用回写")
private Boolean enableCheck = true;
@Schema(title = "issue事件action")
private List<String> includeIssueAction;
@Schema(title = "mr事件action")
private List<String> includeMrAction;
@Schema(title = "push事件action")
private List<String> includePushAction;
@Schema(title = "是否启用第三方过滤")
private Boolean enableThirdFilter = false;
@Schema(title = "跳过WIP")
private Boolean skipWip = false;
}

View File

@ -0,0 +1,52 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.pipeline.enums.StartType;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Set;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameManualTriggerElement
* @Date2025/03/27 10:04
* @FilenameManualTriggerElement
* @descriptionTodo
*/
@Data
@Accessors(chain = true)
@Schema(title = "手动触发")
public class ManualTriggerElement extends Element {
public static final String classType = "manualTrigger";
@Schema(title = "任务名称", required = true)
private String name = "手动触发";
@Schema(title = "id", required = false)
private String id;
@Schema(title = "状态", required = false)
private String status;
@Schema(title = "是否可跳过插件", required = false)
private Boolean canElementSkip;
@Schema(title = "是否使用最近一次的参数进行构建", required = false)
private Boolean useLatestParameters;
@Override
public String getClassType() {
return classType;
}
private Set<String> startTypeSet = Set.of(StartType.MANUAL.name(), StartType.SERVICE.name(), StartType.PIPELINE.name());
@Override
public String findFirstTaskIdByStartType(StartType startType) {
return startTypeSet.contains(startType.name()) ? this.id : super.findFirstTaskIdByStartType(startType);
}
}

View File

@ -0,0 +1,43 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameRemoteTriggerElement
* @Date2025/03/27 10:01
* @FilenameRemoteTriggerElement
* @descriptionTodo
*/
@Data
@Accessors(chain = true)
@Schema(title = "远程触发触发")
public class RemoteTriggerElement extends Element {
public static final String classType = "remoteTrigger";
@Schema(title = "任务名称", required = true)
private String name = "远程触发";
@Schema(title = "id", required = false)
private String id;
@Schema(title = "状态", required = false)
private String status;
@Schema(title = "是否可跳过插件", required = false)
private Boolean canElementSkip;
@Schema(title = "远程触发token", required = true)
private String remoteToken;
@Override
public String getClassType() {
return classType;
}
}

View File

@ -0,0 +1,92 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.api.enums.TriggerRepositoryType;
import cd.casic.ci.common.pipeline.enums.StartType;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.*;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameTimerTriggerElement
* @Date2025/03/27 10:00
* @FilenameTimerTriggerElement
* @descriptionTodo
*/
@Data
@Accessors(chain = true)
@Schema(title = "定时触发")
public class TimerTriggerElement extends Element {
public static final String classType = "timerTrigger";
@Schema(title = "任务名称", required = true)
private String name = "定时触发";
@Schema(title = "id", required = false)
private String id;
@Schema(title = "状态", required = false)
private String status;
@Schema(title = "定时表达式", required = false)
@Deprecated
private String expression;
@Schema(title = "改进后的表达式", required = false)
private List<String> newExpression;
@Schema(title = "高级定时表达式", required = false)
private List<String> advanceExpression;
@Schema(title = "源代码未更新则不触发构建", required = false)
private Boolean noScm;
@Schema(title = "指定代码库分支", required = false)
private List<String> branches;
@Schema(title = "代码库类型", required = false)
private TriggerRepositoryType repositoryType;
@Schema(title = "代码库HashId", required = false)
private String repoHashId;
@Schema(title = "指定代码库别名", required = false)
private String repoName;
@Override
public String getClassType() {
return classType;
}
private boolean isOldExpress() {
return (newExpression == null || newExpression.isEmpty()) &&
(advanceExpression == null || advanceExpression.isEmpty());
}
private String checkAndSetSecond(String expression) {
String trimmedExpression = expression.trim();
String[] expressionParts = trimmedExpression.split(" ");
if (!"0".equals(expressionParts[0])) {
List<String> newExpressionParts = Arrays.asList(expressionParts);
newExpressionParts.set(0, "0");
return String.join(" ", newExpressionParts);
}
return trimmedExpression;
}
@Override
public String findFirstTaskIdByStartType(StartType startType) {
return StartType.TIME_TRIGGER.name().equals(startType.name()) ? this.id : super.findFirstTaskIdByStartType(startType);
}
public boolean enableRepoConfig() {
return TriggerRepositoryType.SELF.equals(repositoryType) ||
(TriggerRepositoryType.ID.equals(repositoryType) && repoHashId != null && !repoHashId.isBlank()) ||
(TriggerRepositoryType.NAME.equals(repositoryType) && repoName != null && !repoName.isBlank());
}
}

View File

@ -0,0 +1,35 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import cd.casic.ci.common.pipeline.pojo.element.ElementProp;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger
* @Projectops-pro
* @nameWebHookTriggerElement
* @Date2025/03/27 9:59
* @FilenameWebHookTriggerElement
* @descriptionTodo
*/
@Data
@Accessors(chain = true)
@Schema(title = "Webhook基础类")
public abstract class WebHookTriggerElement extends Element {
public WebHookTriggerElement(String name, String id, String status) {
super(name, id, status);
}
public List<ElementProp> triggerCondition() {
return List.of();
}
public String joinToString(List<String> list) {
return list == null || list.isEmpty() ? "" : String.join(",", list);
}
}

View File

@ -0,0 +1,103 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger.enums;
import cd.casic.ci.common.api.enums.ScmType;
import java.util.ArrayList;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger.enums
* @Projectops-pro
* @nameCodeEventType
* @Date2025/03/27 10:26
* @FilenameCodeEventType
* @descriptionTodo
*/
public enum CodeEventType {
// Git events
PUSH,
TAG_PUSH,
MERGE_REQUEST,
MERGE_REQUEST_ACCEPT,
ISSUES,
NOTE,
REVIEW,
// GitHub events
CREATE,
PULL_REQUEST,
// SVN events
POST_COMMIT,
LOCK_COMMIT,
PRE_COMMIT,
// Perforce (P4) events
CHANGE_COMMIT,
PUSH_SUBMIT,
CHANGE_CONTENT,
CHANGE_SUBMIT,
PUSH_CONTENT,
PUSH_COMMIT,
FIX_ADD,
FIX_DELETE,
FORM_COMMIT,
SHELVE_COMMIT,
SHELVE_DELETE,
SHELVE_SUBMIT,
// Sub-pipeline
PARENT_PIPELINE;
public static final String MESSAGE_CODE_PREFIX = "EVENT_TYPE";
// Git events
public static final List<CodeEventType> CODE_GIT_EVENTS = List.of(
PUSH, MERGE_REQUEST, MERGE_REQUEST_ACCEPT, TAG_PUSH, NOTE, REVIEW, ISSUES
);
// GitHub events
public static final List<CodeEventType> CODE_GITHUB_EVENTS = List.of(
PUSH, PULL_REQUEST, CREATE, REVIEW, ISSUES, NOTE
);
// Perforce (P4) events
public static final List<CodeEventType> CODE_P4_EVENTS = List.of(
CHANGE_COMMIT, CHANGE_SUBMIT, CHANGE_CONTENT, SHELVE_COMMIT, SHELVE_SUBMIT
);
// GitLab events
public static final List<CodeEventType> CODE_GITLAB_EVENTS = List.of(
PUSH, MERGE_REQUEST, MERGE_REQUEST_ACCEPT, TAG_PUSH
);
// Tencent Git (TGit) events
public static final List<CodeEventType> CODE_TGIT_EVENTS = List.of(
PUSH, MERGE_REQUEST, MERGE_REQUEST_ACCEPT, TAG_PUSH, NOTE, ISSUES
);
// SVN events
public static final List<CodeEventType> CODE_SVN_EVENTS = List.of(
POST_COMMIT, PRE_COMMIT, LOCK_COMMIT
);
public static List<CodeEventType> getEventsByScmType(ScmType scmType) {
if (scmType == null) {
return List.of(values());
}
switch (scmType) {
case CODE_GIT:
return new ArrayList<>(CODE_GIT_EVENTS);
case CODE_TGIT:
return new ArrayList<>(CODE_TGIT_EVENTS);
case GITHUB:
return new ArrayList<>(CODE_GITHUB_EVENTS);
case CODE_GITLAB:
return new ArrayList<>(CODE_GITLAB_EVENTS);
case CODE_SVN:
return new ArrayList<>(CODE_SVN_EVENTS);
case CODE_P4:
return new ArrayList<>(CODE_P4_EVENTS);
default:
return List.of(values());
}
}
}

View File

@ -0,0 +1,19 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger.enums;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger.enums
* @Projectops-pro
* @nameCodeType
* @Date2025/03/27 10:28
* @FilenameCodeType
* @descriptionTodo
*/
public enum CodeType {
SVN,
GIT,
GITLAB,
GITHUB,
TGIT,
P4
}

View File

@ -0,0 +1,15 @@
package cd.casic.ci.common.pipeline.pojo.element.trigger.enums;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.element.trigger.enums
* @Projectops-pro
* @namePathFilterType
* @Date2025/03/27 10:28
* @FilenamePathFilterType
* @descriptionTodo
*/
public enum PathFilterType {
NamePrefixFilter,
RegexBasedFilter
}

View File

@ -0,0 +1,21 @@
package cd.casic.ci.common.pipeline.pojo.git;
import cd.casic.ci.common.pipeline.enums.GitPullModeType;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.pojo.git
* @Projectops-pro
* @nameGitPullMode
* @Date2025/03/27 10:24
* @FilenameGitPullMode
* @descriptionTodo
*/
@Data
@AllArgsConstructor
public class GitPullMode {
private GitPullModeType type;
private String value;
}

View File

@ -0,0 +1,45 @@
package cd.casic.ci.common.pipeline.type;
import cn.hutool.http.useragent.OS;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type
* @Projectops-pro
* @nameBuildType
* @Date2025/03/27 10:45
* @FilenameBuildType
* @descriptionTodo
*/
@Getter
@AllArgsConstructor
public enum BuildType {
ESXI("蓝盾公共构建资源", List.of(OS.MACOS), false, false, false),
MACOS("云托管: MacOS on DevCloud", List.of(OS.MACOS), false, false, false),
WINDOWS("云托管Windows on DevCloud", List.of(OS.WINDOWS), false, false, false),
KUBERNETES("Kubernetes构建资源", List.of(OS.LINUX), false, false, false),
PUBLIC_DEVCLOUD("公共Docker on DevCloud", List.of(OS.LINUX), true, false, false),
PUBLIC_BCS("公共Docker on Bcs", List.of(OS.LINUX), false, false, false),
THIRD_PARTY_AGENT_ID("私有:单构建机", List.of(OS.MACOS, OS.LINUX, OS.WINDOWS), false, true, true),
THIRD_PARTY_AGENT_ENV("私有:构建集群", List.of(OS.MACOS, OS.LINUX, OS.WINDOWS), false, true, true),
THIRD_PARTY_PCG("PCG公共构建资源", List.of(OS.LINUX), false, false, false),
THIRD_PARTY_DEVCLOUD("腾讯自研云云devnet资源", List.of(OS.LINUX), false, false, false),
GIT_CI("工蜂CI", List.of(OS.LINUX), false, false, false),
DOCKER("Docker公共构建机", List.of(OS.LINUX), true, true, true),
STREAM("stream", List.of(OS.LINUX), false, false, false),
AGENT_LESS("无编译环境", List.of(OS.LINUX), false, false, false);
private final String value;
private final List<OS> osList;
private final boolean enableApp;
private final boolean clickable;
private final boolean visible;
public String getI18n(String language) {
return MessageUtil.getMessageByLocale("buildType." + this.name(), language);
}
}

View File

@ -0,0 +1,34 @@
package cd.casic.ci.common.pipeline.type;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type
* @Projectops-pro
* @nameDispatchRouteKeySuffix
* @Date2025/03/27 10:45
* @FilenameDispatchRouteKeySuffix
* @descriptionTodo
*/
@Getter
@AllArgsConstructor
public enum DispatchRouteKeySuffix {
DOCKER_VM(".docker.vm"),
DOCKER_VM_DEMOTE(".docker.vm.demote"),
KUBERNETES(".kubernetes"),
KUBERNETES_DEMOTE(".kubernetes.demote"),
PCG(".pcg.sumeru"),
DEVCLOUD(".devcloud.public"),
BCS(".bcs.public"),
IDC(".idc.public"),
GITCI(".gitci.public"),
STREAM(".stream.docker"),
STREAM_DEMOTE(".stream.docker.demote"),
CODECC(".codecc.scan"),
MACOS(".macos"),
WINDOWS(".windows");
private final String routeKeySuffix;
}

View File

@ -0,0 +1,47 @@
package cd.casic.ci.common.pipeline.type;
import cd.casic.ci.common.pipeline.type.agent.ThirdPartyAgentEnvDispatchType;
import cd.casic.ci.common.pipeline.type.agent.ThirdPartyAgentIDDispatchType;
import cd.casic.ci.common.pipeline.utils.EnvUtils;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Getter;
import lombok.Setter;
import java.util.Map;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type
* @Projectops-pro
* @nameDispatchType
* @Date2025/03/27 10:45
* @FilenameDispatchType
* @descriptionTodo
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "buildType", visible = false)
@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")
})
@Getter
@Setter
public abstract class DispatchType {
private String value;
private DispatchRouteKeySuffix routeKeySuffix;
public void replaceVariable(Map<String, String> variables) {
this.value = EnvUtils.parseEnv(value, variables);
replaceField(variables);
}
public abstract BuildType buildType();
protected abstract void replaceField(Map<String, String> variables);
public abstract void cleanDataBeforeSave();
}

View File

@ -0,0 +1,55 @@
package cd.casic.ci.common.pipeline.type;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.Optional;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type
* @Projectops-pro
* @nameStoreDispatchType
* @Date2025/03/27 10:45
* @FilenameStoreDispatchType
* @descriptionTodo
*/
@Getter
@Setter
public abstract class StoreDispatchType extends DispatchType {
@Schema(title = "docker构建版本", required = false)
private String dockerBuildVersion;
@Schema(title = "镜像类型", required = false)
private ImageType imageType = ImageType.BKDEVOPS;
@Schema(title = "凭证id", required = false)
private String credentialId = "";
@Schema(title = "凭证项目id", required = false)
private String credentialProject = "";
@Schema(title = "商店镜像代码", required = false)
private String imageCode = "";
@Schema(title = "商店镜像版本", required = false)
private String imageVersion = "";
@Schema(title = "商店镜像名称", required = false)
private String imageName = "";
@Schema(title = "商店镜像公共标识", required = false)
private Boolean imagePublicFlag = false;
@Schema(title = "商店镜像研发来源", required = false)
private String imageRDType = "";
@Schema(title = "商店镜像是否推荐", required = false)
private Boolean recommendFlag = true;
public StoreDispatchType(String dockerBuildVersion, DispatchRouteKeySuffix routeKeySuffix) {
super(Optional.ofNullable(dockerBuildVersion).orElse("StoreDispatchType empty image"), routeKeySuffix);
this.dockerBuildVersion = dockerBuildVersion;
}
}

View File

@ -0,0 +1,25 @@
package cd.casic.ci.common.pipeline.type.agent;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type.agent
* @Projectops-pro
* @nameAgentType
* @Date2025/03/27 10:47
* @FilenameAgentType
* @descriptionTodo
*/
@Getter
@AllArgsConstructor
public enum AgentType {
ID, // This is only for old pipeline, new pipelines will use name
NAME,
REUSE_JOB_ID; // 是否复用其他job的Agent节点
public boolean isReuse() {
return this == REUSE_JOB_ID;
}
}

View File

@ -0,0 +1,60 @@
package cd.casic.ci.common.pipeline.type.agent;
import cd.casic.ci.common.pipeline.type.DispatchType;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;
import java.util.Map;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type.agent
* @Projectops-pro
* @nameThirdPartyAgentDispatch
* @Date2025/03/27 10:47
* @FilenameThirdPartyAgentDispatch
* @descriptionTodo
*/
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class ThirdPartyAgentDispatch extends DispatchType {
private String value;
private AgentType agentType;
private String workspace;
private ThirdPartyAgentDockerInfo dockerInfo;
private ReusedInfo reusedInfo;
public ThirdPartyAgentDispatch(String value, AgentType agentType, String workspace, ThirdPartyAgentDockerInfo dockerInfo, ReusedInfo reusedInfo) {
super(value);
this.value = value;
this.agentType = agentType;
this.workspace = workspace;
this.dockerInfo = dockerInfo;
this.reusedInfo = reusedInfo;
}
public boolean idType() {
return (agentType == AgentType.ID) || (reusedInfo != null && reusedInfo.getAgentType() == AgentType.ID) ||
(agentType == AgentType.REUSE_JOB_ID && reusedInfo == null);
}
public boolean hasReuseMutex() {
return this.agentType.isReuse() || this.reusedInfo != null;
}
public boolean isEnv() {
return this instanceof ThirdPartyAgentEnvDispatchType;
}
public boolean isSingle() {
return this instanceof ThirdPartyAgentIDDispatchType;
}
@Override
public abstract void replaceField(Map<String, String> variables);
@Override
public abstract void cleanDataBeforeSave();
}

View File

@ -0,0 +1,112 @@
package cd.casic.ci.common.pipeline.type.agent;
import cd.casic.ci.common.pipeline.utils.EnvUtils;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type.agent
* @Projectops-pro
* @nameThirdPartyAgentDockerInfo
* @Date2025/03/27 10:47
* @FilenameThirdPartyAgentDockerInfo
* @descriptionTodo
*/
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ThirdPartyAgentDockerInfo {
private String image;
private Credential credential;
private DockerOptions options;
private String imagePullPolicy;
private ImageType imageType = ImageType.THIRD;
private ThirdPartyAgentDockerInfoStoreImage storeImage;
public void replaceField(Map<String, String> variables) {
image = EnvUtils.parseEnv(image, variables);
if (credential != null) {
if (!credential.getUser().isBlank()) {
credential.setUser(EnvUtils.parseEnv(credential.getUser(), variables));
}
if (!credential.getPassword().isBlank()) {
credential.setPassword(EnvUtils.parseEnv(credential.getPassword(), variables));
}
if (!credential.getCredentialId().isBlank()) {
credential.setCredentialId(EnvUtils.parseEnv(credential.getCredentialId(), variables));
}
}
if (options != null) {
options.setVolumes(options.getVolumes().stream().map(v -> EnvUtils.parseEnv(v, variables)).toList());
options.setMounts(options.getMounts().stream().map(m -> EnvUtils.parseEnv(m, variables)).toList());
options.setGpus(EnvUtils.parseEnv(options.getGpus(), variables));
options.setPrivileged(Boolean.parseBoolean(EnvUtils.parseEnv(String.valueOf(options.getPrivileged()), variables)));
}
if (imagePullPolicy != null) {
imagePullPolicy = EnvUtils.parseEnv(imagePullPolicy, variables);
}
}
}
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
class Credential {
private String user;
private String password;
private String credentialId;
private String acrossTemplateId;
private String jobId;
private String credentialProjectId;
}
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
class DockerOptions {
private List<String> volumes;
private List<String> mounts;
private String gpus;
private Boolean privileged;
}
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
class ThirdPartyAgentDockerInfoStoreImage {
private String imageName;
private String imageCode;
private String imageVersion;
}
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
class ThirdPartyAgentDockerInfoDispatch {
private String agentId;
private String secretKey;
private String image;
private Credential credential;
private DockerOptions options;
private String imagePullPolicy;
public ThirdPartyAgentDockerInfoDispatch(String agentId, String secretKey, ThirdPartyAgentDockerInfo info) {
this.agentId = agentId;
this.secretKey = secretKey;
this.image = info.getImage();
this.credential = info.getCredential();
this.options = info.getOptions();
this.imagePullPolicy = info.getImagePullPolicy();
}
}

View File

@ -0,0 +1,63 @@
package cd.casic.ci.common.pipeline.type.agent;
import cd.casic.ci.common.pipeline.type.BuildType;
import cd.casic.ci.common.pipeline.utils.EnvUtils;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
import java.util.Map;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type.agent
* @Projectops-pro
* @nameThirdPartyAgentEnvDispatchType
* @Date2025/03/27 10:48
* @FilenameThirdPartyAgentEnvDispatchType
* @descriptionTodo
*/
@Getter
@Setter
public class ThirdPartyAgentEnvDispatchType extends ThirdPartyAgentDispatch {
@JsonProperty("value")
private String envName;
private String workspace;
private String envProjectId;
private AgentType agentType = AgentType.NAME;
private ThirdPartyAgentDockerInfo dockerInfo;
private ReusedInfo reusedInfo;
public ThirdPartyAgentEnvDispatchType(String envName, String workspace, String envProjectId, ThirdPartyAgentDockerInfo dockerInfo, ReusedInfo reusedInfo) {
super(envName, agentType, workspace, dockerInfo, reusedInfo);
this.envName = envName;
this.workspace = workspace;
this.envProjectId = envProjectId;
this.dockerInfo = dockerInfo;
this.reusedInfo = reusedInfo;
}
@Override
public void cleanDataBeforeSave() {
this.envName = this.envName.trim();
this.envProjectId = this.envProjectId != null ? this.envProjectId.trim() : null;
this.workspace = this.workspace != null ? this.workspace.trim() : null;
}
@Override
public void replaceField(Map<String, String> variables) {
envName = EnvUtils.parseEnv(envName, variables);
envProjectId = EnvUtils.parseEnv(envProjectId, variables);
if (workspace != null && !workspace.isBlank()) {
workspace = EnvUtils.parseEnv(workspace, variables);
}
if (dockerInfo != null) {
dockerInfo.replaceField(variables);
}
}
@Override
public BuildType buildType() {
return BuildType.THIRD_PARTY_AGENT_ENV;
}
}

View File

@ -0,0 +1,59 @@
package cd.casic.ci.common.pipeline.type.agent;
import cd.casic.ci.common.pipeline.type.BuildType;
import cd.casic.ci.common.pipeline.utils.EnvUtils;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
import java.util.Map;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type.agent
* @Projectops-pro
* @nameThirdPartyAgentIDDispatchType
* @Date2025/03/27 10:48
* @FilenameThirdPartyAgentIDDispatchType
* @descriptionTodo
*/
@Getter
@Setter
public class ThirdPartyAgentIDDispatchType extends ThirdPartyAgentDispatch {
@JsonProperty("value")
private String displayName;
private String workspace;
private AgentType agentType = AgentType.NAME;
private ThirdPartyAgentDockerInfo dockerInfo;
private ReusedInfo reusedInfo;
public ThirdPartyAgentIDDispatchType(String displayName, String workspace, ThirdPartyAgentDockerInfo dockerInfo, ReusedInfo reusedInfo) {
super(displayName, agentType, workspace, dockerInfo, reusedInfo);
this.displayName = displayName;
this.workspace = workspace;
this.dockerInfo = dockerInfo;
this.reusedInfo = reusedInfo;
}
@Override
public void cleanDataBeforeSave() {
this.displayName = this.displayName.trim();
this.workspace = this.workspace != null ? this.workspace.trim() : null;
}
@Override
public void replaceField(Map<String, String> variables) {
displayName = EnvUtils.parseEnv(displayName, variables);
if (workspace != null && !workspace.isBlank()) {
workspace = EnvUtils.parseEnv(workspace, variables);
}
if (dockerInfo != null) {
dockerInfo.replaceField(variables);
}
}
@Override
public BuildType buildType() {
return BuildType.THIRD_PARTY_AGENT_ID;
}
}

View File

@ -0,0 +1,10 @@
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.type
* @Projectops-pro
* @namepackage-info
* @Date2025/03/27 10:44
* @Filenamepackage-info
* @descriptionTodo
*/
package cd.casic.ci.common.pipeline.type;

View File

@ -0,0 +1,79 @@
package cd.casic.ci.common.pipeline.utils;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.utils
* @Projectops-pro
* @nameEnvUtils
* @Date2025/03/27 15:20
* @FilenameEnvUtils
* @descriptionTodo
*/
public class EnvUtils {
private static final Pattern tPattern = Pattern.compile("(\\$[{](?<single>[^$^{}]+)})|(\\$[{]{2}(?<double>[^$^{}]+)[}]{2})");
public static String parseEnv(String command, Map<String, String> data) {
return parseEnv(command, data, false, false);
}
public static String parseEnv(String command, Map<String, String> data, boolean replaceWithEmpty, boolean isEscape) {
return parseEnv(command, data, replaceWithEmpty, isEscape, new HashMap<>());
}
public static String parseEnv(String command, Map<String, String> data, boolean replaceWithEmpty, boolean isEscape, Map<String, String> contextMap) {
if (command == null || command.isEmpty()) {
return command == null ? "" : command;
}
return parseTokenTwice(command, data, contextMap, replaceWithEmpty, isEscape, 1);
}
private static String parseTokenTwice(String command, Map<String, String> data, Map<String, String> contextMap, boolean replaceWithEmpty, boolean isEscape, int depth) {
if (depth < 0) {
return command;
}
Matcher matcher = tPattern.matcher(command);
StringBuffer buff = new StringBuffer();
while (matcher.find()) {
String key = matcher.group("single");
if (key == null) {
key = matcher.group("double");
}
key = key.trim();
String value = data.get(key);
if (value == null && contextMap != null) {
value = contextMap.get(key);
}
if (value == null) {
value = !replaceWithEmpty ? matcher.group() : "";
} else {
if (depth > 0 && tPattern.matcher(value).find()) {
value = parseTokenTwice(value, data, contextMap, replaceWithEmpty, isEscape, depth - 1);
} else if (isEscape) {
value = escapeSpecialWord(value);
}
}
matcher.appendReplacement(buff, Matcher.quoteReplacement(value));
}
matcher.appendTail(buff);
return buff.toString();
}
private static String escapeSpecialWord(String keyword) {
String replaceWord = keyword;
if (!keyword.isEmpty()) {
String[] wordList = {"\\", "\""};
for (String word : wordList) {
if (replaceWord.contains(word)) {
replaceWord = replaceWord.replace(word, "\\" + word);
}
}
}
return replaceWord;
}
}

View File

@ -8,10 +8,16 @@ import cd.casic.ci.common.pipeline.container.VMBuildContainer;
import cd.casic.ci.common.pipeline.enums.BuildStatus;
import cd.casic.ci.common.pipeline.enums.JobRunCondition;
import cd.casic.ci.common.pipeline.option.JobControlOption;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import cd.casic.ci.common.pipeline.pojo.element.ElementAdditionalOptions;
import cd.casic.ci.common.pipeline.pojo.element.trigger.ManualTriggerElement;
import cd.casic.ci.common.pipeline.pojo.element.trigger.RemoteTriggerElement;
import cd.casic.framework.commons.util.reflect.ReflectUtil;
import java.util.*;
import static cd.casic.ci.common.pipeline.pojo.element.ElementAdditionalOptions.RunCondition.*;
/**
* @Authormianbin
* @Packagecd.casic.ci.common.pipeline.utils
@ -99,9 +105,9 @@ public class ModelUtils {
} else {
element.setCanRetry(null);
}
} else if (additionalOptions.getRunCondition() == RunCondition.PRE_TASK_FAILED_ONLY ||
additionalOptions.getRunCondition() == RunCondition.PRE_TASK_FAILED_BUT_CANCEL ||
additionalOptions.getRunCondition() == RunCondition.PRE_TASK_FAILED_EVEN_CANCEL) {
} else if (additionalOptions.getRunCondition() == PRE_TASK_FAILED_ONLY ||
additionalOptions.getRunCondition() == PRE_TASK_FAILED_BUT_CANCEL ||
additionalOptions.getRunCondition() == PRE_TASK_FAILED_EVEN_CANCEL) {
element.setCanRetry(null);
element.setCanSkip(null);
failElements.forEach(e -> {
@ -166,8 +172,8 @@ public class ModelUtils {
public static Set<String> getModelAtoms(Model model) {
var atomCodes = new HashSet<String>();
for (var stage : model.getStages()) {
for (var container : stage.getContainers()) {
for (var element : container.getElements()) {
for (Container container : stage.getContainers()) {
for (Element element : container.getElements()) {
atomCodes.add(element.getAtomCode());
}
}

View File

@ -0,0 +1,139 @@
/*
* 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 cd.casic.ci.common.pipeline.utils;
import cn.hutool.json.JSONUtil;
import java.util.*;
public class ObjectReplaceEnvVarUtil {
/**
* 把对象字段值中的占位符替换成环境变量
*
* @param obj 需要把占位符替换环境变量的对象(对象如果是集合对象注意要选择支持增加删除等操作的集合类型不要选择类似SingletonMap这种)
* @param envMap 环境变量Map
* @return 变量替换后的对象
*/
@SuppressWarnings("all")
public static Object replaceEnvVar(Object obj, Map<String, String> envMap) {
if (obj instanceof Map) {
// 递归替换map对象中的变量
Set<Map.Entry<String, Object>> entrySet = ((Map) obj).entrySet();
for (Map.Entry entry : entrySet) {
Object value = entry.getValue();
if (!isNormalReplaceEnvVar(value)) {
entry.setValue(replaceEnvVar(value, envMap));
} else {
entry.setValue(handleNormalEnvVar(value, envMap));
}
}
} else if (obj instanceof List) {
// 递归替换list对象中的变量
List dataList = (List) obj;
for (int i = 0; i < dataList.size(); i++) {
Object value = dataList.get(i);
if (!isNormalReplaceEnvVar(value)) {
dataList.set(i, replaceEnvVar(value, envMap));
} else {
dataList.set(i, handleNormalEnvVar(value, envMap));
}
}
} else if (obj instanceof Set) {
// 递归替换set对象中的变量
Set objSet = (Set) obj;
Set replaceObjSet = new HashSet(objSet);
Iterator it = replaceObjSet.iterator();
while (it.hasNext()) {
Object value = it.next();
objSet.remove(value);
if (!isNormalReplaceEnvVar(value)) {
objSet.add(replaceEnvVar(value, envMap));
} else {
objSet.add(handleNormalEnvVar(value, envMap));
}
}
} else if (isNormalReplaceEnvVar(obj)) {
// 替换基本类型对象或字符串对象中的变量
obj = handleNormalEnvVar(obj, envMap);
} else {
try {
// 把对象转换成map后进行递归替换变量
Map<String, Object> dataMap = JSONUtil.toBean(JSONUtil.toJsonStr(obj), Map.class);
replaceEnvVar(dataMap, envMap);
obj = JSONUtil.toBean(JSONUtil.toJsonStr(dataMap), obj.getClass());
} catch (Throwable e) {
// 转换不了map的对象则进行直接替换
obj = EnvUtils.parseEnv(JSONUtil.toJsonStr(obj), envMap, false, false);
}
}
return obj;
}
private static Object handleNormalEnvVar(Object obj, Map<String, String> envMap) {
// 只有字符串参数才需要进行变量替换其它基本类型参数无需进行变量替换
if (obj instanceof String) {
String objStr = ((String) obj).trim();
if (JSONUtil.isJsonObj(objStr)) {
try {
Object dataObj = JSONUtil.toBean((String) obj, Map.class);
// string能正常转换成map则说明是json串那么把dataObj进行递归替换变量后再转成json串
dataObj = replaceEnvVar(dataObj, envMap);
obj = JSONUtil.toJsonStr(dataObj);
} catch (Throwable e) {
// 转换不了map的字符串对象则直接替换
obj = EnvUtils.parseEnv(JSONUtil.toJsonStr(obj), envMap, false, false);
}
} else if (JSONUtil.isJsonObj(objStr)) {
try {
JSONUtil.toList(JSONUtil.parseArray((String) obj), Object.class);
Object dataObj = JSONUtil.toList((String) obj, List.class);
// string能正常转成list说明是json串把dataObj进行递归替换变量后再转成json串
dataObj = replaceEnvVar(dataObj, envMap);
obj = JSONUtil.toJsonStr(dataObj);
} catch (Throwable e1) {
// 转换不了list的字符串对象则直接替换
obj = EnvUtils.parseEnv(JSONUtil.toJsonStr(obj), envMap, false, false);
}
} else {
// 转换不了map或者list的字符串对象则直接替换
obj = EnvUtils.parseEnv(JSONUtil.toJsonStr(obj), envMap, false, false);
}
}
return obj;
}
/**
* 判断对象是否是普通替换对象
*
* @param obj 需要把占位符替换环境变量的对象(对象如果是集合对象注意要选择支持增加删除等操作的集合类型不要选择类似SingletonMap这种)
* @return 是否是普通替换对象
*/
private static Boolean isNormalReplaceEnvVar(Object obj) {
return obj == null || ReflectUtil.INSTANCE.isNativeType(obj) || obj instanceof String;
}
}

View File

@ -1,6 +1,7 @@
package cd.casic.ci.common.pipeline.utils;
import cd.casic.ci.common.pipeline.pojo.BuildParameters;
import cd.casic.ci.common.pipeline.pojo.element.Element;
import cd.casic.framework.commons.util.json.JsonUtils;
import java.util.List;

View File

@ -1,5 +1,7 @@
package cd.casic.ci.common.pipeline.utils;
import cd.casic.ci.common.pipeline.pojo.element.ElementProp;
import java.util.List;
/**
@ -17,20 +19,20 @@ public class TriggerElementPropUtils {
if (value == null || value.isBlank()) {
return null;
}
return new ElementProp(name, value.split(","), ElementPropType.VUEX_INPUT);
return new ElementProp(name, value.split(","), ElementProp.ElementPropType.VUEX_INPUT);
}
public static ElementProp staffInput(String name, List<String> value) {
if (value == null || value.isEmpty()) {
return null;
}
return new ElementProp(name, value, ElementPropType.STAFF_INPUT);
return new ElementProp(name, value, ElementProp.ElementPropType.STAFF_INPUT);
}
public static ElementProp selector(String name, List<String> value) {
if (value == null || value.isEmpty()) {
return null;
}
return new ElementProp(name, value, ElementPropType.SELECTOR);
return new ElementProp(name, value, ElementProp.ElementPropType.SELECTOR);
}
}

View File

@ -22,7 +22,11 @@
<dependencies>
<dependency>
<groupId>cd.casic.ci</groupId>
<artifactId>ci-commons</artifactId>
<artifactId>commons-api</artifactId>
</dependency>
<dependency>
<groupId>cd.casic.ci</groupId>
<artifactId>commons-pipeline</artifactId>
</dependency>
<dependency>
<groupId>cd.casic.ci</groupId>

View File

@ -0,0 +1,143 @@
package cd.casic.ci.process.api.process.utils;
/**
* @Authormianbin
* @Packagecd.casic.ci.process.api.process.utils
* @Projectops-pro
* @nameConstants
* @Date2025/03/27 11:09
* @FilenameConstants
*/
public class Constants {
public static final String PIPELINE_VERSION = "BK_CI_PIPELINE_VERSION";
public static final String PIPELINE_START_PARENT_PROJECT_ID = "BK_CI_PARENT_PROJECT_ID";
public static final String PIPELINE_START_PARENT_PIPELINE_ID = "BK_CI_PARENT_PIPELINE_ID";
public static final String PIPELINE_START_PARENT_PIPELINE_NAME = "BK_CI_PARENT_PIPELINE_NAME";
public static final String PIPELINE_START_PARENT_BUILD_ID = "BK_CI_PARENT_BUILD_ID";
public static final String PIPELINE_START_PARENT_BUILD_NUM = "BK_CI_PARENT_BUILD_NUM";
public static final String PIPELINE_START_PARENT_BUILD_TASK_ID = "BK_CI_PARENT_BUILD_TASK_ID";
public static final String PIPELINE_START_PARENT_EXECUTE_COUNT = "BK_CI_PARENT_EXECUTE_COUNT";
public static final String PIPELINE_START_USER_ID = "BK_CI_START_USER_ID";
public static final String PIPELINE_START_USER_NAME = "BK_CI_START_USER_NAME";
public static final String PIPELINE_START_WEBHOOK_USER_ID = "BK_CI_START_WEBHOOK_USER_ID";
public static final String PIPELINE_START_PIPELINE_USER_ID = "BK_CI_START_PIPELINE_USER_ID";
public static final String PIPELINE_START_SERVICE_USER_ID = "BK_CI_START_SERVICE_USER_ID";
public static final String PIPELINE_START_MANUAL_USER_ID = "BK_CI_START_MANUAL_USER_ID";
public static final String PIPELINE_START_TIME_TRIGGER_USER_ID = "BK_CI_START_TIME_TRIGGER_USER_ID";
public static final String PIPELINE_START_REMOTE_USER_ID = "BK_CI_START_REMOTE_USER_ID";
public static final String PIPELINE_START_REMOTE_CLIENT_IP = "BK_CI_START_REMOTE_CLIENT_IP";
public static final String PIPELINE_START_TYPE = "BK_CI_START_TYPE";
public static final String PIPELINE_START_CHANNEL = "BK_CI_START_CHANNEL";
public static final String PIPELINE_BUILD_NUM = "BK_CI_BUILD_NUM";
public static final String PIPELINE_BUILD_LAST_UPDATE = "BK_CI_BUILD_LAST_UPDATE";
public static final String PIPELINE_BUILD_SVN_REVISION = "BK_CI_BUILD_SVN_REVISION";
public static final String PIPELINE_BUILD_NUM_ALIAS = "BK_CI_BUILD_NUM_ALIAS";
public static final String PIPELINE_BUILD_URL = "BK_CI_BUILD_URL";
public static final String PIPELINE_TIMER_DISABLE = "BK_CI_TIMER_DISABLE";
public static final String PIPELINE_START_SUB_RUN_MODE = "BK_CI_SUB_PIPELINE_RUN_MODE";
public static final String GIT_MR_NUMBER = "BK_CI_GIT_MR_NUMBER";
public static final String GITHUB_PR_NUMBER = "BK_CI_GITHUB_PR_NUMBER";
public static final String PIPELINE_NAME = "BK_CI_PIPELINE_NAME";
public static final String PIPELINE_ID = "BK_CI_PIPELINE_ID";
public static final String WORKSPACE = "WORKSPACE";
public static final String PIPELINE_TIME_DURATION = "BK_CI_BUILD_TOTAL_TIME";
public static final String PIPELINE_BUILD_ID = "BK_CI_BUILD_ID";
public static final String PIPELINE_VMSEQ_ID = "BK_CI_BUILD_JOB_ID";
public static final String PIPELINE_ELEMENT_ID = "BK_CI_BUILD_TASK_ID";
public static final String PIPELINE_TURBO_TASK_ID = "BK_CI_TURBO_ID";
public static final String PROJECT_NAME = "BK_CI_PROJECT_NAME";
public static final String REPORT_DYNAMIC_ROOT_URL = "BK_CI_REPORT_DYNAMIC_ROOT_URL";
public static final String PROJECT_NAME_CHINESE = "BK_CI_PROJECT_NAME_CN";
public static final String PIPELINE_START_MOBILE = "BK_CI_IS_MOBILE";
public static final String PIPELINE_START_TASK_ID = "BK_CI_START_TASK_ID";
public static final String PIPELINE_RETRY_COUNT = "BK_CI_RETRY_COUNT";
public static final String PIPELINE_RETRY_BUILD_ID = "BK_CI_RETRY_BUILD_ID";
public static final String PIPELINE_RETRY_START_TASK_ID = "BK_CI_RETRY_TASK_ID";
public static final String PIPELINE_RETRY_ALL_FAILED_CONTAINER = "BK_CI_RETRY_ALL_FAILED_CONTAINER";
public static final String PIPELINE_SKIP_FAILED_TASK = "BK_CI_SKIP_FAILED_TASK";
public static final String BK_CI_BUILD_FAIL_TASKS = "BK_CI_BUILD_FAIL_TASKS";
public static final String BK_CI_BUILD_FAIL_TASKNAMES = "BK_CI_BUILD_FAIL_TASKNAMES";
public static final String PIPELINE_VIEW_MY_PIPELINES = "myPipeline";
public static final String PIPELINE_VIEW_MY_LIST_PIPELINES = "myListPipeline";
public static final String PIPELINE_VIEW_FAVORITE_PIPELINES = "collect";
public static final String PIPELINE_VIEW_ALL_PIPELINES = "allPipeline";
public static final String PIPELINE_VIEW_UNCLASSIFIED = "unclassified";
public static final String PIPELINE_VIEW_RECENT_USE = "recentUse";
public static final String PIPELINE_MATERIAL_URL = "BK_CI_PIEPLEINE_MATERIAL_URL";
public static final String PIPELINE_MATERIAL_BRANCHNAME = "BK_CI_PIPELINE_MATERIAL_BRANCHNAME";
public static final String PIPELINE_MATERIAL_ALIASNAME = "BK_CI_PIPELINE_MATERIAL_ALIASNAME";
public static final String PIPELINE_MATERIAL_NEW_COMMIT_ID = "BK_CI_PIPELINE_MATERIAL_NEW_COMMIT_ID";
public static final String PIPELINE_MATERIAL_NEW_COMMIT_COMMENT = "BK_CI_PIPELINE_MATERIAL_NEW_COMMIT_COMMENT";
public static final String PIPELINE_MATERIAL_NEW_COMMIT_TIMES = "BK_CI_PIPELINE_MATERIAL_NEW_COMMIT_TIMES";
public static final String MAJORVERSION = "BK_CI_MAJOR_VERSION";
public static final String MINORVERSION = "BK_CI_MINOR_VERSION";
public static final String FIXVERSION = "BK_CI_FIX_VERSION";
public static final String BUILD_NO = "BK_CI_BUILD_NO";
public static final String BUILD_STATUS = "BK_CI_BUILD_STATUS";
public static final String BK_DOCKER_TARGE_IMAGE_TAG = "BK_DOCKER_TARGE_IMAGE_TAG";
public static final String BK_DOCKER_TARGE_IMAGE_NAME = "BK_DOCKER_TARGE_IMAGE_NAME";
public static final String JOB_RETRY_TASK_ID = "job.retry_task_id";
public static final String PIPELINE_CREATE_USER = "BK_CI_PIPELINE_CREATE_USER";
public static final String PIPELINE_UPDATE_USER = "BK_CI_PIPELINE_UPDATE_USER";
public static final String PIPELINE_BUILD_REMARK = "BK_CI_BUILD_REMARK";
public static final String PIPELINE_ATOM_NAME = "BK_CI_ATOM_NAME";
public static final String PIPELINE_ATOM_CODE = "BK_CI_ATOM_CODE";
public static final String PIPELINE_ATOM_VERSION = "BK_CI_ATOM_VERSION";
public static final String PIPELINE_TASK_NAME = "BK_CI_TASK_NAME";
public static final String PIPELINE_STEP_ID = "BK_CI_STEP_ID";
public static final String PIPELINE_ATOM_TIMEOUT = "BK_CI_ATOM_TIMEOUT";
public static final String PIPELINE_DIALECT = "BK_CI_PIPELINE_DIALECT";
public static final String BK_CI_MATERIAL_ID = "BK_CI_MATERIAL_ID";
public static final String BK_CI_MATERIAL_NAME = "BK_CI_MATERIAL_NAME";
public static final String BK_CI_MATERIAL_URL = "BK_CI_MATERIAL_URL";
public static final String BK_CI_AUTHORIZER = "BK_CI_AUTHORIZER";
public static final int PIPELINE_SETTING_MAX_QUEUE_SIZE_DEFAULT = 10;
public static final int TASK_FAIL_RETRY_MAX_COUNT = 5;
public static final int TASK_FAIL_RETRY_MIN_COUNT = 1;
public static final int PIPELINE_SETTING_MAX_QUEUE_SIZE_MIN = 0;
public static final int PIPELINE_SETTING_MAX_QUEUE_SIZE_MAX = 200;
public static final int PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_DEFAULT = 50;
public static final int PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX = 200;
public static final int PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_DEFAULT = 1;
public static final int PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_MIN = 1;
public static final int PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_MAX = 1440;
public static final int PIPELINE_TASK_MESSAGE_STRING_LENGTH_MAX = 4000;
public static final int PIPELINE_MESSAGE_STRING_LENGTH_MAX = 30000;
public static final int PIPELINE_CON_RUNNING_CONTAINER_SIZE_MAX = 30;
public static final int PIPELINE_MATRIX_MAX_CON_RUNNING_SIZE_DEFAULT = 5;
public static final int PIPELINE_MATRIX_CON_RUNNING_SIZE_MAX = 20;
public static final int PIPELINE_STAGE_CONTAINERS_COUNT_MAX = 256;
public static final int PIPELINE_CONDITION_EXPRESSION_LENGTH_MAX = 512;
public static final int PIPELINE_VARIABLES_STRING_LENGTH_MAX = 4000;
public static final String PIPELINE_TIME_START = "BK_CI_BUILD_START_TIME";
public static final String PIPELINE_TIME_END = "BK_CI_BUILD_END_TIME";
public static final String PIPELINE_BUILD_MSG = "BK_CI_BUILD_MSG";
public static final int PIPELINE_RES_NUM_MIN = 50;
public static final String KEY_PIPELINE_ID = "pipelineId";
public static final String KEY_PIPELINE_NAME = "pipelineName";
public static final String KEY_PROJECT_ID = "projectId";
public static final String KEY_TEMPLATE_ID = "templateId";
public static final String KEY_STAGE = "stage";
public static final String KEY_JOB = "job";
public static final String KEY_TASK = "task";
public static final String KEY_TASK_ATOM = "taskAtom";
}

View File

@ -0,0 +1,91 @@
package cd.casic.ci.process.api.process.utils;
import cd.casic.ci.log.scm.dal.pojo.CodeP4Repository;
import cd.casic.ci.log.scm.dal.pojo.CodeSvnRepository;
import cd.casic.ci.ticket.dal.enums.CredentialType;
import cd.casic.framework.commons.util.encrypt.DHUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.util.Base64;
import java.util.List;
/**
* @Authormianbin
* @Packagecd.casic.ci.process.api.process.utils
* @Projectops-pro
* @nameCredentialUtils
* @Date2025/03/27 14:35
* @FilenameCredentialUtils
* @descriptionTodo
*/
@Slf4j
public class CredentialUtils {
public static Credential getCredential(Repository repository, List<String> credentials, CredentialType credentialType) {
if (repository instanceof CodeSvnRepository && ((CodeSvnRepository) repository).getSvnType() == CodeSvnRepository.SVN_TYPE_HTTP) {
return switch (credentialType) {
case USERNAME_PASSWORD -> {
if (credentials.size() <= 1) {
log.warn("Fail to get the username of the svn repo {}", repository);
yield new Credential(repository.getUserName(), credentials.get(0), null);
} else {
yield new Credential(credentials.get(0), credentials.get(1), null);
}
}
case TOKEN_USERNAME_PASSWORD -> {
yield new Credential(
credentials.get(0),
credentials.getOrDefault(1, ""),
credentials.getOrDefault(2, ""),
null
);
}
default -> new Credential(repository.getUserName(), credentials.get(0), null);
};
} else if (repository instanceof CodeSvnRepository && credentialType == CredentialType.TOKEN_SSH_PRIVATEKEY) {
return new Credential(
credentials.get(0),
credentials.getOrDefault(1, ""),
repository.getUserName(),
credentials.getOrDefault(2, "")
);
} else if (repository instanceof CodeP4Repository && credentialType == CredentialType.USERNAME_PASSWORD) {
return new Credential(credentials.get(0), "", credentials.get(1));
} else {
String privateKey = credentials.get(0);
String passPhrase = credentials.size() > 1 && !credentials.get(1).isEmpty() ? credentials.get(1) : null;
return new Credential(repository.getUserName(), privateKey, passPhrase);
}
}
public static String decode(String encode, String publicKey, byte[] privateKey) {
if (!StringUtils.hasText(encode)) return "";
byte[] decodedBytes = Base64.getDecoder().decode(encode);
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKey);
return new String(DHUtil.decrypt(decodedBytes, publicKeyBytes, privateKey));
}
@Data
public static class Credential {
private String username;
private String privateKey;
private String passPhrase;
private String svnToken;
private CredentialType credentialType;
public Credential(String username, String privateKey, String passPhrase) {
this.username = username;
this.privateKey = privateKey;
this.passPhrase = passPhrase;
}
public Credential(String svnToken, String username, String privateKey, String passPhrase) {
this.svnToken = svnToken;
this.username = username;
this.privateKey = privateKey;
this.passPhrase = passPhrase;
}
}
}

View File

@ -0,0 +1,183 @@
package cd.casic.ci.process.api.process.utils;
import cd.casic.ci.process.api.process.constant.ProcessMessageCode;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.regex.Pattern;
/**
* @Authormianbin
* @Packagecd.casic.ci.process.api.process.utils
* @Projectops-pro
* @nameDependOnUtils
* @Date2025/03/27 14:39
* @FilenameDependOnUtils
* @descriptionTodo
*/
@Slf4j
public class DependOnUtils {
private static final Pattern REGEX = Pattern.compile("[,;]");
public static void checkRepeatedJobId(Stage stage) {
Set<String> jobIdSet = new HashSet<>();
for (Container c : stage.getContainers()) {
String jobId = c.getJobId();
if (jobId == null || jobId.isEmpty()) {
continue;
}
if (jobIdSet.contains(jobId)) {
String jobName = getContainerName(stage, c, jobId);
throw new ErrorCodeException(
Response.Status.CONFLICT.getStatusCode(),
ProcessMessageCode.ERROR_PIPELINE_JOBID_EXIST,
new String[]{jobName, jobId},
jobName + " 的jobId(" + jobId + ")已存在"
);
}
jobIdSet.add(jobId);
}
removeNonexistentJob(stage, jobIdSet);
}
private static void removeNonexistentJob(Stage stage, Set<String> jobIdSet) {
for (Container c : stage.getContainers()) {
JobControlOption jobControlOption = null;
if (c instanceof VMBuildContainer) {
jobControlOption = ((VMBuildContainer) c).getJobControlOption();
} else if (c instanceof NormalContainer) {
jobControlOption = ((NormalContainer) c).getJobControlOption();
}
if (jobControlOption == null) {
continue;
}
if (jobControlOption.getDependOnType() != DependOnType.ID || jobControlOption.getDependOnId() == null || jobControlOption.getDependOnId().isEmpty()) {
continue;
}
List<String> existJobIds = jobControlOption.getDependOnId().stream()
.filter(jobIdSet::contains)
.toList();
jobControlOption.setDependOnId(existJobIds);
}
}
public static void initDependOn(Stage stage, Map<String, String> params) {
Map<String, Container> allJobId2JobMap = new HashMap<>();
for (Container c : stage.getContainers()) {
String jobId = c.getJobId();
if (jobId == null || jobId.isEmpty()) {
continue;
}
allJobId2JobMap.put(jobId, c);
}
if (allJobId2JobMap.isEmpty()) {
return;
}
Map<String, List<String>> cycleCheckJobMap = new HashMap<>();
for (Container c : stage.getContainers()) {
JobControlOption jobControlOption = null;
if (c instanceof VMBuildContainer) {
jobControlOption = ((VMBuildContainer) c).getJobControlOption();
} else if (c instanceof NormalContainer) {
jobControlOption = ((NormalContainer) c).getJobControlOption();
}
if (jobControlOption == null) {
continue;
}
List<String> dependOnJobIds = getDependOnJobIds(new DependOnConfig(
jobControlOption.getDependOnType(),
jobControlOption.getDependOnId(),
jobControlOption.getDependOnName()
), params);
if (dependOnJobIds.isEmpty()) {
continue;
}
if (c.getJobId() != null) {
cycleCheckJobMap.put(c.getJobId(), dependOnJobIds);
}
Map<String, String> containerId2JobIds = new HashMap<>();
for (String dependOnJobId : dependOnJobIds) {
Container dependOnJob = allJobId2JobMap.get(dependOnJobId);
if (dependOnJob == null) {
continue;
}
containerId2JobIds.put(dependOnJob.getId(), dependOnJobId);
}
if (!containerId2JobIds.isEmpty()) {
jobControlOption.setDependOnContainerId2JobIds(containerId2JobIds);
}
}
Map<String, Integer> visited = new HashMap<>();
for (String jobId : cycleCheckJobMap.keySet()) {
dsf(jobId, cycleCheckJobMap, visited, stage, allJobId2JobMap);
}
}
public static boolean enableDependOn(Container container) {
JobControlOption jobControlOption = null;
if (container instanceof VMBuildContainer) {
jobControlOption = ((VMBuildContainer) container).getJobControlOption();
} else if (container instanceof NormalContainer) {
jobControlOption = ((NormalContainer) container).getJobControlOption();
}
if (jobControlOption == null) {
return false;
}
return switch (jobControlOption.getDependOnType()) {
case ID -> jobControlOption.getDependOnId() != null && !jobControlOption.getDependOnId().isEmpty();
case NAME -> jobControlOption.getDependOnName() != null && !jobControlOption.getDependOnName().isEmpty();
default -> false;
};
}
private static List<String> getDependOnJobIds(DependOnConfig dependOnConfig, Map<String, String> params) {
return switch (dependOnConfig.getDependOnType()) {
case ID -> dependOnConfig.getDependOnId() != null ? dependOnConfig.getDependOnId() : Collections.emptyList();
case NAME -> {
if (dependOnConfig.getDependOnName() == null || dependOnConfig.getDependOnName().isEmpty()) {
yield Collections.emptyList();
} else {
String[] dependONames = dependOnConfig.getDependOnName().split(REGEX);
List<String> result = new ArrayList<>();
for (String name : dependONames) {
result.add(EnvUtils.parseEnv(name, params));
}
yield result;
}
}
default -> Collections.emptyList();
};
}
private static void dsf(String jobId, Map<String, List<String>> dependOnMap, Map<String, Integer> visited, Stage stage, Map<String, Container> allJobId2JobMap) {
if (visited.getOrDefault(jobId, 0) == 1) {
String jobName = getContainerName(stage, allJobId2JobMap.get(jobId), jobId);
String dependJobName = getContainerName(stage, allJobId2JobMap.get(dependOnJobId), dependOnJobId);
throw new ErrorCodeException(
ProcessMessageCode.ERROR_PIPELINE_DEPENDON_CYCLE,
new String[]{jobName, dependJobName}
);
}
if (visited.getOrDefault(jobId, 0) == 2) {
return;
}
visited.put(jobId, 1);
List<String> dependOnJobIds = dependOnMap.getOrDefault(jobId, Collections.emptyList());
for (String dependOnJobId : dependOnJobIds) {
dsf(dependOnJobId, dependOnMap, visited, stage, allJobId2JobMap);
}
visited.put(jobId, 2);
}
private static String getContainerName(Stage stage, Container container, String jobId) {
if (container == null) {
return jobId;
}
String namePrefix = stage.getName() != null ? stage.getName().replaceFirst("stage-", "") : "";
return namePrefix + "-" + container.getId();
}
}