From eb5589d0645881ce5f22bcb47715bc244246c020 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Thu, 4 Apr 2024 21:37:33 +0100 Subject: [PATCH] Add includeStage to allow starting check immediately for JUnit (#254) Co-authored-by: Ullrich Hafner --- README.md | 49 +++++++++++- docs/images/multiple-with-checks.png | Bin 0 -> 54480 bytes pom.xml | 8 +- .../checks/status/FlowExecutionAnalyzer.java | 69 ++--------------- .../plugins/checks/steps/WithChecksStep.java | 42 +++++++++-- .../plugins/checks/utils/FlowNodeUtils.java | 71 ++++++++++++++++++ .../BuildStatusChecksPublisherITest.java | 5 +- .../checks/steps/WithChecksStepITest.java | 15 ++++ src/test/resources/design.puml | 3 + 9 files changed, 191 insertions(+), 71 deletions(-) create mode 100644 docs/images/multiple-with-checks.png create mode 100644 src/main/java/io/jenkins/plugins/checks/utils/FlowNodeUtils.java diff --git a/README.md b/README.md index b2d0ec88..6d0879ef 100644 --- a/README.md +++ b/README.md @@ -53,12 +53,57 @@ see [the handler](https://github.com/jenkinsci/github-checks-plugin/blob/ea060be - withChecks: you can inject the check's name into the closure for other steps to use: -``` -withChecks(name: 'injected name') { +```groovy +withChecks('injected name') { // some other steps that will extract the name } ``` +`withChecks` will publish an in progress check immediately and then other consuming plugins will publish the final check. + +You can also include the checks stage name with `includeStage`: + +```groovy +withChecks(name: 'Tests', includeStage: true) { + sh 'mvn -Dmaven.test.failure.ignore=true clean verify' + junit '**/target/surefire-reports/TEST-*.xml' +} +``` + +Combining `includeStage` with the JUnit plugin works well to publish checks for each test suite: + +![With Checks multiple stages](docs/images/github-status.png) + +
+ +Example full pipeline with parallel stages + +```groovy +def axes = [ + platforms: ['linux', 'windows'], + jdks: [17, 21], +] +def builds = [:] +axes.values().combinations { + def (platform, jdk) = it + builds["${platform}-jdk${jdk}"] = { + node(platform) { + stage("${platform.capitalize()} - JDK ${jdk} - Test") { + checkout scm + withChecks(name: 'Tests', includeStage: true) { + sh 'mvn -Dmaven.test.failure.ignore=true clean verify' + junit '**/target/surefire-reports/TEST-*.xml' + } + } + } + } +} + +parallel builds +``` + +
+ ## Guides - [Consumers Guide](docs/consumers-guide.md) diff --git a/docs/images/multiple-with-checks.png b/docs/images/multiple-with-checks.png new file mode 100644 index 0000000000000000000000000000000000000000..86a5e48d665051786f9e8c8aa62ead33779565fb GIT binary patch literal 54480 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 z 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 an API for Jenkins to publish checks to SCM platforms. - 2.1.0 + 2.2.0 -SNAPSHOT jenkinsci/${project.artifactId}-plugin @@ -166,6 +166,12 @@ 1 Adding actions list with an initial empty value should not break the compatibility. + + java.field.serialVersionUIDUnchanged + field io.jenkins.plugins.checks.steps.WithChecksStep.serialVersionUID + 1 + Boolean value added, will have no impact on backwards compat. + true java.annotation.* diff --git a/src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java b/src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java index 4083c49a..2f7a09fe 100644 --- a/src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java +++ b/src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java @@ -1,9 +1,16 @@ package io.jenkins.plugins.checks.status; +import static io.jenkins.plugins.checks.utils.FlowNodeUtils.getEnclosingBlockNames; +import static io.jenkins.plugins.checks.utils.FlowNodeUtils.getEnclosingStagesAndParallels; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import hudson.model.Result; +import hudson.model.Run; +import io.jenkins.plugins.checks.api.ChecksOutput; +import io.jenkins.plugins.checks.api.TruncatedString; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -11,14 +18,9 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; - import org.apache.commons.collections.iterators.ReverseListIterator; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; - -import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; - import org.jenkinsci.plugins.workflow.actions.ArgumentsAction; import org.jenkinsci.plugins.workflow.actions.ErrorAction; import org.jenkinsci.plugins.workflow.actions.LabelAction; @@ -28,14 +30,7 @@ import org.jenkinsci.plugins.workflow.flow.FlowExecution; import org.jenkinsci.plugins.workflow.graph.BlockStartNode; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import org.jenkinsci.plugins.workflow.graph.StepNode; -import org.jenkinsci.plugins.workflow.steps.StepDescriptor; import org.jenkinsci.plugins.workflow.support.visualization.table.FlowGraphTable; -import hudson.model.Result; -import hudson.model.Run; - -import io.jenkins.plugins.checks.api.ChecksOutput; -import io.jenkins.plugins.checks.api.TruncatedString; @SuppressWarnings("PMD.GodClass") class FlowExecutionAnalyzer { @@ -200,54 +195,6 @@ private String getPotentialTitle(final FlowNode flowNode, final ErrorAction erro return StringUtils.join(new ReverseListIterator(enclosingBlockNames), "/") + ": " + whereBuildFailed; } - private static boolean isStageNode(@NonNull final FlowNode node) { - if (node instanceof StepNode) { - StepDescriptor d = ((StepNode) node).getDescriptor(); - return d != null && d.getFunctionName().equals("stage"); - } - else { - return false; - } - } - - /** - * Get the stage and parallel branch start node IDs (not the body nodes) for this node, innermost first. - * @param node A flownode. - * @return A nonnull, possibly empty list of stage/parallel branch start nodes, innermost first. - */ - @NonNull - private static List getEnclosingStagesAndParallels(final FlowNode node) { - List enclosingBlocks = new ArrayList<>(); - for (FlowNode enclosing : node.getEnclosingBlocks()) { - if (enclosing != null && enclosing.getAction(LabelAction.class) != null - && (isStageNode(enclosing) || enclosing.getAction(ThreadNameAction.class) != null)) { - enclosingBlocks.add(enclosing); - } - } - - return enclosingBlocks; - } - - @NonNull - private static List getEnclosingBlockNames(@NonNull final List nodes) { - List names = new ArrayList<>(); - for (FlowNode n : nodes) { - ThreadNameAction threadNameAction = n.getPersistentAction(ThreadNameAction.class); - LabelAction labelAction = n.getPersistentAction(LabelAction.class); - if (threadNameAction != null) { - // If we're on a parallel branch with the same name as the previous (inner) node, that generally - // means we're in a Declarative parallel stages situation, so don't add the redundant branch name. - if (names.isEmpty() || !threadNameAction.getThreadName().equals(names.get(names.size() - 1))) { - names.add(threadNameAction.getThreadName()); - } - } - else if (labelAction != null) { - names.add(labelAction.getDisplayName()); - } - } - return names; - } - @CheckForNull private static String getLog(final FlowNode flowNode) { LogAction logAction = flowNode.getAction(LogAction.class); diff --git a/src/main/java/io/jenkins/plugins/checks/steps/WithChecksStep.java b/src/main/java/io/jenkins/plugins/checks/steps/WithChecksStep.java index 0e0e9201..5ebad69a 100644 --- a/src/main/java/io/jenkins/plugins/checks/steps/WithChecksStep.java +++ b/src/main/java/io/jenkins/plugins/checks/steps/WithChecksStep.java @@ -6,9 +6,13 @@ import hudson.model.Run; import hudson.model.TaskListener; import io.jenkins.plugins.checks.api.*; +import io.jenkins.plugins.checks.utils.FlowNodeUtils; import io.jenkins.plugins.util.PluginLogger; import jenkins.model.CauseOfInterruption; +import org.apache.commons.collections.iterators.ReverseListIterator; +import org.apache.commons.lang3.StringUtils; import org.jenkinsci.plugins.displayurlapi.DisplayURLProvider; +import org.jenkinsci.plugins.workflow.graph.FlowNode; import org.jenkinsci.plugins.workflow.steps.*; import org.kohsuke.stapler.DataBoundConstructor; @@ -18,6 +22,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import org.kohsuke.stapler.DataBoundSetter; import static hudson.Util.fixNull; @@ -28,6 +33,7 @@ public class WithChecksStep extends Step implements Serializable { private static final long serialVersionUID = 1L; private final String name; + private boolean includeStage; /** * Creates the step with a name to inject. @@ -45,6 +51,15 @@ public String getName() { return name; } + public boolean isIncludeStage() { + return includeStage; + } + + @DataBoundSetter + public void setIncludeStage(boolean includeStage) { + this.includeStage = includeStage; + } + @Override public StepExecution start(final StepContext stepContext) { return new WithChecksStepExecution(stepContext, this); @@ -109,7 +124,7 @@ static class WithChecksStepExecution extends AbstractStepExecutionImpl { } @Override - public boolean start() { + public boolean start() throws IOException, InterruptedException { ChecksInfo info = extractChecksInfo(); getContext().newBodyInvoker() .withContext(info) @@ -119,19 +134,36 @@ public boolean start() { } @VisibleForTesting - ChecksInfo extractChecksInfo() { - return new ChecksInfo(step.name); + ChecksInfo extractChecksInfo() throws IOException, InterruptedException { + return new ChecksInfo(getName()); + } + + private String getName() throws IOException, InterruptedException { + if (step.isIncludeStage()) { + FlowNode flowNode = getContext().get(FlowNode.class); + if (flowNode == null) { + throw new IllegalStateException("No FlowNode found in the context."); + } + + List enclosingStagesAndParallels = FlowNodeUtils.getEnclosingStagesAndParallels(flowNode); + List checksComponents = FlowNodeUtils.getEnclosingBlockNames(enclosingStagesAndParallels); + + checksComponents.add(step.getName()); + + return StringUtils.join(new ReverseListIterator(checksComponents), " / "); + } + return step.getName(); } @Override public void stop(final Throwable cause) { try { publish(getContext(), new ChecksDetails.ChecksDetailsBuilder() - .withName(step.getName()) + .withName(getName()) .withStatus(ChecksStatus.COMPLETED) .withConclusion(ChecksConclusion.CANCELED)); } - catch (WithChecksPublishException e) { + catch (WithChecksPublishException | IOException | InterruptedException e) { cause.addSuppressed(e); } getContext().onFailure(cause); diff --git a/src/main/java/io/jenkins/plugins/checks/utils/FlowNodeUtils.java b/src/main/java/io/jenkins/plugins/checks/utils/FlowNodeUtils.java new file mode 100644 index 00000000..521b02d8 --- /dev/null +++ b/src/main/java/io/jenkins/plugins/checks/utils/FlowNodeUtils.java @@ -0,0 +1,71 @@ +package io.jenkins.plugins.checks.utils; + +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.ArrayList; +import java.util.List; +import org.jenkinsci.plugins.workflow.actions.LabelAction; +import org.jenkinsci.plugins.workflow.actions.ThreadNameAction; +import org.jenkinsci.plugins.workflow.graph.FlowNode; +import org.jenkinsci.plugins.workflow.graph.StepNode; +import org.jenkinsci.plugins.workflow.steps.StepDescriptor; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +/** + * Utility methods for working with FlowNodes. + */ +@Restricted(NoExternalUse.class) +public class FlowNodeUtils { + /** + * Get the stage and parallel branch start node IDs (not the body nodes) for this node, innermost first. + * @param node A flownode. + * @return A nonnull, possibly empty list of stage/parallel branch start nodes, innermost first. + */ + @NonNull + public static List getEnclosingStagesAndParallels(final FlowNode node) { + List enclosingBlocks = new ArrayList<>(); + for (FlowNode enclosing : node.getEnclosingBlocks()) { + if (enclosing != null && enclosing.getAction(LabelAction.class) != null + && (isStageNode(enclosing) || enclosing.getAction(ThreadNameAction.class) != null)) { + enclosingBlocks.add(enclosing); + } + } + + return enclosingBlocks; + } + + /** + * Get the stage and parallel branch names for these nodes, innermost first. + * @param nodes A flownode. + * @return A nonnull, possibly empty list of stage/parallel branch names, innermost first. + */ + @NonNull + public static List getEnclosingBlockNames(@NonNull final List nodes) { + List names = new ArrayList<>(); + for (FlowNode n : nodes) { + ThreadNameAction threadNameAction = n.getPersistentAction(ThreadNameAction.class); + LabelAction labelAction = n.getPersistentAction(LabelAction.class); + if (threadNameAction != null) { + // If we're on a parallel branch with the same name as the previous (inner) node, that generally + // means we're in a Declarative parallel stages situation, so don't add the redundant branch name. + if (names.isEmpty() || !threadNameAction.getThreadName().equals(names.get(names.size() - 1))) { + names.add(threadNameAction.getThreadName()); + } + } + else if (labelAction != null) { + names.add(labelAction.getDisplayName()); + } + } + return names; + } + + private static boolean isStageNode(@NonNull final FlowNode node) { + if (node instanceof StepNode) { + StepDescriptor d = ((StepNode) node).getDescriptor(); + return d != null && d.getFunctionName().equals("stage"); + } + else { + return false; + } + } +} diff --git a/src/test/java/io/jenkins/plugins/checks/status/BuildStatusChecksPublisherITest.java b/src/test/java/io/jenkins/plugins/checks/status/BuildStatusChecksPublisherITest.java index 6f92a5bb..3e64b049 100644 --- a/src/test/java/io/jenkins/plugins/checks/status/BuildStatusChecksPublisherITest.java +++ b/src/test/java/io/jenkins/plugins/checks/status/BuildStatusChecksPublisherITest.java @@ -88,13 +88,14 @@ public void shouldNotPublishStatusWhenSkipped() { * a status checks using the specified name should be published. */ @Test - public void shouldPublishStatusWithProperties() { + public void shouldPublishStatusWithProperties() throws Exception { getProperties().setApplicable(true); getProperties().setSkipped(false); getProperties().setName("Test Status"); buildSuccessfully(createFreeStyleProject()); - + // Wait for the job to finish to work around slow Windows builds sometimes + this.getJenkins().waitUntilNoActivity(); assertThat(getFactory().getPublishedChecks()).hasSize(3); ChecksDetails details = getFactory().getPublishedChecks().get(0); diff --git a/src/test/java/io/jenkins/plugins/checks/steps/WithChecksStepITest.java b/src/test/java/io/jenkins/plugins/checks/steps/WithChecksStepITest.java index 3e871993..dd3fad92 100644 --- a/src/test/java/io/jenkins/plugins/checks/steps/WithChecksStepITest.java +++ b/src/test/java/io/jenkins/plugins/checks/steps/WithChecksStepITest.java @@ -82,6 +82,21 @@ public void publishChecksShouldTakeNameFromWithChecks() { assertThat(manualChecks.getConclusion()).isEqualTo(ChecksConclusion.SUCCESS); } + @Test + public void publishChecksShouldIncludeEnclosingBlocksWhenEnabled() { + WorkflowJob job = createPipeline(); + job.setDefinition(asStage("withChecks(name: 'tests', includeStage: true) {}")); + + buildSuccessfully(job); + + assertThat(getFactory().getPublishedChecks()).hasSize(1); + ChecksDetails autoChecks = getFactory().getPublishedChecks().get(0); + + assertThat(autoChecks.getName()).contains("tests / Integration Test"); + assertThat(autoChecks.getStatus()).isEqualTo(ChecksStatus.IN_PROGRESS); + assertThat(autoChecks.getConclusion()).isEqualTo(ChecksConclusion.NONE); + } + /** * Tests that withChecks step ignores names from the withChecks context if one has been explicitly set. */ diff --git a/src/test/resources/design.puml b/src/test/resources/design.puml index 79d0923f..8bef7833 100644 --- a/src/test/resources/design.puml +++ b/src/test/resources/design.puml @@ -9,10 +9,13 @@ skinparam component { [API] <<..checks.api>> [Steps] <<..checks.steps>> [Status] <<..checks.status>> +[Utils] <<..checks.utils>> [Checks] <<..checks>> [Steps] --> [API] +[Steps] --> [Utils] [Status] --> [API] +[Status] --> [Utils] [Checks] --> [API] @enduml