增加构建通知
This commit is contained in:
parent
c81006177f
commit
22ad888c9f
@ -2,7 +2,9 @@ package com.qqchen.deploy.backend.deploy.enums;
|
|||||||
|
|
||||||
// Jenkins构建状态枚举
|
// Jenkins构建状态枚举
|
||||||
public enum JenkinsBuildStatus {
|
public enum JenkinsBuildStatus {
|
||||||
|
|
||||||
SUCCESS, // 构建成功
|
SUCCESS, // 构建成功
|
||||||
|
|
||||||
FAILURE, // 构建失败
|
FAILURE, // 构建失败
|
||||||
IN_PROGRESS,// 构建中
|
IN_PROGRESS,// 构建中
|
||||||
ABORTED, // 构建被取消
|
ABORTED, // 构建被取消
|
||||||
|
|||||||
@ -21,4 +21,28 @@ public class DateUtils {
|
|||||||
public static LocalDateTime toLocalDateTime(long millis) {
|
public static LocalDateTime toLocalDateTime(long millis) {
|
||||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault());
|
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a duration in milliseconds to HH:mm:ss (zero-padded).
|
||||||
|
* Example: 159000 -> 00:02:39
|
||||||
|
*/
|
||||||
|
public static String formatDurationHMS(long millis) {
|
||||||
|
if (millis < 0) millis = 0;
|
||||||
|
long totalSeconds = millis / 1000L;
|
||||||
|
long hours = totalSeconds / 3600L;
|
||||||
|
long minutes = (totalSeconds % 3600L) / 60L;
|
||||||
|
long seconds = totalSeconds % 60L;
|
||||||
|
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a duration in seconds to HH:mm:ss (zero-padded).
|
||||||
|
*/
|
||||||
|
public static String formatDurationHMSBySeconds(long seconds) {
|
||||||
|
if (seconds < 0) seconds = 0;
|
||||||
|
long hours = seconds / 3600L;
|
||||||
|
long minutes = (seconds % 3600L) / 60L;
|
||||||
|
long sec = seconds % 60L;
|
||||||
|
return String.format("%02d:%02d:%02d", hours, minutes, sec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +1,23 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.delegate;
|
package com.qqchen.deploy.backend.workflow.delegate;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
|
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
|
||||||
import com.qqchen.deploy.backend.deploy.entity.JenkinsJob;
|
|
||||||
import com.qqchen.deploy.backend.deploy.enums.JenkinsBuildStatus;
|
import com.qqchen.deploy.backend.deploy.enums.JenkinsBuildStatus;
|
||||||
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
|
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
|
||||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsConsoleOutputResponse;
|
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsConsoleOutputResponse;
|
||||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsBuildResponse;
|
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsBuildResponse;
|
||||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsQueueBuildInfoResponse;
|
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsQueueBuildInfoResponse;
|
||||||
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
|
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
|
||||||
import com.qqchen.deploy.backend.deploy.repository.IJenkinsJobRepository;
|
|
||||||
import com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants;
|
|
||||||
import com.qqchen.deploy.backend.workflow.dto.inputmapping.JenkinsBuildInputMapping;
|
import com.qqchen.deploy.backend.workflow.dto.inputmapping.JenkinsBuildInputMapping;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.outputs.JenkinsBuildOutputs;
|
import com.qqchen.deploy.backend.workflow.dto.outputs.JenkinsBuildOutputs;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowNodeInstance;
|
|
||||||
import com.qqchen.deploy.backend.workflow.enums.LogLevel;
|
import com.qqchen.deploy.backend.workflow.enums.LogLevel;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.LogSource;
|
import com.qqchen.deploy.backend.workflow.enums.LogSource;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeExecutionStatusEnum;
|
|
||||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowNodeInstanceService;
|
|
||||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowNodeLogService;
|
import com.qqchen.deploy.backend.workflow.service.IWorkflowNodeLogService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flowable.engine.delegate.BpmnError;
|
|
||||||
import org.flowable.engine.delegate.DelegateExecution;
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.framework.utils.DateUtils;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -90,29 +84,13 @@ public class JenkinsBuildDelegate extends BaseNodeDelegate<JenkinsBuildInputMapp
|
|||||||
// 5. 获取构建详细信息(包括 duration, changeSets, artifacts)
|
// 5. 获取构建详细信息(包括 duration, changeSets, artifacts)
|
||||||
JenkinsBuildResponse buildDetails = jenkinsServiceIntegration.getBuildDetails(externalSystem, jobName, buildInfo.getBuildNumber());
|
JenkinsBuildResponse buildDetails = jenkinsServiceIntegration.getBuildDetails(externalSystem, jobName, buildInfo.getBuildNumber());
|
||||||
|
|
||||||
// 打印调试信息
|
// 打印调试信息(仅数量,避免大对象噪音)
|
||||||
log.info("Build details - changeSets: {}, artifacts: {}", buildDetails.getChangeSets(), buildDetails.getArtifacts());
|
int changeSetsCount = (buildDetails.getChangeSets() != null) ? buildDetails.getChangeSets().size() : 0;
|
||||||
|
int artifactsCount = (buildDetails.getArtifacts() != null) ? buildDetails.getArtifacts().size() : 0;
|
||||||
|
log.info("Build details - changeSetsCount={}, artifactsCount={}", changeSetsCount, artifactsCount);
|
||||||
|
|
||||||
// 6. 设置输出结果(执行到这里说明构建成功)
|
// 6. 设置输出结果(执行到这里说明构建成功)
|
||||||
// 直接修改预初始化的 output 对象
|
fillOutputsFrom(buildInfo, buildDetails, buildStatus, output);
|
||||||
|
|
||||||
// 设置 Jenkins 特有字段
|
|
||||||
output.setBuildStatus(buildStatus.name());
|
|
||||||
output.setBuildNumber(buildInfo.getBuildNumber());
|
|
||||||
output.setBuildUrl(buildInfo.getBuildUrl());
|
|
||||||
|
|
||||||
// 从构建详情中提取信息
|
|
||||||
output.setBuildDuration(buildDetails.getDuration() != null ? buildDetails.getDuration().intValue() : 0);
|
|
||||||
|
|
||||||
// 直接使用服务层计算与提取的结束时间与提交ID
|
|
||||||
output.setDeployEndTimeMillis(buildDetails.getEndTimeMillis());
|
|
||||||
output.setDeployEndTime(buildDetails.getEndTime());
|
|
||||||
|
|
||||||
// 提取 Git Commit ID(由服务层兜底填充)
|
|
||||||
output.setGitCommitId(buildDetails.getGitCommitId() != null ? buildDetails.getGitCommitId() : "");
|
|
||||||
|
|
||||||
// 直接使用服务层拼接的制品URL串
|
|
||||||
output.setArtifactUrl(buildDetails.getArtifactUrl() != null ? buildDetails.getArtifactUrl() : "");
|
|
||||||
|
|
||||||
// 记录完成日志
|
// 记录完成日志
|
||||||
workflowNodeLogService.info(execution.getProcessInstanceId(), execution.getCurrentActivityId(), LogSource.JENKINS, "Jenkins 构建任务执行完成");
|
workflowNodeLogService.info(execution.getProcessInstanceId(), execution.getCurrentActivityId(), LogSource.JENKINS, "Jenkins 构建任务执行完成");
|
||||||
@ -173,29 +151,18 @@ public class JenkinsBuildDelegate extends BaseNodeDelegate<JenkinsBuildInputMapp
|
|||||||
JenkinsBuildStatus status = jenkinsServiceIntegration.getBuildStatus(externalSystem, jobName, buildNumber);
|
JenkinsBuildStatus status = jenkinsServiceIntegration.getBuildStatus(externalSystem, jobName, buildNumber);
|
||||||
log.info("Jenkins build status: job={}, buildNumber={}, status={}", jobName, buildNumber, status);
|
log.info("Jenkins build status: job={}, buildNumber={}, status={}", jobName, buildNumber, status);
|
||||||
|
|
||||||
switch (status) {
|
if (status == JenkinsBuildStatus.IN_PROGRESS) {
|
||||||
case SUCCESS:
|
|
||||||
// 构建成功,拉取剩余日志后返回状态
|
|
||||||
log.info("Jenkins build succeeded: job={}, buildNumber={}", jobName, buildNumber);
|
|
||||||
fetchRemainingLogs(execution, externalSystem, jobName, buildNumber, logOffset);
|
|
||||||
logInfo(String.format("✅ Jenkins 构建成功: buildNumber=%d", buildNumber));
|
|
||||||
return status;
|
|
||||||
case FAILURE:
|
|
||||||
// 构建失败,拉取剩余日志后抛出异常
|
|
||||||
fetchRemainingLogs(execution, externalSystem, jobName, buildNumber, logOffset);
|
|
||||||
throw new RuntimeException(String.format("Jenkins build failed: job=%s, buildNumber=%d", jobName, buildNumber));
|
|
||||||
case ABORTED:
|
|
||||||
// 构建被取消,拉取剩余日志后抛出异常
|
|
||||||
fetchRemainingLogs(execution, externalSystem, jobName, buildNumber, logOffset);
|
|
||||||
throw new RuntimeException(String.format("Jenkins build was aborted: job=%s, buildNumber=%d", jobName, buildNumber));
|
|
||||||
case IN_PROGRESS:
|
|
||||||
// 继续轮询
|
// 继续轮询
|
||||||
attempts++;
|
attempts++;
|
||||||
break;
|
continue;
|
||||||
case NOT_FOUND:
|
|
||||||
// 构建记录丢失,抛出异常
|
|
||||||
throw new RuntimeException(String.format("Jenkins build not found: job=%s, buildNumber=%d", jobName, buildNumber));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 统一处理终止态(SUCCESS/FAILURE/ABORTED/NOT_FOUND)
|
||||||
|
if (needsFetchLogs(status)) {
|
||||||
|
fetchRemainingLogs(execution, externalSystem, jobName, buildNumber, logOffset);
|
||||||
|
}
|
||||||
|
logTerminalStatus(status, jobName, buildNumber);
|
||||||
|
return status;
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
throw new RuntimeException("Build status polling was interrupted", e);
|
throw new RuntimeException("Build status polling was interrupted", e);
|
||||||
@ -225,4 +192,63 @@ public class JenkinsBuildDelegate extends BaseNodeDelegate<JenkinsBuildInputMapp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终止状态是否需要抓取剩余日志
|
||||||
|
*/
|
||||||
|
private boolean needsFetchLogs(JenkinsBuildStatus status) {
|
||||||
|
return status == JenkinsBuildStatus.SUCCESS
|
||||||
|
|| status == JenkinsBuildStatus.FAILURE
|
||||||
|
|| status == JenkinsBuildStatus.ABORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一终止态日志输出
|
||||||
|
*/
|
||||||
|
private void logTerminalStatus(JenkinsBuildStatus status, String jobName, Integer buildNumber) {
|
||||||
|
switch (status) {
|
||||||
|
case SUCCESS -> {
|
||||||
|
log.info("Jenkins build succeeded: job={}, buildNumber={}", jobName, buildNumber);
|
||||||
|
logInfo(String.format("✅ Jenkins 构建成功: buildNumber=%d", buildNumber));
|
||||||
|
}
|
||||||
|
case FAILURE -> {
|
||||||
|
log.info("Jenkins build failed: job={}, buildNumber={}", jobName, buildNumber);
|
||||||
|
logInfo(String.format("❌ Jenkins 构建失败: buildNumber=%d", buildNumber));
|
||||||
|
}
|
||||||
|
case ABORTED -> {
|
||||||
|
log.info("Jenkins build aborted: job={}, buildNumber={}", jobName, buildNumber);
|
||||||
|
logInfo(String.format("⏹ Jenkins 构建被取消: buildNumber=%d", buildNumber));
|
||||||
|
}
|
||||||
|
case NOT_FOUND -> {
|
||||||
|
log.warn("Jenkins build not found: job={}, buildNumber={}", jobName, buildNumber);
|
||||||
|
logInfo(String.format("⚠ Jenkins 构建记录不存在: buildNumber=%d", buildNumber));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将服务层返回的构建详情填充到输出对象
|
||||||
|
*/
|
||||||
|
private void fillOutputsFrom(JenkinsQueueBuildInfoResponse buildInfo, JenkinsBuildResponse buildDetails, JenkinsBuildStatus buildStatus, JenkinsBuildOutputs output) {
|
||||||
|
// Jenkins 基本信息
|
||||||
|
output.setBuildStatus(buildStatus);
|
||||||
|
output.setBuildNumber(buildInfo.getBuildNumber());
|
||||||
|
output.setBuildUrl(buildInfo.getBuildUrl());
|
||||||
|
|
||||||
|
// 构建时长(毫秒与秒)
|
||||||
|
long durationMs = buildDetails.getDuration() != null ? Math.max(0L, buildDetails.getDuration()) : 0L;
|
||||||
|
int durationSeconds = (int) Math.min(Integer.MAX_VALUE, durationMs / 1000L);
|
||||||
|
output.setBuildDuration(durationSeconds);
|
||||||
|
output.setBuildDurationMillis(durationMs);
|
||||||
|
output.setBuildDurationFormatted(DateUtils.formatDurationHMS(durationMs));
|
||||||
|
|
||||||
|
// 结束时间(服务层已计算)
|
||||||
|
output.setBuildEndTimeMillis(buildDetails.getEndTimeMillis());
|
||||||
|
output.setBuildEndTime(buildDetails.getEndTime());
|
||||||
|
|
||||||
|
// Git 提交ID(服务层已兜底)
|
||||||
|
output.setGitCommitId(buildDetails.getGitCommitId() != null ? buildDetails.getGitCommitId() : "");
|
||||||
|
|
||||||
|
// 制品URL(服务层已拼接)
|
||||||
|
output.setArtifactUrl(buildDetails.getArtifactUrl() != null ? buildDetails.getArtifactUrl() : "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.dto.outputs;
|
package com.qqchen.deploy.backend.workflow.dto.outputs;
|
||||||
|
|
||||||
import lombok.Builder;
|
import com.qqchen.deploy.backend.deploy.enums.JenkinsBuildStatus;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ public class JenkinsBuildOutputs extends BaseNodeOutputs {
|
|||||||
/**
|
/**
|
||||||
* 构建状态
|
* 构建状态
|
||||||
*/
|
*/
|
||||||
private String buildStatus;
|
private JenkinsBuildStatus buildStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建URL
|
* 构建URL
|
||||||
@ -44,8 +44,18 @@ public class JenkinsBuildOutputs extends BaseNodeOutputs {
|
|||||||
*/
|
*/
|
||||||
private Integer buildDuration;
|
private Integer buildDuration;
|
||||||
|
|
||||||
private Long deployEndTimeMillis;
|
/**
|
||||||
|
* 构建时长(毫秒)
|
||||||
|
*/
|
||||||
|
private Long buildDurationMillis;
|
||||||
|
|
||||||
private String deployEndTime;
|
/**
|
||||||
|
* 构建时长(格式:HH:mm:ss),例如 00:02:39
|
||||||
|
*/
|
||||||
|
private String buildDurationFormatted;
|
||||||
|
|
||||||
|
private Long buildEndTimeMillis;
|
||||||
|
|
||||||
|
private String buildEndTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user