diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 64a6693..05a08a7 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -7,9 +7,15 @@ on: types: - completed +permissions: + checks: read + contents: write + jobs: maven-cd: uses: jenkins-infra/github-reusable-workflows/.github/workflows/maven-cd.yml@v1 + with: + java-version: 11 secrets: MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} \ No newline at end of file diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 1f36364..f969f8e 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -1,7 +1,7 @@ - - io.jenkins.tools.incrementals - git-changelist-maven-extension - 1.7 - - + + io.jenkins.tools.incrementals + git-changelist-maven-extension + 1.7 + + \ No newline at end of file diff --git a/README.md b/README.md index cf51167..766570c 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,9 @@ node { text: [ 'hello', 'world', - 'env: ${ENV_NAME}' // 使用环境变量 + "env: ${ENV_NAME}", // 使用环境变量 + "params: ${params.name}", // 使用构建参数变量 + "displayName: ${currentBuild.displayName}", // 使用全局变量 ] ) @@ -45,7 +47,9 @@ node { '- ppppppppp', '- ppppppppp', '> content', - 'env: ${ENV_NAME}' // 使用环境变量 + "env: ${ENV_NAME}", // 使用环境变量 + "params: ${params.name}", // 使用构建参数变量 + "displayName: ${currentBuild.displayName}", // 使用全局变量 ] ) diff --git a/docker-compose.yaml b/docker-compose.yaml index 37650e7..c1f432c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,9 +1,7 @@ version: "3.6" services: jenkins: - image: jenkins/jenkins:2.319.3-jdk8 + image: jenkins/jenkins:2.387.3-jdk17 ports: - "8080:8080" restart: on-failure - volumes: - - .data:/var/jenkins_home diff --git a/pom.xml b/pom.xml index bdf2009..d224ab8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,8 @@ org.jenkins-ci.plugins plugin - 4.51 + + 4.55 io.jenkins.plugins @@ -16,17 +17,17 @@ https://github.com/jenkinsci/${project.artifactId}-plugin - 1.0.1 + 1.0.2 999999-SNAPSHOT - 2.324 + 2.401.3 jenkinsci/${project.artifactId}-plugin nekoimi - JinBo Yang + nekoimi nekoimime@gmail.com @@ -48,10 +49,10 @@ - + io.jenkins.tools.bom - bom-2.319.x - 1654.vcb_69d035fa_20 + bom-2.401.x + 2745.vc7b_fe4c876fa_ pom import @@ -63,10 +64,34 @@ org.jenkins-ci.plugins jackson2-api + + org.jenkins-ci.plugins + token-macro + 2.15 + + + org.kohsuke.stapler + stapler + + + org.jenkins-ci.plugins.workflow + workflow-step-api + compile + + + org.jenkins-ci.plugins.workflow + workflow-cps + compile + + + org.jenkins-ci.main + jenkins-core + provided + org.projectlombok lombok - 1.18.28 + 1.18.30 provided diff --git a/src/main/java/io/jenkins/plugins/wxwork/WXWorkRobotProperty.java b/src/main/java/io/jenkins/plugins/wxwork/WXWorkRobotProperty.java index 6e8f849..da2c655 100644 --- a/src/main/java/io/jenkins/plugins/wxwork/WXWorkRobotProperty.java +++ b/src/main/java/io/jenkins/plugins/wxwork/WXWorkRobotProperty.java @@ -19,6 +19,8 @@ import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.verb.POST; +import java.util.Objects; + /** *

WXWorkRobotProperty

* @@ -125,13 +127,16 @@ public FormValidation doTest(@QueryParameter("id") String id, @QueryParameter("n RobotProperty property = new WXWorkRobotProperty(id, name, webhook); RobotRequest message = TextMessage.builder().content("企业微信机器人测试成功!").atAll(true).build(); RobotResponse robotResponse = WXWorkRobotMessageSender.instance().send(property, message); - if (robotResponse != null && robotResponse.isOk()) { - // ok - String rootUrl = Jenkins.get().getRootUrl(); - return FormValidation.respond(FormValidation.Kind.OK, "" + "测试成功"); - } else { - return FormValidation.error(robotResponse.errorMessage()); + if (Objects.nonNull(robotResponse)) { + if (robotResponse.isOk()) { + // ok + String rootUrl = Jenkins.get().getRootUrl(); + return FormValidation.respond(FormValidation.Kind.OK, "" + "测试成功"); + } else { + return FormValidation.error(robotResponse.errorMessage()); + } } + return FormValidation.error("企业微信机器人测试出现错误!"); } } } diff --git a/src/main/java/io/jenkins/plugins/wxwork/WXWorkPipelineBuilder.java b/src/main/java/io/jenkins/plugins/wxwork/WXWorkStep.java similarity index 52% rename from src/main/java/io/jenkins/plugins/wxwork/WXWorkPipelineBuilder.java rename to src/main/java/io/jenkins/plugins/wxwork/WXWorkStep.java index a867590..f87f6e7 100644 --- a/src/main/java/io/jenkins/plugins/wxwork/WXWorkPipelineBuilder.java +++ b/src/main/java/io/jenkins/plugins/wxwork/WXWorkStep.java @@ -4,45 +4,36 @@ import hudson.EnvVars; import hudson.Extension; import hudson.FilePath; -import hudson.Launcher; -import hudson.model.AbstractProject; import hudson.model.Run; import hudson.model.TaskListener; -import hudson.tasks.BuildStepDescriptor; -import hudson.tasks.Builder; import io.jenkins.plugins.wxwork.bo.RobotPipelineVars; import io.jenkins.plugins.wxwork.bo.RunUser; +import io.jenkins.plugins.wxwork.contract.RobotMessageSender; import io.jenkins.plugins.wxwork.contract.RobotProperty; import io.jenkins.plugins.wxwork.contract.RobotRequest; import io.jenkins.plugins.wxwork.contract.RobotResponse; -import io.jenkins.plugins.wxwork.contract.RobotMessageSender; import io.jenkins.plugins.wxwork.enums.MessageType; import io.jenkins.plugins.wxwork.factory.RobotMessageFactory; import io.jenkins.plugins.wxwork.robot.WXWorkRobotMessageSender; import io.jenkins.plugins.wxwork.utils.JenkinsUtils; -import jenkins.tasks.SimpleBuildStep; import lombok.Getter; import lombok.Setter; -import org.jenkinsci.Symbol; +import org.jenkinsci.plugins.workflow.steps.Step; +import org.jenkinsci.plugins.workflow.steps.StepContext; +import org.jenkinsci.plugins.workflow.steps.StepDescriptor; +import org.jenkinsci.plugins.workflow.steps.StepExecution; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /** - *

Pipeline支持

- * - * @author nekoimi 2022/07/12 + * @author nekoimi 2024/8/6 23:56 */ @Getter @Setter @SuppressWarnings("unused") -public class WXWorkPipelineBuilder extends Builder implements SimpleBuildStep { - +public class WXWorkStep extends Step { /** * 机器人ID */ @@ -84,7 +75,7 @@ public class WXWorkPipelineBuilder extends Builder implements SimpleBuildStep { private final RobotMessageSender robotSender = WXWorkRobotMessageSender.instance(); @DataBoundConstructor - public WXWorkPipelineBuilder(String robot) { + public WXWorkStep(String robot) { this.robot = robot; } @@ -120,43 +111,100 @@ public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } - @Override - public void perform( - @NonNull Run run, - @NonNull FilePath workspace, - @NonNull EnvVars envVars, - @NonNull Launcher launcher, - @NonNull TaskListener listener) throws InterruptedException, IOException { + /** + *

发送机器人消息

+ * + * @param run + * @param workspace + * @param envVars + * @param listener + */ + public void send(Run run, FilePath workspace, EnvVars envVars, TaskListener listener) { RobotProperty property = WXWorkGlobalConfig.instance().getRobotPropertyById(robot); - if (property == null) { + if (Objects.isNull(property)) { listener.error("机器人[%s]配置找不到!", robot); return; } RunUser runUser = JenkinsUtils.getRunUser(run, listener); RobotPipelineVars pipelineVars = RobotPipelineVars.builder() - .robot(envVars.expand(this.robot)).type(this.type).atMe(this.atMe).atAll(this.atAll) - .at(this.at).text(this.text).imageUrl(envVars.expand(this.imageUrl)).runUser(runUser) - .envVars(envVars).workspace(workspace).listener(listener).build(); + .run(run).envVars(envVars).workspace(workspace).listener(listener) + .runUser(runUser) + .robot(JenkinsUtils.expandAll(run, workspace, listener, this.robot)) + .type(this.type).atMe(this.atMe).atAll(this.atAll).at(this.at) + .text(this.text) + .imageUrl(JenkinsUtils.expandAll(run, workspace, listener, this.imageUrl)) + .build(); RobotRequest robotRequest = RobotMessageFactory.makeRobotRequest(pipelineVars); if (robotRequest == null) { listener.error("不支持的消息!"); return; } RobotResponse robotResponse = robotSender.send(property, robotRequest); - if (robotResponse != null && robotResponse.isOk()) { - listener.getLogger().println("WXWORK: 微信机器人[" + property.name() + "]推送消息成功!"); - } else { - listener.error(robotResponse.errorMessage()); + if (Objects.nonNull(robotResponse)) { + if (robotResponse.isOk()) { + listener.getLogger().println("WXWORK: 微信机器人[" + property.name() + "]推送消息成功!"); + } else { + listener.error("WXWORK: 微信机器人[" + property.name() + "]推送消息失败:" + robotResponse.errorMessage()); + } + } + } + + @Override + public StepExecution start(StepContext context) throws Exception { + return new WXWorkStepExecution(this, context); + } + + @Override + public StepDescriptor getDescriptor() { + return new WXWorkStepDescriptor(); + } + + /** + * 执行 + */ + static class WXWorkStepExecution extends StepExecution { + private static final long serialVersionUID = 1L; + private final transient WXWorkStep step; + + public WXWorkStepExecution(WXWorkStep step, @NonNull StepContext context) { + super(context); + this.step = step; + } + + @Override + public boolean start() throws Exception { + StepContext context = this.getContext(); + + Run run = context.get(Run.class); + FilePath workspace = context.get(FilePath.class); + EnvVars envVars = context.get(EnvVars.class); + TaskListener listener = context.get(TaskListener.class); + + try { + this.step.send(run, workspace, envVars, listener); + context.onSuccess(true); + } catch (Exception e) { + context.onFailure(e); + } + + return true; } } - @Symbol({"wxwork", "wxWork"}) @Extension - public final static class WXWorkDescriptorImpl extends BuildStepDescriptor { + public static class WXWorkStepDescriptor extends StepDescriptor { + + @Override + public Set> getRequiredContext() { + return new HashSet>() {{ + add(Run.class); + add(TaskListener.class); + }}; + } @Override - public boolean isApplicable(Class jobType) { - return false; + public String getFunctionName() { + return "wxwork"; } @NonNull diff --git a/src/main/java/io/jenkins/plugins/wxwork/bo/RobotPipelineVars.java b/src/main/java/io/jenkins/plugins/wxwork/bo/RobotPipelineVars.java index 95b806a..fe1bdd5 100644 --- a/src/main/java/io/jenkins/plugins/wxwork/bo/RobotPipelineVars.java +++ b/src/main/java/io/jenkins/plugins/wxwork/bo/RobotPipelineVars.java @@ -2,6 +2,7 @@ import hudson.EnvVars; import hudson.FilePath; +import hudson.model.Run; import hudson.model.TaskListener; import io.jenkins.plugins.wxwork.enums.MessageType; import lombok.*; @@ -22,6 +23,32 @@ @NoArgsConstructor @AllArgsConstructor public class RobotPipelineVars { + + /** + * Run + */ + Run run; + + /** + * 环境变量 + */ + private EnvVars envVars; + + /** + * workspace + */ + private FilePath workspace; + + /** + * taskListener + */ + private TaskListener listener; + + /** + * 当前执行用户信息 + */ + private RunUser runUser; + /** * 机器人ID */ @@ -56,24 +83,4 @@ public class RobotPipelineVars { * Image消息图片地址 */ private String imageUrl; - - /** - * 当前执行用户信息 - */ - private RunUser runUser; - - /** - * 环境变量 - */ - private EnvVars envVars; - - /** - * workspace - */ - private FilePath workspace; - - /** - * taskListener - */ - private TaskListener listener; } diff --git a/src/main/java/io/jenkins/plugins/wxwork/contract/RobotMessageTransfer.java b/src/main/java/io/jenkins/plugins/wxwork/contract/RobotMessageTransfer.java index 29a4014..3d5d3d0 100644 --- a/src/main/java/io/jenkins/plugins/wxwork/contract/RobotMessageTransfer.java +++ b/src/main/java/io/jenkins/plugins/wxwork/contract/RobotMessageTransfer.java @@ -2,6 +2,12 @@ import io.jenkins.plugins.wxwork.bo.RobotPipelineVars; import io.jenkins.plugins.wxwork.enums.MessageType; +import io.jenkins.plugins.wxwork.utils.JenkinsUtils; +import io.jenkins.plugins.wxwork.utils.StrUtils; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; /** *

RobotMessageTransfer

@@ -23,4 +29,20 @@ public interface RobotMessageTransfer { * @param pipelineVars Pipeline参数 */ RobotRequest transferRobotRequest(RobotPipelineVars pipelineVars); + + /** + *

处理文本列表消息

+ * + * @param pipelineVars Pipeline参数 + * @return + */ + default String transferTextList(RobotPipelineVars pipelineVars) { + List textCollect = pipelineVars.getText() + .stream() + .filter(Objects::nonNull) + .filter(StrUtils::isNotBlank) + .map(s -> JenkinsUtils.expandAll(pipelineVars, s)) + .collect(Collectors.toList()); + return String.join("\n", textCollect); + } } diff --git a/src/main/java/io/jenkins/plugins/wxwork/httpclient/DefaultHttpClient.java b/src/main/java/io/jenkins/plugins/wxwork/httpclient/DefaultHttpClient.java index 4631eee..30659e8 100644 --- a/src/main/java/io/jenkins/plugins/wxwork/httpclient/DefaultHttpClient.java +++ b/src/main/java/io/jenkins/plugins/wxwork/httpclient/DefaultHttpClient.java @@ -8,8 +8,11 @@ import javax.net.ssl.*; import java.io.*; import java.net.HttpURLConnection; +import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.zip.GZIPInputStream; @@ -36,7 +39,7 @@ public HttpResponse send(HttpRequest request) { defaultHttpResponse.setStatusCode(connection.getResponseCode()); defaultHttpResponse.setBody(responseBytes); } - } catch (IOException e) { + } catch (Exception e) { defaultHttpResponse.setStatusCode(500); defaultHttpResponse.setErrorMessage(e.getMessage()); } finally { @@ -73,7 +76,7 @@ private byte[] getStreamAsString(InputStream stream) throws IOException { } } - private HttpURLConnection getConnection(URL url, String method, String contentType) throws IOException { + private HttpURLConnection getConnection(URL url, String method, String contentType) throws Exception { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); if (connection instanceof HttpsURLConnection) { HttpsURLConnection httpsConnection = HttpsURLConnection.class.cast(connection); @@ -83,7 +86,7 @@ private HttpURLConnection getConnection(URL url, String method, String contentTy httpsConnection.setSSLSocketFactory(ctx.getSocketFactory()); httpsConnection.setHostnameVerifier(webhookApiHostnameVerifier); } catch (Exception e) { - httpsConnection.setHostnameVerifier(webhookApiHostnameVerifier); + throw new RuntimeException(e); } } diff --git a/src/main/java/io/jenkins/plugins/wxwork/transfer/MarkdownMessageTransfer.java b/src/main/java/io/jenkins/plugins/wxwork/transfer/MarkdownMessageTransfer.java index e0e06fc..90051a6 100644 --- a/src/main/java/io/jenkins/plugins/wxwork/transfer/MarkdownMessageTransfer.java +++ b/src/main/java/io/jenkins/plugins/wxwork/transfer/MarkdownMessageTransfer.java @@ -5,10 +5,6 @@ import io.jenkins.plugins.wxwork.contract.RobotMessageTransfer; import io.jenkins.plugins.wxwork.contract.RobotRequest; import io.jenkins.plugins.wxwork.enums.MessageType; -import io.jenkins.plugins.wxwork.utils.StrUtils; - -import java.util.List; -import java.util.stream.Collectors; /** *

MarkdownMessageTransfer

@@ -25,9 +21,7 @@ public boolean supports(MessageType messageType) { @Override public RobotRequest transferRobotRequest(RobotPipelineVars pipelineVars) { MarkdownMessage.Builder builder = MarkdownMessage.builder(); - List textList = pipelineVars.getText(); - List textCollect = textList.stream().filter(StrUtils::isNotBlank).collect(Collectors.toList()); - builder.content(pipelineVars.getEnvVars().expand(String.join("\n", textCollect))); + builder.content(transferTextList(pipelineVars)); return builder.build(); } } diff --git a/src/main/java/io/jenkins/plugins/wxwork/transfer/TextMessageTransfer.java b/src/main/java/io/jenkins/plugins/wxwork/transfer/TextMessageTransfer.java index d524d39..e58adce 100644 --- a/src/main/java/io/jenkins/plugins/wxwork/transfer/TextMessageTransfer.java +++ b/src/main/java/io/jenkins/plugins/wxwork/transfer/TextMessageTransfer.java @@ -5,11 +5,6 @@ import io.jenkins.plugins.wxwork.contract.RobotMessageTransfer; import io.jenkins.plugins.wxwork.contract.RobotRequest; import io.jenkins.plugins.wxwork.enums.MessageType; -import io.jenkins.plugins.wxwork.utils.StrUtils; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; /** *

TextMessageTransfer

@@ -33,10 +28,7 @@ public RobotRequest transferRobotRequest(RobotPipelineVars pipelineVars) { if (Boolean.TRUE.equals(pipelineVars.getAtAll())) { builder.atAll(true); } - List textList = pipelineVars.getText(); - List textCollect = - textList.stream().filter(Objects::nonNull).filter(StrUtils::isNotBlank).collect(Collectors.toList()); - builder.content(pipelineVars.getEnvVars().expand(String.join("\n", textCollect))); + builder.content(transferTextList(pipelineVars)); return builder.build(); } } diff --git a/src/main/java/io/jenkins/plugins/wxwork/utils/JenkinsUtils.java b/src/main/java/io/jenkins/plugins/wxwork/utils/JenkinsUtils.java index 75563d0..6ac88f2 100644 --- a/src/main/java/io/jenkins/plugins/wxwork/utils/JenkinsUtils.java +++ b/src/main/java/io/jenkins/plugins/wxwork/utils/JenkinsUtils.java @@ -1,13 +1,19 @@ package io.jenkins.plugins.wxwork.utils; import groovy.util.logging.Slf4j; +import hudson.EnvVars; +import hudson.FilePath; import hudson.model.Cause; import hudson.model.Run; import hudson.model.TaskListener; import hudson.model.User; import io.jenkins.plugins.wxwork.WXWorkUserExtensionProperty; +import io.jenkins.plugins.wxwork.bo.RobotPipelineVars; import io.jenkins.plugins.wxwork.bo.RunUser; +import org.jenkinsci.plugins.tokenmacro.MacroEvaluationException; +import org.jenkinsci.plugins.tokenmacro.TokenMacro; +import java.io.IOException; import java.util.stream.Collectors; /** @@ -21,10 +27,51 @@ public class JenkinsUtils { private JenkinsUtils() { } + /** + * 处理内容包含的环境变量 + * + * @param pipelineVars + * @param text + * @return + */ + public static String expandAll(RobotPipelineVars pipelineVars, String text) { + return expandAll(pipelineVars.getRun(), pipelineVars.getWorkspace(), pipelineVars.getListener(), text); + } + + /** + * 处理内容包含的环境变量 + * + * @param run + * @param workspace + * @param listener + * @param text + * @return + */ + public static String expandAll(Run run, FilePath workspace, TaskListener listener, String text) { + try { + return TokenMacro.expandAll(run, workspace, listener, text); + } catch (MacroEvaluationException | IOException e) { + listener.getLogger().println("WXWORK: 请检查文本【" + text + "】是否使用双引号包裹,非变量插值请忽略该提示"); + + try { + EnvVars envVars = run.getEnvironment(listener); + return envVars.expand(text); + } catch (IOException ex) { + ex.printStackTrace(listener.getLogger()); + } catch (InterruptedException ei) { + Thread.currentThread().interrupt(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + return ""; + } + /** *

获取执行用户

* - * @param run {@link Run} + * @param run {@link Run} * @param listener {@link TaskListener} */ public static RunUser getRunUser(Run run, TaskListener listener) { @@ -46,7 +93,7 @@ public static RunUser getRunUser(Run run, TaskListener listener) { listener.error("用户【%s】暂未设置手机号码,请前往 %s 添加。", executorName, user.getAbsoluteUrl() + "/configure"); } else { executorMobile = executorProperty.getMobile(); - if (StrUtils.isBlank(executorMobile)){ + if (StrUtils.isBlank(executorMobile)) { listener.error("用户【%s】暂未设置手机号码,请前往 %s 添加。", executorName, user.getAbsoluteUrl() + "/configure"); } } diff --git a/src/main/resources/io/jenkins/plugins/wxwork/WXWorkGlobalConfig/config.jelly b/src/main/resources/io/jenkins/plugins/wxwork/WXWorkGlobalConfig/config.jelly index bab4ee3..60d9db7 100644 --- a/src/main/resources/io/jenkins/plugins/wxwork/WXWorkGlobalConfig/config.jelly +++ b/src/main/resources/io/jenkins/plugins/wxwork/WXWorkGlobalConfig/config.jelly @@ -1,6 +1,6 @@ + xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" > diff --git a/src/main/resources/io/jenkins/plugins/wxwork/WXWorkRobotProperty/config.jelly b/src/main/resources/io/jenkins/plugins/wxwork/WXWorkRobotProperty/config.jelly index 54237bf..add09e9 100644 --- a/src/main/resources/io/jenkins/plugins/wxwork/WXWorkRobotProperty/config.jelly +++ b/src/main/resources/io/jenkins/plugins/wxwork/WXWorkRobotProperty/config.jelly @@ -1,17 +1,17 @@ + xmlns:t="/lib/hudson" xmlns:f="/lib/form"> - +
@@ -62,7 +62,7 @@ data-request-url="${descriptor.descriptorFullUrl}" data-request-method="test" data-validate-button-with="${with}" - onclick="wxworkSendRobotTest(this)"/> + onclick="sendWxWorkPluginRobotTest(this)"/>
${attrs.progress} @@ -70,5 +70,5 @@
- +