package io.jenkins.plugins; import hudson.EnvVars; import hudson.Extension; import hudson.model.Cause.UserIdCause; import hudson.model.Job; import hudson.model.Result; import hudson.model.Run; import hudson.model.TaskListener; import hudson.model.User; import hudson.model.listeners.RunListener; import io.jenkins.plugins.enums.BuildStatusEnum; import io.jenkins.plugins.enums.MsgTypeEnum; import io.jenkins.plugins.enums.NoticeOccasionEnum; import io.jenkins.plugins.model.BuildJobModel; import io.jenkins.plugins.model.ButtonModel; import io.jenkins.plugins.model.MessageModel; import io.jenkins.plugins.service.impl.DingTalkServiceImpl; import io.jenkins.plugins.tools.Logger; import io.jenkins.plugins.tools.Logger.LineType; import io.jenkins.plugins.tools.Utils; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import jenkins.model.Jenkins; import lombok.extern.log4j.Log4j; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; /** * 所有项目触发 * * @author liuwei * @date 2019/12/28 15:31 */ @SuppressWarnings("unused") @Log4j @Extension public class DingTalkRunListener extends RunListener<Run<?, ?>> { private final DingTalkServiceImpl service = new DingTalkServiceImpl(); private final DingTalkGlobalConfig globalConfig = DingTalkGlobalConfig.get(); private final String rootPath = Jenkins.get().getRootUrl(); public void send(Run<?, ?> run, TaskListener listener, BuildStatusEnum statusType) { boolean isVerbose = globalConfig.isVerbose(); Job<?, ?> job = run.getParent(); UserIdCause userIdCause = run.getCause(UserIdCause.class); // 执行人信息 User user = null; String executorName; String executorMobile = null; if (userIdCause != null && userIdCause.getUserId() != null) { user = User.getById(userIdCause.getUserId(), false); } if (user == null) { if (isVerbose) { Logger.debug(listener, "未获取到构建人信息,将尝试从构建信息中模糊匹配。"); } executorName = run.getCauses() .stream() .map( item -> item.getShortDescription().replace( "Started by remote host", "Host" ) ) .collect(Collectors.joining()); } else { executorName = user.getDisplayName(); executorMobile = user.getProperty(DingTalkUserProperty.class).getMobile(); if (isVerbose && executorMobile == null) { Logger.debug( listener, "用户【%s】暂未设置手机号码,请前往 %s 添加。", executorName, user.getAbsoluteUrl() + "/configure" ); } } // 项目信息 String projectName = job.getFullDisplayName(); String projectUrl = job.getAbsoluteUrl(); // 构建信息 String jobName = run.getDisplayName(); String jobUrl = rootPath + run.getUrl(); String duration = run.getDurationString(); List<ButtonModel> btns = Utils.createDefaultBtns(jobUrl); List<String> result = new ArrayList<>(); DingTalkJobProperty property = job.getProperty(DingTalkJobProperty.class); List<DingTalkNotifierConfig> notifierConfigs = property.getCheckedNotifierConfigs(); EnvVars envVars = null; try { envVars = run.getEnvironment(listener); if (isVerbose) { Logger.debug(listener, "当前可用的环境变量:%s", envVars); } } catch (InterruptedException | IOException e) { log.error(e); Logger.debug(listener, "获取环境变量时发生异常,钉钉自定义内容将跳过环境变量解析。"); Logger.debug(listener, ExceptionUtils.getStackTrace(e)); } for (DingTalkNotifierConfig item : notifierConfigs) { String robotId = item.getRobotId(); String content = item.getContent(); Set<String> atMobiles = item.getAtMobiles(); if(StringUtils.isNotEmpty(executorMobile)){ atMobiles.add(executorMobile); } String text = BuildJobModel.builder() .projectName(projectName) .projectUrl(projectUrl) .jobName(jobName) .jobUrl(jobUrl) .statusType(statusType) .duration(duration) .executorName(executorName) .executorMobile(executorMobile) .content( envVars == null ? content : envVars.expand(content).replaceAll("\\\\n","\n") ) .build() .toMarkdown(); MessageModel message = MessageModel.builder() .type(MsgTypeEnum.ACTION_CARD) .atMobiles(atMobiles) .text(text) .btns(btns) .build(); if (isVerbose) { Logger.debug(listener, "当前钉钉机器人信息:%s", item); Logger.debug(listener, "发送的消息详情:%s", message); } String msg = service.send(robotId, message); if (msg != null) { result.add(msg); } } if (!result.isEmpty()) { result.forEach(msg -> Logger.error(listener, msg)); } } @Override public void onStarted(Run<?, ?> build, TaskListener listener) { boolean isVerbose = globalConfig.isVerbose(); if (isVerbose) { Logger.line(listener, LineType.START); Logger.debug(listener, "钉钉全局配置信息:%s", globalConfig); } if ( globalConfig.getNoticeOccasions().contains( NoticeOccasionEnum.START.name() ) ) { this.send(build, listener, BuildStatusEnum.START); } else if (isVerbose) { Logger.debug(listener, "项目开始构建:未匹配的通知时机,无需触发钉钉"); } if (isVerbose) { Logger.line(listener, LineType.END); } } @Override public void onCompleted(Run<?, ?> build, @Nonnull TaskListener listener) { BuildStatusEnum statusType = null; boolean skipped = true; boolean isVerbose = globalConfig.isVerbose(); Set<String> noticeOccasions = globalConfig.getNoticeOccasions(); Result result = build.getResult(); if (isVerbose) { Logger.line(listener, LineType.START); } if (Result.SUCCESS.equals(result)) { if (noticeOccasions.contains(NoticeOccasionEnum.SUCCESS.name())) { skipped = false; statusType = BuildStatusEnum.SUCCESS; } } else if (Result.FAILURE.equals(result)) { if (noticeOccasions.contains(NoticeOccasionEnum.FAILURE.name())) { skipped = false; statusType = BuildStatusEnum.FAILURE; } } else if (Result.ABORTED.equals(result)) { if (noticeOccasions.contains(NoticeOccasionEnum.ABORTED.name())) { skipped = false; statusType = BuildStatusEnum.ABORTED; } } else if (Result.UNSTABLE.equals(result)) { if (noticeOccasions.contains(NoticeOccasionEnum.UNSTABLE.name())) { skipped = false; statusType = BuildStatusEnum.UNSTABLE; } } else if (Result.NOT_BUILT.equals(result)) { if (noticeOccasions.contains(NoticeOccasionEnum.NOT_BUILT.name())) { skipped = false; statusType = BuildStatusEnum.NOT_BUILT; } } else { statusType = BuildStatusEnum.UNKNOWN; if (isVerbose) { Logger.debug(listener, "不匹配的构建结果类型:%s", result == null ? "null" : result); } } if (skipped) { if (isVerbose) { Logger.debug(listener, "构建已结束:无匹配的通知时机,无需触发钉钉"); } return; } this.send(build, listener, statusType); if (isVerbose) { Logger.line(listener, LineType.END); } } }