From 25f83092b0ecd195a8e08fcdad729d4cf8a436a5 Mon Sep 17 00:00:00 2001 From: Kshithij Iyer Date: Fri, 16 Aug 2019 10:48:52 +0530 Subject: [PATCH 001/114] Adding LICENSE file. --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..ccf63147 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2014 CloudBees, Inc. + +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. From 2eb15c40fbf916cedfe2ff638ac01f15026bda24 Mon Sep 17 00:00:00 2001 From: Rodion Gushchin Date: Wed, 27 Nov 2019 09:56:19 +0100 Subject: [PATCH 002/114] JENKINS-38669 first stage - adding getSCMs method to FlowDefinition --- .../jenkinsci/plugins/workflow/flow/FlowDefinition.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDefinition.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDefinition.java index c9444e0e..33d77c60 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDefinition.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDefinition.java @@ -30,8 +30,11 @@ import hudson.model.Action; import hudson.model.TaskListener; import hudson.util.LogTaskListener; +import hudson.scm.SCM; import java.io.IOException; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -76,4 +79,8 @@ public FlowExecution create(FlowExecutionOwner handle, List ac return (FlowDefinitionDescriptor) super.getDescriptor(); } + public Collection getSCMs() { + return Collections.emptyList(); + } + } From eb77dfcc8ffc67f9fbb1346d84877f2a8ce7afc5 Mon Sep 17 00:00:00 2001 From: Rodion Gushchin Date: Wed, 4 Dec 2019 09:58:23 +0100 Subject: [PATCH 003/114] added javadoc to getSCMs method --- .../jenkinsci/plugins/workflow/flow/FlowDefinition.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDefinition.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDefinition.java index 33d77c60..4ece9202 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDefinition.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDefinition.java @@ -79,6 +79,15 @@ public FlowExecution create(FlowExecutionOwner handle, List ac return (FlowDefinitionDescriptor) super.getDescriptor(); } + /** + * Returns a list of all {@link SCM SCMs} that are part of the static configuration of the {@link FlowDefinition}. + * Subclasses of {@link FlowDefinition} may override this method to return statically configured SCMs + * that they may be aware of. For example, {@code CpsScmFlowDefinition} returns SCM used to retrieve + * Jenkinsfile. + * Does not include any SCMs used dynamically during Pipeline execution. + * May be empty (or not overridden) if the Pipeline does not include any statically configured SCMs. + * This method is used in {@code WorkflowJob} class which will combine lists of static and dynamic SCMs. + */ public Collection getSCMs() { return Collections.emptyList(); } From e290ec5b055472f5f5df3b47933c9ef6abacf8fe Mon Sep 17 00:00:00 2001 From: Rodion Gushchin Date: Wed, 4 Dec 2019 16:14:59 +0100 Subject: [PATCH 004/114] bump workflow-step-api and script-security versions --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ab301f94..4548e4eb 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ org.jenkins-ci.plugins.workflow workflow-step-api - 2.16 + 2.20 org.jenkins-ci.plugins @@ -127,7 +127,7 @@ org.jenkins-ci.plugins script-security - 1.46 + 1.62 test From 48323892c81e18c358892d69046fc38c7910714b Mon Sep 17 00:00:00 2001 From: Rodion Gushchin Date: Wed, 4 Dec 2019 16:34:15 +0100 Subject: [PATCH 005/114] bump structs versions --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4548e4eb..9a006cf0 100644 --- a/pom.xml +++ b/pom.xml @@ -139,7 +139,7 @@ org.jenkins-ci.plugins structs - 1.17 + 1.18 org.jenkins-ci.test From a4b9535285c9cd45f48e21fdb89550ed4b9acc18 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Mon, 15 Mar 2021 21:21:06 +0000 Subject: [PATCH 006/114] Make workflow-api compatible with Guava 21.0 and newer (#135) --- .mvn/extensions.xml | 2 +- pom.xml | 13 +-- .../plugins/workflow/flow/DirectExecutor.java | 40 +++++++++ .../workflow/flow/FlowExecutionList.java | 5 +- .../plugins/workflow/flow/MoreExecutors.java | 88 +++++++++++++++++++ 5 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/jenkinsci/plugins/workflow/flow/DirectExecutor.java create mode 100644 src/main/java/org/jenkinsci/plugins/workflow/flow/MoreExecutors.java diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index a2d496cc..43d62816 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -2,6 +2,6 @@ io.jenkins.tools.incrementals git-changelist-maven-extension - 1.0-beta-4 + 1.2 diff --git a/pom.xml b/pom.xml index e93dcf5a..861ceb33 100644 --- a/pom.xml +++ b/pom.xml @@ -44,9 +44,9 @@ - scm:git:git://github.com/jenkinsci/${project.artifactId}-plugin.git - scm:git:git@github.com:jenkinsci/${project.artifactId}-plugin.git - https://github.com/jenkinsci/${project.artifactId}-plugin + scm:git:git://github.com/${gitHubRepo}.git + scm:git:git@github.com:${gitHubRepo}.git + https://github.com/${gitHubRepo} ${scmTag} @@ -64,17 +64,18 @@ 2.42 -SNAPSHOT - 2.176.4 + 2.222.4 8 false true + jenkinsci/${project.artifactId}-plugin io.jenkins.tools.bom - bom-2.176.x - 16 + bom-2.222.x + 25 import pom diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/DirectExecutor.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/DirectExecutor.java new file mode 100644 index 00000000..fdf1532a --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/DirectExecutor.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.jenkinsci.plugins.workflow.flow; + +import com.google.common.annotations.GwtCompatible; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import java.util.concurrent.Executor; + +/** + * An {@link Executor} that runs each task in the thread that invokes {@link Executor#execute + * execute}. + */ +@GwtCompatible +@Restricted(NoExternalUse.class) +enum DirectExecutor implements Executor { + INSTANCE; + + @Override + public void execute(Runnable command) { + command.run(); + } + + @Override + public String toString() { + return "MoreExecutors.directExecutor()"; + } +} \ No newline at end of file diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index e33661d1..0e20f266 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -194,7 +194,7 @@ public void onFailure(Throwable t) { LOGGER.log(WARNING, "Failed to load " + e, t); } } - }); + }, MoreExecutors.directExecutor()); } } } @@ -230,7 +230,7 @@ public void onSuccess(List result) { public void onFailure(Throwable t) { LOGGER.log(Level.WARNING, null, t); } - }); + }, MoreExecutors.directExecutor()); } return Futures.allAsList(all); @@ -253,5 +253,4 @@ public void onFailure(Throwable t) { executor.shutdown(); executor.awaitTermination(1, TimeUnit.MINUTES); } - } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/MoreExecutors.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/MoreExecutors.java new file mode 100644 index 00000000..86e05657 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/MoreExecutors.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.jenkinsci.plugins.workflow.flow; + +import com.google.common.annotations.GwtCompatible; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; + +/** + * Factory and utility methods for {@link java.util.concurrent.Executor}, {@link ExecutorService}, + * and {@link java.util.concurrent.ThreadFactory}. + * + * @author Eric Fellheimer + * @author Kyle Littlefield + * @author Justin Mahoney + * @since 3.0 + */ +@GwtCompatible(emulated = true) +@Restricted(NoExternalUse.class) +public final class MoreExecutors { + private MoreExecutors() {} + + /** + * Returns an {@link Executor} that runs each task in the thread that invokes {@link + * Executor#execute execute}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}. + * + *

This executor is appropriate for tasks that are lightweight and not deeply chained. + * Inappropriate {@code directExecutor} usage can cause problems, and these problems can be + * difficult to reproduce because they depend on timing. For example: + * + *

    + *
  • A call like {@code future.transform(function, directExecutor())} may execute the function + * immediately in the thread that is calling {@code transform}. (This specific case happens + * if the future is already completed.) If {@code transform} call was made from a UI thread + * or other latency-sensitive thread, a heavyweight function can harm responsiveness. + *
  • If the task will be executed later, consider which thread will trigger the execution -- + * since that thread will execute the task inline. If the thread is a shared system thread + * like an RPC network thread, a heavyweight task can stall progress of the whole system or + * even deadlock it. + *
  • If many tasks will be triggered by the same event, one heavyweight task may delay other + * tasks -- even tasks that are not themselves {@code directExecutor} tasks. + *
  • If many such tasks are chained together (such as with {@code + * future.transform(...).transform(...).transform(...)....}), they may overflow the stack. + * (In simple cases, callers can avoid this by registering all tasks with the same {@code + * MoreExecutors#newSequentialExecutor} wrapper around {@code directExecutor()}. More + * complex cases may require using thread pools or making deeper changes.) + *
+ * + * Additionally, beware of executing tasks with {@code directExecutor} while holding a lock. Since + * the task you submit to the executor (or any other arbitrary work the executor does) may do slow + * work or acquire other locks, you risk deadlocks. + * + *

This instance is equivalent to: + * + *

{@code
+   * final class DirectExecutor implements Executor {
+   *   public void execute(Runnable r) {
+   *     r.run();
+   *   }
+   * }
+   * }
+ * + *

This should be preferred to {@code #newDirectExecutorService()} because implementing the + * {@link ExecutorService} subinterface necessitates significant performance overhead. + * + * + * @since 18.0 + */ + public static Executor directExecutor() { + return DirectExecutor.INSTANCE; + } +} \ No newline at end of file From 030bebdd1fa83deaf28df52308b47177c3c57cc7 Mon Sep 17 00:00:00 2001 From: Carroll Chiou Date: Tue, 16 Mar 2021 00:46:52 -0600 Subject: [PATCH 007/114] enable release-drafter github action --- .github/release-drafter.yml | 3 +++ .github/workflows/release-drafter.yml | 16 ++++++++++++++++ README.md | 7 ++++++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..b6111be0 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,3 @@ +_extends: .github +name-template: v$NEXT_MINOR_VERSION 🌈 +tag-template: workflow-api-$NEXT_MINOR_VERSION diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000..832442dd --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,16 @@ +# Automates creation of Release Drafts using Release Drafter +# More Info: https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc + +on: + push: + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 23f7d5d0..51651327 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Pipeline API Plugin [![Jenkins Plugin](https://img.shields.io/jenkins/plugin/v/workflow-api)](https://plugins.jenkins.io/workflow-api) -[![Changelog](https://img.shields.io/github/v/tag/jenkinsci/workflow-api-plugin?label=changelog)](https://github.com/jenkinsci/workflow-api-plugin/blob/master/CHANGELOG.md) +[![GitHub Release](https://img.shields.io/github/v/tag/jenkinsci/workflow-api-plugin?label=changelog)](https://github.com/jenkinsci/workflow-api-plugin/releases/latest) [![Jenkins Plugin Installs](https://img.shields.io/jenkins/plugin/i/workflow-api?color=blue)](https://plugins.jenkins.io/workflow-api) # Introduction @@ -10,3 +10,8 @@ Plugin that defines Pipeline API. A component of [Pipeline Plugin](https://plugins.jenkins.io/workflow-aggregator). + +# Changelog + +* For new versions, see [GitHub Releases](https://github.com/jenkinsci/workflow-api-plugin/releases) +* For versions 2.41 and older, see the [CHANGELOG](https://github.com/jenkinsci/workflow-api-plugin/blob/master/CHANGELOG.md) From 5f558d4b8b18bd4bca1b0a39b137ac75280650d0 Mon Sep 17 00:00:00 2001 From: Carroll Chiou Date: Tue, 16 Mar 2021 17:28:58 -0600 Subject: [PATCH 008/114] [maven-release-plugin] prepare release workflow-api-2.42 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 861ceb33..d421a952 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - ${revision}${changelist} + 2.42 hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - ${scmTag} + workflow-api-2.42 From fa8a6b3e6d5248e4489545ccb33f1f623ad4c9bb Mon Sep 17 00:00:00 2001 From: Carroll Chiou Date: Tue, 16 Mar 2021 17:29:10 -0600 Subject: [PATCH 009/114] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d421a952..ca44854e 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - 2.42 + ${revision}${changelist} hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - workflow-api-2.42 + ${scmTag} @@ -62,7 +62,7 @@ - 2.42 + 2.43 -SNAPSHOT 2.222.4 8 From f8641c7cb3f48f0931591e4fb78f83240448a6a2 Mon Sep 17 00:00:00 2001 From: Kenneth Rogers <37545700+kerogers-cloudbees@users.noreply.github.com> Date: Fri, 7 May 2021 19:14:44 -0400 Subject: [PATCH 010/114] [BEE-2537] Terminology updates for public strings. slave->agent, master->controller, blacklist->denylist (#147) --- docs/flowgraph.md | 4 +-- .../plugins/workflow/FilePathUtils.java | 6 ++-- .../workflow/actions/WorkspaceAction.java | 2 +- .../workflow/flow/FlowDurabilityHint.java | 2 +- .../workflow/flow/FlowExecutionOwner.java | 2 +- .../graphanalysis/AbstractFlowScanner.java | 28 +++++++++---------- .../workflow/graphanalysis/LinearScanner.java | 4 +-- .../workflow/log/BufferedBuildListener.java | 2 +- .../workflow/log/TaskListenerDecorator.java | 2 +- .../workflow/log/LogStorageTestBase.java | 8 +++--- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/flowgraph.md b/docs/flowgraph.md index 2bb4653f..3b429dc4 100644 --- a/docs/flowgraph.md +++ b/docs/flowgraph.md @@ -75,7 +75,7 @@ This may seem backwards, but it enables us to freely append to the Flow Graph as * **There are no bounds on the size of the flow graph, it may have thousands of nodes in it.** Real users with complex Pipelines will really generate graphs this size. Yes, really. * **Repeat: there are no bounds on the size of the flow graph. This means if you use recursive function calls to iterate over the Flow Graph you will get a `StackOverFlowError`!!!** Use the `AbstractFlowScanner` implementations - they're free of stack overflows and well-tested. * As a back of napkin estimate, most Flow Graphs fall in the 200-700 `FlowNode` range -* `GraphListener` gotcha: because the listener is invoked for each new `FlowNode`, if you implement some operation that iterates over a lot of the Flow Graph then you've just done an O(n^2) operation and it can result in very high CPU use. This can bog down a Jenkins master if not done carefully. +* `GraphListener` gotcha: because the listener is invoked for each new `FlowNode`, if you implement some operation that iterates over a lot of the Flow Graph then you've just done an O(n^2) operation and it can result in very high CPU use. This can bog down a Jenkins controller if not done carefully. - Careful use of the methods above to iterate/find enclosing flownodes can make this much safer @@ -83,4 +83,4 @@ This may seem backwards, but it enables us to freely append to the Flow Graph as * It may seem intimidating but the Flow Graph has a TON of useful information ripe for analysis and visualization! * The gotchas above are mostly a problem for plugins that try to manipulate the Flow Graph or do automated analysis while Pipelines are running -- if you use the utilities and wait for Pipelines to complete then most of the problems go away. -* The actual data model is quite simple. \ No newline at end of file +* The actual data model is quite simple. diff --git a/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java b/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java index 1cac64e7..72326695 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java @@ -63,7 +63,7 @@ public class FilePathUtils { * Note that if an administrator disconnects an agent, configures and connects an unrelated agent with the same name, * and then this method is called on a path created against the original connection, the result may be misleading. * @param f a file, possibly remote - * @return a node name ({@code ""} for master), if known, else null + * @return a node name ({@code ""} for the controller), if known, else null */ public static @CheckForNull String getNodeNameOrNull(@Nonnull FilePath f) { return Listener.getChannelName(f.getChannel()); @@ -72,7 +72,7 @@ public class FilePathUtils { /** * Same as {@link #getNodeNameOrNull} but throws a diagnostic exception in case of failure. * @param f a file, possible remote - * @return a node name ({@code ""} for master), if known + * @return a node name ({@code ""} for the controller), if known * @throws IllegalStateException if the association to a node is unknown */ public static @Nonnull String getNodeName(@Nonnull FilePath f) throws IllegalStateException { @@ -80,7 +80,7 @@ public class FilePathUtils { if (name != null) { return name; } else { - throw new IllegalStateException("no known slave for " + f + " among " + Listener.getChannelNames()); + throw new IllegalStateException("no known agent for " + f + " among " + Listener.getChannelNames()); } } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/WorkspaceAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/WorkspaceAction.java index e41ccbb0..ed16f972 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/WorkspaceAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/WorkspaceAction.java @@ -48,7 +48,7 @@ public abstract class WorkspaceAction implements PersistentAction { * The {@link Node#getAssignedLabels} of the node owning the workspace. * {@link Node#getSelfLabel} should be exempted, so this set may be empty in the typical case. * (Could be reconstructed in most cases via {@link Jenkins#getNode} on {@link #getNode}, - * but not for a slave which has since been removed, common with clouds.) + * but not for an agent which has since been removed, common with clouds.) */ public abstract @Nonnull Set getLabels(); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDurabilityHint.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDurabilityHint.java index 9abca387..1a4b8f0a 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDurabilityHint.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDurabilityHint.java @@ -26,7 +26,7 @@ import javax.annotation.Nonnull; /** - * Provides hints about just how hard we should try to protect our workflow from failures of the master. + * Provides hints about just how hard we should try to protect our workflow from failures of the controller. * There is a trade-off between durability and performance, with higher levels carrying much higher overheads to execution. * Storage and persistence of data should try to provide at least the specified level (may offer more). * diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java index 2a09113e..8d481196 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java @@ -75,7 +75,7 @@ void notifyShutdown() { } /** - * A directory (on the master) where information may be persisted. + * A directory (on the controller) where information may be persisted. * @see Run#getRootDir */ public abstract File getRootDir() throws IOException; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java index 088b5bb5..397e1a03 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java @@ -57,8 +57,8 @@ * * *

All APIs visit the parent nodes, walking backward from heads(inclusive) until they they hit {@link #myBlackList} nodes (exclusive) or reach the end of the DAG. - * If blackList nodes are an empty collection or null, APIs will walk to the beginning of the FlowGraph. - * Multiple blackList nodes are helpful for putting separate bounds on walking different parallel branches. + * If denyList nodes are an empty collection or null, APIs will walk to the beginning of the FlowGraph. + * Multiple denyList nodes are helpful for putting separate bounds on walking different parallel branches. * *

Key Points: *

  • There are many helper methods offering syntactic sugar for the above APIs in common use cases (simpler method signatures).
  • @@ -83,7 +83,7 @@ *
  • Scan through all nodes *just* within a block *
      *
    • Use the {@link org.jenkinsci.plugins.workflow.graph.BlockEndNode} as the head
    • - *
    • Use the {@link org.jenkinsci.plugins.workflow.graph.BlockStartNode} as its blacklist with {@link Collections#singleton(Object)}
    • + *
    • Use the {@link org.jenkinsci.plugins.workflow.graph.BlockStartNode} as its denylist with {@link Collections#singleton(Object)}
    • *
  • *
* @@ -99,7 +99,7 @@ public abstract class AbstractFlowScanner implements Iterable , Filter protected Collection myBlackList = Collections.emptySet(); - /** When checking for blacklist membership, we convert to a hashset when checking more than this many elements */ + /** When checking for denylist membership, we convert to a hashset when checking more than this many elements */ protected static final int MAX_LIST_CHECK_SIZE = 5; /** Helper: convert stop nodes to a collection that can efficiently be checked for membership, handling null if needed */ @@ -142,7 +142,7 @@ public boolean setup(@CheckForNull Collection heads, @CheckForNull Col } /** - * Helper: version of {@link #setup(Collection, Collection)} where we don't have any nodes to blacklist + * Helper: version of {@link #setup(Collection, Collection)} where we don't have any nodes to denylist */ public boolean setup(@CheckForNull Collection heads) { if (heads == null) { @@ -152,7 +152,7 @@ public boolean setup(@CheckForNull Collection heads) { } /** - * Helper: version of {@link #setup(Collection, Collection)} where we don't have any nodes to blacklist, and have just a single head + * Helper: version of {@link #setup(Collection, Collection)} where we don't have any nodes to denylist, and have just a single head */ public boolean setup(@CheckForNull FlowNode head, @CheckForNull Collection blackList) { if (head == null) { @@ -162,7 +162,7 @@ public boolean setup(@CheckForNull FlowNode head, @CheckForNull Collection filteredHeads); @@ -261,19 +261,19 @@ public FlowNode findFirstMatch(@CheckForNull Collection heads, // Polymorphic methods for syntactic sugar - /** Syntactic sugar for {@link #findFirstMatch(Collection, Collection, Predicate)} where there is no blackList */ + /** Syntactic sugar for {@link #findFirstMatch(Collection, Collection, Predicate)} where there is no denyList */ @CheckForNull public FlowNode findFirstMatch(@CheckForNull Collection heads, @Nonnull Predicate matchPredicate) { return this.findFirstMatch(heads, null, matchPredicate); } - /** Syntactic sugar for {@link #findFirstMatch(Collection, Collection, Predicate)} where there is a single head and no blackList */ + /** Syntactic sugar for {@link #findFirstMatch(Collection, Collection, Predicate)} where there is a single head and no denyList */ @CheckForNull public FlowNode findFirstMatch(@CheckForNull FlowNode head, @Nonnull Predicate matchPredicate) { return this.findFirstMatch(Collections.singleton(head), null, matchPredicate); } - /** Syntactic sugar for {@link #findFirstMatch(Collection, Collection, Predicate)} using {@link FlowExecution#getCurrentHeads()} to get heads and no blackList */ + /** Syntactic sugar for {@link #findFirstMatch(Collection, Collection, Predicate)} using {@link FlowExecution#getCurrentHeads()} to get heads and no denyList */ @CheckForNull public FlowNode findFirstMatch(@CheckForNull FlowExecution exec, @Nonnull Predicate matchPredicate) { if (exec != null && exec.getCurrentHeads() != null && !exec.getCurrentHeads().isEmpty()) { @@ -326,13 +326,13 @@ public List allNodes(@CheckForNull FlowExecution exec) { return (exec == null) ? Collections.emptyList() : allNodes(exec.getCurrentHeads()); } - /** Syntactic sugar for {@link #filteredNodes(Collection, Collection, Predicate)} with no blackList nodes */ + /** Syntactic sugar for {@link #filteredNodes(Collection, Collection, Predicate)} with no denyList nodes */ @Nonnull public List filteredNodes(@CheckForNull Collection heads, @Nonnull Predicate matchPredicate) { return this.filteredNodes(heads, null, matchPredicate); } - /** Syntactic sugar for {@link #filteredNodes(Collection, Collection, Predicate)} with a single head and no blackList nodes */ + /** Syntactic sugar for {@link #filteredNodes(Collection, Collection, Predicate)} with a single head and no denyList nodes */ @Nonnull public List filteredNodes(@CheckForNull FlowNode head, @Nonnull Predicate matchPredicate) { return this.filteredNodes(Collections.singleton(head), null, matchPredicate); @@ -368,7 +368,7 @@ public void visitAll(@CheckForNull Collection heads, @CheckForNull Col } } - /** Syntactic sugar for {@link #visitAll(Collection, Collection, FlowNodeVisitor)} where we don't blacklist any nodes */ + /** Syntactic sugar for {@link #visitAll(Collection, Collection, FlowNodeVisitor)} where we don't denylist any nodes */ public void visitAll(@CheckForNull Collection heads, @Nonnull FlowNodeVisitor visitor) { visitAll(heads, null, visitor); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java index 60e1e1e6..66c36b2c 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java @@ -46,7 +46,7 @@ *

Use case: we don't care about parallel branches or know they don't exist, we just want to walk through the top-level blocks. * *

This is the fastest and simplest way to walk a flow, because you only care about a single node at a time. - * Nuance: where there are multiple parent nodes (in a parallel block), and one is blacklisted, we'll find the first non-blacklisted one. + * Nuance: where there are multiple parent nodes (in a parallel block), and one is denylisted, we'll find the first non-denylisted one. * @author Sam Van Oort */ @NotThreadSafe @@ -63,7 +63,7 @@ protected void reset() { /** * {@inheritDoc} - * @param heads Head nodes that have been filtered against blackList. Do not pass multiple heads. + * @param heads Head nodes that have been filtered against denyList. Do not pass multiple heads. */ @Override protected void setHeads(@Nonnull Collection heads) { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/BufferedBuildListener.java b/src/main/java/org/jenkinsci/plugins/workflow/log/BufferedBuildListener.java index 5fc2f817..2fc29832 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/BufferedBuildListener.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/BufferedBuildListener.java @@ -67,7 +67,7 @@ private static final class Replacement implements SerializableOnlyOverRemoting { private static final long serialVersionUID = 1; private final RemoteOutputStream ros; - private final DelayBufferedOutputStream.Tuning tuning = DelayBufferedOutputStream.Tuning.DEFAULT; // load defaults on master + private final DelayBufferedOutputStream.Tuning tuning = DelayBufferedOutputStream.Tuning.DEFAULT; // load defaults on controller Replacement(BufferedBuildListener cbl) { this.ros = new RemoteOutputStream(new CloseProofOutputStream(cbl.out)); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java index c90cd53b..2345f654 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java @@ -62,7 +62,7 @@ * using {@link #merge} to pick up any earlier decorator in {@link StepContext#get}. *

Expected to be serializable either locally or over Remoting, * so an implementation of {@link #decorate} cannot assume that {@link JenkinsJVM#isJenkinsJVM}. - * Any master-side configuration should thus be saved into instance fields when the decorator is constructed. + * Any controller-side configuration should thus be saved into instance fields when the decorator is constructed. * @see JENKINS-45693 */ public abstract class TaskListenerDecorator implements /* TODO Remotable */ Serializable { diff --git a/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java b/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java index 6db161e0..5eaae303 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java @@ -161,11 +161,11 @@ protected static void close(TaskListener listener) throws Exception { logging.capture(100).record(Channel.class, Level.WARNING); LogStorage ls = createStorage(); TaskListener overall = ls.overallListener(); - overall.getLogger().println("overall from master"); + overall.getLogger().println("overall from controller"); TaskListener step = ls.nodeListener(new MockNode("1")); - step.getLogger().println("step from master"); - long overallPos = assertOverallLog(0, "overall from master\nstep from master\n", true); - long stepPos = assertStepLog("1", 0, "step from master\n", true); + step.getLogger().println("step from controller"); + long overallPos = assertOverallLog(0, "overall from controller\nstep from controller\n", true); + long stepPos = assertStepLog("1", 0, "step from controller\n", true); DumbSlave s = r.createOnlineSlave(); r.showAgentLogs(s, agentLoggers()); VirtualChannel channel = s.getChannel(); From 620362f67c9675267c2c4405f5887d1c49323277 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Mon, 10 May 2021 19:58:39 +0100 Subject: [PATCH 011/114] Build on java 11 as well (#138) --- Jenkinsfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index a229fa51..a205a3c2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1 +1,5 @@ -buildPlugin() +buildPlugin(configurations: [ + [ platform: "linux", jdk: "8" ], + [ platform: "windows", jdk: "8" ], + [ platform: "linux", jdk: "11" ] +]) From 513163ef752962d746a195a3678c25c6cd9f7d90 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Mon, 31 May 2021 13:23:08 -0700 Subject: [PATCH 012/114] Update plugin POM and BOM --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index ca44854e..a2a0acc3 100644 --- a/pom.xml +++ b/pom.xml @@ -23,12 +23,12 @@ ~ THE SOFTWARE. --> - + 4.0.0 org.jenkins-ci.plugins plugin - 4.16 + 4.19 org.jenkins-ci.plugins.workflow @@ -75,7 +75,7 @@ io.jenkins.tools.bom bom-2.222.x - 25 + 831.v9814430e6383 import pom From bd29437c8f2d3ddc30ba38c48326046ccc26f2a5 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Mon, 31 May 2021 09:36:48 -0700 Subject: [PATCH 013/114] Fix Javadoc error: tag not supported in the generated HTML version: tt --- .../workflow/visualization/table/FlowNodeViewColumn.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java index bdb794e4..8375a186 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java @@ -10,13 +10,13 @@ * Extension point for adding a column to a table rendering of {@link FlowNode}s. * *

- * This object must have the column.groovy. This view + * This object must have the {@code column.groovy}. This view * is called for each cell of this column. The {@link FlowNode} object * is passed in the "node" variable. The view should render * a {@code } tag. * *

- * This object may have an additional columnHeader.groovy. The default column header + * This object may have an additional {@code columnHeader.groovy}. The default column header * will render {@link #getColumnCaption()}. * * @author Kohsuke Kawaguchi From 61da1b2f1ac9fd3c0e13c7c98b70b5b5c8380316 Mon Sep 17 00:00:00 2001 From: Thierry Wasylczenko Date: Fri, 4 Jun 2021 10:26:35 +0200 Subject: [PATCH 014/114] Introducing some synchronization mechanisms to try improving performance --- .../graph/StandardGraphLookupView.java | 92 +++++++++++-------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java index 5d6e2ffc..215f450f 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java @@ -31,13 +31,17 @@ public final class StandardGraphLookupView implements GraphLookupView, GraphList HashMap nearestEnclosingBlock = new HashMap<>(); public void clearCache() { - blockStartToEnd.clear(); - nearestEnclosingBlock.clear(); + synchronized (blockStartToEnd) { + blockStartToEnd.clear(); + } + synchronized (nearestEnclosingBlock) { + nearestEnclosingBlock.clear(); + } } /** Update with a new node added to the flowgraph */ @Override - public void onNewHead(@Nonnull FlowNode newHead) { + public synchronized void onNewHead(@Nonnull FlowNode newHead) { if (newHead instanceof BlockEndNode) { blockStartToEnd.put(((BlockEndNode)newHead).getStartNode().getId(), newHead.getId()); String overallEnclosing = nearestEnclosingBlock.get(((BlockEndNode) newHead).getStartNode().getId()); @@ -92,10 +96,12 @@ BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { scan.setup(start.getExecution().getCurrentHeads()); for (FlowNode f : scan) { if (f instanceof BlockEndNode) { - BlockEndNode end = (BlockEndNode)f; + BlockEndNode end = (BlockEndNode) f; BlockStartNode maybeStart = end.getStartNode(); // Cache start in case we need to scan again in the future - blockStartToEnd.put(maybeStart.getId(), end.getId()); + synchronized (blockStartToEnd) { + blockStartToEnd.put(maybeStart.getId(), end.getId()); + } if (start.equals(maybeStart)) { return end; } @@ -103,7 +109,9 @@ BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { BlockStartNode maybeThis = (BlockStartNode) f; // We're walking from the end to the start and see the start without finding the end first, block is incomplete - blockStartToEnd.putIfAbsent(maybeThis.getId(), INCOMPLETE); + synchronized (blockStartToEnd) { + blockStartToEnd.putIfAbsent(maybeThis.getId(), INCOMPLETE); + } if (start.equals(maybeThis)) { // Early exit, the end can't be encountered before the start return null; } @@ -123,19 +131,23 @@ BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { if (current instanceof BlockEndNode) { // Hop over the block to the start BlockStartNode start = ((BlockEndNode) current).getStartNode(); - blockStartToEnd.put(start.getId(), current.getId()); + synchronized (blockStartToEnd) { + blockStartToEnd.put(start.getId(), current.getId()); + } current = start; continue; // Simplifies cases below we only need to look at the immediately preceding node. } // Try for a cache hit if (current != node) { - String enclosingIdFromCache = nearestEnclosingBlock.get(current.getId()); - if (enclosingIdFromCache != null) { - try { - return (BlockStartNode) node.getExecution().getNode(enclosingIdFromCache); - } catch (IOException ioe) { - throw new RuntimeException(ioe); + synchronized (nearestEnclosingBlock) { + String enclosingIdFromCache = nearestEnclosingBlock.get(current.getId()); + if (enclosingIdFromCache != null) { + try { + return (BlockStartNode) node.getExecution().getNode(enclosingIdFromCache); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } } } } @@ -146,7 +158,9 @@ BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { } FlowNode parent = current.getParents().get(0); if (parent instanceof BlockStartNode) { - nearestEnclosingBlock.put(current.getId(), parent.getId()); + synchronized (nearestEnclosingBlock) { + nearestEnclosingBlock.put(current.getId(), parent.getId()); + } return (BlockStartNode) parent; } current = parent; @@ -159,19 +173,21 @@ BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { @Override public BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { - String id = blockStartToEnd.get(startNode.getId()); - if (id != null) { - try { - return id == INCOMPLETE ? null : (BlockEndNode) startNode.getExecution().getNode(id); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } else { - BlockEndNode node = bruteForceScanForEnd(startNode); - if (node != null) { - blockStartToEnd.put(startNode.getId(), node.getId()); + synchronized (blockStartToEnd) { + String id = blockStartToEnd.get(startNode.getId()); + if (id != null) { + try { + return id == INCOMPLETE ? null : (BlockEndNode) startNode.getExecution().getNode(id); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } else { + BlockEndNode node = bruteForceScanForEnd(startNode); + if (node != null) { + blockStartToEnd.put(startNode.getId(), node.getId()); + } + return node; } - return node; } } @@ -182,19 +198,21 @@ public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { return null; } - String id = nearestEnclosingBlock.get(node.getId()); - if (id != null) { - try { - return (BlockStartNode) node.getExecution().getNode(id); - } catch (IOException ioe) { - throw new RuntimeException(ioe); + synchronized (nearestEnclosingBlock) { + String id = nearestEnclosingBlock.get(node.getId()); + if (id != null) { + try { + return (BlockStartNode) node.getExecution().getNode(id); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } } - } - BlockStartNode enclosing = bruteForceScanForEnclosingBlock(node); - if (enclosing != null) { - nearestEnclosingBlock.put(node.getId(), enclosing.getId()); - return enclosing; + BlockStartNode enclosing = bruteForceScanForEnclosingBlock(node); + if (enclosing != null) { + nearestEnclosingBlock.put(node.getId(), enclosing.getId()); + return enclosing; + } } return null; } From d13c9a6b1a13c89912fb84cce6fa873e8197783f Mon Sep 17 00:00:00 2001 From: Thierry Wasylczenko Date: Fri, 4 Jun 2021 14:32:11 +0200 Subject: [PATCH 015/114] Using a lock instead of synchronization --- .../graph/StandardGraphLookupView.java | 160 ++++++++++-------- 1 file changed, 86 insertions(+), 74 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java index 215f450f..e755eb84 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.concurrent.locks.ReentrantLock; /** * Provides overall insight into the structure of a flow graph... but with limited visibility so we can change implementation. @@ -24,54 +25,62 @@ public final class StandardGraphLookupView implements GraphLookupView, GraphList static final String INCOMPLETE = ""; - /** Map the blockStartNode to its endNode, to accellerate a range of operations */ + /** Map the blockStartNode to its endNode, to accelerate a range of operations */ HashMap blockStartToEnd = new HashMap<>(); /** Map a node to its nearest enclosing block */ HashMap nearestEnclosingBlock = new HashMap<>(); + final ReentrantLock lock = new ReentrantLock(); + public void clearCache() { - synchronized (blockStartToEnd) { + lock.lock(); + try { blockStartToEnd.clear(); - } - synchronized (nearestEnclosingBlock) { nearestEnclosingBlock.clear(); + } finally { + lock.unlock(); } } /** Update with a new node added to the flowgraph */ @Override - public synchronized void onNewHead(@Nonnull FlowNode newHead) { - if (newHead instanceof BlockEndNode) { - blockStartToEnd.put(((BlockEndNode)newHead).getStartNode().getId(), newHead.getId()); - String overallEnclosing = nearestEnclosingBlock.get(((BlockEndNode) newHead).getStartNode().getId()); - if (overallEnclosing != null) { - nearestEnclosingBlock.put(newHead.getId(), overallEnclosing); - } - } else { - if (newHead instanceof BlockStartNode) { - blockStartToEnd.put(newHead.getId(), INCOMPLETE); - } - - // Now we try to generate enclosing block info for caching, by looking at parents - // But we don't try to hard -- usually the cache is populated and we defer recursive walks of the graph - List parents = newHead.getParents(); - if (parents.size() > 0) { - FlowNode parent = parents.get(0); // Multiple parents only for end of a parallel, and then both are BlockEndNodes + public void onNewHead(@Nonnull FlowNode newHead) { + lock.lock(); + try { + if (newHead instanceof BlockEndNode) { + blockStartToEnd.put(((BlockEndNode) newHead).getStartNode().getId(), newHead.getId()); + String overallEnclosing = nearestEnclosingBlock.get(((BlockEndNode) newHead).getStartNode().getId()); + if (overallEnclosing != null) { + nearestEnclosingBlock.put(newHead.getId(), overallEnclosing); + } + } else { + if (newHead instanceof BlockStartNode) { + blockStartToEnd.put(newHead.getId(), INCOMPLETE); + } - if (parent instanceof BlockStartNode) { - nearestEnclosingBlock.put(newHead.getId(), parent.getId()); - } else { - // Try to reuse info from cache for this node: - // If the parent ended a block, we use info from the start of the block since that is at the same block nesting level as our new head - // Otherwise the parent is a normal atom node, and the head is enclosed by the same block - String lookupId = (parent instanceof BlockEndNode) ? ((BlockEndNode) parent).getStartNode().getId() : parent.getId(); - String enclosingId = nearestEnclosingBlock.get(lookupId); - if (enclosingId != null) { - nearestEnclosingBlock.put(newHead.getId(), enclosingId); + // Now we try to generate enclosing block info for caching, by looking at parents + // But we don't try to hard -- usually the cache is populated and we defer recursive walks of the graph + List parents = newHead.getParents(); + if (parents.size() > 0) { + FlowNode parent = parents.get(0); // Multiple parents only for end of a parallel, and then both are BlockEndNodes + + if (parent instanceof BlockStartNode) { + nearestEnclosingBlock.put(newHead.getId(), parent.getId()); + } else { + // Try to reuse info from cache for this node: + // If the parent ended a block, we use info from the start of the block since that is at the same block nesting level as our new head + // Otherwise the parent is a normal atom node, and the head is enclosed by the same block + String lookupId = (parent instanceof BlockEndNode) ? ((BlockEndNode) parent).getStartNode().getId() : parent.getId(); + String enclosingId = nearestEnclosingBlock.get(lookupId); + if (enclosingId != null) { + nearestEnclosingBlock.put(newHead.getId(), enclosingId); + } } } } + } finally { + lock.unlock(); } } @@ -94,28 +103,29 @@ public boolean isActive(@Nonnull FlowNode node) { BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { DepthFirstScanner scan = new DepthFirstScanner(); scan.setup(start.getExecution().getCurrentHeads()); - for (FlowNode f : scan) { - if (f instanceof BlockEndNode) { - BlockEndNode end = (BlockEndNode) f; - BlockStartNode maybeStart = end.getStartNode(); - // Cache start in case we need to scan again in the future - synchronized (blockStartToEnd) { + lock.lock(); + try { + for (FlowNode f : scan) { + if (f instanceof BlockEndNode) { + BlockEndNode end = (BlockEndNode) f; + BlockStartNode maybeStart = end.getStartNode(); + // Cache start in case we need to scan again in the future blockStartToEnd.put(maybeStart.getId(), end.getId()); - } - if (start.equals(maybeStart)) { - return end; - } - } else if (f instanceof BlockStartNode) { - BlockStartNode maybeThis = (BlockStartNode) f; + if (start.equals(maybeStart)) { + return end; + } + } else if (f instanceof BlockStartNode) { + BlockStartNode maybeThis = (BlockStartNode) f; - // We're walking from the end to the start and see the start without finding the end first, block is incomplete - synchronized (blockStartToEnd) { + // We're walking from the end to the start and see the start without finding the end first, block is incomplete blockStartToEnd.putIfAbsent(maybeThis.getId(), INCOMPLETE); - } - if (start.equals(maybeThis)) { // Early exit, the end can't be encountered before the start - return null; + if (start.equals(maybeThis)) { // Early exit, the end can't be encountered before the start + return null; + } } } + } finally { + lock.unlock(); } return null; } @@ -126,21 +136,19 @@ BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { /** Do a brute-force scan for the enclosing blocks **/ BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { FlowNode current = node; - - while (!(current instanceof FlowStartNode)) { // Hunt back for enclosing blocks, a potentially expensive operation - if (current instanceof BlockEndNode) { - // Hop over the block to the start - BlockStartNode start = ((BlockEndNode) current).getStartNode(); - synchronized (blockStartToEnd) { + lock.lock(); + try { + while (!(current instanceof FlowStartNode)) { // Hunt back for enclosing blocks, a potentially expensive operation + if (current instanceof BlockEndNode) { + // Hop over the block to the start + BlockStartNode start = ((BlockEndNode) current).getStartNode(); blockStartToEnd.put(start.getId(), current.getId()); + current = start; + continue; // Simplifies cases below we only need to look at the immediately preceding node. } - current = start; - continue; // Simplifies cases below we only need to look at the immediately preceding node. - } - // Try for a cache hit - if (current != node) { - synchronized (nearestEnclosingBlock) { + // Try for a cache hit + if (current != node) { String enclosingIdFromCache = nearestEnclosingBlock.get(current.getId()); if (enclosingIdFromCache != null) { try { @@ -150,22 +158,21 @@ BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { } } } - } - // Now see if we have a winner among parents - if (current.getParents().isEmpty()) { - return null; - } - FlowNode parent = current.getParents().get(0); - if (parent instanceof BlockStartNode) { - synchronized (nearestEnclosingBlock) { + // Now see if we have a winner among parents + if (current.getParents().isEmpty()) { + return null; + } + FlowNode parent = current.getParents().get(0); + if (parent instanceof BlockStartNode) { nearestEnclosingBlock.put(current.getId(), parent.getId()); + return (BlockStartNode) parent; } - return (BlockStartNode) parent; + current = parent; } - current = parent; + } finally { + lock.unlock(); } - return null; } @@ -173,7 +180,8 @@ BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { @Override public BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { - synchronized (blockStartToEnd) { + lock.lock(); + try { String id = blockStartToEnd.get(startNode.getId()); if (id != null) { try { @@ -188,6 +196,8 @@ public BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { } return node; } + } finally { + lock.unlock(); } } @@ -197,8 +207,8 @@ public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { if (node instanceof FlowStartNode || node instanceof FlowEndNode) { return null; } - - synchronized (nearestEnclosingBlock) { + lock.lock(); + try { String id = nearestEnclosingBlock.get(node.getId()); if (id != null) { try { @@ -213,6 +223,8 @@ public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { nearestEnclosingBlock.put(node.getId(), enclosing.getId()); return enclosing; } + } finally { + lock.unlock(); } return null; } From 689f233b220f3955747070aaf52331cb1db123b7 Mon Sep 17 00:00:00 2001 From: Thierry Wasylczenko Date: Mon, 7 Jun 2021 15:07:36 +0200 Subject: [PATCH 016/114] Synchronization at instance level as suggested in PR --- .../graph/StandardGraphLookupView.java | 234 ++++++++---------- 1 file changed, 99 insertions(+), 135 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java index e755eb84..9e3093ff 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java @@ -13,7 +13,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.concurrent.locks.ReentrantLock; /** * Provides overall insight into the structure of a flow graph... but with limited visibility so we can change implementation. @@ -31,56 +30,44 @@ public final class StandardGraphLookupView implements GraphLookupView, GraphList /** Map a node to its nearest enclosing block */ HashMap nearestEnclosingBlock = new HashMap<>(); - final ReentrantLock lock = new ReentrantLock(); - - public void clearCache() { - lock.lock(); - try { - blockStartToEnd.clear(); - nearestEnclosingBlock.clear(); - } finally { - lock.unlock(); - } + public synchronized void clearCache() { + blockStartToEnd.clear(); + nearestEnclosingBlock.clear(); } /** Update with a new node added to the flowgraph */ @Override - public void onNewHead(@Nonnull FlowNode newHead) { - lock.lock(); - try { - if (newHead instanceof BlockEndNode) { - blockStartToEnd.put(((BlockEndNode) newHead).getStartNode().getId(), newHead.getId()); - String overallEnclosing = nearestEnclosingBlock.get(((BlockEndNode) newHead).getStartNode().getId()); - if (overallEnclosing != null) { - nearestEnclosingBlock.put(newHead.getId(), overallEnclosing); - } - } else { - if (newHead instanceof BlockStartNode) { - blockStartToEnd.put(newHead.getId(), INCOMPLETE); - } + public synchronized void onNewHead(@Nonnull FlowNode newHead) { + if (newHead instanceof BlockEndNode) { + blockStartToEnd.put(((BlockEndNode) newHead).getStartNode().getId(), newHead.getId()); + String overallEnclosing = nearestEnclosingBlock.get(((BlockEndNode) newHead).getStartNode().getId()); + if (overallEnclosing != null) { + nearestEnclosingBlock.put(newHead.getId(), overallEnclosing); + } + } else { + if (newHead instanceof BlockStartNode) { + blockStartToEnd.put(newHead.getId(), INCOMPLETE); + } + + // Now we try to generate enclosing block info for caching, by looking at parents + // But we don't try to hard -- usually the cache is populated and we defer recursive walks of the graph + List parents = newHead.getParents(); + if (parents.size() > 0) { + FlowNode parent = parents.get(0); // Multiple parents only for end of a parallel, and then both are BlockEndNodes - // Now we try to generate enclosing block info for caching, by looking at parents - // But we don't try to hard -- usually the cache is populated and we defer recursive walks of the graph - List parents = newHead.getParents(); - if (parents.size() > 0) { - FlowNode parent = parents.get(0); // Multiple parents only for end of a parallel, and then both are BlockEndNodes - - if (parent instanceof BlockStartNode) { - nearestEnclosingBlock.put(newHead.getId(), parent.getId()); - } else { - // Try to reuse info from cache for this node: - // If the parent ended a block, we use info from the start of the block since that is at the same block nesting level as our new head - // Otherwise the parent is a normal atom node, and the head is enclosed by the same block - String lookupId = (parent instanceof BlockEndNode) ? ((BlockEndNode) parent).getStartNode().getId() : parent.getId(); - String enclosingId = nearestEnclosingBlock.get(lookupId); - if (enclosingId != null) { - nearestEnclosingBlock.put(newHead.getId(), enclosingId); - } + if (parent instanceof BlockStartNode) { + nearestEnclosingBlock.put(newHead.getId(), parent.getId()); + } else { + // Try to reuse info from cache for this node: + // If the parent ended a block, we use info from the start of the block since that is at the same block nesting level as our new head + // Otherwise the parent is a normal atom node, and the head is enclosed by the same block + String lookupId = (parent instanceof BlockEndNode) ? ((BlockEndNode) parent).getStartNode().getId() : parent.getId(); + String enclosingId = nearestEnclosingBlock.get(lookupId); + if (enclosingId != null) { + nearestEnclosingBlock.put(newHead.getId(), enclosingId); } } } - } finally { - lock.unlock(); } } @@ -100,131 +87,108 @@ public boolean isActive(@Nonnull FlowNode node) { } // Do a brute-force scan for the block end matching the start, caching info along the way for future use - BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { + synchronized BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { DepthFirstScanner scan = new DepthFirstScanner(); scan.setup(start.getExecution().getCurrentHeads()); - lock.lock(); - try { - for (FlowNode f : scan) { - if (f instanceof BlockEndNode) { - BlockEndNode end = (BlockEndNode) f; - BlockStartNode maybeStart = end.getStartNode(); - // Cache start in case we need to scan again in the future - blockStartToEnd.put(maybeStart.getId(), end.getId()); - if (start.equals(maybeStart)) { - return end; - } - } else if (f instanceof BlockStartNode) { - BlockStartNode maybeThis = (BlockStartNode) f; + for (FlowNode f : scan) { + if (f instanceof BlockEndNode) { + BlockEndNode end = (BlockEndNode) f; + BlockStartNode maybeStart = end.getStartNode(); + // Cache start in case we need to scan again in the future + blockStartToEnd.put(maybeStart.getId(), end.getId()); + if (start.equals(maybeStart)) { + return end; + } + } else if (f instanceof BlockStartNode) { + BlockStartNode maybeThis = (BlockStartNode) f; - // We're walking from the end to the start and see the start without finding the end first, block is incomplete - blockStartToEnd.putIfAbsent(maybeThis.getId(), INCOMPLETE); - if (start.equals(maybeThis)) { // Early exit, the end can't be encountered before the start - return null; - } + // We're walking from the end to the start and see the start without finding the end first, block is incomplete + blockStartToEnd.putIfAbsent(maybeThis.getId(), INCOMPLETE); + if (start.equals(maybeThis)) { // Early exit, the end can't be encountered before the start + return null; } } - } finally { - lock.unlock(); } return null; } - - - /** Do a brute-force scan for the enclosing blocks **/ - BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { + synchronized BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { FlowNode current = node; - lock.lock(); - try { - while (!(current instanceof FlowStartNode)) { // Hunt back for enclosing blocks, a potentially expensive operation - if (current instanceof BlockEndNode) { - // Hop over the block to the start - BlockStartNode start = ((BlockEndNode) current).getStartNode(); - blockStartToEnd.put(start.getId(), current.getId()); - current = start; - continue; // Simplifies cases below we only need to look at the immediately preceding node. - } + while (!(current instanceof FlowStartNode)) { // Hunt back for enclosing blocks, a potentially expensive operation + if (current instanceof BlockEndNode) { + // Hop over the block to the start + BlockStartNode start = ((BlockEndNode) current).getStartNode(); + blockStartToEnd.put(start.getId(), current.getId()); + current = start; + continue; // Simplifies cases below we only need to look at the immediately preceding node. + } - // Try for a cache hit - if (current != node) { - String enclosingIdFromCache = nearestEnclosingBlock.get(current.getId()); - if (enclosingIdFromCache != null) { - try { - return (BlockStartNode) node.getExecution().getNode(enclosingIdFromCache); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } + // Try for a cache hit + if (current != node) { + String enclosingIdFromCache = nearestEnclosingBlock.get(current.getId()); + if (enclosingIdFromCache != null) { + try { + return (BlockStartNode) node.getExecution().getNode(enclosingIdFromCache); + } catch (IOException ioe) { + throw new RuntimeException(ioe); } } + } - // Now see if we have a winner among parents - if (current.getParents().isEmpty()) { - return null; - } - FlowNode parent = current.getParents().get(0); - if (parent instanceof BlockStartNode) { - nearestEnclosingBlock.put(current.getId(), parent.getId()); - return (BlockStartNode) parent; - } - current = parent; + // Now see if we have a winner among parents + if (current.getParents().isEmpty()) { + return null; + } + FlowNode parent = current.getParents().get(0); + if (parent instanceof BlockStartNode) { + nearestEnclosingBlock.put(current.getId(), parent.getId()); + return (BlockStartNode) parent; } - } finally { - lock.unlock(); + current = parent; } return null; } @CheckForNull @Override - public BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { - - lock.lock(); - try { - String id = blockStartToEnd.get(startNode.getId()); - if (id != null) { - try { - return id == INCOMPLETE ? null : (BlockEndNode) startNode.getExecution().getNode(id); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } else { - BlockEndNode node = bruteForceScanForEnd(startNode); - if (node != null) { - blockStartToEnd.put(startNode.getId(), node.getId()); - } - return node; + public synchronized BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { + + String id = blockStartToEnd.get(startNode.getId()); + if (id != null) { + try { + return id == INCOMPLETE ? null : (BlockEndNode) startNode.getExecution().getNode(id); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } else { + BlockEndNode node = bruteForceScanForEnd(startNode); + if (node != null) { + blockStartToEnd.put(startNode.getId(), node.getId()); } - } finally { - lock.unlock(); + return node; } } @CheckForNull @Override - public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { + public synchronized BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { if (node instanceof FlowStartNode || node instanceof FlowEndNode) { return null; } - lock.lock(); - try { - String id = nearestEnclosingBlock.get(node.getId()); - if (id != null) { - try { - return (BlockStartNode) node.getExecution().getNode(id); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } + String id = nearestEnclosingBlock.get(node.getId()); + if (id != null) { + try { + return (BlockStartNode) node.getExecution().getNode(id); + } catch (IOException ioe) { + throw new RuntimeException(ioe); } + } - BlockStartNode enclosing = bruteForceScanForEnclosingBlock(node); - if (enclosing != null) { - nearestEnclosingBlock.put(node.getId(), enclosing.getId()); - return enclosing; - } - } finally { - lock.unlock(); + BlockStartNode enclosing = bruteForceScanForEnclosingBlock(node); + if (enclosing != null) { + nearestEnclosingBlock.put(node.getId(), enclosing.getId()); + return enclosing; } return null; } From 8f5016ca2041d3e8053f4e483098de2840ebb097 Mon Sep 17 00:00:00 2001 From: Thierry Wasylczenko Date: Mon, 7 Jun 2021 15:10:25 +0200 Subject: [PATCH 017/114] Revert some unrelated changes --- .../plugins/workflow/graph/StandardGraphLookupView.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java index 9e3093ff..1c3df128 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java @@ -39,7 +39,7 @@ public synchronized void clearCache() { @Override public synchronized void onNewHead(@Nonnull FlowNode newHead) { if (newHead instanceof BlockEndNode) { - blockStartToEnd.put(((BlockEndNode) newHead).getStartNode().getId(), newHead.getId()); + blockStartToEnd.put(((BlockEndNode)newHead).getStartNode().getId(), newHead.getId()); String overallEnclosing = nearestEnclosingBlock.get(((BlockEndNode) newHead).getStartNode().getId()); if (overallEnclosing != null) { nearestEnclosingBlock.put(newHead.getId(), overallEnclosing); @@ -92,7 +92,7 @@ synchronized BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { scan.setup(start.getExecution().getCurrentHeads()); for (FlowNode f : scan) { if (f instanceof BlockEndNode) { - BlockEndNode end = (BlockEndNode) f; + BlockEndNode end = (BlockEndNode)f; BlockStartNode maybeStart = end.getStartNode(); // Cache start in case we need to scan again in the future blockStartToEnd.put(maybeStart.getId(), end.getId()); @@ -115,6 +115,7 @@ synchronized BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { /** Do a brute-force scan for the enclosing blocks **/ synchronized BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { FlowNode current = node; + while (!(current instanceof FlowStartNode)) { // Hunt back for enclosing blocks, a potentially expensive operation if (current instanceof BlockEndNode) { // Hop over the block to the start From 4f4aaa5f7feaa3ab986118d844840ee7e43ad27f Mon Sep 17 00:00:00 2001 From: Thierry Wasylczenko Date: Mon, 7 Jun 2021 15:11:51 +0200 Subject: [PATCH 018/114] Revert some unrelated changes --- .../plugins/workflow/graph/StandardGraphLookupView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java index 1c3df128..f16ebced 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java @@ -148,6 +148,7 @@ synchronized BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowN } current = parent; } + return null; } @@ -177,6 +178,7 @@ public synchronized BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode nod if (node instanceof FlowStartNode || node instanceof FlowEndNode) { return null; } + String id = nearestEnclosingBlock.get(node.getId()); if (id != null) { try { From 72f1fec5f7528a401988aec3ed189cf551da8e70 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 7 Jun 2021 11:30:21 -0700 Subject: [PATCH 019/114] [maven-release-plugin] prepare release workflow-api-2.43 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ca44854e..4325b4c5 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - ${revision}${changelist} + 2.43 hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - ${scmTag} + workflow-api-2.43 From a42af08b4479f7820e66ac9d6c63b3ec9764b9f1 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 7 Jun 2021 11:30:30 -0700 Subject: [PATCH 020/114] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4325b4c5..4dc240b1 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - 2.43 + ${revision}${changelist} hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - workflow-api-2.43 + ${scmTag} @@ -62,7 +62,7 @@ - 2.43 + 2.44 -SNAPSHOT 2.222.4 8 From 324b9b556f8b0115d6fe7208fe4a12e190babe8b Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 7 Jun 2021 11:58:35 -0700 Subject: [PATCH 021/114] [maven-release-plugin] prepare release workflow-api-2.44 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4dc240b1..bb3898a2 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - ${revision}${changelist} + 2.44 hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - ${scmTag} + workflow-api-2.44 From d8342fd9fb77f3df195c4551ef09378c1dcf0769 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 7 Jun 2021 11:58:44 -0700 Subject: [PATCH 022/114] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index bb3898a2..2659b041 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - 2.44 + ${revision}${changelist} hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - workflow-api-2.44 + ${scmTag} @@ -62,7 +62,7 @@ - 2.44 + 2.45 -SNAPSHOT 2.222.4 8 From 501eb84a9226b523a30c3001542ac9635e873b30 Mon Sep 17 00:00:00 2001 From: Thierry Wasylczenko Date: Mon, 7 Jun 2021 20:59:25 +0200 Subject: [PATCH 023/114] Fixing Javadoc for fixing the pipeline --- .../workflow/graphanalysis/ChunkFinder.java | 10 ++++---- .../workflow/graphanalysis/FlowChunk.java | 5 ++-- .../graphanalysis/SimpleChunkVisitor.java | 24 +++++++++---------- .../table/FlowNodeViewColumn.java | 4 +++- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java index 7a696cbb..b895ec82 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java @@ -7,18 +7,18 @@ /** * Think of this as setting conditions to mark a region of interest in the graph of {@link FlowNode} from a {@link org.jenkinsci.plugins.workflow.flow.FlowExecution}. - *

This is used to define a linear "chunk" from the graph of FlowNodes returned by a {@link ForkScanner}, after it applies ordering. - *

This is done by invoking {@link ForkScanner#visitSimpleChunks(SimpleChunkVisitor, ChunkFinder)}. + *

This is used to define a linear "chunk" from the graph of FlowNodes returned by a {@link ForkScanner}, after it applies ordering.

+ *

This is done by invoking {@link ForkScanner#visitSimpleChunks(SimpleChunkVisitor, ChunkFinder)}.

*

Your {@link SimpleChunkVisitor} will receive callbacks about chunk boundaries on the basis of the ChunkFinder. - * It is responsible for tracking the state based on events fired + * It is responsible for tracking the state based on events fired

* - *

Common uses: + *

Common uses:

*
    *
  • Find all {@link FlowNode}s within a specific block type, such the block created by a timeout block, 'node' (executor) block, etc
  • *
  • Find all {@link FlowNode}s between specific markers, such as labels, milestones, or steps generating an error
  • *
* - *

Implementation Notes: + *

Implementation Notes:

*
    *
  • This can be used to detect both block-delimited regions of interest and marker-based regions
  • *
  • Block-delimited regions should END when encountering the right kind of {@link org.jenkinsci.plugins.workflow.graph.BlockEndNode} diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java index 6bce6d0c..f8e0e749 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java @@ -29,8 +29,8 @@ import javax.annotation.Nonnull; /** - * Common container interface for a series of {@link FlowNode}s with a logical start and end. - *

    We use this because every plugin has a different way of storing info about the nodes. + *

    Common container interface for a series of {@link FlowNode}s with a logical start and end.

    + *

    We use this because every plugin has a different way of storing info about the nodes.

    * *

    Common uses: *

      @@ -41,6 +41,7 @@ *
    • A parallel branch within a parallel block
    • *
    • A mix of types in sequence, such as nested structures
    • *
    + *

    * * @author Sam Van Oort */ diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java index 0e50dd1f..53ba6c7d 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java @@ -32,21 +32,21 @@ /** * This visitor's callbacks are invoked as we walk through a pipeline flow graph, and it splits it into chunks. - *

    A {@link ForkScanner#visitSimpleChunks(SimpleChunkVisitor, ChunkFinder)} creates these FlowChunks using a {@link ChunkFinder} to define the chunk boundaries. + *

    A {@link ForkScanner#visitSimpleChunks(SimpleChunkVisitor, ChunkFinder)} creates these FlowChunks using a {@link ChunkFinder} to define the chunk boundaries.

    * *

    We walk through the {@link FlowNode}s in reverse order from end to start, so end callbacks are invoked before - * their corresponding start callbacks. + * their corresponding start callbacks.

    * - *

    Callback types - *

    There are two kinds of callbacks - chunk callbacks, and parallel structure callbacks - *

    Chunk Callbacks: + *

    Callback types

    + *

    There are two kinds of callbacks - chunk callbacks, and parallel structure callbacks

    + *

    Chunk Callbacks:

    *
      *
    • {@link #chunkStart(FlowNode, FlowNode, ForkScanner)} - detected the start of a chunk beginning with a node
    • *
    • {@link #chunkEnd(FlowNode, FlowNode, ForkScanner)} - detected the end of a chunk, terminating with a node
    • *
    • {@link #atomNode(FlowNode, FlowNode, FlowNode, ForkScanner)} - most nodes, which aren't boundaries of chunks
    • *
    * - *

    Chunk callback rules: + *

    Chunk callback rules:

    *
      *
    1. For a single node, it may have EITHER OR BOTH chunkStart and chunkEnd events
    2. *
    3. Every node that doesn't get a startChunk/endChunk callback gets an atomNode callback.
    4. @@ -55,20 +55,20 @@ *
    5. You cannot have a atomNode callback AND a start/end for the same flownode (application of the above).
    6. *
    * - *

    Parallel Structure Callbacks: Zero, One, or (in niche cases) several different ones may be invoked for any given FlowNode

    - *

    These are used to provide awareness of parallel/branching structures if they need special handling. + *

    Parallel Structure Callbacks: Zero, One, or (in niche cases) several different ones may be invoked for any given FlowNode.

    + *

    These are used to provide awareness of parallel/branching structures if they need special handling.

    *
      *
    • {@link #parallelStart(FlowNode, FlowNode, ForkScanner)}
    • *
    • {@link #parallelEnd(FlowNode, FlowNode, ForkScanner)}
    • *
    • {@link #parallelBranchStart(FlowNode, FlowNode, ForkScanner)}
    • *
    • {@link #parallelBranchEnd(FlowNode, FlowNode, ForkScanner)}
    • *
    - * The cases where a node triggers multiple callbacks are where it is one of several forked branches of an incomplete parallel + *

    The cases where a node triggers multiple callbacks are where it is one of several forked branches of an incomplete parallel * block. In this case it can be a parallelBranchEnd, also potentially a parallelEnd, plus whatever role that node might normally - * have (such as the start of another parallel). + * have (such as the start of another parallel).

    * - *

    Implementations get to decide how to use and handle chunks, and should be stateful. - *

    At a minimum they should handle:

    + *

    Implementations get to decide how to use and handle chunks, and should be stateful.

    + *

    At a minimum they should handle:

    *
      *
    • Cases where there is no enclosing chunk (no start/end found, or outside a chunk)
    • *
    • Cases where there is no chunk end to match the start, because we haven't finished running a block
    • diff --git a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java index bdb794e4..94831bc9 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java @@ -14,10 +14,12 @@ * is called for each cell of this column. The {@link FlowNode} object * is passed in the "node" variable. The view should render * a {@code } tag. + *

      * *

      - * This object may have an additional columnHeader.groovy. The default column header + * This object may have an additional {@code columnHeader.groovy}. The default column header * will render {@link #getColumnCaption()}. + *

      * * @author Kohsuke Kawaguchi * @since 1.279 From 5855331613ae9e09df21e785659364b011ca54df Mon Sep 17 00:00:00 2001 From: Thierry Wasylczenko Date: Mon, 7 Jun 2021 21:15:54 +0200 Subject: [PATCH 024/114] One more missing error --- .../jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java index f8e0e749..4cb97b53 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java @@ -32,7 +32,7 @@ *

      Common container interface for a series of {@link FlowNode}s with a logical start and end.

      *

      We use this because every plugin has a different way of storing info about the nodes.

      * - *

      Common uses: + *

      Common uses:

      *
        *
      • A single FlowNode (when coupling with timing/status APIs)
      • *
      • A block (with a {@link org.jenkinsci.plugins.workflow.graph.BlockStartNode} and {@link org.jenkinsci.plugins.workflow.graph.BlockEndNode})
      • @@ -41,7 +41,6 @@ *
      • A parallel branch within a parallel block
      • *
      • A mix of types in sequence, such as nested structures
      • *
      - *

      * * @author Sam Van Oort */ From 5acd0812cb386c253e3b83849b57ec5adeb71ca9 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 7 Jun 2021 12:51:31 -0700 Subject: [PATCH 025/114] Update FlowNodeViewColumn.java --- .../workflow/visualization/table/FlowNodeViewColumn.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java index 94831bc9..20152336 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java @@ -10,14 +10,14 @@ * Extension point for adding a column to a table rendering of {@link FlowNode}s. * *

      - * This object must have the column.groovy. This view + * This object must have the column.groovy. This view * is called for each cell of this column. The {@link FlowNode} object * is passed in the "node" variable. The view should render * a {@code } tag. *

      * *

      - * This object may have an additional {@code columnHeader.groovy}. The default column header + * This object may have an additional columnHeader.groovy. The default column header * will render {@link #getColumnCaption()}. *

      * From c5b2e1b2b5223c17f47a9094a1c0c5d03afe53e0 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 7 Jun 2021 13:13:10 -0700 Subject: [PATCH 026/114] Apply suggestions from code review --- .../workflow/graphanalysis/ChunkFinder.java | 10 +++++----- .../workflow/graphanalysis/FlowChunk.java | 6 +++--- .../graphanalysis/SimpleChunkVisitor.java | 20 +++++++++---------- .../table/FlowNodeViewColumn.java | 1 - 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java index b895ec82..0a3da2ad 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java @@ -7,18 +7,18 @@ /** * Think of this as setting conditions to mark a region of interest in the graph of {@link FlowNode} from a {@link org.jenkinsci.plugins.workflow.flow.FlowExecution}. - *

      This is used to define a linear "chunk" from the graph of FlowNodes returned by a {@link ForkScanner}, after it applies ordering.

      - *

      This is done by invoking {@link ForkScanner#visitSimpleChunks(SimpleChunkVisitor, ChunkFinder)}.

      + *

      This is used to define a linear "chunk" from the graph of FlowNodes returned by a {@link ForkScanner}, after it applies ordering. + *

      This is done by invoking {@link ForkScanner#visitSimpleChunks(SimpleChunkVisitor, ChunkFinder)}. *

      Your {@link SimpleChunkVisitor} will receive callbacks about chunk boundaries on the basis of the ChunkFinder. - * It is responsible for tracking the state based on events fired

      + * It is responsible for tracking the state based on events fired. * - *

      Common uses:

      + *

      Common uses: *

        *
      • Find all {@link FlowNode}s within a specific block type, such the block created by a timeout block, 'node' (executor) block, etc
      • *
      • Find all {@link FlowNode}s between specific markers, such as labels, milestones, or steps generating an error
      • *
      * - *

      Implementation Notes:

      + *

      Implementation Notes: *

        *
      • This can be used to detect both block-delimited regions of interest and marker-based regions
      • *
      • Block-delimited regions should END when encountering the right kind of {@link org.jenkinsci.plugins.workflow.graph.BlockEndNode} diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java index 4cb97b53..059604b8 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java @@ -29,10 +29,10 @@ import javax.annotation.Nonnull; /** - *

        Common container interface for a series of {@link FlowNode}s with a logical start and end.

        - *

        We use this because every plugin has a different way of storing info about the nodes.

        + *

        Common container interface for a series of {@link FlowNode}s with a logical start and end. + *

        We use this because every plugin has a different way of storing info about the nodes. * - *

        Common uses:

        + *

        Common uses: *

          *
        • A single FlowNode (when coupling with timing/status APIs)
        • *
        • A block (with a {@link org.jenkinsci.plugins.workflow.graph.BlockStartNode} and {@link org.jenkinsci.plugins.workflow.graph.BlockEndNode})
        • diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java index 53ba6c7d..780f396d 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java @@ -32,21 +32,21 @@ /** * This visitor's callbacks are invoked as we walk through a pipeline flow graph, and it splits it into chunks. - *

          A {@link ForkScanner#visitSimpleChunks(SimpleChunkVisitor, ChunkFinder)} creates these FlowChunks using a {@link ChunkFinder} to define the chunk boundaries.

          + *

          A {@link ForkScanner#visitSimpleChunks(SimpleChunkVisitor, ChunkFinder)} creates these FlowChunks using a {@link ChunkFinder} to define the chunk boundaries. * *

          We walk through the {@link FlowNode}s in reverse order from end to start, so end callbacks are invoked before - * their corresponding start callbacks.

          + * their corresponding start callbacks. * - *

          Callback types

          - *

          There are two kinds of callbacks - chunk callbacks, and parallel structure callbacks

          - *

          Chunk Callbacks:

          + *

          Callback types + *

          There are two kinds of callbacks - chunk callbacks, and parallel structure callbacks. + *

          Chunk Callbacks: *

            *
          • {@link #chunkStart(FlowNode, FlowNode, ForkScanner)} - detected the start of a chunk beginning with a node
          • *
          • {@link #chunkEnd(FlowNode, FlowNode, ForkScanner)} - detected the end of a chunk, terminating with a node
          • *
          • {@link #atomNode(FlowNode, FlowNode, FlowNode, ForkScanner)} - most nodes, which aren't boundaries of chunks
          • *
          * - *

          Chunk callback rules:

          + *

          Chunk callback rules: *

            *
          1. For a single node, it may have EITHER OR BOTH chunkStart and chunkEnd events
          2. *
          3. Every node that doesn't get a startChunk/endChunk callback gets an atomNode callback.
          4. @@ -55,8 +55,8 @@ *
          5. You cannot have a atomNode callback AND a start/end for the same flownode (application of the above).
          6. *
          * - *

          Parallel Structure Callbacks: Zero, One, or (in niche cases) several different ones may be invoked for any given FlowNode.

          - *

          These are used to provide awareness of parallel/branching structures if they need special handling.

          + *

          Parallel Structure Callbacks: Zero, One, or (in niche cases) several different ones may be invoked for any given FlowNode. + *

          These are used to provide awareness of parallel/branching structures if they need special handling. *

            *
          • {@link #parallelStart(FlowNode, FlowNode, ForkScanner)}
          • *
          • {@link #parallelEnd(FlowNode, FlowNode, ForkScanner)}
          • @@ -65,9 +65,9 @@ *
          *

          The cases where a node triggers multiple callbacks are where it is one of several forked branches of an incomplete parallel * block. In this case it can be a parallelBranchEnd, also potentially a parallelEnd, plus whatever role that node might normally - * have (such as the start of another parallel).

          + * have (such as the start of another parallel). * - *

          Implementations get to decide how to use and handle chunks, and should be stateful.

          + *

          Implementations get to decide how to use and handle chunks, and should be stateful. *

          At a minimum they should handle:

          *
            *
          • Cases where there is no enclosing chunk (no start/end found, or outside a chunk)
          • diff --git a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java index 20152336..bffc6e9f 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumn.java @@ -19,7 +19,6 @@ *

            * This object may have an additional columnHeader.groovy. The default column header * will render {@link #getColumnCaption()}. - *

            * * @author Kohsuke Kawaguchi * @since 1.279 From bd0347ac9da2df5ad3770468dd5b18d46f80d5c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 16:06:32 +0000 Subject: [PATCH 027/114] Bump bom-2.222.x from 25 to 841.vd6e713d848ab Bumps [bom-2.222.x](https://github.com/jenkinsci/bom) from 25 to 841.vd6e713d848ab. - [Release notes](https://github.com/jenkinsci/bom/releases) - [Commits](https://github.com/jenkinsci/bom/commits) --- updated-dependencies: - dependency-name: io.jenkins.tools.bom:bom-2.222.x dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 304ecd08..12a80aac 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.jenkins.tools.bom bom-2.222.x - 831.v9814430e6383 + 841.vd6e713d848ab import pom From ee39c7304b5d4f4db4e1448614c7963e4b4e5bf6 Mon Sep 17 00:00:00 2001 From: olivier lamy Date: Wed, 9 Jun 2021 10:59:24 +1000 Subject: [PATCH 028/114] add basic gh actions Signed-off-by: olivier lamy --- .github/workflows/maven.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/maven.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..d5e27de6 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,36 @@ +name: GitHub CI + +on: [push, pull_request] + +jobs: + build: + + strategy: + matrix: + os: [ubuntu-latest] + java: [8, 11] + fail-fast: false + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up cache for ~./m2/repository + uses: actions/cache@v2.1.6 + with: + path: ~/.m2/repository + key: maven-${{ matrix.os }}-java${{ matrix.java }}-${{ hashFiles('**/pom.xml') }} + restore-keys: | + maven-${{ matrix.os }}-java${{ matrix.java }}- + maven-${{ matrix.os }}- + + - name: Set up JDK + uses: actions/setup-java@v2.1.0 + with: + distribution: adopt + java-version: ${{ matrix.java }} + + - name: Build with Maven + run: mvn verify -e -B -V javadoc:javadoc From fc996d165f0997ba1009bb694d7af6f5b1a62d0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 13:37:35 +1000 Subject: [PATCH 029/114] Bump docker-fixtures from 1.10 to 1.11 (#143) Bumps [docker-fixtures](https://github.com/jenkinsci/docker-fixtures) from 1.10 to 1.11. - [Release notes](https://github.com/jenkinsci/docker-fixtures/releases) - [Changelog](https://github.com/jenkinsci/docker-fixtures/blob/master/old-changelog.md) - [Commits](https://github.com/jenkinsci/docker-fixtures/compare/docker-fixtures-1.10...docker-fixtures-1.11) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 12a80aac..4d8b5094 100644 --- a/pom.xml +++ b/pom.xml @@ -120,7 +120,7 @@ org.jenkins-ci.test docker-fixtures - 1.10 + 1.11 test From 4a7d66f6cd7f8c3c6bf6768da7ee509dfa25aad9 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Tue, 8 Jun 2021 20:41:21 -0700 Subject: [PATCH 030/114] Migrate from `RestartableJenkinsRule` to `JenkinsSessionRule` (#151) * Migrate from RestartableJenkinsRule to JenkinsSessionRule * Fix Javadoc error: tag not supported in the generated HTML version: tt Co-authored-by: Liam Newman --- .../workflow/flow/DurabilityBasicsTest.java | 32 +++++++++---------- .../workflow/flow/FlowExecutionListTest.java | 28 +++++++--------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java index 89571c0e..774a6b8b 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java @@ -14,7 +14,7 @@ import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.MockAuthorizationStrategy; -import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.jvnet.hudson.test.JenkinsSessionRule; import java.lang.reflect.InvocationTargetException; import java.util.Collection; @@ -28,42 +28,42 @@ public class DurabilityBasicsTest { @Rule - public RestartableJenkinsRule r = new RestartableJenkinsRule(); + public JenkinsSessionRule sessions = new JenkinsSessionRule(); @Test - public void configRoundTrip() { - r.then(r -> { - GlobalDefaultFlowDurabilityLevel.DescriptorImpl level = r.jenkins.getExtensionList(GlobalDefaultFlowDurabilityLevel.DescriptorImpl.class).get(0); + public void configRoundTrip() throws Throwable { + sessions.then(j -> { + GlobalDefaultFlowDurabilityLevel.DescriptorImpl level = j.jenkins.getExtensionList(GlobalDefaultFlowDurabilityLevel.DescriptorImpl.class).get(0); level.setDurabilityHint(FlowDurabilityHint.PERFORMANCE_OPTIMIZED); - r.configRoundtrip(); + j.configRoundtrip(); Assert.assertEquals(FlowDurabilityHint.PERFORMANCE_OPTIMIZED, level.getDurabilityHint()); level.setDurabilityHint(null); - r.configRoundtrip(); + j.configRoundtrip(); Assert.assertNull(level.getDurabilityHint()); // Customize again so we can check for persistence level.setDurabilityHint(FlowDurabilityHint.PERFORMANCE_OPTIMIZED); Assert.assertEquals(FlowDurabilityHint.PERFORMANCE_OPTIMIZED, level.getDurabilityHint()); }); - r.then(r -> { - GlobalDefaultFlowDurabilityLevel.DescriptorImpl level = r.jenkins.getExtensionList(GlobalDefaultFlowDurabilityLevel.DescriptorImpl.class).get(0); + sessions.then(j -> { + GlobalDefaultFlowDurabilityLevel.DescriptorImpl level = j.jenkins.getExtensionList(GlobalDefaultFlowDurabilityLevel.DescriptorImpl.class).get(0); Assert.assertEquals(FlowDurabilityHint.PERFORMANCE_OPTIMIZED, level.getDurabilityHint()); }); } @Test - public void defaultHandling() { - r.then(r -> { + public void defaultHandling() throws Throwable { + sessions.then(j -> { Assert.assertEquals(GlobalDefaultFlowDurabilityLevel.SUGGESTED_DURABILITY_HINT, GlobalDefaultFlowDurabilityLevel.getDefaultDurabilityHint()); - GlobalDefaultFlowDurabilityLevel.DescriptorImpl level = r.jenkins.getExtensionList(GlobalDefaultFlowDurabilityLevel.DescriptorImpl.class).get(0); + GlobalDefaultFlowDurabilityLevel.DescriptorImpl level = j.jenkins.getExtensionList(GlobalDefaultFlowDurabilityLevel.DescriptorImpl.class).get(0); level.setDurabilityHint(FlowDurabilityHint.PERFORMANCE_OPTIMIZED); Assert.assertEquals(FlowDurabilityHint.PERFORMANCE_OPTIMIZED, GlobalDefaultFlowDurabilityLevel.getDefaultDurabilityHint()); }); } @Test - public void managePermissionShouldAccessGlobalConfig() { - r.then(r -> { + public void managePermissionShouldAccessGlobalConfig() throws Throwable { + sessions.then(j -> { Permission jenkinsManage; try { jenkinsManage = getJenkinsManage(); @@ -73,8 +73,8 @@ public void managePermissionShouldAccessGlobalConfig() { } final String USER = "user"; final String MANAGER = "manager"; - r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); - r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy() + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy() // Read access .grant(Jenkins.READ).everywhere().to(USER) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java index c1a1d238..4838aab5 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java @@ -41,22 +41,21 @@ import org.jvnet.hudson.test.BuildWatcher; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; -import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.jvnet.hudson.test.JenkinsSessionRule; public class FlowExecutionListTest { @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher(); - @Rule public RestartableJenkinsRule rr = new RestartableJenkinsRule(); + @Rule public JenkinsSessionRule sessions = new JenkinsSessionRule(); @Rule public LoggerRule logging = new LoggerRule().record(FlowExecutionList.class, Level.FINE); @Issue("JENKINS-40771") - @Test public void simultaneousRegister() { - rr.addStep(new Statement() { - @Override public void evaluate() throws Throwable { - WorkflowJob p = rr.j.createProject(WorkflowJob.class, "p"); + @Test public void simultaneousRegister() throws Throwable { + sessions.then(j -> { + WorkflowJob p = j.createProject(WorkflowJob.class, "p"); { // make sure there is an initial FlowExecutionList.xml p.setDefinition(new CpsFlowDefinition("", true)); - rr.j.buildAndAssertSuccess(p); + j.buildAndAssertSuccess(p); } p.setDefinition(new CpsFlowDefinition("echo params.key; sleep 5", true)); p.addProperty(new ParametersDefinitionProperty(new StringParameterDefinition("key", null))); @@ -68,18 +67,15 @@ public class FlowExecutionListTest { assertNotNull(b2); WorkflowRun b3 = p.getBuildByNumber(3); assertNotNull(b3); - rr.j.waitForMessage("Sleeping for ", b2); - rr.j.waitForMessage("Sleeping for ", b3); - } + j.waitForMessage("Sleeping for ", b2); + j.waitForMessage("Sleeping for ", b3); }); - rr.addStep(new Statement() { - @Override public void evaluate() throws Throwable { - WorkflowJob p = rr.j.jenkins.getItemByFullName("p", WorkflowJob.class); + sessions.then(j -> { + WorkflowJob p = j.jenkins.getItemByFullName("p", WorkflowJob.class); WorkflowRun b2 = p.getBuildByNumber(2); WorkflowRun b3 = p.getBuildByNumber(3); - rr.j.assertBuildStatusSuccess(rr.j.waitForCompletion(b2)); - rr.j.assertBuildStatusSuccess(rr.j.waitForCompletion(b3)); - } + j.assertBuildStatusSuccess(j.waitForCompletion(b2)); + j.assertBuildStatusSuccess(j.waitForCompletion(b3)); }); } From 6c8c07c3836f5d18ec2a301bdd710b7f8006dc56 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Wed, 9 Jun 2021 14:44:38 -0700 Subject: [PATCH 031/114] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index ccf63147..e1d10771 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2013-2014 CloudBees, Inc. +Copyright (c) 2013-2021 CloudBees, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 6372cff71c845080c7eb1165274e0522c34290ca Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Wed, 16 Jun 2021 10:56:33 -0700 Subject: [PATCH 032/114] Revert "Merge pull request #153 from twasyl/synchronization-for-optimisation" This reverts commit 927249bfb514e076e97eea4430b6c479a91e1d53, reversing changes made to 620362f67c9675267c2c4405f5887d1c49323277. --- .../workflow/graph/StandardGraphLookupView.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java index f16ebced..5d6e2ffc 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java @@ -24,20 +24,20 @@ public final class StandardGraphLookupView implements GraphLookupView, GraphList static final String INCOMPLETE = ""; - /** Map the blockStartNode to its endNode, to accelerate a range of operations */ + /** Map the blockStartNode to its endNode, to accellerate a range of operations */ HashMap blockStartToEnd = new HashMap<>(); /** Map a node to its nearest enclosing block */ HashMap nearestEnclosingBlock = new HashMap<>(); - public synchronized void clearCache() { + public void clearCache() { blockStartToEnd.clear(); nearestEnclosingBlock.clear(); } /** Update with a new node added to the flowgraph */ @Override - public synchronized void onNewHead(@Nonnull FlowNode newHead) { + public void onNewHead(@Nonnull FlowNode newHead) { if (newHead instanceof BlockEndNode) { blockStartToEnd.put(((BlockEndNode)newHead).getStartNode().getId(), newHead.getId()); String overallEnclosing = nearestEnclosingBlock.get(((BlockEndNode) newHead).getStartNode().getId()); @@ -87,7 +87,7 @@ public boolean isActive(@Nonnull FlowNode node) { } // Do a brute-force scan for the block end matching the start, caching info along the way for future use - synchronized BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { + BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { DepthFirstScanner scan = new DepthFirstScanner(); scan.setup(start.getExecution().getCurrentHeads()); for (FlowNode f : scan) { @@ -112,8 +112,11 @@ synchronized BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { return null; } + + + /** Do a brute-force scan for the enclosing blocks **/ - synchronized BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { + BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { FlowNode current = node; while (!(current instanceof FlowStartNode)) { // Hunt back for enclosing blocks, a potentially expensive operation @@ -154,7 +157,7 @@ synchronized BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowN @CheckForNull @Override - public synchronized BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { + public BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { String id = blockStartToEnd.get(startNode.getId()); if (id != null) { @@ -174,7 +177,7 @@ public synchronized BlockEndNode getEndNode(@Nonnull final BlockStartNode startN @CheckForNull @Override - public synchronized BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { + public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { if (node instanceof FlowStartNode || node instanceof FlowEndNode) { return null; } From f94d76454ecedd4587349e151024455659e17d64 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Wed, 16 Jun 2021 11:13:54 -0700 Subject: [PATCH 033/114] [maven-release-plugin] prepare release workflow-api-2.45 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4d8b5094..4fb38eca 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - ${revision}${changelist} + 2.45 hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - ${scmTag} + workflow-api-2.45 From 3859653f0bf654924b30aefc9cdb4263d78ada45 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Wed, 16 Jun 2021 11:14:29 -0700 Subject: [PATCH 034/114] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4fb38eca..e014ffb6 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - 2.45 + ${revision}${changelist} hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - workflow-api-2.45 + ${scmTag} @@ -62,7 +62,7 @@ - 2.45 + 2.46 -SNAPSHOT 2.222.4 8 From f9b50da31c26bebfc72b493e4016c24ef81f718e Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 21 Jun 2021 09:44:13 -0700 Subject: [PATCH 035/114] Use ConcurrentHashMap --- .../plugins/workflow/graph/StandardGraphLookupView.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java index 5d6e2ffc..696eb611 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; /** * Provides overall insight into the structure of a flow graph... but with limited visibility so we can change implementation. @@ -25,10 +26,10 @@ public final class StandardGraphLookupView implements GraphLookupView, GraphList static final String INCOMPLETE = ""; /** Map the blockStartNode to its endNode, to accellerate a range of operations */ - HashMap blockStartToEnd = new HashMap<>(); + ConcurrentHashMap blockStartToEnd = new ConcurrentHashMap<>(); /** Map a node to its nearest enclosing block */ - HashMap nearestEnclosingBlock = new HashMap<>(); + ConcurrentHashMap nearestEnclosingBlock = new ConcurrentHashMap<>(); public void clearCache() { blockStartToEnd.clear(); From ea384e4fecd29132c2e551511093af27eaa3eb0e Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 21 Jun 2021 11:06:11 -0700 Subject: [PATCH 036/114] Clean up and clarification --- .../graph/StandardGraphLookupView.java | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java index 696eb611..33d1e761 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java @@ -11,13 +11,12 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.concurrent.ConcurrentHashMap; /** * Provides overall insight into the structure of a flow graph... but with limited visibility so we can change implementation. - * Designed to work entirely on the basis of the {@link FlowNode#id} rather than the {@link FlowNode}s themselves. + * Designed to work entirely on the basis of the {@link FlowNode#getId()} rather than the {@link FlowNode}s themselves. */ @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "Can can use instance identity when comparing to a final constant") @Restricted(NoExternalUse.class) @@ -40,10 +39,11 @@ public void clearCache() { @Override public void onNewHead(@Nonnull FlowNode newHead) { if (newHead instanceof BlockEndNode) { - blockStartToEnd.put(((BlockEndNode)newHead).getStartNode().getId(), newHead.getId()); - String overallEnclosing = nearestEnclosingBlock.get(((BlockEndNode) newHead).getStartNode().getId()); - if (overallEnclosing != null) { - nearestEnclosingBlock.put(newHead.getId(), overallEnclosing); + String startNodeId = ((BlockEndNode)newHead).getStartNode().getId(); + blockStartToEnd.put(startNodeId, newHead.getId()); + String enclosingId = nearestEnclosingBlock.get(startNodeId); + if (enclosingId != null) { + nearestEnclosingBlock.put(newHead.getId(), enclosingId); } } else { if (newHead instanceof BlockStartNode) { @@ -168,11 +168,9 @@ public BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { throw new RuntimeException(ioe); } } else { - BlockEndNode node = bruteForceScanForEnd(startNode); - if (node != null) { - blockStartToEnd.put(startNode.getId(), node.getId()); - } - return node; + // returns the end node or null + // if this returns end node, it also adds start and end to blockStartToEnd + return bruteForceScanForEnd(startNode); } } @@ -192,12 +190,8 @@ public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { } } - BlockStartNode enclosing = bruteForceScanForEnclosingBlock(node); - if (enclosing != null) { - nearestEnclosingBlock.put(node.getId(), enclosing.getId()); - return enclosing; - } - return null; + // when scan completes, enclosing is in the cache if it exists + return bruteForceScanForEnclosingBlock(node); } @Override From 405ec3c9594a66c842f30580c9ae41f5e946dc79 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Wed, 23 Jun 2021 16:33:44 -0700 Subject: [PATCH 037/114] [maven-release-plugin] prepare release workflow-api-2.46 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e014ffb6..71993578 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - ${revision}${changelist} + 2.46 hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - ${scmTag} + workflow-api-2.46 From 8221a5c0e77db01362e54f0b4db274ea3cfa1c0c Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Wed, 23 Jun 2021 16:33:52 -0700 Subject: [PATCH 038/114] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 71993578..d4a99998 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - 2.46 + ${revision}${changelist} hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - workflow-api-2.46 + ${scmTag} @@ -62,7 +62,7 @@ - 2.46 + 2.47 -SNAPSHOT 2.222.4 8 From 3345603d2332894a39fbff246c4dd3e9400e30a0 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Sun, 11 Jul 2021 20:39:02 +1000 Subject: [PATCH 039/114] remove gh action Signed-off-by: Olivier Lamy --- .github/workflows/maven.yml | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 .github/workflows/maven.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml deleted file mode 100644 index d5e27de6..00000000 --- a/.github/workflows/maven.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: GitHub CI - -on: [push, pull_request] - -jobs: - build: - - strategy: - matrix: - os: [ubuntu-latest] - java: [8, 11] - fail-fast: false - - runs-on: ${{ matrix.os }} - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Set up cache for ~./m2/repository - uses: actions/cache@v2.1.6 - with: - path: ~/.m2/repository - key: maven-${{ matrix.os }}-java${{ matrix.java }}-${{ hashFiles('**/pom.xml') }} - restore-keys: | - maven-${{ matrix.os }}-java${{ matrix.java }}- - maven-${{ matrix.os }}- - - - name: Set up JDK - uses: actions/setup-java@v2.1.0 - with: - distribution: adopt - java-version: ${{ matrix.java }} - - - name: Build with Maven - run: mvn verify -e -B -V javadoc:javadoc From 10c0dad5da4e256979e023e570d404a12c742d3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 01:03:36 +0000 Subject: [PATCH 040/114] Bump plugin from 4.19 to 4.28 Bumps [plugin](https://github.com/jenkinsci/plugin-pom) from 4.19 to 4.28. - [Release notes](https://github.com/jenkinsci/plugin-pom/releases) - [Changelog](https://github.com/jenkinsci/plugin-pom/blob/master/CHANGELOG.md) - [Commits](https://github.com/jenkinsci/plugin-pom/compare/plugin-4.19...plugin-4.28) --- updated-dependencies: - dependency-name: org.jenkins-ci.plugins:plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d4a99998..fe568023 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.jenkins-ci.plugins plugin - 4.19 + 4.28 org.jenkins-ci.plugins.workflow From ae6e98df21a4e9a9b754602c25da21f083a268ae Mon Sep 17 00:00:00 2001 From: megathaum Date: Fri, 8 Oct 2021 08:52:37 +0200 Subject: [PATCH 041/114] [JENKINS-66826] Fix NPE in ErrorAction (#175) --- .../java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java index df3049c8..de42e1a5 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java @@ -75,6 +75,7 @@ private boolean isUnserializableException(@CheckForNull Throwable error) { // contains references to the class loader for the Pipeline Script. Storing it leads // to memory leaks. if (error instanceof MissingPropertyException && + ((MissingPropertyException)error).getType() != null && ((MissingPropertyException)error).getType().getClassLoader() instanceof GroovyClassLoader) { return true; } From 02a7752f7461d60f3b5b2208d06b6481d26ce2c9 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Thu, 7 Oct 2021 23:54:25 -0700 Subject: [PATCH 042/114] Remove star imports and unused imports (#170) --- .../workflow/flow/FlowExecutionList.java | 27 +++--- .../plugins/workflow/flow/MoreExecutors.java | 2 - .../plugins/workflow/ArtifactManagerTest.java | 13 ++- .../workflow/actions/ErrorActionTest.java | 2 +- .../workflow/flow/DurabilityBasicsTest.java | 1 - .../workflow/flow/FlowExecutionListTest.java | 4 +- .../plugins/workflow/graph/FlowNodeTest.java | 6 +- .../graphanalysis/FlowScannerTest.java | 83 +++++++++---------- .../graphanalysis/ForkScannerTest.java | 17 ++-- .../graphanalysis/MemoryFlowChunkTest.java | 2 +- .../workflow/log/FileLogStorageTest.java | 3 +- .../workflow/log/LogStorageTestBase.java | 6 +- .../workflow/log/SpanCoalescerTest.java | 3 +- 13 files changed, 88 insertions(+), 81 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 0e20f266..39d91a1c 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -29,7 +29,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import static java.util.logging.Level.*; import javax.annotation.CheckForNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; @@ -69,7 +68,7 @@ protected FlowExecution computeNext() { return e; } } catch (Throwable e) { - LOGGER.log(WARNING, "Failed to load " + o + ". Unregistering", e); + LOGGER.log(Level.WARNING, "Failed to load " + o + ". Unregistering", e); unregister(o); } } @@ -97,9 +96,9 @@ private synchronized void load() { if (cf.exists()) { try { runningTasks.replaceBy((List) cf.read()); - LOGGER.log(FINE, "loaded: {0}", runningTasks); + LOGGER.log(Level.FINE, "loaded: {0}", runningTasks); } catch (Exception x) { - LOGGER.log(WARNING, "ignoring broken " + cf, x); + LOGGER.log(Level.WARNING, "ignoring broken " + cf, x); } } } @@ -111,7 +110,7 @@ private synchronized void load() { */ public synchronized void register(final FlowExecutionOwner self) { if (runningTasks.contains(self)) { - LOGGER.log(WARNING, "{0} was already in the list: {1}", new Object[] {self, runningTasks.getView()}); + LOGGER.log(Level.WARNING, "{0} was already in the list: {1}", new Object[] {self, runningTasks.getView()}); } else { runningTasks.add(self); saveLater(); @@ -120,16 +119,16 @@ public synchronized void register(final FlowExecutionOwner self) { public synchronized void unregister(final FlowExecutionOwner self) { if (runningTasks.remove(self)) { - LOGGER.log(FINE, "unregistered {0}", new Object[] {self}); + LOGGER.log(Level.FINE, "unregistered {0}", new Object[] {self}); saveLater(); } else { - LOGGER.log(WARNING, "{0} was not in the list to begin with: {1}", new Object[] {self, runningTasks.getView()}); + LOGGER.log(Level.WARNING, "{0} was not in the list to begin with: {1}", new Object[] {self, runningTasks.getView()}); } } private synchronized void saveLater() { final List copy = new ArrayList<>(runningTasks.getView()); - LOGGER.log(FINE, "scheduling save of {0}", copy); + LOGGER.log(Level.FINE, "scheduling save of {0}", copy); try { executor.submit(new Runnable() { @Override public void run() { @@ -137,20 +136,20 @@ private synchronized void saveLater() { } }); } catch (RejectedExecutionException x) { - LOGGER.log(FINE, "could not schedule save, perhaps because Jenkins is shutting down; saving immediately", x); + LOGGER.log(Level.FINE, "could not schedule save, perhaps because Jenkins is shutting down; saving immediately", x); save(copy); } } private void save(List copy) { XmlFile cf = configFile(); - LOGGER.log(FINE, "saving {0} to {1}", new Object[] {copy, cf}); + LOGGER.log(Level.FINE, "saving {0} to {1}", new Object[] {copy, cf}); if (cf == null) { return; // oh well } try { cf.write(copy); } catch (IOException x) { - LOGGER.log(WARNING, null, x); + LOGGER.log(Level.WARNING, null, x); } } @@ -176,11 +175,11 @@ public static class ItemListenerImpl extends ItemListener { @Override public void onLoaded() { for (final FlowExecution e : list) { - LOGGER.log(FINE, "Eager loading {0}", e); + LOGGER.log(Level.FINE, "Eager loading {0}", e); Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { @Override public void onSuccess(List result) { - LOGGER.log(FINE, "Will resume {0}", result); + LOGGER.log(Level.FINE, "Will resume {0}", result); for (StepExecution se : result) { se.onResume(); } @@ -191,7 +190,7 @@ public void onFailure(Throwable t) { if (t instanceof CancellationException) { LOGGER.log(Level.FINE, "Cancelled load of " + e, t); } else { - LOGGER.log(WARNING, "Failed to load " + e, t); + LOGGER.log(Level.WARNING, "Failed to load " + e, t); } } }, MoreExecutors.directExecutor()); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/MoreExecutors.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/MoreExecutors.java index 86e05657..1d95f6f0 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/MoreExecutors.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/MoreExecutors.java @@ -19,8 +19,6 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; /** * Factory and utility methods for {@link java.util.concurrent.Executor}, {@link ExecutorService}, diff --git a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java index ffd4d1a9..1b3fbabb 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java @@ -24,6 +24,17 @@ package org.jenkinsci.plugins.workflow; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.arrayContainingInAnyOrder; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials; @@ -60,12 +71,10 @@ import jenkins.security.MasterToSlaveCallable; import jenkins.util.VirtualFile; import org.apache.commons.io.IOUtils; -import static org.hamcrest.Matchers.*; import org.jenkinsci.plugins.workflow.flow.StashManager; import org.jenkinsci.test.acceptance.docker.Docker; import org.jenkinsci.test.acceptance.docker.DockerImage; import org.jenkinsci.test.acceptance.docker.fixtures.JavaContainer; -import static org.junit.Assert.*; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; diff --git a/src/test/java/org/jenkinsci/plugins/workflow/actions/ErrorActionTest.java b/src/test/java/org/jenkinsci/plugins/workflow/actions/ErrorActionTest.java index 1f53a0e2..2835f095 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/actions/ErrorActionTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/actions/ErrorActionTest.java @@ -24,8 +24,8 @@ package org.jenkinsci.plugins.workflow.actions; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java index 774a6b8b..2d2ddb67 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java @@ -1,6 +1,5 @@ package org.jenkinsci.plugins.workflow.flow; -import hudson.ExtensionList; import hudson.Functions; import hudson.model.Descriptor; import hudson.model.User; diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java index 4838aab5..733a3859 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java @@ -24,6 +24,8 @@ package org.jenkinsci.plugins.workflow.flow; +import static org.junit.Assert.assertNotNull; + import hudson.model.ParametersAction; import hudson.model.ParametersDefinitionProperty; import hudson.model.StringParameterDefinition; @@ -35,9 +37,7 @@ import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.junit.ClassRule; import org.junit.Test; -import static org.junit.Assert.*; import org.junit.Rule; -import org.junit.runners.model.Statement; import org.jvnet.hudson.test.BuildWatcher; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graph/FlowNodeTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graph/FlowNodeTest.java index 830d5d91..569c1610 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graph/FlowNodeTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graph/FlowNodeTest.java @@ -54,9 +54,13 @@ import org.junit.Test; import org.kohsuke.stapler.DataBoundConstructor; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; -import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import org.junit.Rule; import org.junit.runners.model.Statement; diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java index c0acd64d..6f3e1379 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java @@ -50,9 +50,6 @@ import java.util.List; import java.util.NoSuchElementException; -// Slightly dirty but it removes a ton of FlowTestUtils.* class qualifiers -import static org.jenkinsci.plugins.workflow.graphanalysis.FlowTestUtils.*; - /** * Tests for all the core parts of graph analysis except the ForkScanner, internals which is complex enough to merit its own tests * @author Sam Van Oort @@ -148,31 +145,31 @@ public void testAbstractScanner() throws Exception { FlowNode firstEchoNode = exec.getNode("5"); FlowExecution nullExecution = null; - Assert.assertEquals(firstEchoNode, linear.findFirstMatch(heads, Collections.EMPTY_LIST, MATCH_ECHO_STEP)); - Assert.assertEquals(firstEchoNode, linear.findFirstMatch(heads, MATCH_ECHO_STEP)); - Assert.assertEquals(firstEchoNode, linear.findFirstMatch(lastNode, MATCH_ECHO_STEP)); - Assert.assertEquals(firstEchoNode, linear.findFirstMatch(exec, MATCH_ECHO_STEP)); - Assert.assertNull(linear.findFirstMatch(nullColl, MATCH_ECHO_STEP)); - Assert.assertNull(linear.findFirstMatch(Collections.EMPTY_SET, MATCH_ECHO_STEP)); - Assert.assertNull(linear.findFirstMatch(nullNode, MATCH_ECHO_STEP)); - Assert.assertNull(linear.findFirstMatch(nullExecution, MATCH_ECHO_STEP)); + Assert.assertEquals(firstEchoNode, linear.findFirstMatch(heads, Collections.EMPTY_LIST, FlowTestUtils.MATCH_ECHO_STEP)); + Assert.assertEquals(firstEchoNode, linear.findFirstMatch(heads, FlowTestUtils.MATCH_ECHO_STEP)); + Assert.assertEquals(firstEchoNode, linear.findFirstMatch(lastNode, FlowTestUtils.MATCH_ECHO_STEP)); + Assert.assertEquals(firstEchoNode, linear.findFirstMatch(exec, FlowTestUtils.MATCH_ECHO_STEP)); + Assert.assertNull(linear.findFirstMatch(nullColl, FlowTestUtils.MATCH_ECHO_STEP)); + Assert.assertNull(linear.findFirstMatch(Collections.EMPTY_SET, FlowTestUtils.MATCH_ECHO_STEP)); + Assert.assertNull(linear.findFirstMatch(nullNode, FlowTestUtils.MATCH_ECHO_STEP)); + Assert.assertNull(linear.findFirstMatch(nullExecution, FlowTestUtils.MATCH_ECHO_STEP)); // Filtered nodes - assertNodeOrder("Filtered echo nodes", linear.filteredNodes(heads, MATCH_ECHO_STEP), 5, 4); - assertNodeOrder("Filtered echo nodes", linear.filteredNodes(heads, Collections.singleton(intermediateNode), MATCH_ECHO_STEP), 5); + FlowTestUtils.assertNodeOrder("Filtered echo nodes", linear.filteredNodes(heads, FlowTestUtils.MATCH_ECHO_STEP), 5, 4); + FlowTestUtils.assertNodeOrder("Filtered echo nodes", linear.filteredNodes(heads, Collections.singleton(intermediateNode), FlowTestUtils.MATCH_ECHO_STEP), 5); Assert.assertEquals(0, linear.filteredNodes(heads, null, (Predicate) Predicates.alwaysFalse()).size()); - Assert.assertEquals(0, linear.filteredNodes(nullNode, MATCH_ECHO_STEP).size()); - Assert.assertEquals(0, linear.filteredNodes(Collections.EMPTY_SET, MATCH_ECHO_STEP).size()); + Assert.assertEquals(0, linear.filteredNodes(nullNode, FlowTestUtils.MATCH_ECHO_STEP).size()); + Assert.assertEquals(0, linear.filteredNodes(Collections.EMPTY_SET, FlowTestUtils.MATCH_ECHO_STEP).size()); // Same filter using the filterator linear.setup(heads); ArrayList collected = new ArrayList<>(); - Filterator filt = linear.filter(MATCH_ECHO_STEP); + Filterator filt = linear.filter(FlowTestUtils.MATCH_ECHO_STEP); while (filt.hasNext()) { collected.add(filt.next()); } - assertNodeOrder("Filterator filtered echo nodes", collected, 5, 4); + FlowTestUtils.assertNodeOrder("Filterator filtered echo nodes", collected, 5, 4); // Visitor pattern tests @@ -182,12 +179,12 @@ public void testAbstractScanner() throws Exception { visitor.reset(); linear.visitAll(heads, visitor); - assertNodeOrder("Visiting all nodes", visitor.getVisited(), 6, 5, 4, 3, 2); + FlowTestUtils.assertNodeOrder("Visiting all nodes", visitor.getVisited(), 6, 5, 4, 3, 2); // And visiting with blacklist visitor.reset(); linear.visitAll(heads, Collections.singleton(intermediateNode), visitor); - assertNodeOrder("Visiting all nodes with blacklist", visitor.getVisited(), 6, 5); + FlowTestUtils.assertNodeOrder("Visiting all nodes with blacklist", visitor.getVisited(), 6, 5); // Tests for edge cases of the various basic APIs linear.myNext = null; @@ -238,12 +235,12 @@ public void testSimpleScan() throws Exception { for (AbstractFlowScanner scan : scans) { System.out.println("Iteration test with scanner: " + scan.getClass()); scan.setup(heads, null); - assertNodeOrder("Testing full scan for scanner " + scan.getClass(), scan, 6, 5, 4, 3, 2); + FlowTestUtils.assertNodeOrder("Testing full scan for scanner " + scan.getClass(), scan, 6, 5, 4, 3, 2); Assert.assertFalse(scan.hasNext()); // Blacklist tests scan.setup(heads, Collections.singleton(exec.getNode("4"))); - assertNodeOrder("Testing full scan for scanner " + scan.getClass(), scan, 6, 5); + FlowTestUtils.assertNodeOrder("Testing full scan for scanner " + scan.getClass(), scan, 6, 5); FlowNode f = scan.findFirstMatch(heads, Collections.singleton(exec.getNode("6")), Predicates.alwaysTrue()); Assert.assertNull(f); } @@ -282,9 +279,9 @@ public void testBasicScanWithBlock() throws Exception { // Linear analysis LinearScanner linearScanner = new LinearScanner(); linearScanner.setup(heads); - assertNodeOrder("Linear scan with block", linearScanner, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2); + FlowTestUtils.assertNodeOrder("Linear scan with block", linearScanner, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2); linearScanner.setup(exec.getNode("7")); - assertNodeOrder("Linear scan with block from middle ", linearScanner, 7, 6, 5, 4, 3, 2); + FlowTestUtils.assertNodeOrder("Linear scan with block from middle ", linearScanner, 7, 6, 5, 4, 3, 2); LinearBlockHoppingScanner linearBlockHoppingScanner = new LinearBlockHoppingScanner(); @@ -297,9 +294,9 @@ public void testBasicScanWithBlock() throws Exception { linearBlockHoppingScanner.setup(heads); Assert.assertFalse(linearBlockHoppingScanner.hasNext()); linearBlockHoppingScanner.setup(exec.getNode("8")); - assertNodeOrder("Hopping over one block", linearBlockHoppingScanner, 4, 3, 2); + FlowTestUtils.assertNodeOrder("Hopping over one block", linearBlockHoppingScanner, 4, 3, 2); linearBlockHoppingScanner.setup(exec.getNode("7")); - assertNodeOrder("Hopping over one block", linearBlockHoppingScanner, 7, 6, 5, 4, 3, 2); + FlowTestUtils.assertNodeOrder("Hopping over one block", linearBlockHoppingScanner, 7, 6, 5, 4, 3, 2); // Test the black list in combination with hopping linearBlockHoppingScanner.setup(exec.getNode("8"), Collections.singleton(exec.getNode("5"))); @@ -349,9 +346,9 @@ public void testParallelScan() throws Exception { AbstractFlowScanner scanner = new LinearScanner(); scanner.setup(heads); - assertNodeOrder("Linear", scanner, 15, 14, 13, 9, 8, 6, 4, 3, 2); + FlowTestUtils.assertNodeOrder("Linear", scanner, 15, 14, 13, 9, 8, 6, 4, 3, 2); scanner.setup(heads, Collections.singleton(exec.getNode("9"))); - assertNodeOrder("Linear", scanner, 15, 14, 13, 12, 11, 10, 7, 4, 3, 2); + FlowTestUtils.assertNodeOrder("Linear", scanner, 15, 14, 13, 12, 11, 10, 7, 4, 3, 2); // Depth first scanner and with blacklist @@ -359,33 +356,33 @@ public void testParallelScan() throws Exception { scanner.setup(heads); // Compatibility test for ordering - assertNodeOrder("FlowGraphWalker", new FlowGraphWalker(exec), 15, 14, 13, + FlowTestUtils.assertNodeOrder("FlowGraphWalker", new FlowGraphWalker(exec), 15, 14, 13, 9, 8, 6, // Branch 1 4, 3, 2, // Before parallel 12, 11, 10, 7); // Branch 2 - assertNodeOrder("Depth first", new FlowGraphWalker(exec), 15, 14, 13, + FlowTestUtils.assertNodeOrder("Depth first", new FlowGraphWalker(exec), 15, 14, 13, 9, 8, 6, // Branch 1 4, 3, 2, // Before parallel 12, 11, 10, 7); // Branch 2 scanner.setup(heads, Collections.singleton(exec.getNode("9"))); - assertNodeOrder("Linear", scanner, 15, 14, 13, 12, 11, 10, 7, 4, 3, 2); + FlowTestUtils.assertNodeOrder("Linear", scanner, 15, 14, 13, 12, 11, 10, 7, 4, 3, 2); scanner.setup(Arrays.asList(exec.getNode("9"), exec.getNode("12"))); - assertNodeOrder("Depth-first scanner from inside parallels", scanner, 9, 8, 6, 4, 3, 2, 12, 11, 10, 7); + FlowTestUtils.assertNodeOrder("Depth-first scanner from inside parallels", scanner, 9, 8, 6, 4, 3, 2, 12, 11, 10, 7); // We're going to test the ForkScanner in more depth since this is its natural use scanner = new ForkScanner(); scanner.setup(heads); - assertNodeOrder("ForkedScanner", scanner, 15, 14, 13, + FlowTestUtils.assertNodeOrder("ForkedScanner", scanner, 15, 14, 13, 12, 11, 10, 7,// One parallel 9, 8, 6, // other parallel 4, 3, 2); // end bit scanner.setup(heads, Collections.singleton(exec.getNode("9"))); - assertNodeOrder("ForkedScanner", scanner, 15, 14, 13, 12, 11, 10, 7, 4, 3, 2); + FlowTestUtils.assertNodeOrder("ForkedScanner", scanner, 15, 14, 13, 12, 11, 10, 7, 4, 3, 2); // Test forkscanner midflow scanner.setup(exec.getNode("14")); - assertNodeOrder("ForkedScanner", scanner, 14, 13, + FlowTestUtils.assertNodeOrder("ForkedScanner", scanner, 14, 13, 12, 11, 10, 7, // Last parallel 9, 8, 6, // First parallel 4, 3, 2); // end bit @@ -393,19 +390,19 @@ public void testParallelScan() throws Exception { // Test forkscanner inside a parallel List startingPoints = Arrays.asList(exec.getNode("9"), exec.getNode("12")); scanner.setup(startingPoints); - assertNodeOrder("ForkedScanner", scanner, 9, 8, 6, 12, 11, 10, 7, 4, 3, 2); + FlowTestUtils.assertNodeOrder("ForkedScanner", scanner, 9, 8, 6, 12, 11, 10, 7, 4, 3, 2); startingPoints = Arrays.asList(exec.getNode("9"), exec.getNode("11")); scanner.setup(startingPoints); - assertNodeOrder("ForkedScanner", scanner, 9, 8, 6, 11, 10, 7, 4, 3, 2); + FlowTestUtils.assertNodeOrder("ForkedScanner", scanner, 9, 8, 6, 11, 10, 7, 4, 3, 2); // Filtering at different points within branches List blackList = Arrays.asList(exec.getNode("6"), exec.getNode("7")); - Assert.assertEquals(4, scanner.filteredNodes(heads, blackList, MATCH_ECHO_STEP).size()); - Assert.assertEquals(4, scanner.filteredNodes(heads, Collections.singletonList(exec.getNode("4")), MATCH_ECHO_STEP).size()); + Assert.assertEquals(4, scanner.filteredNodes(heads, blackList, FlowTestUtils.MATCH_ECHO_STEP).size()); + Assert.assertEquals(4, scanner.filteredNodes(heads, Collections.singletonList(exec.getNode("4")), FlowTestUtils.MATCH_ECHO_STEP).size()); blackList = Arrays.asList(exec.getNode("6"), exec.getNode("10")); - Assert.assertEquals(3, scanner.filteredNodes(heads, blackList, MATCH_ECHO_STEP).size()); + Assert.assertEquals(3, scanner.filteredNodes(heads, blackList, FlowTestUtils.MATCH_ECHO_STEP).size()); } @Test @@ -465,7 +462,7 @@ public void testNestedParallelScan() throws Exception { // Basic test of DepthFirstScanner AbstractFlowScanner scanner = new DepthFirstScanner(); - Collection matches = scanner.filteredNodes(heads, null, MATCH_ECHO_STEP); + Collection matches = scanner.filteredNodes(heads, null, FlowTestUtils.MATCH_ECHO_STEP); Assert.assertEquals(7, matches.size()); scanner.setup(heads); @@ -474,11 +471,11 @@ public void testNestedParallelScan() throws Exception { // We're going to test the ForkScanner in more depth since this is its natural use scanner = new ForkScanner(); - matches = scanner.filteredNodes(heads, null, MATCH_ECHO_STEP); + matches = scanner.filteredNodes(heads, null, FlowTestUtils.MATCH_ECHO_STEP); Assert.assertEquals(7, matches.size()); heads = Arrays.asList(exec.getNode("20"), exec.getNode("17"), exec.getNode("9")); - matches = scanner.filteredNodes(heads, null, MATCH_ECHO_STEP); + matches = scanner.filteredNodes(heads, null, FlowTestUtils.MATCH_ECHO_STEP); Assert.assertEquals(6, matches.size()); // Commented out since temporarily failing } -} \ No newline at end of file +} diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java index 661319e7..fd686523 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java @@ -58,9 +58,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -// Slightly dirty but it removes a ton of FlowTestUtils.* class qualifiers -import static org.jenkinsci.plugins.workflow.graphanalysis.FlowTestUtils.*; - /** * Tests for internals of ForkScanner */ @@ -362,7 +359,7 @@ public void testFlowSegmentSplit() throws Exception { for (FlowNode f : mainBranch.visited) { nodeMap.put(f, mainBranch); } - assertNodeOrder("Visited nodes", mainBranch.visited, 9, 8, 6, 4, 3); + FlowTestUtils.assertNodeOrder("Visited nodes", mainBranch.visited, 9, 8, 6, 4, 3); // Branch 2 sideBranch.add(BRANCH2_END); @@ -372,16 +369,16 @@ public void testFlowSegmentSplit() throws Exception { for (FlowNode f : sideBranch.visited) { nodeMap.put(f, sideBranch); } - assertNodeOrder("Visited nodes", sideBranch.visited, 12, 11, 10, 7); + FlowTestUtils.assertNodeOrder("Visited nodes", sideBranch.visited, 12, 11, 10, 7); ForkScanner.Fork forked = mainBranch.split(nodeMap, (BlockStartNode)exec.getNode("4"), sideBranch); ForkScanner.FlowSegment splitSegment = (ForkScanner.FlowSegment)nodeMap.get(BRANCH1_END); // New branch Assert.assertNull(splitSegment.after); - assertNodeOrder("Branch 1 split after fork", splitSegment.visited, 9, 8, 6); + FlowTestUtils.assertNodeOrder("Branch 1 split after fork", splitSegment.visited, 9, 8, 6); // Just the single node before the fork Assert.assertEquals(forked, mainBranch.after); - assertNodeOrder("Head of flow, pre-fork", mainBranch.visited, 3); + FlowTestUtils.assertNodeOrder("Head of flow, pre-fork", mainBranch.visited, 3); // Fork point Assert.assertEquals(forked, nodeMap.get(START_PARALLEL)); @@ -390,7 +387,7 @@ public void testFlowSegmentSplit() throws Exception { // Branch 2 Assert.assertEquals(sideBranch, nodeMap.get(BRANCH2_END)); - assertNodeOrder("Branch 2", sideBranch.visited, 12, 11, 10, 7); + FlowTestUtils.assertNodeOrder("Branch 2", sideBranch.visited, 12, 11, 10, 7); // Test me where splitting right at a fork point, where we should have a fork with and main branch shoudl become following // Along with side branch (branch2) @@ -410,9 +407,9 @@ public void testFlowSegmentSplit() throws Exception { follows[0] = mainBranch; follows[1] = sideBranch; Assert.assertArrayEquals(follows, forked.following.toArray()); - assertNodeOrder("Branch1", mainBranch.visited, 6); + FlowTestUtils.assertNodeOrder("Branch1", mainBranch.visited, 6); Assert.assertNull(mainBranch.after); - assertNodeOrder("Branch2", sideBranch.visited, 7); + FlowTestUtils.assertNodeOrder("Branch2", sideBranch.visited, 7); Assert.assertNull(sideBranch.after); Assert.assertEquals(forked, nodeMap.get(START_PARALLEL)); Assert.assertEquals(mainBranch, nodeMap.get(exec.getNode("6"))); diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunkTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunkTest.java index 90a7a492..794e12d3 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunkTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunkTest.java @@ -27,8 +27,8 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; public class MemoryFlowChunkTest { diff --git a/src/test/java/org/jenkinsci/plugins/workflow/log/FileLogStorageTest.java b/src/test/java/org/jenkinsci/plugins/workflow/log/FileLogStorageTest.java index ae92a4fe..c54bb0f5 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/log/FileLogStorageTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/log/FileLogStorageTest.java @@ -24,9 +24,10 @@ package org.jenkinsci.plugins.workflow.log; +import static org.junit.Assert.assertTrue; + import hudson.model.TaskListener; import java.io.File; -import static org.junit.Assert.*; import org.junit.Before; import org.junit.Rule; import org.junit.Test; diff --git a/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java b/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java index 5eaae303..d5221237 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java @@ -24,6 +24,10 @@ package org.jenkinsci.plugins.workflow.log; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.junit.Assert.assertEquals; + import hudson.console.AnnotatedLargeText; import hudson.console.HyperlinkNote; import hudson.model.Action; @@ -51,7 +55,6 @@ import org.apache.commons.io.output.NullOutputStream; import org.apache.commons.io.output.NullWriter; import org.apache.commons.io.output.WriterOutputStream; -import static org.hamcrest.Matchers.*; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.flow.FlowExecution; import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner; @@ -59,7 +62,6 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; -import static org.junit.Assert.*; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; diff --git a/src/test/java/org/jenkinsci/plugins/workflow/log/SpanCoalescerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/log/SpanCoalescerTest.java index 48e27676..b000c2a0 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/log/SpanCoalescerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/log/SpanCoalescerTest.java @@ -24,10 +24,11 @@ package org.jenkinsci.plugins.workflow.log; +import static org.junit.Assert.assertEquals; + import hudson.console.AnnotatedLargeText; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.junit.Assert.*; import org.junit.Test; public class SpanCoalescerTest { From 456f163162951a68d81daa16af656027b1384fe7 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 12 Oct 2021 10:10:22 +1000 Subject: [PATCH 043/114] simple change to trigger a build Signed-off-by: Olivier Lamy --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index fe568023..aa06f4c6 100644 --- a/pom.xml +++ b/pom.xml @@ -26,10 +26,10 @@ 4.0.0 - org.jenkins-ci.plugins - plugin - 4.28 - + org.jenkins-ci.plugins + plugin + 4.28 + org.jenkins-ci.plugins.workflow workflow-api From 7a3688b2e804aa2b448a7a6b6ac73c4aeddd1e0a Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 12 Oct 2021 10:33:27 +1000 Subject: [PATCH 044/114] [maven-release-plugin] prepare release workflow-api-2.47 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aa06f4c6..c29fe422 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - ${revision}${changelist} + 2.47 hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - ${scmTag} + workflow-api-2.47 From b922745a12d0a7816c74028cfed232b73b531767 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 12 Oct 2021 10:33:37 +1000 Subject: [PATCH 045/114] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index c29fe422..95a88014 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - 2.47 + ${revision}${changelist} hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -47,7 +47,7 @@ scm:git:git://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - workflow-api-2.47 + ${scmTag} @@ -62,7 +62,7 @@ - 2.47 + 2.48 -SNAPSHOT 2.222.4 8 From f76ced1a7fe90e9f06217c06706f3ed346cc8b02 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 17 Nov 2021 13:24:46 -0800 Subject: [PATCH 046/114] Add Dependabot configuration (#181) --- .github/dependabot.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b3c016d2..fdc58d1e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,8 +1,12 @@ +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + version: 2 updates: - package-ecosystem: "maven" directory: "/" - reviewers: - - "dwnusbaum" schedule: - interval: "daily" + interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From 76702b984030ddaf71b6c1299e84553e39af4750 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 17 Nov 2021 16:30:35 -0800 Subject: [PATCH 047/114] Use container agent in `Jenkinsfile` (#182) --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index a205a3c2..b94810a2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,4 +1,4 @@ -buildPlugin(configurations: [ +buildPlugin(useContainerAgent: true, configurations: [ [ platform: "linux", jdk: "8" ], [ platform: "windows", jdk: "8" ], [ platform: "linux", jdk: "11" ] From ccd8ef3eb25857d05aa4872561e1c8d0dc6de407 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:57:50 +1000 Subject: [PATCH 048/114] Bump bom-2.222.x from 841.vd6e713d848ab to 887.vae9c8ac09ff7 (#162) Bumps [bom-2.222.x](https://github.com/jenkinsci/bom) from 841.vd6e713d848ab to 887.vae9c8ac09ff7. - [Release notes](https://github.com/jenkinsci/bom/releases) - [Commits](https://github.com/jenkinsci/bom/commits) --- updated-dependencies: - dependency-name: io.jenkins.tools.bom:bom-2.222.x dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 95a88014..c1f534de 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.jenkins.tools.bom bom-2.222.x - 841.vd6e713d848ab + 887.vae9c8ac09ff7 import pom From 697de16e3cc37047524cc0f3240381a7a73f899c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Nov 2021 01:03:31 +0000 Subject: [PATCH 049/114] Bump plugin from 4.28 to 4.31 Bumps [plugin](https://github.com/jenkinsci/plugin-pom) from 4.28 to 4.31. - [Release notes](https://github.com/jenkinsci/plugin-pom/releases) - [Changelog](https://github.com/jenkinsci/plugin-pom/blob/master/CHANGELOG.md) - [Commits](https://github.com/jenkinsci/plugin-pom/compare/plugin-4.28...plugin-4.31) --- updated-dependencies: - dependency-name: org.jenkins-ci.plugins:plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1f534de..6cf3ffd8 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.jenkins-ci.plugins plugin - 4.28 + 4.31 org.jenkins-ci.plugins.workflow From c84aa8c4689a727a9e56f36cba99e7a0243fc3fb Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 17 Nov 2021 16:59:51 -0800 Subject: [PATCH 050/114] Reduce usages of Guava (#177) --- .../org/jenkinsci/plugins/workflow/flow/FlowCopier.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowCopier.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowCopier.java index 63717759..96855988 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowCopier.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowCopier.java @@ -24,7 +24,6 @@ package org.jenkinsci.plugins.workflow.flow; -import com.google.common.collect.ImmutableList; import hudson.Extension; import hudson.ExtensionPoint; import hudson.model.Action; @@ -33,6 +32,8 @@ import hudson.model.Run; import hudson.model.TaskListener; import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; import jenkins.scm.api.SCMRevisionAction; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -75,10 +76,10 @@ public static abstract class ByRun extends FlowCopier { @Extension public static class StandardActions extends FlowCopier.ByRun { // TODO cloned from ReplayAction; consider whether it is appropriate to share these (related but not identical case) - private static final Iterable> COPIED_ACTIONS = ImmutableList.of( + private static final Iterable> COPIED_ACTIONS = Collections.unmodifiableList(Arrays.asList( ParametersAction.class, SCMRevisionAction.class - ); + )); @Override public void copy(Run original, Run copy, TaskListener listener) throws IOException, InterruptedException { for (Class type : COPIED_ACTIONS) { From 37c8c493e3142c9abaaae9598938d9ea56227e71 Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Fri, 19 Nov 2021 17:42:42 -0500 Subject: [PATCH 051/114] [JENKINS-67164] Call StepExecution.onResume directly from WorkflowRun.onLoad rather than via FlowExecutionList.ItemListenerImpl to ensure step resumption (#178) * Call StepExecution.onResume directly from WorkflowRun.onLoad rather than via FlowExecutionList to ensure resumption * Add FlowExecutionList$ItemListenerImpl back in * Register Pipelines in FlowExecutionList when they resume if they are not already present * Remove unnecessary use of Guice so import can be dropped * Check FlowExecution.isComplete ResumeStepExecutionListener before doing anything * Use SpotBugs nullability annotations * [JENKINS-67164] Add issue reference to FlowExecutionListTest.resumeStepExecutions --- .../workflow/flow/FlowExecutionList.java | 84 ++++++++---- .../workflow/flow/FlowExecutionListTest.java | 129 ++++++++++++++++++ 2 files changed, 184 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 39d91a1c..76382bc2 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -5,7 +5,8 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.inject.Inject; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.ExtensionList; import hudson.XmlFile; @@ -29,7 +30,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; @@ -169,31 +169,11 @@ public static FlowExecutionList get() { */ @Extension public static class ItemListenerImpl extends ItemListener { - @Inject - FlowExecutionList list; - @Override public void onLoaded() { - for (final FlowExecution e : list) { - LOGGER.log(Level.FINE, "Eager loading {0}", e); - Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { - @Override - public void onSuccess(List result) { - LOGGER.log(Level.FINE, "Will resume {0}", result); - for (StepExecution se : result) { - se.onResume(); - } - } - - @Override - public void onFailure(Throwable t) { - if (t instanceof CancellationException) { - LOGGER.log(Level.FINE, "Cancelled load of " + e, t); - } else { - LOGGER.log(Level.WARNING, "Failed to load " + e, t); - } - } - }, MoreExecutors.directExecutor()); + for (final FlowExecution e : FlowExecutionList.get()) { + // The call to FlowExecutionOwner.get in the implementation of iterator() is sufficent to load the Pipeline. + LOGGER.log(Level.FINE, "Eagerly loaded {0}", e); } } } @@ -203,14 +183,11 @@ public void onFailure(Throwable t) { */ @Extension public static class StepExecutionIteratorImpl extends StepExecutionIterator { - @Inject - FlowExecutionList list; - @Override public ListenableFuture apply(final Function f) { List> all = new ArrayList<>(); - for (FlowExecution e : list) { + for (FlowExecution e : FlowExecutionList.get()) { ListenableFuture> execs = e.getCurrentExecutions(false); all.add(execs); Futures.addCallback(execs,new FutureCallback>() { @@ -252,4 +229,53 @@ public void onFailure(Throwable t) { executor.shutdown(); executor.awaitTermination(1, TimeUnit.MINUTES); } + + /** + * Whenever a Pipeline resumes, resume all incomplete steps in its {@link FlowExecution}. + * + * Called by {@code WorkflowRun.onLoad}, so guaranteed to run if a Pipeline resumes regardless of its presence in + * {@link FlowExecutionList}. + */ + @Extension + public static class ResumeStepExecutionListener extends FlowExecutionListener { + @Override + public void onResumed(@NonNull FlowExecution e) { + Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { + @Override + public void onSuccess(List result) { + if (e.isComplete()) { + // WorkflowRun.onLoad will not fire onResumed if the serialized execution was already + // complete when loaded, but right now (at least for workflow-cps), the execution resumes + // asynchronously before WorkflowRun.onLoad completes, so it is possible that the execution + // finishes before onResumed gets called. + // That said, there is nothing to prevent the execution from completing right after we check + // isComplete. If we want to fully prevent that, we would need to delay actual execution + // resumption until WorkflowRun.onLoad completes or add some form of synchronization. + return; + } + FlowExecutionList list = FlowExecutionList.get(); + FlowExecutionOwner owner = e.getOwner(); + if (!list.runningTasks.contains(owner)) { + LOGGER.log(Level.WARNING, "Resuming {0}, which is missing from FlowExecutionList ({1}), so registering it now.", + new Object[] {owner, list.runningTasks.getView()}); + list.register(owner); + } + LOGGER.log(Level.FINE, "Will resume {0}", result); + for (StepExecution se : result) { + se.onResume(); + } + } + + @Override + public void onFailure(Throwable t) { + if (t instanceof CancellationException) { + LOGGER.log(Level.FINE, "Cancelled load of " + e, t); + } else { + LOGGER.log(Level.WARNING, "Failed to load " + e, t); + } + } + + }, MoreExecutors.directExecutor()); // TODO: Unclear if we need to run this asynchronously or if StepExecution.onResume has any particular thread requirements. + } + } } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java index 733a3859..2ef4533e 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java @@ -24,17 +24,35 @@ package org.jenkinsci.plugins.workflow.flow; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertNotNull; +import hudson.AbortException; import hudson.model.ParametersAction; import hudson.model.ParametersDefinitionProperty; +import hudson.model.Result; import hudson.model.StringParameterDefinition; import hudson.model.StringParameterValue; +import hudson.model.TaskListener; import hudson.model.queue.QueueTaskFuture; +import java.io.Serializable; +import java.time.Duration; +import java.time.Instant; +import java.util.Collections; +import java.util.Set; +import java.util.function.Supplier; import java.util.logging.Level; +import org.hamcrest.Matcher; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; +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.jenkinsci.plugins.workflow.test.steps.SemaphoreStep; import org.junit.ClassRule; import org.junit.Test; import org.junit.Rule; @@ -42,6 +60,8 @@ import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.JenkinsSessionRule; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.DataBoundConstructor; public class FlowExecutionListTest { @@ -79,4 +99,113 @@ public class FlowExecutionListTest { }); } + @Test public void forceLoadRunningExecutionsAfterRestart() throws Throwable { + logging.capture(50); + sessions.then(r -> { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition("semaphore('wait')", true)); + WorkflowRun b = p.scheduleBuild2(0).waitForStart(); + SemaphoreStep.waitForStart("wait/1", b); + }); + sessions.then(r -> { + /* + Make sure that the build gets loaded automatically by FlowExecutionList$ItemListenerImpl before we load it explictly. + Expected call stack for resuming a Pipelines and its steps: + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ResumeStepExecutionListener$1.onSuccess(FlowExecutionList.java:250) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ResumeStepExecutionListener$1.onSuccess(FlowExecutionList.java:247) + at com.google.common.util.concurrent.Futures$6.run(Futures.java:975) + at org.jenkinsci.plugins.workflow.flow.DirectExecutor.execute(DirectExecutor.java:33) + ... Guava Futures API internals ... + at com.google.common.util.concurrent.Futures.addCallback(Futures.java:985) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ResumeStepExecutionListener.onResumed(FlowExecutionList.java:247) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionListener.fireResumed(FlowExecutionListener.java:84) + at org.jenkinsci.plugins.workflow.job.WorkflowRun.onLoad(WorkflowRun.java:528) + at hudson.model.RunMap.retrieve(RunMap.java:225) + ... RunMap internals ... + at hudson.model.RunMap.getById(RunMap.java:205) + at org.jenkinsci.plugins.workflow.job.WorkflowRun$Owner.run(WorkflowRun.java:937) + at org.jenkinsci.plugins.workflow.job.WorkflowRun$Owner.get(WorkflowRun.java:948) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$1.computeNext(FlowExecutionList.java:65) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$1.computeNext(FlowExecutionList.java:57) + at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:143) + at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:138) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ItemListenerImpl.onLoaded(FlowExecutionList.java:175) + at jenkins.model.Jenkins.(Jenkins.java:1019) + */ + waitFor(logging::getMessages, hasItem(containsString("Will resume [org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep"))); + WorkflowJob p = r.jenkins.getItemByFullName("p", WorkflowJob.class); + SemaphoreStep.success("wait/1", null); + WorkflowRun b = p.getBuildByNumber(1); + r.waitForCompletion(b); + r.assertBuildStatus(Result.SUCCESS, b); + }); + } + + @Issue("JENKINS-67164") + @Test public void resumeStepExecutions() throws Throwable { + sessions.then(r -> { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition("noResume()", true)); + WorkflowRun b = p.scheduleBuild2(0).waitForStart(); + r.waitForMessage("Starting non-resumable step", b); + // TODO: Unclear how this might happen in practice. + FlowExecutionList.get().unregister(b.asFlowExecutionOwner()); + }); + sessions.then(r -> { + WorkflowJob p = r.jenkins.getItemByFullName("p", WorkflowJob.class); + WorkflowRun b = p.getBuildByNumber(1); + r.waitForCompletion(b); + r.assertBuildStatus(Result.FAILURE, b); + r.assertLogContains("Unable to resume NonResumableStep", b); + }); + } + + public static class NonResumableStep extends Step implements Serializable { + public static final long serialVersionUID = 1L; + @DataBoundConstructor + public NonResumableStep() { } + @Override + public StepExecution start(StepContext sc) throws Exception { + return new ExecutionImpl(sc); + } + + private static class ExecutionImpl extends StepExecution implements Serializable { + public static final long serialVersionUID = 1L; + private ExecutionImpl(StepContext sc) { + super(sc); + } + @Override + public boolean start() throws Exception { + getContext().get(TaskListener.class).getLogger().println("Starting non-resumable step"); + return false; + } + @Override + public void onResume() { + getContext().onFailure(new AbortException("Unable to resume NonResumableStep")); + } + } + + @TestExtension public static class DescriptorImpl extends StepDescriptor { + @Override + public Set> getRequiredContext() { + return Collections.singleton(TaskListener.class); + } + @Override + public String getFunctionName() { + return "noResume"; + } + } + } + + /** + * Wait up to 5 seconds for the given supplier to return a matching value. + */ + private static void waitFor(Supplier valueSupplier, Matcher matcher) throws InterruptedException { + Instant end = Instant.now().plus(Duration.ofSeconds(5)); + while (!matcher.matches(valueSupplier.get()) && Instant.now().isBefore(end)) { + Thread.sleep(100L); + } + assertThat("Matcher should have matched after 5s", valueSupplier.get(), matcher); + } + } From 84c8de391ccb6e2ad74983a82318979e24fbe215 Mon Sep 17 00:00:00 2001 From: offa Date: Wed, 8 Dec 2021 08:07:31 +0100 Subject: [PATCH 052/114] Fix typos --- docs/flowgraph.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/flowgraph.md b/docs/flowgraph.md index 3b429dc4..3c2e6550 100644 --- a/docs/flowgraph.md +++ b/docs/flowgraph.md @@ -2,7 +2,7 @@ The "Flow Graph" is how Pipeline stores structural information about Pipelines that have run or *are* running -- this is used for visualization and runtime activities. You won't find any class with this name but we refer to it like this because it's the best terminology we have. "Flow" because originally the plugin was Workflow, and describing it as a flow reminds us that execution is a directional process with a start and end, "graph" because the data is structured as a directed acyclic graph to allow for parallel execution. -This stucture describes all possible Pipelines, whether a simple set of steps, or a complex set of nested parallels, and is designed to grow at runtime as the Pipeline executes. This also means that for a running Pipeline it will be in an incomplete state, with un-terminated blocks. +This structure describes all possible Pipelines, whether a simple set of steps, or a complex set of nested parallels, and is designed to grow at runtime as the Pipeline executes. This also means that for a running Pipeline it will be in an incomplete state, with un-terminated blocks. # Concepts @@ -12,8 +12,8 @@ This may seem backwards, but it enables us to freely append to the Flow Graph as **Key information is stored in a couple ways:** -* The specific subclass of the `FlowNode` used for a particular node is structurally imporant - for example `BlockStartNode` represents the start of a block -* `StepDescriptor`: most `FlowNode`s come from running `Steps` and implement `StepNode`. This means they you can get the `StepDescriptor` to determine their `Step` that produced them. +* The specific subclass of the `FlowNode` used for a particular node is structurally important - for example `BlockStartNode` represents the start of a block +* `StepDescriptor`: most `FlowNode`s come from running `Steps` and implement `StepNode`. This means they can get the `StepDescriptor` to determine their `Step` that produced them. * Parent relationships allow us to so split into parallel branches and track them independently. We may have multiple nodes with a given parent (at the start of a parallel) or one node with multiple parents (the end of a parallel) * `Action`s give us all the key attributes of the `FlowNode`, see the section below for a quick reference From 3de5e2efac972fd4bc7c76f786de0945e5819bc8 Mon Sep 17 00:00:00 2001 From: Carroll Chiou Date: Wed, 8 Dec 2021 09:53:51 -0700 Subject: [PATCH 053/114] enable cd (#183) --- .github/release-drafter.yml | 2 - .github/workflows/cd.yaml | 60 +++++++++++++++++++++++++++ .github/workflows/release-drafter.yml | 16 ------- .mvn/maven.config | 1 + pom.xml | 5 +-- 5 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/cd.yaml delete mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index b6111be0..0d0b1c99 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,3 +1 @@ _extends: .github -name-template: v$NEXT_MINOR_VERSION 🌈 -tag-template: workflow-api-$NEXT_MINOR_VERSION diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml new file mode 100644 index 00000000..6a19fd9b --- /dev/null +++ b/.github/workflows/cd.yaml @@ -0,0 +1,60 @@ +# Note: additional setup is required, see https://www.jenkins.io/redirect/continuous-delivery-of-plugins + +name: cd +on: + workflow_dispatch: + check_run: + types: + - completed + +jobs: + validate: + runs-on: ubuntu-latest + outputs: + should_release: ${{ steps.verify-ci-status.outputs.result == 'success' && steps.interesting-categories.outputs.interesting == 'true' }} + steps: + - name: Verify CI status + uses: jenkins-infra/verify-ci-status-action@v1.2.0 + id: verify-ci-status + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + output_result: true + + - name: Release Drafter + uses: release-drafter/release-drafter@v5 + if: steps.verify-ci-status.outputs.result == 'success' + with: + name: next + tag: next + version: next + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check interesting categories + uses: jenkins-infra/interesting-category-action@v1.0.0 + id: interesting-categories + if: steps.verify-ci-status.outputs.result == 'success' + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + release: + runs-on: ubuntu-latest + needs: [validate] + if: needs.validate.outputs.should_release == 'true' + steps: + - name: Check out + uses: actions/checkout@v2.3.4 + with: + fetch-depth: 0 + - name: Set up JDK 8 + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: 8 + - name: Release + uses: jenkins-infra/jenkins-maven-cd-action@v1.2.0 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} + diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index 832442dd..00000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,16 +0,0 @@ -# Automates creation of Release Drafts using Release Drafter -# More Info: https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc - -on: - push: - branches: - - master - -jobs: - update_release_draft: - runs-on: ubuntu-latest - steps: - # Drafts your next Release notes as Pull Requests are merged into "master" - - uses: release-drafter/release-drafter@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.mvn/maven.config b/.mvn/maven.config index 2a0299c4..f7daf60d 100644 --- a/.mvn/maven.config +++ b/.mvn/maven.config @@ -1,2 +1,3 @@ -Pconsume-incrementals -Pmight-produce-incrementals +-Dchangelist.format=%d.v%s diff --git a/pom.xml b/pom.xml index 6cf3ffd8..c105a7b7 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.jenkins-ci.plugins.workflow workflow-api - ${revision}${changelist} + ${changelist} hpi Pipeline: API https://github.com/jenkinsci/${project.artifactId}-plugin @@ -62,8 +62,7 @@ - 2.48 - -SNAPSHOT + 999999-SNAPSHOT 2.222.4 8 false From 6a04bac22f47f38d19de8ce9523cf86b5744ed06 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 16 Dec 2021 00:30:41 -0500 Subject: [PATCH 054/114] [JENKINS-40161] Handle exceptions from `StepExecution.onResume` (#187) --- .../jenkinsci/plugins/workflow/flow/FlowExecutionList.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 76382bc2..67dc859a 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -262,7 +262,11 @@ public void onSuccess(List result) { } LOGGER.log(Level.FINE, "Will resume {0}", result); for (StepExecution se : result) { - se.onResume(); + try { + se.onResume(); + } catch (Throwable x) { + se.getContext().onFailure(x); + } } } From 6d6de2006ec5ec7d8e7dbf2b612ec9e5a887274c Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Thu, 16 Dec 2021 19:43:18 -0500 Subject: [PATCH 055/114] [JENKINS-67351] Avoid deadlock when resuming Pipelines in some cases (#188) * [JENKINS-67531] Avoid deadlock when resuming Pipelines in some cases * [JENKINS-67531] Switch to an independent ExecutorService as a precaution against other types of deadlock as in JENKINS-25890 * [JENKINS-67351] Add new FlowExecutionList.isResumptionComplete method to help consumers avoid resuming Pipelines inadvertently * [JENKINS-67351] Mark FlowExecutionList.isResumptionComplete as a beta API * Revert "[JENKINS-67531] Switch to an independent ExecutorService as a precaution against other types of deadlock as in JENKINS-25890" This reverts commit c178493dfc4de1c70f8d6807031288a4f21cdef5. --- .../workflow/flow/FlowExecutionList.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 67dc859a..034f7006 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -10,6 +10,7 @@ import hudson.Extension; import hudson.ExtensionList; import hudson.XmlFile; +import hudson.init.InitMilestone; import hudson.init.Terminator; import hudson.model.listeners.ItemListener; import hudson.remoting.SingleLaneExecutorService; @@ -31,6 +32,7 @@ import java.util.logging.Logger; import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.Beta; import org.kohsuke.accmod.restrictions.DoNotUse; /** @@ -44,6 +46,8 @@ public class FlowExecutionList implements Iterable { private final SingleLaneExecutorService executor = new SingleLaneExecutorService(Timer.get()); private XmlFile configFile; + private transient volatile boolean resumptionComplete; + public FlowExecutionList() { load(); } @@ -163,6 +167,18 @@ public static FlowExecutionList get() { return l; } + /** + * Returns true if all executions that were present in this {@link FlowExecutionList} have been loaded and resumed. + * + * This takes place slightly after {@link InitMilestone#COMPLETED} is reached during Jenkins startup. + * + * Useful to avoid resuming Pipelines in contexts that may lead to deadlock. + */ + @Restricted(Beta.class) + public boolean isResumptionComplete() { + return resumptionComplete; + } + /** * When Jenkins starts up and everything is loaded, be sure to proactively resurrect * all the ongoing {@link FlowExecution}s so that they start running again. @@ -171,10 +187,12 @@ public static FlowExecutionList get() { public static class ItemListenerImpl extends ItemListener { @Override public void onLoaded() { - for (final FlowExecution e : FlowExecutionList.get()) { + FlowExecutionList list = FlowExecutionList.get(); + for (final FlowExecution e : list) { // The call to FlowExecutionOwner.get in the implementation of iterator() is sufficent to load the Pipeline. LOGGER.log(Level.FINE, "Eagerly loaded {0}", e); } + list.resumptionComplete = true; } } @@ -279,7 +297,7 @@ public void onFailure(Throwable t) { } } - }, MoreExecutors.directExecutor()); // TODO: Unclear if we need to run this asynchronously or if StepExecution.onResume has any particular thread requirements. + }, Timer.get()); // We always hold RunMap and WorkflowRun locks here, so we resume steps on a different thread to avoid potential deadlocks. See JENKINS-67351. } } } From 57edf648f5d4d6826b4f810a26b46995efd4f53b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Dec 2021 01:31:08 +0000 Subject: [PATCH 056/114] Bump actions/checkout from 2.3.4 to 2.4.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 2.3.4 to 2.4.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2.3.4...v2.4.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/cd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 6a19fd9b..025fac17 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -43,7 +43,7 @@ jobs: if: needs.validate.outputs.should_release == 'true' steps: - name: Check out - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v2.4.0 with: fetch-depth: 0 - name: Set up JDK 8 From 55655b434708b7ed04edcb043210132a5c0806a6 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 31 Dec 2021 09:56:19 -0800 Subject: [PATCH 057/114] EOL JSR 305 --- .../plugins/workflow/FilePathUtils.java | 12 ++--- .../workflow/actions/ArgumentsAction.java | 30 ++++++------ .../plugins/workflow/actions/ErrorAction.java | 10 ++-- .../workflow/actions/QueueItemAction.java | 10 ++-- .../plugins/workflow/actions/TagsAction.java | 12 ++--- .../workflow/actions/ThreadNameAction.java | 4 +- .../workflow/actions/WarningAction.java | 10 ++-- .../workflow/actions/WorkspaceAction.java | 10 ++-- .../workflow/flow/DurabilityHintProvider.java | 8 ++-- .../workflow/flow/FlowDurabilityHint.java | 4 +- .../plugins/workflow/flow/FlowExecution.java | 22 ++++----- .../workflow/flow/FlowExecutionListener.java | 18 +++---- .../workflow/flow/FlowExecutionOwner.java | 8 ++-- .../GlobalDefaultFlowDurabilityLevel.java | 6 +-- .../plugins/workflow/flow/StashManager.java | 48 +++++++++---------- .../plugins/workflow/flow/StepListener.java | 4 +- .../plugins/workflow/graph/BlockEndNode.java | 4 +- .../workflow/graph/BlockStartNode.java | 2 +- .../plugins/workflow/graph/FlowNode.java | 22 ++++----- .../workflow/graph/GraphLookupView.java | 18 +++---- .../graph/StandardGraphLookupView.java | 22 ++++----- .../plugins/workflow/graph/StepNode.java | 2 +- .../graphanalysis/AbstractFlowScanner.java | 44 ++++++++--------- .../graphanalysis/BlockChunkFinder.java | 8 ++-- .../workflow/graphanalysis/ChunkFinder.java | 8 ++-- .../graphanalysis/DepthFirstScanner.java | 6 +-- .../workflow/graphanalysis/Filterator.java | 6 +-- .../graphanalysis/FilteratorImpl.java | 4 +- .../workflow/graphanalysis/FlowChunk.java | 6 +-- .../graphanalysis/FlowChunkWithContext.java | 2 +- .../graphanalysis/FlowNodeVisitor.java | 4 +- .../graphanalysis/FlowScanningUtils.java | 12 ++--- .../workflow/graphanalysis/ForkScanner.java | 36 +++++++------- .../graphanalysis/LabelledChunkFinder.java | 8 ++-- .../LinearBlockHoppingScanner.java | 10 ++-- .../workflow/graphanalysis/LinearScanner.java | 6 +-- .../graphanalysis/MemoryFlowChunk.java | 6 +-- .../graphanalysis/NodeStepNamePredicate.java | 6 +-- .../graphanalysis/NodeStepTypePredicate.java | 8 ++-- .../graphanalysis/ParallelFlowChunk.java | 8 ++-- .../ParallelMemoryFlowChunk.java | 12 ++--- .../graphanalysis/SimpleChunkVisitor.java | 18 +++---- .../graphanalysis/StandardChunkVisitor.java | 22 ++++----- .../plugins/workflow/log/LogStorage.java | 18 +++---- .../workflow/log/LogStorageFactory.java | 6 +-- .../workflow/log/TaskListenerDecorator.java | 26 +++++----- .../workflow/pickles/PickleFactory.java | 6 +-- .../table/FlowNodeViewColumnDescriptor.java | 2 +- .../plugins/workflow/ArtifactManagerTest.java | 16 +++---- .../workflow/graphanalysis/FlowTestUtils.java | 6 +-- .../graphanalysis/NoOpChunkFinder.java | 8 ++-- .../workflow/graphanalysis/TestVisitor.java | 18 +++---- 52 files changed, 316 insertions(+), 316 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java b/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java index 72326695..60586206 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java @@ -39,8 +39,8 @@ import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -65,7 +65,7 @@ public class FilePathUtils { * @param f a file, possibly remote * @return a node name ({@code ""} for the controller), if known, else null */ - public static @CheckForNull String getNodeNameOrNull(@Nonnull FilePath f) { + public static @CheckForNull String getNodeNameOrNull(@NonNull FilePath f) { return Listener.getChannelName(f.getChannel()); } @@ -75,7 +75,7 @@ public class FilePathUtils { * @return a node name ({@code ""} for the controller), if known * @throws IllegalStateException if the association to a node is unknown */ - public static @Nonnull String getNodeName(@Nonnull FilePath f) throws IllegalStateException { + public static @NonNull String getNodeName(@NonNull FilePath f) throws IllegalStateException { String name = getNodeNameOrNull(f); if (name != null) { return name; @@ -90,7 +90,7 @@ public class FilePathUtils { * @param path a path as returned by {@link FilePath#getRemote} * @return a corresponding file handle, if a node with that name is online, else null */ - public static @CheckForNull FilePath find(@Nonnull String node, @Nonnull String path) { + public static @CheckForNull FilePath find(@NonNull String node, @NonNull String path) { Jenkins j = Jenkins.getInstanceOrNull(); if (j == null) { return null; @@ -113,7 +113,7 @@ private FilePathUtils() {} private static final Map channelNames = Collections.synchronizedMap(new WeakHashMap<>()); - static String getChannelName(@Nonnull VirtualChannel channel) { + static String getChannelName(@NonNull VirtualChannel channel) { String channelName = channelNames.get(channel); if (channelName == null) { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java index ad95ddc2..5dbc62aa 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java @@ -36,8 +36,8 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.apache.commons.collections.CollectionUtils; import org.jenkinsci.plugins.structs.describable.DescribableModel; import org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable; @@ -131,7 +131,7 @@ public String getUrlName() { * supplied in the executed pipeline step if that value is filtered for size or security. * @return The arguments for the {@link Step} as with {@link StepDescriptor#defineArguments(Step)} */ - @Nonnull + @NonNull public Map getArguments() { Map args = getArgumentsInternal(); if (args.isEmpty()) { @@ -147,8 +147,8 @@ public Map getArguments() { * * @param n FlowNode to fetch Step arguments for (including placeholders for masked values). */ - @Nonnull - public static Map getArguments(@Nonnull FlowNode n) { + @NonNull + public static Map getArguments(@NonNull FlowNode n) { ArgumentsAction aa = n.getPersistentAction(ArgumentsAction.class); return aa != null ? aa.getArguments() : Collections.emptyMap(); } @@ -158,7 +158,7 @@ public static Map getArguments(@Nonnull FlowNode n) { * This means the arguments with all {@link NotStoredReason} or null values removed * @return Map of all completely stored arguments */ - @Nonnull + @NonNull public Map getFilteredArguments() { Map internalArgs = this.getArgumentsInternal(); if (internalArgs.size() == 0) { @@ -180,8 +180,8 @@ public Map getFilteredArguments() { * @param n FlowNode to get arguments for * @return Map of all completely stored arguments */ - @Nonnull - public static Map getFilteredArguments(@Nonnull FlowNode n) { + @NonNull + public static Map getFilteredArguments(@NonNull FlowNode n) { ArgumentsAction act = n.getPersistentAction(ArgumentsAction.class); return act != null ? act.getFilteredArguments() : Collections.emptyMap(); } @@ -190,7 +190,7 @@ public static Map getFilteredArguments(@Nonnull FlowNode n) { * See {@link StepDescriptor#argumentsToString(Map)} for the rules */ @CheckForNull - public static String getStepArgumentsAsString(@Nonnull FlowNode n) { + public static String getStepArgumentsAsString(@NonNull FlowNode n) { if (n instanceof StepNode) { StepDescriptor descriptor = ((StepNode) n).getDescriptor(); if (descriptor != null) { // Null if plugin providing descriptor was uninstalled @@ -205,7 +205,7 @@ public static String getStepArgumentsAsString(@Nonnull FlowNode n) { * Return a fast view of internal arguments, without creating immutable wrappers * @return Internal arguments */ - @Nonnull + @NonNull protected abstract Map getArgumentsInternal(); /** @@ -215,7 +215,7 @@ public static String getStepArgumentsAsString(@Nonnull FlowNode n) { * @return Argument value or null if not present/not stored. */ @CheckForNull - public Object getArgumentValue(@Nonnull String argumentName) { + public Object getArgumentValue(@NonNull String argumentName) { Object val = getArgumentValueOrReason(argumentName); return (val instanceof NotStoredReason) ? null : val; } @@ -226,7 +226,7 @@ public Object getArgumentValue(@Nonnull String argumentName) { * @return Argument value, null if nonexistent/null, or NotStoredReason if it existed by was masked out. */ @CheckForNull - public Object getArgumentValueOrReason(@Nonnull String argumentName) { + public Object getArgumentValueOrReason(@NonNull String argumentName) { Object ob = getArgumentsInternal().get(argumentName); if (ob instanceof Map) { return Collections.unmodifiableMap((Map)ob); @@ -246,7 +246,7 @@ public Object getArgumentValueOrReason(@Nonnull String argumentName) { * @param namedArgs Set of argument name and argument value pairs, as from {@link StepDescriptor#defineArguments(Step)} * @return True if no argument has a {@link NotStoredReason} placeholder value, else false */ - static boolean checkArgumentsLackPlaceholders(@Nonnull Map namedArgs) { + static boolean checkArgumentsLackPlaceholders(@NonNull Map namedArgs) { for(Object ob : namedArgs.values()) { if (ob instanceof NotStoredReason) { return false; @@ -279,8 +279,8 @@ public boolean isUnmodifiedArguments() { * You could use {@link UninstantiatedDescribable#getModel} (where available) and {@link DescribableModel#getType} to access live classes. * Where information is missing, this will just return the best it can. */ - @Nonnull - public static Map getResolvedArguments(@Nonnull FlowNode n) { + @NonNull + public static Map getResolvedArguments(@NonNull FlowNode n) { ArgumentsAction aa = n.getPersistentAction(ArgumentsAction.class); if (aa == null) { return Collections.emptyMap(); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java index de42e1a5..4ce7f9cf 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java @@ -28,10 +28,10 @@ import groovy.lang.MissingMethodException; import groovy.lang.MissingPropertyException; import hudson.remoting.ProxyException; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import org.codehaus.groovy.control.MultipleCompilationErrorsException; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import org.apache.commons.io.output.NullOutputStream; @@ -42,9 +42,9 @@ */ public class ErrorAction implements PersistentAction { - private final @Nonnull Throwable error; + private final @NonNull Throwable error; - public ErrorAction(@Nonnull Throwable error) { + public ErrorAction(@NonNull Throwable error) { if (isUnserializableException(error)) { error = new ProxyException(error); } else if (error != null) { @@ -93,7 +93,7 @@ private boolean isUnserializableException(@CheckForNull Throwable error) { return false; } - public @Nonnull Throwable getError() { + public @NonNull Throwable getError() { return error; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/QueueItemAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/QueueItemAction.java index 30d18e75..105aa920 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/QueueItemAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/QueueItemAction.java @@ -4,8 +4,8 @@ import hudson.model.Queue; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Records information for a {@code node} block. @@ -57,8 +57,8 @@ public enum QueueState { * @param node A non-null {@link FlowNode} * @return The current queue state of the flownode. */ - @Nonnull - public static QueueState getNodeState(@Nonnull FlowNode node) { + @NonNull + public static QueueState getNodeState(@NonNull FlowNode node) { WorkspaceAction workspaceAction = node.getPersistentAction(WorkspaceAction.class); if (workspaceAction != null) { return QueueState.LAUNCHED; @@ -88,7 +88,7 @@ public static QueueState getNodeState(@Nonnull FlowNode node) { } @CheckForNull - public static Queue.Item getQueueItem(@Nonnull FlowNode node) { + public static Queue.Item getQueueItem(@NonNull FlowNode node) { QueueItemAction action = node.getPersistentAction(QueueItemAction.class); return action != null ? action.itemInQueue() : null; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/TagsAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/TagsAction.java index ca3054e9..fd38eba1 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/TagsAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/TagsAction.java @@ -26,8 +26,8 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -86,7 +86,7 @@ public String getTagValue(@CheckForNull String tag) { * Get the tag-value mappings * @return Unmodifiable view of tag-value mappings */ - @Nonnull + @NonNull public Map getTags() { return Collections.unmodifiableMap(tags); } @@ -97,8 +97,8 @@ public Map getTags() { * Get the set of tag-value mappings for a node * @return Unmodifiable view of tag-value mappings */ - @Nonnull - public static Map getTags(@Nonnull FlowNode node) { + @NonNull + public static Map getTags(@NonNull FlowNode node) { TagsAction tagAction = node.getAction(TagsAction.class); return (tagAction == null) ? Collections.emptyMap() : tagAction.getTags(); } @@ -110,7 +110,7 @@ public static Map getTags(@Nonnull FlowNode node) { * @return Tag value or null if not set */ @CheckForNull - public static String getTagValue(@Nonnull FlowNode node, @CheckForNull String tag) { + public static String getTagValue(@NonNull FlowNode node, @CheckForNull String tag) { if (tag == null || tag.isEmpty()) { return null; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/ThreadNameAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/ThreadNameAction.java index 4e9b703d..5b7747bf 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/ThreadNameAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/ThreadNameAction.java @@ -23,7 +23,7 @@ */ package org.jenkinsci.plugins.workflow.actions; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Thread name action. @@ -32,6 +32,6 @@ */ public interface ThreadNameAction extends PersistentAction { - @Nonnull + @NonNull String getThreadName(); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/WarningAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/WarningAction.java index 182f0b71..7eeaff1a 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/WarningAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/WarningAction.java @@ -1,8 +1,8 @@ package org.jenkinsci.plugins.workflow.actions; import hudson.model.Result; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.jenkinsci.plugins.workflow.graph.FlowNode; /** @@ -13,10 +13,10 @@ * Visualizations should treat FlowNodes with this action as if the FlowNode's result was {@link #result}. */ public class WarningAction implements PersistentAction { - private @Nonnull Result result; + private @NonNull Result result; private @CheckForNull String message; - public WarningAction(@Nonnull Result result) { + public WarningAction(@NonNull Result result) { this.result = result; } @@ -29,7 +29,7 @@ public WarningAction withMessage(String message) { return message; } - public @Nonnull Result getResult() { + public @NonNull Result getResult() { return result; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/WorkspaceAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/WorkspaceAction.java index ed16f972..86917457 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/WorkspaceAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/WorkspaceAction.java @@ -28,8 +28,8 @@ import hudson.model.Node; import hudson.model.labels.LabelAtom; import java.util.Set; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import org.jenkinsci.plugins.workflow.FilePathUtils; @@ -39,10 +39,10 @@ public abstract class WorkspaceAction implements PersistentAction { /** The {@link Node#getNodeName} of the workspace. */ - public abstract @Nonnull String getNode(); + public abstract @NonNull String getNode(); /** The {@link FilePath#getRemote} of the workspace. */ - public abstract @Nonnull String getPath(); + public abstract @NonNull String getPath(); /** * The {@link Node#getAssignedLabels} of the node owning the workspace. @@ -50,7 +50,7 @@ public abstract class WorkspaceAction implements PersistentAction { * (Could be reconstructed in most cases via {@link Jenkins#getNode} on {@link #getNode}, * but not for an agent which has since been removed, common with clouds.) */ - public abstract @Nonnull Set getLabels(); + public abstract @NonNull Set getLabels(); /** Reconstructs the live workspace, if possible. */ public final @CheckForNull FilePath getWorkspace() { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/DurabilityHintProvider.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/DurabilityHintProvider.java index 4eb5a440..0494c919 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/DurabilityHintProvider.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/DurabilityHintProvider.java @@ -4,8 +4,8 @@ import hudson.ExtensionPoint; import hudson.model.Item; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Provides a way to indirectly register durability settings to apply to pipelines. @@ -16,9 +16,9 @@ public interface DurabilityHintProvider extends ExtensionPoint { int ordinal(); @CheckForNull - FlowDurabilityHint suggestFor(@Nonnull Item x); + FlowDurabilityHint suggestFor(@NonNull Item x); - static @Nonnull FlowDurabilityHint suggestedFor(@Nonnull Item x) { + static @NonNull FlowDurabilityHint suggestedFor(@NonNull Item x) { int ordinal = Integer.MAX_VALUE; FlowDurabilityHint hint = GlobalDefaultFlowDurabilityLevel.getDefaultDurabilityHint(); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDurabilityHint.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDurabilityHint.java index 1a4b8f0a..c1c47f9e 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDurabilityHint.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowDurabilityHint.java @@ -23,7 +23,7 @@ */ package org.jenkinsci.plugins.workflow.flow; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Provides hints about just how hard we should try to protect our workflow from failures of the controller. @@ -47,7 +47,7 @@ public enum FlowDurabilityHint { private final String tooltip; - FlowDurabilityHint (boolean useAtomicWrite, boolean persistWithEveryStep, @Nonnull String description, String tooltip) { + FlowDurabilityHint (boolean useAtomicWrite, boolean persistWithEveryStep, @NonNull String description, String tooltip) { this.atomicWrite = useAtomicWrite; this.persistWithEveryStep = persistWithEveryStep; this.description = description; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java index 0b105686..b1fa3af9 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java @@ -43,8 +43,8 @@ import java.io.IOException; import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import jenkins.model.queue.AsynchronousExecution; import org.acegisecurity.Authentication; @@ -91,7 +91,7 @@ protected synchronized GraphLookupView getInternalGraphLookup() { * Get the durability level we're aiming for, or a default value if none is set (defaults may change as implementation evolve). * @return Durability level we are aiming for with this execution. */ - @Nonnull + @NonNull public FlowDurabilityHint getDurabilityHint() { // MAX_SURVIVABILITY is the behavior of builds before the change was introduced. return (durabilityHint != null) ? durabilityHint : FlowDurabilityHint.MAX_SURVIVABILITY; @@ -250,7 +250,7 @@ public boolean blocksRestart() { * A flow run triggered by a user manually might be associated with the runtime, or it might not. * @return an authentication; {@link ACL#SYSTEM} as a fallback, or {@link Jenkins#ANONYMOUS} if the flow is supposed to be limited to a specific user but that user cannot now be looked up */ - public abstract @Nonnull Authentication getAuthentication(); + public abstract @NonNull Authentication getAuthentication(); /** @see GraphLookupView#isActive(FlowNode) @@ -258,7 +258,7 @@ public boolean blocksRestart() { */ @Override @Restricted(NoExternalUse.class) // Only public because graph, flow, and graphanalysis are separate packages - public boolean isActive(@Nonnull FlowNode node) { + public boolean isActive(@NonNull FlowNode node) { if (!this.equals(node.getExecution())) { throw new IllegalArgumentException("Can't look up info for a FlowNode that doesn't belong to this execution!"); } @@ -271,7 +271,7 @@ public boolean isActive(@Nonnull FlowNode node) { @CheckForNull @Override @Restricted(NoExternalUse.class) // Only public because graph, flow, and graphanalysis are separate packages - public BlockEndNode getEndNode(@Nonnull BlockStartNode startNode) { + public BlockEndNode getEndNode(@NonNull BlockStartNode startNode) { if (!this.equals(startNode.getExecution())) { throw new IllegalArgumentException("Can't look up info for a FlowNode that doesn't belong to this execution!"); } @@ -284,7 +284,7 @@ public BlockEndNode getEndNode(@Nonnull BlockStartNode startNode) { @CheckForNull @Override @Restricted(NoExternalUse.class) // Only public because graph, flow, and graphanalysis are separate packages - public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { + public BlockStartNode findEnclosingBlockStart(@NonNull FlowNode node) { if (!this.equals(node.getExecution())) { throw new IllegalArgumentException("Can't look up info for a FlowNode that doesn't belong to this execution!"); } @@ -294,10 +294,10 @@ public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { /** @see GraphLookupView#findAllEnclosingBlockStarts(FlowNode) * @throws IllegalArgumentException If the input {@link FlowNode} does not belong to this execution */ - @Nonnull + @NonNull @Override @Restricted(NoExternalUse.class) // Only public because graph, flow, and graphanalysis are separate packages - public List findAllEnclosingBlockStarts(@Nonnull FlowNode node) { + public List findAllEnclosingBlockStarts(@NonNull FlowNode node) { if (!this.equals(node.getExecution())) { throw new IllegalArgumentException("Can't look up info for a FlowNode that doesn't belong to this execution!"); } @@ -307,10 +307,10 @@ public List findAllEnclosingBlockStarts(@Nonnull FlowNode node) /** @see GraphLookupView#iterateEnclosingBlocks(FlowNode) * @throws IllegalArgumentException If the input {@link FlowNode} does not belong to this execution */ - @Nonnull + @NonNull @Override @Restricted(NoExternalUse.class) - public Iterable iterateEnclosingBlocks(@Nonnull FlowNode node) { + public Iterable iterateEnclosingBlocks(@NonNull FlowNode node) { if (!this.equals(node.getExecution())) { throw new IllegalArgumentException("Can't look up info for a FlowNode that doesn't belong to this execution!"); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListener.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListener.java index 35afcae0..570cc6f8 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListener.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListener.java @@ -5,7 +5,7 @@ import org.jenkinsci.plugins.workflow.graph.FlowEndNode; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Listens for significant status updates for a {@link FlowExecution}, such as started running or completed. @@ -24,7 +24,7 @@ public abstract class FlowExecutionListener implements ExtensionPoint { * * @param execution The {@link FlowExecution} that has been created. */ - public void onCreated(@Nonnull FlowExecution execution) { + public void onCreated(@NonNull FlowExecution execution) { } /** @@ -34,7 +34,7 @@ public void onCreated(@Nonnull FlowExecution execution) { * * @param execution The {@link FlowExecution} that has started running. */ - public void onRunning(@Nonnull FlowExecution execution) { + public void onRunning(@NonNull FlowExecution execution) { } /** @@ -42,7 +42,7 @@ public void onRunning(@Nonnull FlowExecution execution) { * * @param execution The {@link FlowExecution} that has resumed. */ - public void onResumed(@Nonnull FlowExecution execution) { + public void onResumed(@NonNull FlowExecution execution) { } /** @@ -55,13 +55,13 @@ public void onResumed(@Nonnull FlowExecution execution) { * * @param execution The {@link FlowExecution} that has completed. */ - public void onCompleted(@Nonnull FlowExecution execution) { + public void onCompleted(@NonNull FlowExecution execution) { } /** * Fires the {@link #onCreated(FlowExecution)} event. */ - public static void fireCreated(@Nonnull FlowExecution execution) { + public static void fireCreated(@NonNull FlowExecution execution) { for (FlowExecutionListener listener : ExtensionList.lookup(FlowExecutionListener.class)) { listener.onCreated(execution); } @@ -70,7 +70,7 @@ public static void fireCreated(@Nonnull FlowExecution execution) { /** * Fires the {@link #onRunning(FlowExecution)} event. */ - public static void fireRunning(@Nonnull FlowExecution execution) { + public static void fireRunning(@NonNull FlowExecution execution) { for (FlowExecutionListener listener : ExtensionList.lookup(FlowExecutionListener.class)) { listener.onRunning(execution); } @@ -79,7 +79,7 @@ public static void fireRunning(@Nonnull FlowExecution execution) { /** * Fires the {@link #onResumed(FlowExecution)} event. */ - public static void fireResumed(@Nonnull FlowExecution execution) { + public static void fireResumed(@NonNull FlowExecution execution) { for (FlowExecutionListener listener : ExtensionList.lookup(FlowExecutionListener.class)) { listener.onResumed(execution); } @@ -88,7 +88,7 @@ public static void fireResumed(@Nonnull FlowExecution execution) { /** * Fires the {@link #onCompleted(FlowExecution)} event. */ - public static void fireCompleted(@Nonnull FlowExecution execution) { + public static void fireCompleted(@NonNull FlowExecution execution) { for (FlowExecutionListener listener : ExtensionList.lookup(FlowExecutionListener.class)) { listener.onCompleted(execution); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java index 8d481196..af3d8f84 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java @@ -32,8 +32,8 @@ import java.io.Serializable; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.TransientActionFactory; import org.jenkinsci.plugins.workflow.log.LogStorage; import org.jenkinsci.plugins.workflow.steps.StepContext; @@ -50,7 +50,7 @@ public abstract class FlowExecutionOwner implements Serializable { * @throws IOException * if fails to find {@link FlowExecution}. */ - @Nonnull + @NonNull public abstract FlowExecution get() throws IOException; /** Invoked in {@link FlowExecutionList#saveAll()} to notify that execution has been suspended */ @@ -125,7 +125,7 @@ public String getUrlOfExecution() throws IOException { *

            The listener should be remotable: if sent to an agent, messages printed to it should still appear in the log. * The same will then apply to calls to {@link StepContext#get} on {@link TaskListener}. */ - public @Nonnull TaskListener getListener() throws IOException { + public @NonNull TaskListener getListener() throws IOException { try { return LogStorage.of(this).overallListener(); } catch (InterruptedException x) { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java index 3c00074a..f6ae5838 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java @@ -9,8 +9,8 @@ import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.lang.reflect.InvocationTargetException; /** @@ -73,7 +73,7 @@ public static FlowDurabilityHint[] getDurabilityHintValues() { return FlowDurabilityHint.values(); } - @Nonnull + @NonNull // TODO: Add @Override when Jenkins core baseline is 2.222+ public Permission getRequiredGlobalConfigPagePermission() { return getJenkinsManageOrAdmin(); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java index bdbc0d01..7f90b3b5 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java @@ -50,8 +50,8 @@ import java.util.HashMap; import java.util.Map; import java.util.TreeMap; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.ArtifactManager; import jenkins.model.Jenkins; import jenkins.util.BuildListenerAdapter; @@ -77,19 +77,19 @@ public class StashManager { @Deprecated - public static void stash(@Nonnull Run build, @Nonnull String name, @Nonnull FilePath workspace, @Nonnull TaskListener listener, + public static void stash(@NonNull Run build, @NonNull String name, @NonNull FilePath workspace, @NonNull TaskListener listener, @CheckForNull String includes, @CheckForNull String excludes) throws IOException, InterruptedException { stash(build, name, workspace, listener, includes, excludes, true, false); } @Deprecated - public static void stash(@Nonnull Run build, @Nonnull String name, @Nonnull FilePath workspace, @Nonnull TaskListener listener, + public static void stash(@NonNull Run build, @NonNull String name, @NonNull FilePath workspace, @NonNull TaskListener listener, @CheckForNull String includes, @CheckForNull String excludes, boolean useDefaultExcludes) throws IOException, InterruptedException { stash(build, name, workspace, listener, includes, excludes, useDefaultExcludes, false); } @Deprecated - public static void stash(@Nonnull Run build, @Nonnull String name, @Nonnull FilePath workspace, @Nonnull TaskListener listener, + public static void stash(@NonNull Run build, @NonNull String name, @NonNull FilePath workspace, @NonNull TaskListener listener, @CheckForNull String includes, @CheckForNull String excludes, boolean useDefaultExcludes, boolean allowEmpty) throws IOException, InterruptedException { stash(build, name, workspace, launcherFor(workspace, listener), envFor(build, workspace, listener), listener, includes, excludes, useDefaultExcludes, allowEmpty); } @@ -109,7 +109,7 @@ public static void stash(@Nonnull Run build, @Nonnull String name, @Nonnull * @see StashAwareArtifactManager#stash */ @SuppressFBWarnings(value="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification="fine if mkdirs returns false") - public static void stash(@Nonnull Run build, @Nonnull String name, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull EnvVars env, @Nonnull TaskListener listener, + public static void stash(@NonNull Run build, @NonNull String name, @NonNull FilePath workspace, @NonNull Launcher launcher, @NonNull EnvVars env, @NonNull TaskListener listener, @CheckForNull String includes, @CheckForNull String excludes, boolean useDefaultExcludes, boolean allowEmpty) throws IOException, InterruptedException { Jenkins.checkGoodName(name); StashAwareArtifactManager saam = stashAwareArtifactManager(build); @@ -132,7 +132,7 @@ public static void stash(@Nonnull Run build, @Nonnull String name, @Nonnull } @Deprecated - public static void unstash(@Nonnull Run build, @Nonnull String name, @Nonnull FilePath workspace, @Nonnull TaskListener listener) throws IOException, InterruptedException { + public static void unstash(@NonNull Run build, @NonNull String name, @NonNull FilePath workspace, @NonNull TaskListener listener) throws IOException, InterruptedException { unstash(build, name, workspace, launcherFor(workspace, listener), envFor(build, workspace, listener), listener); } @@ -147,7 +147,7 @@ public static void unstash(@Nonnull Run build, @Nonnull String name, @Nonnu * @throws AbortException in case there is no such saved stash * @see StashAwareArtifactManager#unstash */ - public static void unstash(@Nonnull Run build, @Nonnull String name, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull EnvVars env, @Nonnull TaskListener listener) throws IOException, InterruptedException { + public static void unstash(@NonNull Run build, @NonNull String name, @NonNull FilePath workspace, @NonNull Launcher launcher, @NonNull EnvVars env, @NonNull TaskListener listener) throws IOException, InterruptedException { Jenkins.checkGoodName(name); StashAwareArtifactManager saam = stashAwareArtifactManager(build); if (saam != null) { @@ -162,7 +162,7 @@ public static void unstash(@Nonnull Run build, @Nonnull String name, @Nonnu } @Deprecated - public static void clearAll(@Nonnull Run build) throws IOException { + public static void clearAll(@NonNull Run build) throws IOException { try { clearAll(build, TaskListener.NULL); } catch (InterruptedException x) { @@ -176,7 +176,7 @@ public static void clearAll(@Nonnull Run build) throws IOException { * @param listener a way to report progress or problems * @see StashAwareArtifactManager#clearAllStashes */ - public static void clearAll(@Nonnull Run build, @Nonnull TaskListener listener) throws IOException, InterruptedException { + public static void clearAll(@NonNull Run build, @NonNull TaskListener listener) throws IOException, InterruptedException { StashAwareArtifactManager saam = stashAwareArtifactManager(build); if (saam != null) { saam.clearAllStashes(listener); @@ -186,7 +186,7 @@ public static void clearAll(@Nonnull Run build, @Nonnull TaskListener liste } @Deprecated - public static void maybeClearAll(@Nonnull Run build) throws IOException { + public static void maybeClearAll(@NonNull Run build) throws IOException { try { maybeClearAll(build, TaskListener.NULL); } catch (InterruptedException x) { @@ -200,7 +200,7 @@ public static void maybeClearAll(@Nonnull Run build) throws IOException { * @param build a build possibly passed to {@link #stash} in the past * @see #clearAll(Run, TaskListener) */ - public static void maybeClearAll(@Nonnull Run build, @Nonnull TaskListener listener) throws IOException, InterruptedException { + public static void maybeClearAll(@NonNull Run build, @NonNull TaskListener listener) throws IOException, InterruptedException { for (StashBehavior behavior : ExtensionList.lookup(StashBehavior.class)) { if (!behavior.shouldClearAll(build)) { return; @@ -213,7 +213,7 @@ public static void maybeClearAll(@Nonnull Run build, @Nonnull TaskListener * @deprecated without replacement; only used from {@link CopyStashesAndArtifacts} anyway */ @Deprecated - public static void copyAll(@Nonnull Run from, @Nonnull Run to) throws IOException { + public static void copyAll(@NonNull Run from, @NonNull Run to) throws IOException { File fromStorage = storage(from); if (!fromStorage.isDirectory()) { return; @@ -223,7 +223,7 @@ public static void copyAll(@Nonnull Run from, @Nonnull Run to) throws @Restricted(DoNotUse.class) // just for tests, and incompatible with StashAwareArtifactManager @SuppressFBWarnings(value="DM_DEFAULT_ENCODING", justification="test code") - public static Map> stashesOf(@Nonnull Run build) throws IOException { + public static Map> stashesOf(@NonNull Run build) throws IOException { Map> result = new TreeMap<>(); File[] kids = storage(build).listFiles(); if (kids != null) { @@ -248,12 +248,12 @@ public static Map> stashesOf(@Nonnull Run build) return result; } - private static @Nonnull File storage(@Nonnull Run build) throws IOException { + private static @NonNull File storage(@NonNull Run build) throws IOException { assert stashAwareArtifactManager(build) == null; return new File(build.getRootDir(), "stashes"); } - private static @Nonnull File storage(@Nonnull Run build, @Nonnull String name) throws IOException { + private static @NonNull File storage(@NonNull Run build, @NonNull String name) throws IOException { File dir = storage(build); File f = new File(dir, name + SUFFIX); if (!f.getParentFile().equals(dir)) { @@ -276,7 +276,7 @@ public static abstract class StashBehavior implements ExtensionPoint { * @param build a build which has finished * @return true (the default) to go ahead and call {@link #clearAll}, false to stop */ - public boolean shouldClearAll(@Nonnull Run build) { + public boolean shouldClearAll(@NonNull Run build) { return true; } @@ -298,13 +298,13 @@ public boolean shouldClearAll(@Nonnull Run build) { public interface StashAwareArtifactManager /* extends ArtifactManager */ { /** @see StashManager#stash(Run, String, FilePath, Launcher, EnvVars, TaskListener, String, String, boolean, boolean) */ - void stash(@Nonnull String name, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull EnvVars env, @Nonnull TaskListener listener, @CheckForNull String includes, @CheckForNull String excludes, boolean useDefaultExcludes, boolean allowEmpty) throws IOException, InterruptedException; + void stash(@NonNull String name, @NonNull FilePath workspace, @NonNull Launcher launcher, @NonNull EnvVars env, @NonNull TaskListener listener, @CheckForNull String includes, @CheckForNull String excludes, boolean useDefaultExcludes, boolean allowEmpty) throws IOException, InterruptedException; /** @see StashManager#unstash(Run, String, FilePath, Launcher, EnvVars, TaskListener) */ - void unstash(@Nonnull String name, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull EnvVars env, @Nonnull TaskListener listener) throws IOException, InterruptedException; + void unstash(@NonNull String name, @NonNull FilePath workspace, @NonNull Launcher launcher, @NonNull EnvVars env, @NonNull TaskListener listener) throws IOException, InterruptedException; /** @see StashManager#clearAll(Run, TaskListener) */ - void clearAllStashes(@Nonnull TaskListener listener) throws IOException, InterruptedException; + void clearAllStashes(@NonNull TaskListener listener) throws IOException, InterruptedException; /** * Copy all stashes and artifacts from one build to another. @@ -312,17 +312,17 @@ public interface StashAwareArtifactManager /* extends ArtifactManager */ { * If the implementation cannot handle {@code to} for whatever reason, it may throw {@link AbortException}. * @see CopyStashesAndArtifacts */ - void copyAllArtifactsAndStashes(@Nonnull Run to, @Nonnull TaskListener listener) throws IOException, InterruptedException; + void copyAllArtifactsAndStashes(@NonNull Run to, @NonNull TaskListener listener) throws IOException, InterruptedException; } - private static @CheckForNull StashAwareArtifactManager stashAwareArtifactManager(@Nonnull Run build) throws IOException { + private static @CheckForNull StashAwareArtifactManager stashAwareArtifactManager(@NonNull Run build) throws IOException { ArtifactManager am = build.pickArtifactManager(); return am instanceof StashAwareArtifactManager ? (StashAwareArtifactManager) am : null; } @Deprecated - private static @Nonnull Launcher launcherFor(@Nonnull FilePath workspace, @Nonnull TaskListener listener) { + private static @NonNull Launcher launcherFor(@NonNull FilePath workspace, @NonNull TaskListener listener) { Computer c = workspace.toComputer(); if (c != null) { Node n = c.getNode(); @@ -339,7 +339,7 @@ public interface StashAwareArtifactManager /* extends ArtifactManager */ { } @Deprecated - private static @Nonnull EnvVars envFor(@Nonnull Run build, @Nonnull FilePath workspace, @Nonnull TaskListener listener) throws IOException, InterruptedException { + private static @NonNull EnvVars envFor(@NonNull Run build, @NonNull FilePath workspace, @NonNull TaskListener listener) throws IOException, InterruptedException { Computer c = workspace.toComputer(); if (c != null) { EnvVars e = c.getEnvironment(); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/StepListener.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/StepListener.java index ac6bb7fb..e514aa9d 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/StepListener.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/StepListener.java @@ -29,7 +29,7 @@ import org.jenkinsci.plugins.workflow.steps.Step; import org.jenkinsci.plugins.workflow.steps.StepContext; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * {@link StepListener}s are fired before invoking a step but after the {@link FlowNode} has been created and the @@ -41,5 +41,5 @@ public interface StepListener extends ExtensionPoint { * Called before a {@link Step} is invoked, but after its {@link FlowNode} and {@link StepContext} have been created. * Listeners can make the step fail by calling {@link StepContext#onFailure}. */ - void notifyOfNewStep(@Nonnull Step step, @Nonnull StepContext context); + void notifyOfNewStep(@NonNull Step step, @NonNull StepContext context); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/BlockEndNode.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/BlockEndNode.java index 2c0d2494..bcfe4bea 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/BlockEndNode.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/BlockEndNode.java @@ -28,7 +28,7 @@ import org.jenkinsci.plugins.workflow.flow.FlowExecution; import java.util.List; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * End of a block. @@ -55,7 +55,7 @@ public BlockEndNode(FlowExecution exec, String id, START start, List p * @return an earlier node matching this block * @throws IllegalStateException if the start node could not be reloaded after deserialization */ - public @Nonnull START getStartNode() { + public @NonNull START getStartNode() { if (start == null) { try { start = (START) getExecution().getNode(startId); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/BlockStartNode.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/BlockStartNode.java index 69553974..b40135ae 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/BlockStartNode.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/BlockStartNode.java @@ -26,7 +26,7 @@ import org.jenkinsci.plugins.workflow.flow.FlowExecution; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import java.util.List; /** diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowNode.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowNode.java index 271ef53f..007ac787 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowNode.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowNode.java @@ -43,8 +43,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.jenkinsci.plugins.workflow.actions.ErrorAction; import org.jenkinsci.plugins.workflow.actions.LabelAction; @@ -143,14 +143,14 @@ public final boolean isActive() { return getPersistentAction(ErrorAction.class); } - public @Nonnull FlowExecution getExecution() { + public @NonNull FlowExecution getExecution() { return exec; } /** * Returns a read-only view of parents. */ - @Nonnull + @NonNull public List getParents() { if (parents == null) { parents = loadParents(parentIds); @@ -158,7 +158,7 @@ public List getParents() { return parents; } - @Nonnull + @NonNull private List loadParents(List parentIds) { try { if (parentIds.size() == 1) { @@ -190,14 +190,14 @@ public String getEnclosingId() { * Get the list of enclosing {@link BlockStartNode}s, starting from innermost, for this node. * May be empty if we are the {@link FlowStartNode} or {@link FlowEndNode} */ - @Nonnull + @NonNull public List getEnclosingBlocks() { return this.exec.findAllEnclosingBlockStarts(this); } /** Return an iterator over all enclosing blocks, from the nearest-enclosing outward ("inside-out" order). * Prefer this to {@link #getEnclosingBlocks()} unless you need ALL nodes, because it can evaluate lazily. */ - @Nonnull + @NonNull public Iterable iterateEnclosingBlocks() { return this.exec.iterateEnclosingBlocks(this); } @@ -205,7 +205,7 @@ public Iterable iterateEnclosingBlocks() { /** * Returns a read-only view of the IDs for enclosing blocks of this flow node, innermost first. May be empty. */ - @Nonnull + @NonNull public List getAllEnclosingIds() { List nodes = getEnclosingBlocks(); ArrayList output = new ArrayList<>(nodes.size()); @@ -217,7 +217,7 @@ public List getAllEnclosingIds() { @Restricted(DoNotUse.class) @Exported(name="parents") - @Nonnull + @NonNull public List getParentIds() { if (parentIds != null) { return Collections.unmodifiableList(parentIds); @@ -297,7 +297,7 @@ public BallColor getIconColor() { return c; } - private static BallColor resultToBallColor(@Nonnull Result result) { + private static BallColor resultToBallColor(@NonNull Result result) { if (result == Result.SUCCESS) { return BallColor.BLUE; } else if (result == Result.UNSTABLE) { @@ -370,7 +370,7 @@ protected synchronized void setActions(List actions) { * @return First nontransient action or null if not found. */ @CheckForNull - public final T getPersistentAction(@Nonnull Class type) { + public final T getPersistentAction(@NonNull Class type) { loadActions(); for (Action a : actions) { if (type.isInstance(a)) { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/GraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/GraphLookupView.java index 9573a94c..2b77ff7e 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/GraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/GraphLookupView.java @@ -1,7 +1,7 @@ package org.jenkinsci.plugins.workflow.graph; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; @@ -21,13 +21,13 @@ */ public interface GraphLookupView { /** Tests if the node is a currently running head, or the start of a block that has not completed executing */ - boolean isActive(@Nonnull FlowNode node); + boolean isActive(@NonNull FlowNode node); /** Find the end node corresponding to a start node, and can be used to tell if the block is completed. * @return {@link BlockEndNode} matching the given start node, or null if block hasn't completed */ @CheckForNull - BlockEndNode getEndNode(@Nonnull BlockStartNode startNode); + BlockEndNode getEndNode(@NonNull BlockStartNode startNode); /** * Find the immediately enclosing {@link BlockStartNode} around a {@link FlowNode} @@ -35,7 +35,7 @@ public interface GraphLookupView { * @return Null if node is a {@link FlowStartNode} or {@link FlowEndNode} */ @CheckForNull - BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node); + BlockStartNode findEnclosingBlockStart(@NonNull FlowNode node); /** * Provide an {@link Iterable} over all enclosing blocks, which can be used similarly to {@link #findAllEnclosingBlockStarts(FlowNode)} but @@ -45,15 +45,15 @@ public interface GraphLookupView { * @param node Node to find enclosing blocks for * @return Iterable over enclosing blocks, from the nearest-enclosing outward ("inside-out" order) */ - Iterable iterateEnclosingBlocks(@Nonnull FlowNode node); + Iterable iterateEnclosingBlocks(@NonNull FlowNode node); /** Return all enclosing block start nodes, as with {@link #findEnclosingBlockStart(FlowNode)}. *

            Usage note:Prefer using {@link #iterateEnclosingBlocks(FlowNode)} unless you know you need ALL blocks, since that can lazy-load. * @param node Node to find enclosing blocks for * @return All enclosing block starts from the nearest-enclosing outward ("inside-out" order), or EMPTY_LIST if this is a start or end node */ - @Nonnull - List findAllEnclosingBlockStarts(@Nonnull FlowNode node); + @NonNull + List findAllEnclosingBlockStarts(@NonNull FlowNode node); /** Provides a trivial implementation to facilitate implementing {@link #iterateEnclosingBlocks(FlowNode)}*/ class EnclosingBlocksIterable implements Iterable { @@ -90,7 +90,7 @@ public void remove() { } } - public EnclosingBlocksIterable(@Nonnull GraphLookupView view, @Nonnull FlowNode node) { + public EnclosingBlocksIterable(@NonNull GraphLookupView view, @NonNull FlowNode node) { this.view = view; this.node = node; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java index 33d1e761..e721410a 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java @@ -6,8 +6,8 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -37,7 +37,7 @@ public void clearCache() { /** Update with a new node added to the flowgraph */ @Override - public void onNewHead(@Nonnull FlowNode newHead) { + public void onNewHead(@NonNull FlowNode newHead) { if (newHead instanceof BlockEndNode) { String startNodeId = ((BlockEndNode)newHead).getStartNode().getId(); blockStartToEnd.put(startNodeId, newHead.getId()); @@ -77,7 +77,7 @@ public StandardGraphLookupView() { } @Override - public boolean isActive(@Nonnull FlowNode node) { + public boolean isActive(@NonNull FlowNode node) { if (node instanceof FlowEndNode) { // cf. JENKINS-26139 return !node.getExecution().isComplete(); } else if (node instanceof BlockStartNode){ // BlockStartNode @@ -88,7 +88,7 @@ public boolean isActive(@Nonnull FlowNode node) { } // Do a brute-force scan for the block end matching the start, caching info along the way for future use - BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { + BlockEndNode bruteForceScanForEnd(@NonNull BlockStartNode start) { DepthFirstScanner scan = new DepthFirstScanner(); scan.setup(start.getExecution().getCurrentHeads()); for (FlowNode f : scan) { @@ -117,7 +117,7 @@ BlockEndNode bruteForceScanForEnd(@Nonnull BlockStartNode start) { /** Do a brute-force scan for the enclosing blocks **/ - BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { + BlockStartNode bruteForceScanForEnclosingBlock(@NonNull final FlowNode node) { FlowNode current = node; while (!(current instanceof FlowStartNode)) { // Hunt back for enclosing blocks, a potentially expensive operation @@ -158,7 +158,7 @@ BlockStartNode bruteForceScanForEnclosingBlock(@Nonnull final FlowNode node) { @CheckForNull @Override - public BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { + public BlockEndNode getEndNode(@NonNull final BlockStartNode startNode) { String id = blockStartToEnd.get(startNode.getId()); if (id != null) { @@ -176,7 +176,7 @@ public BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) { @CheckForNull @Override - public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { + public BlockStartNode findEnclosingBlockStart(@NonNull FlowNode node) { if (node instanceof FlowStartNode || node instanceof FlowEndNode) { return null; } @@ -195,13 +195,13 @@ public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) { } @Override - public Iterable iterateEnclosingBlocks(@Nonnull FlowNode node) { + public Iterable iterateEnclosingBlocks(@NonNull FlowNode node) { return new EnclosingBlocksIterable(this, node); } - @Nonnull + @NonNull @Override - public List findAllEnclosingBlockStarts(@Nonnull FlowNode node) { + public List findAllEnclosingBlockStarts(@NonNull FlowNode node) { if (node instanceof FlowStartNode || node instanceof FlowEndNode) { return Collections.emptyList(); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/StepNode.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/StepNode.java index 9c24b67b..81db7d4d 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/StepNode.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/StepNode.java @@ -26,7 +26,7 @@ import org.jenkinsci.plugins.workflow.steps.Step; import org.jenkinsci.plugins.workflow.steps.StepDescriptor; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Optional interface for a {@link FlowNode} that has an associated {@link StepDescriptor}. diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java index 397e1a03..6597606f 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java @@ -28,8 +28,8 @@ import org.jenkinsci.plugins.workflow.flow.FlowExecution; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.annotation.concurrent.NotThreadSafe; import java.util.ArrayList; import java.util.Collection; @@ -103,7 +103,7 @@ public abstract class AbstractFlowScanner implements Iterable , Filter protected static final int MAX_LIST_CHECK_SIZE = 5; /** Helper: convert stop nodes to a collection that can efficiently be checked for membership, handling null if needed */ - @Nonnull + @NonNull protected Collection convertToFastCheckable(@CheckForNull Collection nodeCollection) { if (nodeCollection == null || nodeCollection.size()==0) { return Collections.emptySet(); @@ -185,7 +185,7 @@ public boolean setup(@CheckForNull FlowNode head) { * - none of the filteredHeads are null * @param filteredHeads Head nodes that have been filtered against denyList */ - protected abstract void setHeads(@Nonnull Collection filteredHeads); + protected abstract void setHeads(@NonNull Collection filteredHeads); /** * Actual meat of the iteration, get the next node to visit, using and updating state as needed @@ -194,7 +194,7 @@ public boolean setup(@CheckForNull FlowNode head) { * @return Next node to visit, or null if we've exhausted the node list */ @CheckForNull - protected abstract FlowNode next(@Nonnull FlowNode current, @Nonnull Collection blackList); + protected abstract FlowNode next(@NonNull FlowNode current, @NonNull Collection blackList); @Override public boolean hasNext() { @@ -218,7 +218,7 @@ public final void remove() { } @Override - @Nonnull + @NonNull public Iterator iterator() { return this; } @@ -229,8 +229,8 @@ public Iterator iterator() { * @return A {@link Filterator} against this FlowScanner, which can be filtered in additional ways. */ @Override - @Nonnull - public Filterator filter(@Nonnull Predicate filterCondition) { + @NonNull + public Filterator filter(@NonNull Predicate filterCondition) { return new FilteratorImpl<>(this, filterCondition); } @@ -263,19 +263,19 @@ public FlowNode findFirstMatch(@CheckForNull Collection heads, /** Syntactic sugar for {@link #findFirstMatch(Collection, Collection, Predicate)} where there is no denyList */ @CheckForNull - public FlowNode findFirstMatch(@CheckForNull Collection heads, @Nonnull Predicate matchPredicate) { + public FlowNode findFirstMatch(@CheckForNull Collection heads, @NonNull Predicate matchPredicate) { return this.findFirstMatch(heads, null, matchPredicate); } /** Syntactic sugar for {@link #findFirstMatch(Collection, Collection, Predicate)} where there is a single head and no denyList */ @CheckForNull - public FlowNode findFirstMatch(@CheckForNull FlowNode head, @Nonnull Predicate matchPredicate) { + public FlowNode findFirstMatch(@CheckForNull FlowNode head, @NonNull Predicate matchPredicate) { return this.findFirstMatch(Collections.singleton(head), null, matchPredicate); } /** Syntactic sugar for {@link #findFirstMatch(Collection, Collection, Predicate)} using {@link FlowExecution#getCurrentHeads()} to get heads and no denyList */ @CheckForNull - public FlowNode findFirstMatch(@CheckForNull FlowExecution exec, @Nonnull Predicate matchPredicate) { + public FlowNode findFirstMatch(@CheckForNull FlowExecution exec, @NonNull Predicate matchPredicate) { if (exec != null && exec.getCurrentHeads() != null && !exec.getCurrentHeads().isEmpty()) { return this.findFirstMatch(exec.getCurrentHeads(), null, matchPredicate); } @@ -290,7 +290,7 @@ public FlowNode findFirstMatch(@CheckForNull FlowExecution exec, @Nonnull Predic * @param matchCondition Predicate that must be met for nodes to be included in output. Input is always non-null. * @return List of flownodes matching the predicate. */ - @Nonnull + @NonNull public List filteredNodes(@CheckForNull Collection heads, @CheckForNull Collection blackList, Predicate matchCondition) { @@ -308,7 +308,7 @@ public List filteredNodes(@CheckForNull Collection heads, } /** Convenience method to get the list all flownodes in the iterator order. */ - @Nonnull + @NonNull public List allNodes(@CheckForNull Collection heads) { if (!setup(heads)) { return Collections.emptyList(); @@ -321,25 +321,25 @@ public List allNodes(@CheckForNull Collection heads) { } /** Convenience method to get the list of all {@link FlowNode}s for the execution, in iterator order. */ - @Nonnull + @NonNull public List allNodes(@CheckForNull FlowExecution exec) { return (exec == null) ? Collections.emptyList() : allNodes(exec.getCurrentHeads()); } /** Syntactic sugar for {@link #filteredNodes(Collection, Collection, Predicate)} with no denyList nodes */ - @Nonnull - public List filteredNodes(@CheckForNull Collection heads, @Nonnull Predicate matchPredicate) { + @NonNull + public List filteredNodes(@CheckForNull Collection heads, @NonNull Predicate matchPredicate) { return this.filteredNodes(heads, null, matchPredicate); } /** Syntactic sugar for {@link #filteredNodes(Collection, Collection, Predicate)} with a single head and no denyList nodes */ - @Nonnull - public List filteredNodes(@CheckForNull FlowNode head, @Nonnull Predicate matchPredicate) { + @NonNull + public List filteredNodes(@CheckForNull FlowNode head, @NonNull Predicate matchPredicate) { return this.filteredNodes(Collections.singleton(head), null, matchPredicate); } - @Nonnull - public List filteredNodes(@CheckForNull FlowExecution exec, @Nonnull Predicate matchPredicate) { + @NonNull + public List filteredNodes(@CheckForNull FlowExecution exec, @NonNull Predicate matchPredicate) { if (exec == null) { return Collections.emptyList(); } @@ -356,7 +356,7 @@ public List filteredNodes(@CheckForNull FlowExecution exec, @Nonnull P * @param blackList Nodes we can't visit or pass beyond. * @param visitor Visitor that will see each FlowNode encountered. */ - public void visitAll(@CheckForNull Collection heads, @CheckForNull Collection blackList, @Nonnull FlowNodeVisitor visitor) { + public void visitAll(@CheckForNull Collection heads, @CheckForNull Collection blackList, @NonNull FlowNodeVisitor visitor) { if (!setup(heads, blackList)) { return; } @@ -369,7 +369,7 @@ public void visitAll(@CheckForNull Collection heads, @CheckForNull Col } /** Syntactic sugar for {@link #visitAll(Collection, Collection, FlowNodeVisitor)} where we don't denylist any nodes */ - public void visitAll(@CheckForNull Collection heads, @Nonnull FlowNodeVisitor visitor) { + public void visitAll(@CheckForNull Collection heads, @NonNull FlowNodeVisitor visitor) { visitAll(heads, null, visitor); } } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/BlockChunkFinder.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/BlockChunkFinder.java index f3b4f168..e2ad8023 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/BlockChunkFinder.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/BlockChunkFinder.java @@ -4,8 +4,8 @@ import org.jenkinsci.plugins.workflow.graph.BlockStartNode; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Matches start and end of a block. Any block! @@ -21,12 +21,12 @@ public boolean isStartInsideChunk() { } @Override - public boolean isChunkStart(@Nonnull FlowNode current, @CheckForNull FlowNode previous) { + public boolean isChunkStart(@NonNull FlowNode current, @CheckForNull FlowNode previous) { return current instanceof BlockStartNode; } @Override - public boolean isChunkEnd(@Nonnull FlowNode current, @CheckForNull FlowNode previous) { + public boolean isChunkEnd(@NonNull FlowNode current, @CheckForNull FlowNode previous) { return current instanceof BlockEndNode; } } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java index 0a3da2ad..37012494 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ChunkFinder.java @@ -2,8 +2,8 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Think of this as setting conditions to mark a region of interest in the graph of {@link FlowNode} from a {@link org.jenkinsci.plugins.workflow.flow.FlowExecution}. @@ -42,7 +42,7 @@ public interface ChunkFinder { * @param previous Previous node, to use in testing chunk * @return True if current node is the beginning of chunk */ - boolean isChunkStart(@Nonnull FlowNode current, @CheckForNull FlowNode previous); + boolean isChunkStart(@NonNull FlowNode current, @CheckForNull FlowNode previous); /** * Test if the current node is the end of a chunk (inclusive) @@ -52,5 +52,5 @@ public interface ChunkFinder { * @param previous Previous node, to use in testing chunk * @return True if current is the end of a chunk (inclusive) */ - boolean isChunkEnd(@Nonnull FlowNode current, @CheckForNull FlowNode previous); + boolean isChunkEnd(@NonNull FlowNode current, @CheckForNull FlowNode previous); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/DepthFirstScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/DepthFirstScanner.java index 5677e2d0..58a1255b 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/DepthFirstScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/DepthFirstScanner.java @@ -27,7 +27,7 @@ import org.jenkinsci.plugins.workflow.graph.BlockStartNode; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.annotation.concurrent.NotThreadSafe; import java.util.ArrayDeque; import java.util.ArrayList; @@ -65,7 +65,7 @@ protected void reset() { } @Override - protected void setHeads(@Nonnull Collection heads) { + protected void setHeads(@NonNull Collection heads) { if (heads.isEmpty()) { return; } @@ -87,7 +87,7 @@ protected boolean testCandidate(FlowNode f, Collection blackList) { } @Override - protected FlowNode next(@Nonnull FlowNode current, @Nonnull final Collection blackList) { + protected FlowNode next(@NonNull FlowNode current, @NonNull final Collection blackList) { FlowNode output = null; // Walk through parents of current node diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/Filterator.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/Filterator.java index b77e40ae..a003e7b6 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/Filterator.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/Filterator.java @@ -26,7 +26,7 @@ import com.google.common.base.Predicate; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Iterator; /** Iterator that may be navigated through a filtered wrapper. @@ -38,6 +38,6 @@ */ public interface Filterator extends Iterator { /** Returns a filtered view of the iterator, which calls the iterator until matches are found */ - @Nonnull - Filterator filter(@Nonnull Predicate matchCondition); + @NonNull + Filterator filter(@NonNull Predicate matchCondition); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java index b49ebf26..e91228bd 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java @@ -26,7 +26,7 @@ import com.google.common.base.Predicate; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.annotation.concurrent.NotThreadSafe; import java.util.Iterator; @@ -45,7 +45,7 @@ public FilteratorImpl filter(Predicate matchCondition) { return new FilteratorImpl<>(this, matchCondition); } - public FilteratorImpl(@Nonnull Iterator it, @Nonnull Predicate matchCondition) { + public FilteratorImpl(@NonNull Iterator it, @NonNull Predicate matchCondition) { this.wrapped = it; this.matchCondition = matchCondition; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java index 059604b8..254deda6 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunk.java @@ -26,7 +26,7 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** *

            Common container interface for a series of {@link FlowNode}s with a logical start and end. @@ -45,9 +45,9 @@ * @author Sam Van Oort */ public interface FlowChunk { - @Nonnull + @NonNull FlowNode getFirstNode(); - @Nonnull + @NonNull FlowNode getLastNode(); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunkWithContext.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunkWithContext.java index 1285808b..f1ac1321 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunkWithContext.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowChunkWithContext.java @@ -2,7 +2,7 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** FlowChunk with information about what comes before/after */ public interface FlowChunkWithContext extends FlowChunk { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowNodeVisitor.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowNodeVisitor.java index 790963e0..2362edfd 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowNodeVisitor.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowNodeVisitor.java @@ -26,7 +26,7 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collection; /** @@ -42,5 +42,5 @@ public interface FlowNodeVisitor { * @param f Node to visit * @return False if we should stop visiting nodes */ - boolean visit(@Nonnull FlowNode f); + boolean visit(@NonNull FlowNode f); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScanningUtils.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScanningUtils.java index eb89b256..6b6debf3 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScanningUtils.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScanningUtils.java @@ -31,8 +31,8 @@ import org.jenkinsci.plugins.workflow.graph.BlockStartNode; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Comparator; import java.util.Iterator; @@ -50,8 +50,8 @@ private FlowScanningUtils() {} * @param actionClass Action class to look for * @return Predicate that will match when FlowNode has the action given */ - @Nonnull - public static Predicate hasActionPredicate(@Nonnull final Class actionClass) { + @NonNull + public static Predicate hasActionPredicate(@NonNull final Class actionClass) { return new Predicate() { @Override public boolean apply(FlowNode input) { @@ -119,9 +119,9 @@ public int compare(@CheckForNull FlowNode first, @CheckForNull FlowNode second) * @param f {@link FlowNode} to start from. * @return Iterator that returns all enclosing BlockStartNodes from the inside out. */ - @Nonnull + @NonNull @Deprecated - public static Filterator fetchEnclosingBlocks(@Nonnull FlowNode f) { + public static Filterator fetchEnclosingBlocks(@NonNull FlowNode f) { return new FilteratorImpl<>((Iterator) f.iterateEnclosingBlocks().iterator(), Predicates.alwaysTrue()); } } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java index b161deb1..aca1d3ac 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java @@ -34,9 +34,9 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; import org.jenkinsci.plugins.workflow.steps.StepDescriptor; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import javax.annotation.concurrent.NotThreadSafe; import java.util.ArrayDeque; import java.util.ArrayList; @@ -122,11 +122,11 @@ public ForkScanner() { } - public ForkScanner(@Nonnull Collection heads) { + public ForkScanner(@NonNull Collection heads) { this.setup(heads); } - public ForkScanner(@Nonnull Collection heads, @Nonnull Collection blackList) { + public ForkScanner(@NonNull Collection heads, @NonNull Collection blackList) { this.setup(heads, blackList); } @@ -158,7 +158,7 @@ public boolean apply(@Nullable FlowNode input) { * Specifically, requiring classes from workflow-cps to detect if something is a parallel step. */ @Deprecated - public static void setParallelStartPredicate(@Nonnull Predicate pred) { + public static void setParallelStartPredicate(@NonNull Predicate pred) { } // Needed because the *next* node might be a parallel start if we start in middle and we don't know it @@ -217,7 +217,7 @@ static class ParallelBlockStart { BlockStartNode forkStart; // This is the node with child branches ArrayDeque unvisited = new ArrayDeque<>(); // Remaining branches of this that we have have not visited yet - ParallelBlockStart(@Nonnull BlockStartNode forkStart) { + ParallelBlockStart(@NonNull BlockStartNode forkStart) { this.forkStart = forkStart; } @@ -251,7 +251,7 @@ public boolean isLeaf() { * @throws IllegalStateException When you try to split a segment on a node that it doesn't contain, or invalid graph structure * @return Recreated fork */ - Fork split(@Nonnull HashMap nodeMapping, @Nonnull BlockStartNode joinPoint, @Nonnull FlowPiece joiningBranch) { + Fork split(@NonNull HashMap nodeMapping, @NonNull BlockStartNode joinPoint, @NonNull FlowPiece joiningBranch) { int index = visited.lastIndexOf(joinPoint); // Fork will be closer to end, so this is better than indexOf Fork newFork = new Fork(joinPoint); @@ -360,7 +360,7 @@ ArrayDeque convertForksToBlockStarts(ArrayDeque parall *

          • Heads are all separate branches
          • *
          */ - ArrayDeque leastCommonAncestor(@Nonnull final Set heads) { + ArrayDeque leastCommonAncestor(@NonNull final Set heads) { HashMap branches = new HashMap<>(); ArrayList> iterators = new ArrayList<>(); ArrayList livePieces = new ArrayList<>(); @@ -444,7 +444,7 @@ ArrayDeque leastCommonAncestor(@Nonnull final Set } @Override - protected void setHeads(@Nonnull Collection heads) { + protected void setHeads(@NonNull Collection heads) { if (heads.size() > 1) { for (FlowNode f : heads) { headIds.add(f.getId()); @@ -566,7 +566,7 @@ public FlowNode next() { @Override @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "Function call to modify state, special case where we don't need the returnVal") - protected FlowNode next(@Nonnull FlowNode current, @Nonnull Collection blackList) { + protected FlowNode next(@NonNull FlowNode current, @NonNull Collection blackList) { FlowNode output = null; // First we look at the parents of the current node if present @@ -619,13 +619,13 @@ protected FlowNode next(@Nonnull FlowNode current, @Nonnull Collection return output; } - public static void visitSimpleChunks(@Nonnull Collection heads, @Nonnull Collection blacklist, @Nonnull SimpleChunkVisitor visitor, @Nonnull ChunkFinder finder) { + public static void visitSimpleChunks(@NonNull Collection heads, @NonNull Collection blacklist, @NonNull SimpleChunkVisitor visitor, @NonNull ChunkFinder finder) { ForkScanner scanner = new ForkScanner(); scanner.setup(heads, blacklist); scanner.visitSimpleChunks(visitor, finder); } - public static void visitSimpleChunks(@Nonnull Collection heads, @Nonnull SimpleChunkVisitor visitor, @Nonnull ChunkFinder finder) { + public static void visitSimpleChunks(@NonNull Collection heads, @NonNull SimpleChunkVisitor visitor, @NonNull ChunkFinder finder) { ForkScanner scanner = new ForkScanner(); scanner.setup(heads); scanner.visitSimpleChunks(visitor, finder); @@ -638,7 +638,7 @@ public static void visitSimpleChunks(@Nonnull Collection heads, @Nonnu * not just the last declared branch. (See issue JENKINS-38536) */ @CheckForNull - static FlowNode findLastRunningNode(@Nonnull List candidates) { + static FlowNode findLastRunningNode(@NonNull List candidates) { if (candidates.size() == 0) { return null; } else if (candidates.size() == 1) { @@ -679,7 +679,7 @@ List currentParallelHeads() { /** Pulls out firing the callbacks for parallels */ static void fireVisitParallelCallbacks(@CheckForNull FlowNode next, @CheckForNull FlowNode current, @CheckForNull FlowNode prev, - @Nonnull SimpleChunkVisitor visitor, @Nonnull ChunkFinder finder, @Nonnull ForkScanner scanner) { + @NonNull SimpleChunkVisitor visitor, @NonNull ChunkFinder finder, @NonNull ForkScanner scanner) { // Trigger on parallels switch (scanner.currentType) { case NORMAL: @@ -726,8 +726,8 @@ static void fireVisitParallelCallbacks(@CheckForNull FlowNode next, @CheckForNul /** Abstracts out the simpleChunkVisitor callback-triggering logic. * Note that a null value of "prev" is assumed to mean we're the last node. */ @SuppressFBWarnings(value="NP_LOAD_OF_KNOWN_NULL_VALUE", justification = "FindBugs doesn't like passing nulls to a method that can take null") - static void fireVisitChunkCallbacks(@CheckForNull FlowNode next, @Nonnull FlowNode current, @CheckForNull FlowNode prev, - @Nonnull SimpleChunkVisitor visitor, @Nonnull ChunkFinder finder, @Nonnull ForkScanner scanner) { + static void fireVisitChunkCallbacks(@CheckForNull FlowNode next, @NonNull FlowNode current, @CheckForNull FlowNode prev, + @NonNull SimpleChunkVisitor visitor, @NonNull ChunkFinder finder, @NonNull ForkScanner scanner) { boolean boundary = false; if (prev == null && finder.isStartInsideChunk()) { // Last node, need to fire end event to start inside chunk visitor.chunkEnd(current, prev, scanner); @@ -751,7 +751,7 @@ static void fireVisitChunkCallbacks(@CheckForNull FlowNode next, @Nonnull FlowNo } /** Walk through flows */ - public void visitSimpleChunks(@Nonnull SimpleChunkVisitor visitor, @Nonnull ChunkFinder finder) { + public void visitSimpleChunks(@NonNull SimpleChunkVisitor visitor, @NonNull ChunkFinder finder) { FlowNode prev; if (this.currentParallelStart != null) { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LabelledChunkFinder.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LabelledChunkFinder.java index 7c8e6762..38b90fac 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LabelledChunkFinder.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LabelledChunkFinder.java @@ -5,8 +5,8 @@ import org.jenkinsci.plugins.workflow.graph.BlockStartNode; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Splits a flow execution into {@link FlowChunk}s whenever you have a label. @@ -24,7 +24,7 @@ public boolean isStartInsideChunk() { /** Start is anywhere with a {@link LabelAction} */ @Override - public boolean isChunkStart(@Nonnull FlowNode current, @CheckForNull FlowNode previous) { + public boolean isChunkStart(@NonNull FlowNode current, @CheckForNull FlowNode previous) { LabelAction la = current.getPersistentAction(LabelAction.class); return la != null; } @@ -32,7 +32,7 @@ public boolean isChunkStart(@Nonnull FlowNode current, @CheckForNull FlowNode pr /** End is where the previous node is a chunk start * or this is a {@link BlockEndNode} whose {@link BlockStartNode} has a label action */ @Override - public boolean isChunkEnd(@Nonnull FlowNode current, @CheckForNull FlowNode previous) { + public boolean isChunkEnd(@NonNull FlowNode current, @CheckForNull FlowNode previous) { if (previous == null) { return false; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java index 1618b748..d0dcd16c 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java @@ -28,8 +28,8 @@ import org.jenkinsci.plugins.workflow.graph.BlockStartNode; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.annotation.concurrent.NotThreadSafe; import java.util.Collection; import java.util.Iterator; @@ -70,7 +70,7 @@ public boolean setup(@CheckForNull Collection heads, @CheckForNull Col } @Override - protected void setHeads(@Nonnull Collection heads) { + protected void setHeads(@NonNull Collection heads) { Iterator it = heads.iterator(); if (it.hasNext()) { this.myCurrent = jumpBlockScan(it.next(), myBlackList); @@ -83,7 +83,7 @@ protected void setHeads(@Nonnull Collection heads) { /** Keeps jumping over blocks until we hit the first node preceding a block */ @CheckForNull - protected FlowNode jumpBlockScan(@CheckForNull FlowNode node, @Nonnull Collection blacklistNodes) { + protected FlowNode jumpBlockScan(@CheckForNull FlowNode node, @NonNull Collection blacklistNodes) { FlowNode candidate = node; // Find the first candidate node preceding a block... and filtering by blacklist @@ -113,7 +113,7 @@ protected FlowNode jumpBlockScan(@CheckForNull FlowNode node, @Nonnull Collectio } @Override - protected FlowNode next(@Nonnull FlowNode current, @Nonnull Collection blackList) { + protected FlowNode next(@NonNull FlowNode current, @NonNull Collection blackList) { if (current == null) { return null; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java index 66c36b2c..a20af305 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java @@ -27,7 +27,7 @@ import com.google.common.base.Predicate; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.annotation.concurrent.NotThreadSafe; import java.util.Collection; import java.util.Collections; @@ -66,7 +66,7 @@ protected void reset() { * @param heads Head nodes that have been filtered against denyList. Do not pass multiple heads. */ @Override - protected void setHeads(@Nonnull Collection heads) { + protected void setHeads(@NonNull Collection heads) { Iterator it = heads.iterator(); if (it.hasNext()) { this.myCurrent = it.next(); @@ -78,7 +78,7 @@ protected void setHeads(@Nonnull Collection heads) { } @Override - protected FlowNode next(FlowNode current, @Nonnull Collection blackList) { + protected FlowNode next(FlowNode current, @NonNull Collection blackList) { if (current == null) { return null; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunk.java index 60a4b128..db9e8f20 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunk.java @@ -26,8 +26,8 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * FlowChunk that holds direct references to the {@link FlowNode} instances and context info @@ -41,7 +41,7 @@ public class MemoryFlowChunk implements FlowChunkWithContext { protected FlowNode nodeAfter = null; private long pauseTimeMillis = 0; - public MemoryFlowChunk(@CheckForNull FlowNode before, @Nonnull FlowNode firstNode, @Nonnull FlowNode lastNode, @CheckForNull FlowNode nodeAfter) { + public MemoryFlowChunk(@CheckForNull FlowNode before, @NonNull FlowNode firstNode, @NonNull FlowNode lastNode, @CheckForNull FlowNode nodeAfter) { this.setNodeBefore(before); this.setFirstNode(firstNode); this.setLastNode(lastNode); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/NodeStepNamePredicate.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/NodeStepNamePredicate.java index 9890a8e1..f74fa5e9 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/NodeStepNamePredicate.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/NodeStepNamePredicate.java @@ -30,8 +30,8 @@ import org.jenkinsci.plugins.workflow.graph.StepNode; import org.jenkinsci.plugins.workflow.steps.StepDescriptor; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -42,7 +42,7 @@ public final class NodeStepNamePredicate implements Predicate { String descriptorId; - public NodeStepNamePredicate(@Nonnull String descriptorId) { + public NodeStepNamePredicate(@NonNull String descriptorId) { this.descriptorId = descriptorId; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/NodeStepTypePredicate.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/NodeStepTypePredicate.java index c7ae8d7b..c9a0dd52 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/NodeStepTypePredicate.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/NodeStepTypePredicate.java @@ -30,8 +30,8 @@ import org.jenkinsci.plugins.workflow.graph.StepNode; import org.jenkinsci.plugins.workflow.steps.StepDescriptor; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -39,12 +39,12 @@ public final class NodeStepTypePredicate implements Predicate { StepDescriptor stepDescriptor; - public NodeStepTypePredicate(@Nonnull StepDescriptor descriptorType) { + public NodeStepTypePredicate(@NonNull StepDescriptor descriptorType) { stepDescriptor = descriptorType; } /** Create a filter predicate based on the step name */ - public NodeStepTypePredicate(@Nonnull String functionName) { + public NodeStepTypePredicate(@NonNull String functionName) { stepDescriptor = StepDescriptor.byFunctionName(functionName); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelFlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelFlowChunk.java index 024d7a5c..66b6eb83 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelFlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelFlowChunk.java @@ -1,6 +1,6 @@ package org.jenkinsci.plugins.workflow.graphanalysis; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Map; /** @@ -9,9 +9,9 @@ public interface ParallelFlowChunk extends FlowChunk { /** Returns the branches of a parallel flow chunk, mapped by branch name and parallel branch block */ - @Nonnull + @NonNull Map getBranches(); - @Nonnull - void setBranch(@Nonnull String branchName, @Nonnull ChunkType branchBlock); + @NonNull + void setBranch(@NonNull String branchName, @NonNull ChunkType branchBlock); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelMemoryFlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelMemoryFlowChunk.java index 2772d329..f8e15c66 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelMemoryFlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelMemoryFlowChunk.java @@ -26,8 +26,8 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -41,21 +41,21 @@ public class ParallelMemoryFlowChunk extends MemoryFlowChunk implements Parallel // LinkedHashMap to preserve insert order private LinkedHashMap branches = new LinkedHashMap<>(); - public ParallelMemoryFlowChunk(@Nonnull FlowNode firstNode, @Nonnull FlowNode lastNode) { + public ParallelMemoryFlowChunk(@NonNull FlowNode firstNode, @NonNull FlowNode lastNode) { super (null,firstNode, lastNode, null); } - public ParallelMemoryFlowChunk(@CheckForNull FlowNode nodeBefore, @Nonnull FlowNode firstNode, @Nonnull FlowNode lastNode, @CheckForNull FlowNode nodeAfter) { + public ParallelMemoryFlowChunk(@CheckForNull FlowNode nodeBefore, @NonNull FlowNode firstNode, @NonNull FlowNode lastNode, @CheckForNull FlowNode nodeAfter) { super (nodeBefore,firstNode, lastNode, nodeAfter); } @Override - public void setBranch(@Nonnull String branchName, @Nonnull MemoryFlowChunk branchBlock) { + public void setBranch(@NonNull String branchName, @NonNull MemoryFlowChunk branchBlock) { branches.put(branchName, branchBlock); } @Override - @Nonnull + @NonNull public Map getBranches() { return Collections.unmodifiableMap(branches); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java index 780f396d..714b1f0b 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/SimpleChunkVisitor.java @@ -27,8 +27,8 @@ import org.jenkinsci.plugins.workflow.graph.BlockStartNode; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * This visitor's callbacks are invoked as we walk through a pipeline flow graph, and it splits it into chunks. @@ -85,7 +85,7 @@ public interface SimpleChunkVisitor { * @param beforeBlock First node before chunk (null if none exist) * @param scanner Forkscanner used (for state tracking) */ - void chunkStart(@Nonnull FlowNode startNode, @CheckForNull FlowNode beforeBlock, @Nonnull ForkScanner scanner); + void chunkStart(@NonNull FlowNode startNode, @CheckForNull FlowNode beforeBlock, @NonNull ForkScanner scanner); /** * Called when hitting the end of a chunk. @@ -93,7 +93,7 @@ public interface SimpleChunkVisitor { * @param afterChunk Node after chunk (null if we are on the last node) * @param scanner Forkscanner used (for state tracking) */ - void chunkEnd(@Nonnull FlowNode endNode, @CheckForNull FlowNode afterChunk, @Nonnull ForkScanner scanner); + void chunkEnd(@NonNull FlowNode endNode, @CheckForNull FlowNode afterChunk, @NonNull ForkScanner scanner); /** * Notifies that we've hit the start of a parallel block (the point where it branches out). @@ -101,7 +101,7 @@ public interface SimpleChunkVisitor { * @param branchNode {@link org.jenkinsci.plugins.workflow.graph.BlockStartNode} for one of the branches (it will be labelled) * @param scanner ForkScanner used */ - void parallelStart(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchNode, @Nonnull ForkScanner scanner); + void parallelStart(@NonNull FlowNode parallelStartNode, @NonNull FlowNode branchNode, @NonNull ForkScanner scanner); /** * Notifies that we've seen the end of a parallel block @@ -109,7 +109,7 @@ public interface SimpleChunkVisitor { * @param parallelEndNode Last node of parallel ({@link BlockEndNode}) * @param scanner ForkScanner used */ - void parallelEnd(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode parallelEndNode, @Nonnull ForkScanner scanner); + void parallelEnd(@NonNull FlowNode parallelStartNode, @NonNull FlowNode parallelEndNode, @NonNull ForkScanner scanner); /** * Hit the start of a parallel branch @@ -117,7 +117,7 @@ public interface SimpleChunkVisitor { * @param branchStartNode BlockStartNode beginning the branch (this will have the ThreadNameAction giving its name) * @param scanner ForkScanner used */ - void parallelBranchStart(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchStartNode, @Nonnull ForkScanner scanner); + void parallelBranchStart(@NonNull FlowNode parallelStartNode, @NonNull FlowNode branchStartNode, @NonNull ForkScanner scanner); /** * Hit the end start of a parallel branch @@ -126,7 +126,7 @@ public interface SimpleChunkVisitor { * @param branchEndNode Final node of the branch (may be BlockEndNode if done, otherwise just the last one executed) * @param scanner ForkScanner used */ - void parallelBranchEnd(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchEndNode, @Nonnull ForkScanner scanner); + void parallelBranchEnd(@NonNull FlowNode parallelStartNode, @NonNull FlowNode branchEndNode, @NonNull ForkScanner scanner); /** * Called for a flownode neither start nor end. @@ -137,5 +137,5 @@ public interface SimpleChunkVisitor { * @param after Node after the current * @param scan Reference to our forkscanner, if we want to poke at the state within */ - void atomNode(@CheckForNull FlowNode before, @Nonnull FlowNode atomNode, @CheckForNull FlowNode after, @Nonnull ForkScanner scan); + void atomNode(@CheckForNull FlowNode before, @NonNull FlowNode atomNode, @CheckForNull FlowNode after, @NonNull ForkScanner scan); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/StandardChunkVisitor.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/StandardChunkVisitor.java index 2e9495bf..e1ab1ea3 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/StandardChunkVisitor.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/StandardChunkVisitor.java @@ -2,8 +2,8 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Simple handler for linear {@link FlowChunk}s (basic stages, etc), and designed to be extended. @@ -21,11 +21,11 @@ public class StandardChunkVisitor implements SimpleChunkVisitor { /** Override me to do something once the chunk is finished (such as add it to a list). * Note: the chunk will be mutated directly, so you need to copy it if you want to do something. */ - protected void handleChunkDone(@Nonnull MemoryFlowChunk chunk) { + protected void handleChunkDone(@NonNull MemoryFlowChunk chunk) { // NO-OP initially } - protected void resetChunk(@Nonnull MemoryFlowChunk chunk) { + protected void resetChunk(@NonNull MemoryFlowChunk chunk) { chunk.setFirstNode(null); chunk.setLastNode(null); chunk.setNodeBefore(null); @@ -34,7 +34,7 @@ protected void resetChunk(@Nonnull MemoryFlowChunk chunk) { } @Override - public void chunkStart(@Nonnull FlowNode startNode, @CheckForNull FlowNode beforeBlock, @Nonnull ForkScanner scanner) { + public void chunkStart(@NonNull FlowNode startNode, @CheckForNull FlowNode beforeBlock, @NonNull ForkScanner scanner) { chunk.setNodeBefore(beforeBlock); chunk.setFirstNode(startNode); handleChunkDone(chunk); @@ -42,24 +42,24 @@ public void chunkStart(@Nonnull FlowNode startNode, @CheckForNull FlowNode befor } @Override - public void chunkEnd(@Nonnull FlowNode endNode, @CheckForNull FlowNode afterChunk, @Nonnull ForkScanner scanner) { + public void chunkEnd(@NonNull FlowNode endNode, @CheckForNull FlowNode afterChunk, @NonNull ForkScanner scanner) { chunk.setLastNode(endNode); chunk.setNodeAfter(afterChunk); } @Override - public void parallelStart(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchNode, @Nonnull ForkScanner scanner) {} + public void parallelStart(@NonNull FlowNode parallelStartNode, @NonNull FlowNode branchNode, @NonNull ForkScanner scanner) {} @Override - public void parallelEnd(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode parallelEndNode, @Nonnull ForkScanner scanner) {} + public void parallelEnd(@NonNull FlowNode parallelStartNode, @NonNull FlowNode parallelEndNode, @NonNull ForkScanner scanner) {} @Override - public void parallelBranchStart(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchStartNode, @Nonnull ForkScanner scanner) {} + public void parallelBranchStart(@NonNull FlowNode parallelStartNode, @NonNull FlowNode branchStartNode, @NonNull ForkScanner scanner) {} @Override - public void parallelBranchEnd(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchEndNode, @Nonnull ForkScanner scanner) {} + public void parallelBranchEnd(@NonNull FlowNode parallelStartNode, @NonNull FlowNode branchEndNode, @NonNull ForkScanner scanner) {} /** Extend me to do something with nodes inside a chunk */ @Override - public void atomNode(@CheckForNull FlowNode before, @Nonnull FlowNode atomNode, @CheckForNull FlowNode after, @Nonnull ForkScanner scan) {} + public void atomNode(@CheckForNull FlowNode before, @NonNull FlowNode atomNode, @CheckForNull FlowNode after, @NonNull ForkScanner scan) {} } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorage.java b/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorage.java index 87e5ed50..ffe25c6f 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorage.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorage.java @@ -37,7 +37,7 @@ import java.io.OutputStream; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.jenkinsci.plugins.workflow.actions.LogAction; import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner; import org.jenkinsci.plugins.workflow.graph.FlowNode; @@ -62,7 +62,7 @@ public interface LogStorage { * @return a (remotable) build listener; do not bother overriding anything except {@link TaskListener#getLogger} * @see FlowExecutionOwner#getListener */ - @Nonnull BuildListener overallListener() throws IOException, InterruptedException; + @NonNull BuildListener overallListener() throws IOException, InterruptedException; /** * Provides an alternate way of emitting output from a node (such as a step). @@ -73,7 +73,7 @@ public interface LogStorage { * @return a (remotable) task listener; do not bother overriding anything except {@link TaskListener#getLogger} * @see StepContext#get */ - @Nonnull TaskListener nodeListener(@Nonnull FlowNode node) throws IOException, InterruptedException; + @NonNull TaskListener nodeListener(@NonNull FlowNode node) throws IOException, InterruptedException; /** * Provides an alternate way of retrieving output from a build. @@ -84,14 +84,14 @@ public interface LogStorage { * so implementations should be sure to retrieve final log lines * @return a log */ - @Nonnull AnnotatedLargeText overallLog(@Nonnull FlowExecutionOwner.Executable build, boolean complete); + @NonNull AnnotatedLargeText overallLog(@NonNull FlowExecutionOwner.Executable build, boolean complete); /** * Introduces an HTML block with a {@code pipeline-node-} CSS class based on {@link FlowNode#getId}. * @see #endStep * @see #overallLog */ - static @Nonnull String startStep(@Nonnull String id) { + static @NonNull String startStep(@NonNull String id) { return ""; } @@ -100,7 +100,7 @@ public interface LogStorage { * @see #startStep * @see #overallLog */ - static @Nonnull String endStep() { + static @NonNull String endStep() { return ""; } @@ -112,7 +112,7 @@ public interface LogStorage { * @return a log for this just this node * @see LogAction */ - @Nonnull AnnotatedLargeText stepLog(@Nonnull FlowNode node, boolean complete); + @NonNull AnnotatedLargeText stepLog(@NonNull FlowNode node, boolean complete); /** * Provide a file containing the log text. @@ -124,7 +124,7 @@ public interface LogStorage { */ @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "silly rule") @Deprecated - default @Nonnull File getLogFile(@Nonnull FlowExecutionOwner.Executable build, boolean complete) { + default @NonNull File getLogFile(@NonNull FlowExecutionOwner.Executable build, boolean complete) { try { AnnotatedLargeText logText = overallLog(build, complete); FlowExecutionOwner owner = build.asFlowExecutionOwner(); @@ -158,7 +158,7 @@ public interface LogStorage { * @return the mechanism for handling this build, including any necessary fallback * @see LogStorageFactory */ - static @Nonnull LogStorage of(@Nonnull FlowExecutionOwner b) { + static @NonNull LogStorage of(@NonNull FlowExecutionOwner b) { try { for (LogStorageFactory factory : ExtensionList.lookup(LogStorageFactory.class)) { LogStorage storage = factory.forBuild(b); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorageFactory.java b/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorageFactory.java index f5382836..4b3f4c6d 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorageFactory.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorageFactory.java @@ -25,8 +25,8 @@ package org.jenkinsci.plugins.workflow.log; import hudson.ExtensionPoint; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.Beta; @@ -42,6 +42,6 @@ public interface LogStorageFactory extends ExtensionPoint { * @param b a build about to start * @return a mechanism for handling this build, or null to fall back to the next implementation or the default */ - @CheckForNull LogStorage forBuild(@Nonnull FlowExecutionOwner b); + @CheckForNull LogStorage forBuild(@NonNull FlowExecutionOwner b); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java index 2345f654..d2c220ee 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java @@ -46,9 +46,9 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import jenkins.util.BuildListenerAdapter; import jenkins.util.JenkinsJVM; import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner; @@ -77,7 +77,7 @@ public abstract class TaskListenerDecorator implements /* TODO Remotable */ Seri * @param logger a base logger * @return a possibly patched result */ - public abstract @Nonnull OutputStream decorate(@Nonnull OutputStream logger) throws IOException, InterruptedException; + public abstract @NonNull OutputStream decorate(@NonNull OutputStream logger) throws IOException, InterruptedException; /** * Merges two decorators. @@ -129,7 +129,7 @@ public interface Factory extends ExtensionPoint { * @param owner a build * @return a decorator, optionally */ - @CheckForNull TaskListenerDecorator of(@Nonnull FlowExecutionOwner owner); + @CheckForNull TaskListenerDecorator of(@NonNull FlowExecutionOwner owner); } @@ -143,7 +143,7 @@ public interface Factory extends ExtensionPoint { * @param mainDecorator an additional contextual decorator to apply, if any * @return a possibly wrapped {@code listener} */ - public static BuildListener apply(@Nonnull TaskListener listener, @Nonnull FlowExecutionOwner owner, @CheckForNull TaskListenerDecorator mainDecorator) { + public static BuildListener apply(@NonNull TaskListener listener, @NonNull FlowExecutionOwner owner, @CheckForNull TaskListenerDecorator mainDecorator) { JenkinsJVM.checkJenkinsJVM(); List decorators = Stream.concat( ExtensionList.lookup(TaskListenerDecorator.Factory.class).stream().map(f -> f.of(owner)), @@ -162,8 +162,8 @@ private static class MergedTaskListenerDecorator extends TaskListenerDecorator { private static final long serialVersionUID = 1; - private final @Nonnull TaskListenerDecorator original; - private final @Nonnull TaskListenerDecorator subsequent; + private final @NonNull TaskListenerDecorator original; + private final @NonNull TaskListenerDecorator subsequent; MergedTaskListenerDecorator(TaskListenerDecorator original, TaskListenerDecorator subsequent) { this.original = original; @@ -186,7 +186,7 @@ private static class ConsoleLogFilterAdapter extends TaskListenerDecorator { private static final long serialVersionUID = 1; @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Explicitly checking for serializability.") - private final @Nonnull ConsoleLogFilter filter; + private final @NonNull ConsoleLogFilter filter; ConsoleLogFilterAdapter(ConsoleLogFilter filter) { assert filter instanceof Serializable; @@ -212,13 +212,13 @@ private static final class DecoratedTaskListener implements BuildListener { * The listener we are delegating to, which was expected to be remotable. * Note that we ignore all of its methods other than {@link TaskListener#getLogger}. */ - private final @Nonnull TaskListener delegate; + private final @NonNull TaskListener delegate; /** * A (nonempty) list of decorators we delegate to. * They are applied in reverse order, so the first one has the final say in what gets printed. */ - private final @Nonnull List decorators; + private final @NonNull List decorators; private transient PrintStream logger; @@ -266,8 +266,8 @@ static BuildListener of(BuildListener mainDelegate, TaskListener closeDelegate) private static final long serialVersionUID = 1; - private final @Nonnull TaskListener mainDelegate; - private final @Nonnull TaskListener closeDelegate; + private final @NonNull TaskListener mainDelegate; + private final @NonNull TaskListener closeDelegate; private CloseableTaskListener(TaskListener mainDelegate, TaskListener closeDelegate) { this.mainDelegate = mainDelegate; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/pickles/PickleFactory.java b/src/main/java/org/jenkinsci/plugins/workflow/pickles/PickleFactory.java index fd993c70..f64d6a36 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/pickles/PickleFactory.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/pickles/PickleFactory.java @@ -27,15 +27,15 @@ import hudson.ExtensionList; import hudson.ExtensionPoint; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Provides a way of converting transient objects into {@link Pickle}s. */ public abstract class PickleFactory implements ExtensionPoint { - public abstract @CheckForNull Pickle writeReplace(@Nonnull Object object); + public abstract @CheckForNull Pickle writeReplace(@NonNull Object object); public static ExtensionList all() { return ExtensionList.lookup(PickleFactory.class); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumnDescriptor.java b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumnDescriptor.java index 814f4acb..1675680f 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumnDescriptor.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/visualization/table/FlowNodeViewColumnDescriptor.java @@ -5,7 +5,7 @@ import hudson.model.Descriptor; import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java index 1b3fbabb..c371a60b 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java @@ -61,8 +61,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.logging.Level; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.ArtifactManager; import jenkins.model.ArtifactManagerConfiguration; import jenkins.model.ArtifactManagerFactory; @@ -112,7 +112,7 @@ public class ArtifactManagerTest { /** * Creates an agent, in a Docker container when possible, calls {@link #setUpWorkspace}, then runs some tests. */ - private static void wrapInContainer(@Nonnull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, + private static void wrapInContainer(@NonNull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, TestFunction f) throws Exception { if (factory != null) { ArtifactManagerConfiguration.get().getArtifactManagerFactories().add(factory); @@ -151,7 +151,7 @@ private static void wrapInContainer(@Nonnull JenkinsRule r, @CheckForNull Artifa * @param weirdCharacters as in {@link #artifactArchiveAndDelete} * @param image as in {@link #artifactArchiveAndDelete} */ - public static void artifactArchive(@Nonnull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception { + public static void artifactArchive(@NonNull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception { wrapInContainer(r, factory, weirdCharacters, (agent, p, b, ws) -> { VirtualFile root = b.getArtifactManager().root(); new Verify(agent, root, weirdCharacters).run(); @@ -166,7 +166,7 @@ public static void artifactArchive(@Nonnull JenkinsRule r, @CheckForNull Artifac * @param weirdCharacters check behavior of files with Unicode and various unusual characters in the name * @param image use {@link #prepareImage} in a {@link BeforeClass} block */ - public static void artifactArchiveAndDelete(@Nonnull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception { + public static void artifactArchiveAndDelete(@NonNull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception { wrapInContainer(r, factory, weirdCharacters, (agent, p, b, ws) -> { VirtualFile root = b.getArtifactManager().root(); new Verify(agent, root, weirdCharacters).run(); @@ -182,7 +182,7 @@ public static void artifactArchiveAndDelete(@Nonnull JenkinsRule r, @CheckForNul * @param weirdCharacters as in {@link #artifactArchiveAndDelete} * @param image as in {@link #artifactArchiveAndDelete} */ - public static void artifactStash(@Nonnull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception { + public static void artifactStash(@NonNull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception { wrapInContainer(r, factory, weirdCharacters, new StashFunction(r, weirdCharacters, (p, b, ws, launcher, env, listener) -> { // should not have deleted @@ -196,7 +196,7 @@ public static void artifactStash(@Nonnull JenkinsRule r, @CheckForNull ArtifactM * @param weirdCharacters as in {@link #artifactArchiveAndDelete} * @param image as in {@link #artifactArchiveAndDelete} */ - public static void artifactStashAndDelete(@Nonnull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception { + public static void artifactStashAndDelete(@NonNull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception { wrapInContainer(r, factory, weirdCharacters, new StashFunction(r, weirdCharacters, (p, b, ws, launcher, env, listener) -> { try { @@ -257,7 +257,7 @@ private static class StashFunction implements TestFunction { private final boolean weirdCharacters; private final TestStashFunction f; - StashFunction(@Nonnull JenkinsRule r, boolean weirdCharacters, TestStashFunction f) { + StashFunction(@NonNull JenkinsRule r, boolean weirdCharacters, TestStashFunction f) { this.r = r; this.weirdCharacters = weirdCharacters; this.f = f; diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowTestUtils.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowTestUtils.java index fc30d154..59642ab3 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowTestUtils.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowTestUtils.java @@ -30,7 +30,7 @@ import org.jenkinsci.plugins.workflow.steps.StepDescriptor; import org.junit.Assert; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -40,7 +40,7 @@ * @author Sam Van Oort */ public class FlowTestUtils { - public static Predicate predicateMatchStepDescriptor(@Nonnull final String descriptorId) { + public static Predicate predicateMatchStepDescriptor(@NonNull final String descriptorId) { return new Predicate() { @Override public boolean apply(FlowNode input) { @@ -58,7 +58,7 @@ public static final class CollectingVisitor implements FlowNodeVisitor { ArrayList visited = new ArrayList<>(); @Override - public boolean visit(@Nonnull FlowNode f) { + public boolean visit(@NonNull FlowNode f) { visited.add(f); return true; } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/NoOpChunkFinder.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/NoOpChunkFinder.java index 1b866d90..2d902d41 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/NoOpChunkFinder.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/NoOpChunkFinder.java @@ -2,8 +2,8 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * For test use: a ChunkFinder that never returns chunks, to use in testing parallel handling only. @@ -16,12 +16,12 @@ public boolean isStartInsideChunk() { } @Override - public boolean isChunkStart(@Nonnull FlowNode current, @CheckForNull FlowNode previous) { + public boolean isChunkStart(@NonNull FlowNode current, @CheckForNull FlowNode previous) { return false; } @Override - public boolean isChunkEnd(@Nonnull FlowNode current, @CheckForNull FlowNode previous) { + public boolean isChunkEnd(@NonNull FlowNode current, @CheckForNull FlowNode previous) { return false; } } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java index dddc3c5e..fcefa8da 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java @@ -4,8 +4,8 @@ import org.junit.Assert; import org.jvnet.hudson.test.Issue; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -142,37 +142,37 @@ public String toString() { public ArrayList calls = new ArrayList<>(); @Override - public void chunkStart(@Nonnull FlowNode startNode, @CheckForNull FlowNode beforeBlock, @Nonnull ForkScanner scanner) { + public void chunkStart(@NonNull FlowNode startNode, @CheckForNull FlowNode beforeBlock, @NonNull ForkScanner scanner) { calls.add(new CallEntry(CallType.CHUNK_START, startNode, beforeBlock)); } @Override - public void chunkEnd(@Nonnull FlowNode endNode, @CheckForNull FlowNode afterChunk, @Nonnull ForkScanner scanner) { + public void chunkEnd(@NonNull FlowNode endNode, @CheckForNull FlowNode afterChunk, @NonNull ForkScanner scanner) { calls.add(new CallEntry(CallType.CHUNK_END, endNode, afterChunk)); } @Override - public void parallelStart(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchNode, @Nonnull ForkScanner scanner) { + public void parallelStart(@NonNull FlowNode parallelStartNode, @NonNull FlowNode branchNode, @NonNull ForkScanner scanner) { calls.add(new CallEntry(CallType.PARALLEL_START, parallelStartNode, branchNode)); } @Override - public void parallelEnd(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode parallelEndNode, @Nonnull ForkScanner scanner) { + public void parallelEnd(@NonNull FlowNode parallelStartNode, @NonNull FlowNode parallelEndNode, @NonNull ForkScanner scanner) { calls.add(new CallEntry(CallType.PARALLEL_END, parallelStartNode, parallelEndNode)); } @Override - public void parallelBranchStart(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchStartNode, @Nonnull ForkScanner scanner) { + public void parallelBranchStart(@NonNull FlowNode parallelStartNode, @NonNull FlowNode branchStartNode, @NonNull ForkScanner scanner) { calls.add(new CallEntry(CallType.PARALLEL_BRANCH_START, parallelStartNode, branchStartNode)); } @Override - public void parallelBranchEnd(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchEndNode, @Nonnull ForkScanner scanner) { + public void parallelBranchEnd(@NonNull FlowNode parallelStartNode, @NonNull FlowNode branchEndNode, @NonNull ForkScanner scanner) { calls.add(new CallEntry(CallType.PARALLEL_BRANCH_END, parallelStartNode, branchEndNode)); } @Override - public void atomNode(@CheckForNull FlowNode before, @Nonnull FlowNode atomNode, @CheckForNull FlowNode after, @Nonnull ForkScanner scan) { + public void atomNode(@CheckForNull FlowNode before, @NonNull FlowNode atomNode, @CheckForNull FlowNode after, @NonNull ForkScanner scan) { calls.add(new CallEntry(CallType.ATOM_NODE, before, atomNode, after)); } From c334f74c74673f6da93d6e19d6742f46b3423aef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jan 2022 01:15:55 +0000 Subject: [PATCH 058/114] Bump git-changelist-maven-extension from 1.2 to 1.3 Bumps [git-changelist-maven-extension](https://github.com/jenkinsci/incrementals-tools) from 1.2 to 1.3. - [Release notes](https://github.com/jenkinsci/incrementals-tools/releases) - [Commits](https://github.com/jenkinsci/incrementals-tools/compare/parent-1.2...parent-1.3) --- updated-dependencies: - dependency-name: io.jenkins.tools.incrementals:git-changelist-maven-extension dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .mvn/extensions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 43d62816..a65d82e1 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -2,6 +2,6 @@ io.jenkins.tools.incrementals git-changelist-maven-extension - 1.2 + 1.3 From 04f8fe15631ac984e709938a9567ef5d4a66e375 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 10 Jan 2022 08:37:37 -0500 Subject: [PATCH 059/114] Update parent, baseline, BOM --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index c105a7b7..426777b5 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.jenkins-ci.plugins plugin - 4.31 + 4.33 org.jenkins-ci.plugins.workflow @@ -63,7 +63,7 @@ 999999-SNAPSHOT - 2.222.4 + 2.249.1 8 false true @@ -73,8 +73,8 @@ io.jenkins.tools.bom - bom-2.222.x - 887.vae9c8ac09ff7 + bom-2.249.x + 984.vb5eaac999a7e import pom From d01f4564ce337ebc58b2da2b8909425ac45bbe62 Mon Sep 17 00:00:00 2001 From: offa Date: Wed, 8 Dec 2021 10:48:32 +0100 Subject: [PATCH 060/114] Replace deprecated method calls --- .../jenkinsci/plugins/workflow/actions/ArgumentsAction.java | 2 +- .../org/jenkinsci/plugins/workflow/flow/StashManager.java | 5 +---- .../org/jenkinsci/plugins/workflow/ArtifactManagerTest.java | 5 +++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java index 5dbc62aa..0e6baf99 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java @@ -303,7 +303,7 @@ public boolean isUnmodifiedArguments() { } // helper method to capture generic type private static UninstantiatedDescribable resolve(DescribableModel model, Map arguments) throws Exception { - return model.uninstantiate2(model.instantiate(arguments)); + return model.uninstantiate2(model.instantiate(arguments, null)); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java index 7f90b3b5..cc9e8b34 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java @@ -367,11 +367,8 @@ public interface StashAwareArtifactManager /* extends ArtifactManager */ { Map files = new HashMap<>(); for (String path : srcroot.list("**/*", null, false)) { files.put(path, path); - InputStream in = srcroot.child(path).open(); - try { + try(InputStream in = srcroot.child(path).open()) { dstDir.child(path).copyFrom(in); - } finally { - IOUtils.closeQuietly(in); } } if (!files.isEmpty()) { diff --git a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java index c371a60b..057a0ea0 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java @@ -58,6 +58,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collections; import java.util.logging.Level; @@ -401,7 +402,7 @@ private void assertFile(VirtualFile f, String contents) throws Exception { assertEquals(contents.length(), f.length()); assertThat(f.lastModified(), not(is(0))); try (InputStream is = f.open()) { - assertEquals(contents, IOUtils.toString(is)); + assertEquals(contents, IOUtils.toString(is, Charset.defaultCharset())); } URL url = f.toExternalURL(); if (url != null) { @@ -416,7 +417,7 @@ private static final class RemoteOpenURL extends MasterToSlaveCallable Date: Wed, 8 Dec 2021 10:48:32 +0100 Subject: [PATCH 061/114] Fix some unchecked warnings and raw types --- .../workflow/actions/ArgumentsAction.java | 8 ++++---- .../plugins/workflow/flow/FlowCopier.java | 2 +- .../workflow/log/ConsoleAnnotators.java | 2 +- .../plugins/workflow/log/LogStorage.java | 2 +- .../workflow/log/TaskListenerDecorator.java | 2 +- .../graphanalysis/FlowScannerTest.java | 20 +++++++++---------- .../graphanalysis/ForkScannerTest.java | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java index 0e6baf99..a67f2c44 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java @@ -229,13 +229,13 @@ public Object getArgumentValue(@NonNull String argumentName) { public Object getArgumentValueOrReason(@NonNull String argumentName) { Object ob = getArgumentsInternal().get(argumentName); if (ob instanceof Map) { - return Collections.unmodifiableMap((Map)ob); + return Collections.unmodifiableMap((Map)ob); } else if (ob instanceof Set) { - return Collections.unmodifiableSet((Set)ob); + return Collections.unmodifiableSet((Set)ob); } else if (ob instanceof List) { - return Collections.unmodifiableList((List)ob); + return Collections.unmodifiableList((List)ob); } else if (ob instanceof Collection) { - return Collections.unmodifiableCollection((Collection)ob); + return Collections.unmodifiableCollection((Collection)ob); } return ob; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowCopier.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowCopier.java index 96855988..762ed61c 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowCopier.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowCopier.java @@ -66,7 +66,7 @@ public static abstract class ByRun extends FlowCopier { Queue.Executable originalExec = original.getExecutable(); Queue.Executable copyExec = copy.getExecutable(); if (originalExec instanceof Run && copyExec instanceof Run) { - copy((Run) originalExec, (Run) copyExec, copy.getListener()); + copy((Run) originalExec, (Run) copyExec, copy.getListener()); } } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/ConsoleAnnotators.java b/src/main/java/org/jenkinsci/plugins/workflow/log/ConsoleAnnotators.java index 5d5869c3..f25d09f0 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/ConsoleAnnotators.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/ConsoleAnnotators.java @@ -76,7 +76,7 @@ public static ConsoleAnnotator createAnnotator(T context) throws IOExcept ClassFilter.DEFAULT)) { long timestamp = ois.readLong(); if (TimeUnit.HOURS.toMillis(1) > abs(System.currentTimeMillis() - timestamp)) { - @SuppressWarnings("unchecked") ConsoleAnnotator annotator = (ConsoleAnnotator) ois.readObject(); + @SuppressWarnings("unchecked") ConsoleAnnotator annotator = (ConsoleAnnotator) ois.readObject(); return annotator; } } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorage.java b/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorage.java index ffe25c6f..29b7ca07 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorage.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/LogStorage.java @@ -145,7 +145,7 @@ public interface LogStorage { } catch (Exception x) { Logger.getLogger(LogStorage.class.getName()).log(Level.WARNING, null, x); if (build instanceof Run) { - return new File(((Run) build).getRootDir(), "log"); + return new File(((Run) build).getRootDir(), "log"); } else { return new File("broken.log"); // not much we can do } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java index d2c220ee..36a7b1ba 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java @@ -195,7 +195,7 @@ private static class ConsoleLogFilterAdapter extends TaskListenerDecorator { @SuppressWarnings("deprecation") // the compatibility code in ConsoleLogFilter fails to delegate to the old overload when given a null argument @Override public OutputStream decorate(OutputStream logger) throws IOException, InterruptedException { - return filter.decorateLogger((AbstractBuild) null, logger); + return filter.decorateLogger((AbstractBuild) null, logger); } @Override public String toString() { diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java index 6f3e1379..1e8ca87a 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java @@ -87,8 +87,8 @@ public void testAbstractScanner() throws Exception { AbstractFlowScanner linear = new LinearScanner(); // ## Bunch of tests for convertToFastCheckable ## - Assert.assertEquals(Collections.EMPTY_SET, linear.convertToFastCheckable(null)); - Assert.assertEquals(Collections.EMPTY_SET, linear.convertToFastCheckable(new ArrayList<>())); + Assert.assertEquals(Collections.emptySet(), linear.convertToFastCheckable(null)); + Assert.assertEquals(Collections.emptySet(), linear.convertToFastCheckable(new ArrayList<>())); Collection coll = linear.convertToFastCheckable(Arrays.asList(intermediateNode)); Assert.assertTrue("Singleton set used for one element", coll instanceof AbstractSet); @@ -125,13 +125,13 @@ public void testAbstractScanner() throws Exception { Collection nullColl = null; Assert.assertTrue(linear.setup(heads, null)); - Assert.assertTrue(linear.setup(heads, Collections.EMPTY_SET)); + Assert.assertTrue(linear.setup(heads, Collections.emptySet())); Assert.assertFalse(linear.setup(nullColl, heads)); Assert.assertFalse(linear.setup(nullColl, null)); Assert.assertFalse(linear.setup(heads, heads)); Assert.assertTrue(linear.setup(heads)); Assert.assertFalse(linear.setup(nullColl)); - Assert.assertFalse(linear.setup(Collections.EMPTY_SET)); + Assert.assertFalse(linear.setup(Collections.emptySet())); Assert.assertTrue(linear.setup(lastNode)); Assert.assertTrue(linear.setup(lastNode, nullColl)); Assert.assertFalse(linear.setup(nullNode)); @@ -145,12 +145,12 @@ public void testAbstractScanner() throws Exception { FlowNode firstEchoNode = exec.getNode("5"); FlowExecution nullExecution = null; - Assert.assertEquals(firstEchoNode, linear.findFirstMatch(heads, Collections.EMPTY_LIST, FlowTestUtils.MATCH_ECHO_STEP)); + Assert.assertEquals(firstEchoNode, linear.findFirstMatch(heads, Collections.emptySet(), FlowTestUtils.MATCH_ECHO_STEP)); Assert.assertEquals(firstEchoNode, linear.findFirstMatch(heads, FlowTestUtils.MATCH_ECHO_STEP)); Assert.assertEquals(firstEchoNode, linear.findFirstMatch(lastNode, FlowTestUtils.MATCH_ECHO_STEP)); Assert.assertEquals(firstEchoNode, linear.findFirstMatch(exec, FlowTestUtils.MATCH_ECHO_STEP)); Assert.assertNull(linear.findFirstMatch(nullColl, FlowTestUtils.MATCH_ECHO_STEP)); - Assert.assertNull(linear.findFirstMatch(Collections.EMPTY_SET, FlowTestUtils.MATCH_ECHO_STEP)); + Assert.assertNull(linear.findFirstMatch(Collections.emptySet(), FlowTestUtils.MATCH_ECHO_STEP)); Assert.assertNull(linear.findFirstMatch(nullNode, FlowTestUtils.MATCH_ECHO_STEP)); Assert.assertNull(linear.findFirstMatch(nullExecution, FlowTestUtils.MATCH_ECHO_STEP)); @@ -158,9 +158,9 @@ public void testAbstractScanner() throws Exception { // Filtered nodes FlowTestUtils.assertNodeOrder("Filtered echo nodes", linear.filteredNodes(heads, FlowTestUtils.MATCH_ECHO_STEP), 5, 4); FlowTestUtils.assertNodeOrder("Filtered echo nodes", linear.filteredNodes(heads, Collections.singleton(intermediateNode), FlowTestUtils.MATCH_ECHO_STEP), 5); - Assert.assertEquals(0, linear.filteredNodes(heads, null, (Predicate) Predicates.alwaysFalse()).size()); + Assert.assertEquals(0, linear.filteredNodes(heads, null, Predicates.alwaysFalse()).size()); Assert.assertEquals(0, linear.filteredNodes(nullNode, FlowTestUtils.MATCH_ECHO_STEP).size()); - Assert.assertEquals(0, linear.filteredNodes(Collections.EMPTY_SET, FlowTestUtils.MATCH_ECHO_STEP).size()); + Assert.assertEquals(0, linear.filteredNodes(Collections.emptySet(), FlowTestUtils.MATCH_ECHO_STEP).size()); // Same filter using the filterator linear.setup(heads); @@ -174,7 +174,7 @@ public void testAbstractScanner() throws Exception { // Visitor pattern tests FlowTestUtils.CollectingVisitor visitor = new FlowTestUtils.CollectingVisitor(); - linear.visitAll(Collections.EMPTY_SET, visitor); + linear.visitAll(Collections.emptySet(), visitor); Assert.assertEquals(0, visitor.getVisited().size()); visitor.reset(); @@ -287,7 +287,7 @@ public void testBasicScanWithBlock() throws Exception { // // Test block jump core FlowNode headCandidate = exec.getNode("8"); - Assert.assertEquals(exec.getNode("4"), linearBlockHoppingScanner.jumpBlockScan(headCandidate, Collections.EMPTY_SET)); + Assert.assertEquals(exec.getNode("4"), linearBlockHoppingScanner.jumpBlockScan(headCandidate, Collections.emptySet())); Assert.assertTrue("Setup should return true if we can iterate", linearBlockHoppingScanner.setup(headCandidate, null)); // Test the actual iteration diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java index fd686523..261fdba7 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java @@ -707,7 +707,7 @@ public void testSimpleVisitor() throws Exception { FlowExecution exec = this.SIMPLE_PARALLEL_RUN.getExecution(); ForkScanner f = new ForkScanner(); f.setup(exec.getCurrentHeads()); - Assert.assertArrayEquals(new HashSet(exec.getCurrentHeads()).toArray(), new HashSet(f.currentParallelHeads()).toArray()); + Assert.assertArrayEquals(new HashSet<>(exec.getCurrentHeads()).toArray(), new HashSet<>(f.currentParallelHeads()).toArray()); List expectedHeads = f.currentParallelHeads(); sanityTestIterationAndVisiter(exec.getCurrentHeads()); From 4ee389f6e9ac9aeb91c8648930eb1857bec39d06 Mon Sep 17 00:00:00 2001 From: offa Date: Wed, 8 Dec 2021 10:48:32 +0100 Subject: [PATCH 062/114] Make members final --- .../org/jenkinsci/plugins/workflow/actions/TagsAction.java | 2 +- .../plugins/workflow/graphanalysis/ForkScanner.java | 2 +- .../workflow/graphanalysis/ParallelMemoryFlowChunk.java | 2 +- .../plugins/workflow/graphanalysis/ForkScannerTest.java | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/TagsAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/TagsAction.java index fd38eba1..b1380e73 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/TagsAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/TagsAction.java @@ -41,7 +41,7 @@ public class TagsAction implements PersistentAction { private static final String displayName = "Tags"; private static final String urlSuffix = "tags"; - private LinkedHashMap tags = new LinkedHashMap<>(); + private final LinkedHashMap tags = new LinkedHashMap<>(); /** * Add a tag key:value pair to this FlowNode, null or empty values are ignored diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java index aca1d3ac..d00c80c0 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java @@ -368,7 +368,7 @@ ArrayDeque leastCommonAncestor(@NonNull final Set ArrayDeque parallelForks = new ArrayDeque<>(); // Tracks the discovered forks in order of encounter Predicate notAHead = new Predicate() { // Filter out pre-existing heads - Collection checkHeads = convertToFastCheckable(heads); + final Collection checkHeads = convertToFastCheckable(heads); @Override public boolean apply(FlowNode input) { return !checkHeads.contains(input); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelMemoryFlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelMemoryFlowChunk.java index f8e15c66..9026843f 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelMemoryFlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelMemoryFlowChunk.java @@ -39,7 +39,7 @@ public class ParallelMemoryFlowChunk extends MemoryFlowChunk implements ParallelFlowChunk { // LinkedHashMap to preserve insert order - private LinkedHashMap branches = new LinkedHashMap<>(); + private final LinkedHashMap branches = new LinkedHashMap<>(); public ParallelMemoryFlowChunk(@NonNull FlowNode firstNode, @NonNull FlowNode lastNode) { super (null,firstNode, lastNode, null); diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java index 261fdba7..dc0f729e 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java @@ -70,7 +70,7 @@ public class ForkScannerTest { public static Predicate predicateForCallEntryType(final TestVisitor.CallType type) { return new Predicate() { - TestVisitor.CallType myType = type; + final TestVisitor.CallType myType = type; @Override public boolean test(TestVisitor.CallEntry input) { @@ -430,9 +430,9 @@ public void testEmptyParallel() throws Exception { Assert.assertEquals(9, outputs.size()); } - private Function NODE_TO_ID = input -> input != null ? input.getId() : null; + private final Function NODE_TO_ID = input -> input != null ? input.getId() : null; - private Function CALL_TO_NODE_ID = + private final Function CALL_TO_NODE_ID = input -> input != null && input.getNodeId() != null ? input.getNodeId().toString() From b094fa683db5f86b8ae82be65dad941253af7490 Mon Sep 17 00:00:00 2001 From: offa Date: Wed, 8 Dec 2021 10:48:33 +0100 Subject: [PATCH 063/114] Cleanup exception declarations, imports and toString() calls --- .../org/jenkinsci/plugins/workflow/FilePathUtils.java | 3 +-- .../plugins/workflow/actions/ArgumentsAction.java | 2 +- .../jenkinsci/plugins/workflow/flow/FlowExecution.java | 2 +- .../plugins/workflow/flow/FlowExecutionOwner.java | 2 +- .../flow/GlobalDefaultFlowDurabilityLevel.java | 2 +- .../plugins/workflow/log/BrokenLogStorage.java | 4 ++-- .../jenkinsci/plugins/workflow/log/FileLogStorage.java | 4 ++-- .../plugins/workflow/DirectArtifactManagerFactory.java | 2 +- .../plugins/workflow/flow/FlowExecutionListTest.java | 2 +- .../jenkinsci/plugins/workflow/graph/FlowNodeTest.java | 2 +- .../workflow/graphanalysis/ForkScannerTest.java | 2 +- .../plugins/workflow/graphanalysis/TestVisitor.java | 10 +++++----- .../plugins/workflow/log/LogStorageTestBase.java | 8 ++++---- 13 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java b/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java index 60586206..a12de116 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/FilePathUtils.java @@ -32,7 +32,6 @@ import hudson.remoting.Channel; import hudson.remoting.VirtualChannel; import hudson.slaves.ComputerListener; -import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -143,7 +142,7 @@ static Collection getChannelNames() { addChannel(c.getChannel(), c.getName()); } } - @Override public void preOnline(Computer c, Channel channel, FilePath root, TaskListener listener) throws IOException, InterruptedException { + @Override public void preOnline(Computer c, Channel channel, FilePath root, TaskListener listener) { addChannel(channel, c.getName()); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java index a67f2c44..b926b9dc 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/ArgumentsAction.java @@ -302,7 +302,7 @@ public boolean isUnmodifiedArguments() { return args; } // helper method to capture generic type - private static UninstantiatedDescribable resolve(DescribableModel model, Map arguments) throws Exception { + private static UninstantiatedDescribable resolve(DescribableModel model, Map arguments) { return model.uninstantiate2(model.instantiate(arguments, null)); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java index b1fa3af9..ca288540 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java @@ -109,7 +109,7 @@ public FlowDurabilityHint getDurabilityHint() { /** * Should be called by the flow owner after it is deserialized. */ - public /*abstract*/ void onLoad(FlowExecutionOwner owner) throws IOException { + public /*abstract*/ void onLoad(FlowExecutionOwner owner) { if (Util.isOverridden(FlowExecution.class, getClass(), "onLoad")) { onLoad(); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java index af3d8f84..97d29f71 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java @@ -174,7 +174,7 @@ private static class DummyOwner extends FlowExecutionOwner { @Override public int hashCode() { return 0; } - @Override public TaskListener getListener() throws IOException { + @Override public TaskListener getListener() { return TaskListener.NULL; } } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java index f6ae5838..37fe4209 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java @@ -48,7 +48,7 @@ public void setDurabilityHint(FlowDurabilityHint hint){ } @Override - public boolean configure(StaplerRequest req, JSONObject json) throws FormException { + public boolean configure(StaplerRequest req, JSONObject json) { // TODO verify if this is covered by permissions checks or we need an explicit check here. Object ob = json.opt("durabilityHint"); FlowDurabilityHint hint = null; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/BrokenLogStorage.java b/src/main/java/org/jenkinsci/plugins/workflow/log/BrokenLogStorage.java index 3b600903..4472ec7a 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/BrokenLogStorage.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/BrokenLogStorage.java @@ -48,11 +48,11 @@ public BrokenLogStorage(Throwable x) { this.x = x; } - @Override public BuildListener overallListener() throws IOException, InterruptedException { + @Override public BuildListener overallListener() throws IOException { throw new IOException(x); } - @Override public TaskListener nodeListener(FlowNode node) throws IOException, InterruptedException { + @Override public TaskListener nodeListener(FlowNode node) throws IOException { throw new IOException(x); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/FileLogStorage.java b/src/main/java/org/jenkinsci/plugins/workflow/log/FileLogStorage.java index f2789a71..dedb7714 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/FileLogStorage.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/FileLogStorage.java @@ -111,11 +111,11 @@ private synchronized void open() throws IOException { } } - @Override public BuildListener overallListener() throws IOException, InterruptedException { + @Override public BuildListener overallListener() throws IOException { return new BufferedBuildListener(new IndexOutputStream(null)); } - @Override public TaskListener nodeListener(FlowNode node) throws IOException, InterruptedException { + @Override public TaskListener nodeListener(FlowNode node) throws IOException { return new BufferedBuildListener(new IndexOutputStream(node.getId())); } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/DirectArtifactManagerFactory.java b/src/test/java/org/jenkinsci/plugins/workflow/DirectArtifactManagerFactory.java index d8273c3d..f40c9062 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/DirectArtifactManagerFactory.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/DirectArtifactManagerFactory.java @@ -148,7 +148,7 @@ private static final class DirectArtifactManager extends ArtifactManager { dir = new File(Jenkins.get().getRootDir(), Util.getDigestOf(build.getExternalizableId())); } - @Override public boolean delete() throws IOException, InterruptedException { + @Override public boolean delete() throws IOException { if (!dir.exists()) { return false; } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java index 2ef4533e..8cf59f30 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java @@ -165,7 +165,7 @@ public static class NonResumableStep extends Step implements Serializable { @DataBoundConstructor public NonResumableStep() { } @Override - public StepExecution start(StepContext sc) throws Exception { + public StepExecution start(StepContext sc) { return new ExecutionImpl(sc); } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graph/FlowNodeTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graph/FlowNodeTest.java index 569c1610..f713d901 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graph/FlowNodeTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graph/FlowNodeTest.java @@ -593,7 +593,7 @@ public static class DoubleWarningStep extends Step { public DoubleWarningStep() {} @Override - public StepExecution start(StepContext context) throws Exception { + public StepExecution start(StepContext context) { return new StepExecution(context) { @Override public boolean start() throws Exception { diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java index dc0f729e..a62545d1 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java @@ -204,7 +204,7 @@ private void assertIncompleteParallelsHaveEventsForEnd(List heads, Tes } /** Runs a fairly extensive suite of sanity tests of iteration and visitor use */ - private void sanityTestIterationAndVisiter(List heads) throws Exception { + private void sanityTestIterationAndVisiter(List heads) { ForkScanner scan = new ForkScanner(); TestVisitor test = new TestVisitor(); scan.setup(heads); diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java index fcefa8da..e9def9f9 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java @@ -194,7 +194,7 @@ public List filteredCallsByType(TestVisitor.CallType type /** Tests that the rules laid out in {@link SimpleChunkVisitor} javadocs are followed. * Specifically: no atomNode dupes for the same node, no atomNode with a start/end for the same node*/ - public void assertNoDupes() throws Exception { + public void assertNoDupes() { // Full equality check List entries = new ArrayList<>(); HashSet visitedAtomNodes = new HashSet<>(); @@ -236,7 +236,7 @@ public void assertNoDupes() throws Exception { /** Parallel callback events CANNOT have nulls for the parallel start node */ @Issue("JENKINS-39841") - public void assertNoIllegalNullsInEvents() throws Exception { + public void assertNoIllegalNullsInEvents() { for (CallEntry ce : calls) { Integer id = ce.getNodeId(); Assert.assertNotNull("Callback with illegally null node: "+ce, id); @@ -263,7 +263,7 @@ public void assertAllNodesGotChunkEvents(Iterable nodes) { } } - public void assertMatchingParallelBranchStartEnd() throws Exception { + public void assertMatchingParallelBranchStartEnd() { // Map the parallel start node to the start/end nodes for all branches HashMap> branchStartIds = new HashMap<>(); HashMap> branchEndIds = new HashMap<>(); @@ -308,7 +308,7 @@ public void assertMatchingParallelBranchStartEnd() throws Exception { } /** Verify that we have balanced start/end for parallels */ - public void assertMatchingParallelStartEnd() throws Exception { + public void assertMatchingParallelStartEnd() { // It's like balancing parentheses, starts and ends must be equal ArrayDeque openParallelStarts = new ArrayDeque<>(); @@ -334,7 +334,7 @@ public void assertMatchingParallelStartEnd() throws Exception { for (Integer parallelStartId : openParallelStarts) { sb.append(parallelStartId).append(','); } - Assert.fail("Parallel ends with no starts, for parallel(s) with start nodes IDs: "+sb.toString()); + Assert.fail("Parallel ends with no starts, for parallel(s) with start nodes IDs: " + sb); } } } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java b/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java index d5221237..41e5c51b 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java @@ -309,11 +309,11 @@ private long assertLog(Callable> text, long start, String return pos; } - protected final void assertLength(long length) throws Exception { + protected final void assertLength(long length) { assertLength(text(), length); } - protected final void assertLength(String id, long length) throws Exception { + protected final void assertLength(String id, long length) { assertLength(text(id), length); } @@ -321,11 +321,11 @@ private void assertLength(AnnotatedLargeText text, long length) { assertEquals(length, text.length()); } - private AnnotatedLargeText text() throws Exception { + private AnnotatedLargeText text() { return createStorage().overallLog(null, true); } - private AnnotatedLargeText text(String id) throws Exception { + private AnnotatedLargeText text(String id) { return createStorage().stepLog(new MockNode(id), true); } From e27aa3d56d8f785ce091cbb82a1f72bd71320dfb Mon Sep 17 00:00:00 2001 From: offa Date: Wed, 8 Dec 2021 10:48:33 +0100 Subject: [PATCH 064/114] Replace anonymous classes with lambdas --- .../plugins/workflow/flow/FlowExecutionList.java | 6 +----- .../workflow/graphanalysis/FlowScanningUtils.java | 7 +------ .../workflow/graphanalysis/FlowTestUtils.java | 15 ++++++--------- .../workflow/graphanalysis/ForkScannerTest.java | 4 ++-- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 034f7006..5a20b488 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -134,11 +134,7 @@ private synchronized void saveLater() { final List copy = new ArrayList<>(runningTasks.getView()); LOGGER.log(Level.FINE, "scheduling save of {0}", copy); try { - executor.submit(new Runnable() { - @Override public void run() { - save(copy); - } - }); + executor.submit(() -> save(copy)); } catch (RejectedExecutionException x) { LOGGER.log(Level.FINE, "could not schedule save, perhaps because Jenkins is shutting down; saving immediately", x); save(copy); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScanningUtils.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScanningUtils.java index 6b6debf3..b9bc773c 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScanningUtils.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScanningUtils.java @@ -52,12 +52,7 @@ private FlowScanningUtils() {} */ @NonNull public static Predicate hasActionPredicate(@NonNull final Class actionClass) { - return new Predicate() { - @Override - public boolean apply(FlowNode input) { - return (input != null && input.getAction(actionClass) != null); - } - }; + return input -> (input != null && input.getAction(actionClass) != null); } // Default predicates, which may be used for common conditions diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowTestUtils.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowTestUtils.java index 59642ab3..56ea42f3 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowTestUtils.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowTestUtils.java @@ -41,16 +41,13 @@ */ public class FlowTestUtils { public static Predicate predicateMatchStepDescriptor(@NonNull final String descriptorId) { - return new Predicate() { - @Override - public boolean apply(FlowNode input) { - if (input instanceof StepAtomNode) { - StepAtomNode san = (StepAtomNode)input; - StepDescriptor sd = san.getDescriptor(); - return sd != null && descriptorId.equals(sd.getId()); - } - return false; + return input -> { + if (input instanceof StepAtomNode) { + StepAtomNode san = (StepAtomNode)input; + StepDescriptor sd = san.getDescriptor(); + return sd != null && descriptorId.equals(sd.getId()); } + return false; }; } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java index a62545d1..10a9b140 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java @@ -180,7 +180,7 @@ private void assertIncompleteParallelsHaveEventsForEnd(List heads, Tes // Verify we have at least one appropriate parallel end event, for the mandatory parallel List parallelEnds = test.filteredCallsByType(TestVisitor.CallType.PARALLEL_END).stream() - .map(CALL_TO_NODE_ID::apply) + .map(CALL_TO_NODE_ID) .collect(Collectors.toList()); boolean hasMatchingEnd = false; for (FlowNode f : heads) { @@ -193,7 +193,7 @@ private void assertIncompleteParallelsHaveEventsForEnd(List heads, Tes List branchEnds = test.filteredCallsByType(TestVisitor.CallType.PARALLEL_BRANCH_END).stream() - .map(CALL_TO_NODE_ID::apply) + .map(CALL_TO_NODE_ID) .collect(Collectors.toList()); // Verify each branch has a branch end event for (FlowNode f : heads) { From efc1c898e33cd973f138cd19ee263dace9174eda Mon Sep 17 00:00:00 2001 From: offa Date: Wed, 8 Dec 2021 10:48:33 +0100 Subject: [PATCH 065/114] Refactoring --- .../plugins/workflow/flow/DurabilityBasicsTest.java | 3 ++- .../plugins/workflow/graphanalysis/FlowScannerTest.java | 2 +- .../plugins/workflow/graphanalysis/ForkScannerTest.java | 7 ++++--- .../plugins/workflow/graphanalysis/TestVisitor.java | 4 +--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java index 2d2ddb67..786c6f52 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.Optional; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** @@ -84,7 +85,7 @@ public void managePermissionShouldAccessGlobalConfig() throws Throwable { try (ACLContext c = ACL.as(User.getById(USER, true))) { Collection descriptors = Functions.getSortedDescriptorsForGlobalConfigUnclassified(); - assertTrue("Global configuration should not be accessible to READ users", descriptors.size() == 0); + assertEquals("Global configuration should not be accessible to READ users", 0, descriptors.size()); } try (ACLContext c = ACL.as(User.getById(MANAGER, true))) { Collection descriptors = Functions.getSortedDescriptorsForGlobalConfigUnclassified(); diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java index 1e8ca87a..2732725e 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/FlowScannerTest.java @@ -90,7 +90,7 @@ public void testAbstractScanner() throws Exception { Assert.assertEquals(Collections.emptySet(), linear.convertToFastCheckable(null)); Assert.assertEquals(Collections.emptySet(), linear.convertToFastCheckable(new ArrayList<>())); - Collection coll = linear.convertToFastCheckable(Arrays.asList(intermediateNode)); + Collection coll = linear.convertToFastCheckable(Collections.singletonList(intermediateNode)); Assert.assertTrue("Singleton set used for one element", coll instanceof AbstractSet); Assert.assertEquals(1, coll.size()); diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java index 10a9b140..86f1da4b 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScannerTest.java @@ -49,6 +49,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -278,7 +279,7 @@ public void testForkedScanner() throws Exception { Assert.assertNotNull(scanner.parallelBlockStartStack); Assert.assertEquals(0, scanner.parallelBlockStartStack.size()); Assert.assertEquals(exec.getNode("4"), scanner.currentParallelStartNode); - sanityTestIterationAndVisiter(Arrays.asList(exec.getNode("13"))); + sanityTestIterationAndVisiter(Collections.singletonList(exec.getNode("13"))); ForkScanner.ParallelBlockStart start = scanner.currentParallelStart; Assert.assertEquals(1, start.unvisited.size()); @@ -518,7 +519,7 @@ public void testSingleNestedParallelBranches() throws Exception { FlowNode echoNode = new DepthFirstScanner().findFirstMatch(b.getExecution(), new NodeStepTypePredicate(EchoStep.DescriptorImpl.byFunctionName("echo"))); Assert.assertNotNull(echoNode); sanityTestIterationAndVisiter(b.getExecution().getCurrentHeads()); - sanityTestIterationAndVisiter(Arrays.asList(echoNode)); + sanityTestIterationAndVisiter(Collections.singletonList(echoNode)); TestVisitor visitor = new TestVisitor(); ForkScanner scanner = new ForkScanner(); @@ -547,7 +548,7 @@ public void testLeastCommonAncestor() throws Exception { Assert.assertArrayEquals(heads.toArray(), start.unvisited.toArray()); // Ensure no issues with single start triggering least common ancestor - heads = new LinkedHashSet<>(Arrays.asList(exec.getNode("4"))); + heads = new LinkedHashSet<>(Collections.singletonList(exec.getNode("4"))); scan.setup(heads); Assert.assertNull(scan.currentParallelStart); Assert.assertTrue(scan.parallelBlockStartStack == null || scan.parallelBlockStartStack.isEmpty()); diff --git a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java index e9def9f9..f71f2325 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/graphanalysis/TestVisitor.java @@ -59,9 +59,7 @@ public CallEntry(CallType type, FlowNode... nodes) { public CallEntry(CallType type, int... vals) { this.type = type; - for (int i=0; i Date: Wed, 8 Dec 2021 10:48:33 +0100 Subject: [PATCH 066/114] Add missing nullability annotations --- .../workflow/flow/FlowExecutionList.java | 8 ++++---- .../workflow/flow/FlowExecutionOwner.java | 2 ++ .../GlobalDefaultFlowDurabilityLevel.java | 1 + .../plugins/workflow/graph/FlowNode.java | 3 ++- .../plugins/workflow/graph/FlowStartNode.java | 2 ++ .../graphanalysis/FilteratorImpl.java | 3 ++- .../LinearBlockHoppingScanner.java | 2 +- .../workflow/graphanalysis/LinearScanner.java | 15 +++++++++------ .../graphanalysis/MemoryFlowChunk.java | 3 ++- .../graphanalysis/ParallelFlowChunk.java | 1 - .../workflow/log/BrokenLogStorage.java | 19 ++++++++++++------- .../workflow/log/BufferedBuildListener.java | 4 +++- .../log/DelayBufferedOutputStream.java | 4 +++- .../plugins/workflow/log/FileLogStorage.java | 18 ++++++++++++------ .../workflow/log/GCFlushedOutputStream.java | 4 +++- .../workflow/log/TaskListenerDecorator.java | 18 +++++++++++------- .../DirectArtifactManagerFactory.java | 10 ++++++++-- .../workflow/log/LogStorageTestBase.java | 2 ++ 18 files changed, 79 insertions(+), 40 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 5a20b488..d621d84c 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -206,7 +206,7 @@ public ListenableFuture apply(final Function f) { all.add(execs); Futures.addCallback(execs,new FutureCallback>() { @Override - public void onSuccess(List result) { + public void onSuccess(@NonNull List result) { for (StepExecution e : result) { try { f.apply(e); @@ -217,7 +217,7 @@ public void onSuccess(List result) { } @Override - public void onFailure(Throwable t) { + public void onFailure(@NonNull Throwable t) { LOGGER.log(Level.WARNING, null, t); } }, MoreExecutors.directExecutor()); @@ -256,7 +256,7 @@ public static class ResumeStepExecutionListener extends FlowExecutionListener { public void onResumed(@NonNull FlowExecution e) { Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { @Override - public void onSuccess(List result) { + public void onSuccess(@NonNull List result) { if (e.isComplete()) { // WorkflowRun.onLoad will not fire onResumed if the serialized execution was already // complete when loaded, but right now (at least for workflow-cps), the execution resumes @@ -285,7 +285,7 @@ public void onSuccess(List result) { } @Override - public void onFailure(Throwable t) { + public void onFailure(@NonNull Throwable t) { if (t instanceof CancellationException) { LOGGER.log(Level.FINE, "Cancelled load of " + e, t); } else { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java index 97d29f71..f8a0e322 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionOwner.java @@ -156,6 +156,7 @@ public static FlowExecutionOwner dummyOwner() { private static class DummyOwner extends FlowExecutionOwner { DummyOwner() {} + @NonNull @Override public FlowExecution get() throws IOException { throw new IOException("not implemented"); } @@ -174,6 +175,7 @@ private static class DummyOwner extends FlowExecutionOwner { @Override public int hashCode() { return 0; } + @NonNull @Override public TaskListener getListener() { return TaskListener.NULL; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java index 37fe4209..73ecdeef 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java @@ -32,6 +32,7 @@ public DescriptorImpl() { /** Null to use the platform default, which may change over time as enhanced options are available. */ private FlowDurabilityHint durabilityHint = null; + @NonNull @Override public String getDisplayName() { return "Global Default Pipeline Durability Level"; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowNode.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowNode.java index 007ac787..c5655a67 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowNode.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowNode.java @@ -416,6 +416,7 @@ private synchronized void loadActions() { } @Exported + @NonNull @SuppressWarnings("deprecation") // of override @Override @SuppressFBWarnings(value = "UG_SYNC_SET_UNSYNC_GET", justification = "CopyOnWrite ArrayList, and field load & modification is synchronized") @@ -466,7 +467,7 @@ public int size() { } @Override - public boolean removeAll(Collection c) { + public boolean removeAll(@NonNull Collection c) { boolean changed = actions.removeAll(c); if (changed) { persistSafe(); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowStartNode.java b/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowStartNode.java index 96d3f916..f93c1249 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowStartNode.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graph/FlowStartNode.java @@ -24,6 +24,7 @@ package org.jenkinsci.plugins.workflow.graph; +import edu.umd.cs.findbugs.annotations.NonNull; import org.jenkinsci.plugins.workflow.flow.FlowExecution; import java.util.List; @@ -44,6 +45,7 @@ public FlowStartNode(FlowExecution exec, String id) { * @deprecated * Why are you calling a method that always return empty list? */ + @NonNull @Override public List getParents() { return super.getParents(); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java index e91228bd..3c263de1 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java @@ -40,8 +40,9 @@ class FilteratorImpl implements Filterator { private Iterator wrapped = null; private Predicate matchCondition = null; + @NonNull @Override - public FilteratorImpl filter(Predicate matchCondition) { + public FilteratorImpl filter(@NonNull Predicate matchCondition) { return new FilteratorImpl<>(this, matchCondition); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java index d0dcd16c..4a630d95 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java @@ -113,7 +113,7 @@ protected FlowNode jumpBlockScan(@CheckForNull FlowNode node, @NonNull Collectio } @Override - protected FlowNode next(@NonNull FlowNode current, @NonNull Collection blackList) { + protected FlowNode next(@CheckForNull FlowNode current, @NonNull Collection blackList) { if (current == null) { return null; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java index a20af305..eea4ea8c 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java @@ -27,6 +27,7 @@ import com.google.common.base.Predicate; import org.jenkinsci.plugins.workflow.graph.FlowNode; +import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import javax.annotation.concurrent.NotThreadSafe; import java.util.Collection; @@ -78,7 +79,7 @@ protected void setHeads(@NonNull Collection heads) { } @Override - protected FlowNode next(FlowNode current, @NonNull Collection blackList) { + protected FlowNode next(@CheckForNull FlowNode current, @NonNull Collection blackList) { if (current == null) { return null; } @@ -98,8 +99,9 @@ protected FlowNode next(FlowNode current, @NonNull Collection blackLis * @deprecated prefer {@link #filteredNodes(FlowNode, Predicate)} */ @Deprecated + @NonNull @Override - public List filteredNodes(Collection heads, Predicate matchPredicate) { + public List filteredNodes(Collection heads, @NonNull Predicate matchPredicate) { return super.filteredNodes(heads, matchPredicate); } @@ -109,6 +111,7 @@ public List filteredNodes(Collection heads, PredicateDo not pass multiple heads. */ + @NonNull @Override public List filteredNodes(Collection heads, Collection blackList, Predicate matchCondition) { return super.filteredNodes(heads, blackList, matchCondition); @@ -120,7 +123,7 @@ public List filteredNodes(Collection heads, Collection heads, Predicate matchPredicate) { + public FlowNode findFirstMatch(Collection heads, @NonNull Predicate matchPredicate) { return super.findFirstMatch(heads, matchPredicate); } @@ -142,7 +145,7 @@ public FlowNode findFirstMatch(Collection heads, Collection * @param heads Do not pass multiple heads. */ @Override - public void visitAll(Collection heads, FlowNodeVisitor visitor) { + public void visitAll(Collection heads, @NonNull FlowNodeVisitor visitor) { super.visitAll(heads, visitor); } @@ -153,7 +156,7 @@ public void visitAll(Collection heads, FlowNodeVisitor visitor) { * @param heads Nodes to start walking the DAG backwards from. Do not pass multiple heads. */ @Override - public void visitAll(Collection heads, Collection blackList, FlowNodeVisitor visitor) { + public void visitAll(Collection heads, Collection blackList, @NonNull FlowNodeVisitor visitor) { super.visitAll(heads, blackList, visitor); } @@ -163,7 +166,7 @@ public void visitAll(Collection heads, Collection blackList, */ @Deprecated @Override - public FlowNode findFirstMatch(FlowExecution exec, Predicate matchPredicate) { + public FlowNode findFirstMatch(FlowExecution exec, @NonNull Predicate matchPredicate) { return super.findFirstMatch(exec, matchPredicate); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunk.java index db9e8f20..9f65e888 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/MemoryFlowChunk.java @@ -52,6 +52,7 @@ public MemoryFlowChunk() { } + @NonNull @Override public FlowNode getFirstNode() { return firstNode; @@ -61,7 +62,7 @@ public void setFirstNode(FlowNode firstNode) { this.firstNode = firstNode; } - + @NonNull @Override public FlowNode getLastNode() { return lastNode; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelFlowChunk.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelFlowChunk.java index 66b6eb83..7ab75b8e 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelFlowChunk.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ParallelFlowChunk.java @@ -12,6 +12,5 @@ public interface ParallelFlowChunk extends FlowChu @NonNull Map getBranches(); - @NonNull void setBranch(@NonNull String branchName, @NonNull ChunkType branchBlock); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/BrokenLogStorage.java b/src/main/java/org/jenkinsci/plugins/workflow/log/BrokenLogStorage.java index 4472ec7a..d909dda1 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/BrokenLogStorage.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/BrokenLogStorage.java @@ -24,6 +24,7 @@ package org.jenkinsci.plugins.workflow.log; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Functions; import hudson.console.AnnotatedLargeText; import hudson.model.BuildListener; @@ -47,20 +48,24 @@ public final class BrokenLogStorage implements LogStorage { public BrokenLogStorage(Throwable x) { this.x = x; } - + + @NonNull @Override public BuildListener overallListener() throws IOException { throw new IOException(x); } - - @Override public TaskListener nodeListener(FlowNode node) throws IOException { + + @NonNull + @Override public TaskListener nodeListener(@NonNull FlowNode node) throws IOException { throw new IOException(x); } - - @Override public AnnotatedLargeText overallLog(FlowExecutionOwner.Executable build, boolean complete) { + + @NonNull + @Override public AnnotatedLargeText overallLog(@NonNull FlowExecutionOwner.Executable build, boolean complete) { return new BrokenAnnotatedLargeText<>(); } - - @Override public AnnotatedLargeText stepLog(FlowNode node, boolean complete) { + + @NonNull + @Override public AnnotatedLargeText stepLog(@NonNull FlowNode node, boolean complete) { return new BrokenAnnotatedLargeText<>(); } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/BufferedBuildListener.java b/src/main/java/org/jenkinsci/plugins/workflow/log/BufferedBuildListener.java index 2fc29832..85316ff8 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/BufferedBuildListener.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/BufferedBuildListener.java @@ -24,6 +24,7 @@ package org.jenkinsci.plugins.workflow.log; +import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.CloseProofOutputStream; import hudson.model.BuildListener; @@ -49,7 +50,8 @@ final class BufferedBuildListener implements BuildListener, Closeable, Serializa this.out = out; ps = new PrintStream(out, false, "UTF-8"); } - + + @NonNull @Override public PrintStream getLogger() { return ps; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/DelayBufferedOutputStream.java b/src/main/java/org/jenkinsci/plugins/workflow/log/DelayBufferedOutputStream.java index 541e8782..ff366e78 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/DelayBufferedOutputStream.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/DelayBufferedOutputStream.java @@ -33,6 +33,8 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; + +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.util.Timer; import org.jenkinsci.remoting.SerializableOnlyOverRemoting; @@ -121,7 +123,7 @@ private static final class FlushControlledOutputStream extends FilterOutputStrea super(out); } - @Override public void write(byte[] b, int off, int len) throws IOException { + @Override public void write(@NonNull byte[] b, int off, int len) throws IOException { out.write(b, off, len); // super method writes one byte at a time! } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/FileLogStorage.java b/src/main/java/org/jenkinsci/plugins/workflow/log/FileLogStorage.java index dedb7714..afebd958 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/FileLogStorage.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/FileLogStorage.java @@ -24,6 +24,7 @@ package org.jenkinsci.plugins.workflow.log; +import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.console.AnnotatedLargeText; import hudson.console.ConsoleAnnotationOutputStream; @@ -111,11 +112,13 @@ private synchronized void open() throws IOException { } } + @NonNull @Override public BuildListener overallListener() throws IOException { return new BufferedBuildListener(new IndexOutputStream(null)); } - @Override public TaskListener nodeListener(FlowNode node) throws IOException { + @NonNull + @Override public TaskListener nodeListener(@NonNull FlowNode node) throws IOException { return new BufferedBuildListener(new IndexOutputStream(node.getId())); } @@ -153,14 +156,14 @@ private final class IndexOutputStream extends OutputStream { } } - @Override public void write(byte[] b) throws IOException { + @Override public void write(@NonNull byte[] b) throws IOException { synchronized (FileLogStorage.this) { checkId(id); bos.write(b); } } - @Override public void write(byte[] b, int off, int len) throws IOException { + @Override public void write(@NonNull byte[] b, int off, int len) throws IOException { synchronized (FileLogStorage.this) { checkId(id); bos.write(b, off, len); @@ -194,7 +197,8 @@ private void maybeFlush() { } } - @Override public AnnotatedLargeText overallLog(FlowExecutionOwner.Executable build, boolean complete) { + @NonNull + @Override public AnnotatedLargeText overallLog(@NonNull FlowExecutionOwner.Executable build, boolean complete) { maybeFlush(); return new AnnotatedLargeText(log, StandardCharsets.UTF_8, complete, build) { @Override public long writeHtmlTo(long start, Writer w) throws IOException { @@ -252,7 +256,8 @@ private void maybeFlush() { }; } - @Override public AnnotatedLargeText stepLog(FlowNode node, boolean complete) { + @NonNull + @Override public AnnotatedLargeText stepLog(@NonNull FlowNode node, boolean complete) { maybeFlush(); String id = node.getId(); try (ByteBuffer buf = new ByteBuffer(); @@ -323,7 +328,8 @@ private void maybeFlush() { } @Deprecated - @Override public File getLogFile(FlowExecutionOwner.Executable build, boolean complete) { + @NonNull + @Override public File getLogFile(@NonNull FlowExecutionOwner.Executable build, boolean complete) { return log; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/GCFlushedOutputStream.java b/src/main/java/org/jenkinsci/plugins/workflow/log/GCFlushedOutputStream.java index c794c60b..7e47b7ab 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/GCFlushedOutputStream.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/GCFlushedOutputStream.java @@ -33,6 +33,8 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; + +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.util.Timer; /** @@ -48,7 +50,7 @@ final class GCFlushedOutputStream extends FilterOutputStream { FlushRef.register(this, out); } - @Override public void write(byte[] b, int off, int len) throws IOException { + @Override public void write(@NonNull byte[] b, int off, int len) throws IOException { out.write(b, off, len); // super method is surprising } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java index 36a7b1ba..d22033f0 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java @@ -165,12 +165,13 @@ private static class MergedTaskListenerDecorator extends TaskListenerDecorator { private final @NonNull TaskListenerDecorator original; private final @NonNull TaskListenerDecorator subsequent; - MergedTaskListenerDecorator(TaskListenerDecorator original, TaskListenerDecorator subsequent) { + MergedTaskListenerDecorator(@NonNull TaskListenerDecorator original, @NonNull TaskListenerDecorator subsequent) { this.original = original; this.subsequent = subsequent; } - - @Override public OutputStream decorate(OutputStream logger) throws IOException, InterruptedException { + + @NonNull + @Override public OutputStream decorate(@NonNull OutputStream logger) throws IOException, InterruptedException { // TODO BodyInvoker.MergedFilter probably has these backwards return original.decorate(subsequent.decorate(logger)); } @@ -188,13 +189,14 @@ private static class ConsoleLogFilterAdapter extends TaskListenerDecorator { @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Explicitly checking for serializability.") private final @NonNull ConsoleLogFilter filter; - ConsoleLogFilterAdapter(ConsoleLogFilter filter) { + ConsoleLogFilterAdapter(@NonNull ConsoleLogFilter filter) { assert filter instanceof Serializable; this.filter = filter; } + @NonNull @SuppressWarnings("deprecation") // the compatibility code in ConsoleLogFilter fails to delegate to the old overload when given a null argument - @Override public OutputStream decorate(OutputStream logger) throws IOException, InterruptedException { + @Override public OutputStream decorate(@NonNull OutputStream logger) throws IOException, InterruptedException { return filter.decorateLogger((AbstractBuild) null, logger); } @@ -222,13 +224,14 @@ private static final class DecoratedTaskListener implements BuildListener { private transient PrintStream logger; - DecoratedTaskListener(TaskListener delegate, List decorators) { + DecoratedTaskListener(@NonNull TaskListener delegate, @NonNull List decorators) { this.delegate = delegate; assert !decorators.isEmpty(); assert !decorators.contains(null); this.decorators = decorators; } + @NonNull @Override public PrintStream getLogger() { if (logger == null) { OutputStream base = delegate.getLogger(); @@ -269,12 +272,13 @@ static BuildListener of(BuildListener mainDelegate, TaskListener closeDelegate) private final @NonNull TaskListener mainDelegate; private final @NonNull TaskListener closeDelegate; - private CloseableTaskListener(TaskListener mainDelegate, TaskListener closeDelegate) { + private CloseableTaskListener(@NonNull TaskListener mainDelegate, @NonNull TaskListener closeDelegate) { this.mainDelegate = mainDelegate; this.closeDelegate = closeDelegate; assert closeDelegate instanceof AutoCloseable; } + @NonNull @Override public PrintStream getLogger() { return mainDelegate.getLogger(); } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/DirectArtifactManagerFactory.java b/src/test/java/org/jenkinsci/plugins/workflow/DirectArtifactManagerFactory.java index f40c9062..00fedd9b 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/DirectArtifactManagerFactory.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/DirectArtifactManagerFactory.java @@ -24,6 +24,7 @@ package org.jenkinsci.plugins.workflow; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; @@ -192,10 +193,12 @@ private static final class NoOpenVF extends VirtualFile { } } + @NonNull @Override public String getName() { return delegate.getName(); } + @NonNull @Override public URI toURI() { return delegate.toURI(); } @@ -220,15 +223,18 @@ private static final class NoOpenVF extends VirtualFile { return delegate.exists(); } + @NonNull @Override public VirtualFile[] list() throws IOException { return Arrays.stream(delegate.list()).map(vf -> new NoOpenVF(vf, baseURL)).toArray(VirtualFile[]::new); } - @Override public Collection list(String includes, String excludes, boolean useDefaultExcludes) throws IOException { + @NonNull + @Override public Collection list(@NonNull String includes, String excludes, boolean useDefaultExcludes) throws IOException { return delegate.list(includes, excludes, useDefaultExcludes); } - @Override public VirtualFile child(String string) { + @NonNull + @Override public VirtualFile child(@NonNull String string) { return new NoOpenVF(delegate.child(string), baseURL); } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java b/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java index 41e5c51b..69205a5c 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java @@ -28,6 +28,7 @@ import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertEquals; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.console.AnnotatedLargeText; import hudson.console.HyperlinkNote; import hudson.model.Action; @@ -370,6 +371,7 @@ public FlowNode getNode(String id) { throw new UnsupportedOperationException(); } + @NonNull @Override public Authentication getAuthentication() { throw new UnsupportedOperationException(); From a5808c5c8aa4302e685007de05c3e06cc07dac9f Mon Sep 17 00:00:00 2001 From: offa Date: Tue, 11 Jan 2022 16:32:56 +0100 Subject: [PATCH 067/114] Use UTF-8 charset --- .../org/jenkinsci/plugins/workflow/ArtifactManagerTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java index 057a0ea0..4ca402f1 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java @@ -59,6 +59,7 @@ import java.io.InputStream; import java.net.URL; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.logging.Level; @@ -402,7 +403,7 @@ private void assertFile(VirtualFile f, String contents) throws Exception { assertEquals(contents.length(), f.length()); assertThat(f.lastModified(), not(is(0))); try (InputStream is = f.open()) { - assertEquals(contents, IOUtils.toString(is, Charset.defaultCharset())); + assertEquals(contents, IOUtils.toString(is, StandardCharsets.UTF_8)); } URL url = f.toExternalURL(); if (url != null) { @@ -417,7 +418,7 @@ private static final class RemoteOpenURL extends MasterToSlaveCallable Date: Tue, 11 Jan 2022 16:34:20 +0100 Subject: [PATCH 068/114] Use matcher instead of comparison --- .../jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java index 786c6f52..35d487d6 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java @@ -19,6 +19,8 @@ import java.util.Collection; import java.util.Optional; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -85,7 +87,7 @@ public void managePermissionShouldAccessGlobalConfig() throws Throwable { try (ACLContext c = ACL.as(User.getById(USER, true))) { Collection descriptors = Functions.getSortedDescriptorsForGlobalConfigUnclassified(); - assertEquals("Global configuration should not be accessible to READ users", 0, descriptors.size()); + assertThat("Global configuration should not be accessible to READ users", descriptors, empty()); } try (ACLContext c = ACL.as(User.getById(MANAGER, true))) { Collection descriptors = Functions.getSortedDescriptorsForGlobalConfigUnclassified(); From 88d57ba93c2092a6eae7dc1eff7f7e5e34d846dd Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Tue, 11 Jan 2022 12:03:44 -0800 Subject: [PATCH 069/114] EOL JSR 305 --- .../plugins/workflow/graphanalysis/AbstractFlowScanner.java | 2 +- .../plugins/workflow/graphanalysis/DepthFirstScanner.java | 2 +- .../plugins/workflow/graphanalysis/FilteratorImpl.java | 2 +- .../jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java | 2 +- .../workflow/graphanalysis/LinearBlockHoppingScanner.java | 2 +- .../jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java index 6597606f..c0b3e328 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/AbstractFlowScanner.java @@ -30,7 +30,7 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; -import javax.annotation.concurrent.NotThreadSafe; +import net.jcip.annotations.NotThreadSafe; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/DepthFirstScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/DepthFirstScanner.java index 58a1255b..ca85a8bf 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/DepthFirstScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/DepthFirstScanner.java @@ -28,7 +28,7 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; import edu.umd.cs.findbugs.annotations.NonNull; -import javax.annotation.concurrent.NotThreadSafe; +import net.jcip.annotations.NotThreadSafe; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java index e91228bd..ea8facf6 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/FilteratorImpl.java @@ -27,7 +27,7 @@ import com.google.common.base.Predicate; import edu.umd.cs.findbugs.annotations.NonNull; -import javax.annotation.concurrent.NotThreadSafe; +import net.jcip.annotations.NotThreadSafe; import java.util.Iterator; /** Filters an iterator against a match predicate by wrapping an iterator diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java index aca1d3ac..d67d87bf 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java @@ -37,7 +37,7 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import javax.annotation.concurrent.NotThreadSafe; +import net.jcip.annotations.NotThreadSafe; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java index d0dcd16c..cf01b68b 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java @@ -30,7 +30,7 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; -import javax.annotation.concurrent.NotThreadSafe; +import net.jcip.annotations.NotThreadSafe; import java.util.Collection; import java.util.Iterator; import java.util.List; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java index a20af305..95422e04 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearScanner.java @@ -28,7 +28,7 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode; import edu.umd.cs.findbugs.annotations.NonNull; -import javax.annotation.concurrent.NotThreadSafe; +import net.jcip.annotations.NotThreadSafe; import java.util.Collection; import java.util.Collections; import java.util.Iterator; From a38459828777b435cfb3bac5b22502a72c6ee92d Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Fri, 14 Jan 2022 17:29:41 -0500 Subject: [PATCH 070/114] Revert "[JENKINS-67351] Avoid deadlock when resuming Pipelines in some cases (#188)" This reverts commit 6d6de2006ec5ec7d8e7dbf2b612ec9e5a887274c. --- .../workflow/flow/FlowExecutionList.java | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index d621d84c..18dd3a37 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -10,7 +10,6 @@ import hudson.Extension; import hudson.ExtensionList; import hudson.XmlFile; -import hudson.init.InitMilestone; import hudson.init.Terminator; import hudson.model.listeners.ItemListener; import hudson.remoting.SingleLaneExecutorService; @@ -32,7 +31,6 @@ import java.util.logging.Logger; import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.Beta; import org.kohsuke.accmod.restrictions.DoNotUse; /** @@ -46,8 +44,6 @@ public class FlowExecutionList implements Iterable { private final SingleLaneExecutorService executor = new SingleLaneExecutorService(Timer.get()); private XmlFile configFile; - private transient volatile boolean resumptionComplete; - public FlowExecutionList() { load(); } @@ -163,18 +159,6 @@ public static FlowExecutionList get() { return l; } - /** - * Returns true if all executions that were present in this {@link FlowExecutionList} have been loaded and resumed. - * - * This takes place slightly after {@link InitMilestone#COMPLETED} is reached during Jenkins startup. - * - * Useful to avoid resuming Pipelines in contexts that may lead to deadlock. - */ - @Restricted(Beta.class) - public boolean isResumptionComplete() { - return resumptionComplete; - } - /** * When Jenkins starts up and everything is loaded, be sure to proactively resurrect * all the ongoing {@link FlowExecution}s so that they start running again. @@ -183,12 +167,10 @@ public boolean isResumptionComplete() { public static class ItemListenerImpl extends ItemListener { @Override public void onLoaded() { - FlowExecutionList list = FlowExecutionList.get(); - for (final FlowExecution e : list) { + for (final FlowExecution e : FlowExecutionList.get()) { // The call to FlowExecutionOwner.get in the implementation of iterator() is sufficent to load the Pipeline. LOGGER.log(Level.FINE, "Eagerly loaded {0}", e); } - list.resumptionComplete = true; } } @@ -293,7 +275,7 @@ public void onFailure(@NonNull Throwable t) { } } - }, Timer.get()); // We always hold RunMap and WorkflowRun locks here, so we resume steps on a different thread to avoid potential deadlocks. See JENKINS-67351. + }, MoreExecutors.directExecutor()); // TODO: Unclear if we need to run this asynchronously or if StepExecution.onResume has any particular thread requirements. } } } From ca655cf26c4acd5ea4ee82f3e576b92473733b66 Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Fri, 14 Jan 2022 17:30:41 -0500 Subject: [PATCH 071/114] Revert "[JENKINS-40161] Handle exceptions from `StepExecution.onResume` (#187)" This reverts commit 6a04bac22f47f38d19de8ce9523cf86b5744ed06. --- .../jenkinsci/plugins/workflow/flow/FlowExecutionList.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 18dd3a37..ad67159a 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -258,11 +258,7 @@ public void onSuccess(@NonNull List result) { } LOGGER.log(Level.FINE, "Will resume {0}", result); for (StepExecution se : result) { - try { - se.onResume(); - } catch (Throwable x) { - se.getContext().onFailure(x); - } + se.onResume(); } } From fd1ae7f3e63bf7e7958756d6dfc5fdf42b3de15e Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Fri, 14 Jan 2022 17:35:12 -0500 Subject: [PATCH 072/114] Revert "[JENKINS-67164] Call StepExecution.onResume directly from WorkflowRun.onLoad rather than via FlowExecutionList.ItemListenerImpl to ensure step resumption (#178)" This reverts commit 37c8c493e3142c9abaaae9598938d9ea56227e71. --- .../workflow/flow/FlowExecutionList.java | 88 +++++------- .../workflow/flow/FlowExecutionListTest.java | 129 ------------------ 2 files changed, 31 insertions(+), 186 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index ad67159a..68b173d1 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -5,8 +5,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; +import com.google.inject.Inject; import hudson.Extension; import hudson.ExtensionList; import hudson.XmlFile; @@ -30,6 +29,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import javax.annotation.CheckForNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; @@ -165,11 +165,31 @@ public static FlowExecutionList get() { */ @Extension public static class ItemListenerImpl extends ItemListener { + @Inject + FlowExecutionList list; + @Override public void onLoaded() { - for (final FlowExecution e : FlowExecutionList.get()) { - // The call to FlowExecutionOwner.get in the implementation of iterator() is sufficent to load the Pipeline. - LOGGER.log(Level.FINE, "Eagerly loaded {0}", e); + for (final FlowExecution e : list) { + LOGGER.log(Level.FINE, "Eager loading {0}", e); + Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { + @Override + public void onSuccess(List result) { + LOGGER.log(Level.FINE, "Will resume {0}", result); + for (StepExecution se : result) { + se.onResume(); + } + } + + @Override + public void onFailure(Throwable t) { + if (t instanceof CancellationException) { + LOGGER.log(Level.FINE, "Cancelled load of " + e, t); + } else { + LOGGER.log(Level.WARNING, "Failed to load " + e, t); + } + } + }, MoreExecutors.directExecutor()); } } } @@ -179,16 +199,19 @@ public void onLoaded() { */ @Extension public static class StepExecutionIteratorImpl extends StepExecutionIterator { + @Inject + FlowExecutionList list; + @Override public ListenableFuture apply(final Function f) { List> all = new ArrayList<>(); - for (FlowExecution e : FlowExecutionList.get()) { + for (FlowExecution e : list) { ListenableFuture> execs = e.getCurrentExecutions(false); all.add(execs); Futures.addCallback(execs,new FutureCallback>() { @Override - public void onSuccess(@NonNull List result) { + public void onSuccess(List result) { for (StepExecution e : result) { try { f.apply(e); @@ -199,7 +222,7 @@ public void onSuccess(@NonNull List result) { } @Override - public void onFailure(@NonNull Throwable t) { + public void onFailure(Throwable t) { LOGGER.log(Level.WARNING, null, t); } }, MoreExecutors.directExecutor()); @@ -225,53 +248,4 @@ public void onFailure(@NonNull Throwable t) { executor.shutdown(); executor.awaitTermination(1, TimeUnit.MINUTES); } - - /** - * Whenever a Pipeline resumes, resume all incomplete steps in its {@link FlowExecution}. - * - * Called by {@code WorkflowRun.onLoad}, so guaranteed to run if a Pipeline resumes regardless of its presence in - * {@link FlowExecutionList}. - */ - @Extension - public static class ResumeStepExecutionListener extends FlowExecutionListener { - @Override - public void onResumed(@NonNull FlowExecution e) { - Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { - @Override - public void onSuccess(@NonNull List result) { - if (e.isComplete()) { - // WorkflowRun.onLoad will not fire onResumed if the serialized execution was already - // complete when loaded, but right now (at least for workflow-cps), the execution resumes - // asynchronously before WorkflowRun.onLoad completes, so it is possible that the execution - // finishes before onResumed gets called. - // That said, there is nothing to prevent the execution from completing right after we check - // isComplete. If we want to fully prevent that, we would need to delay actual execution - // resumption until WorkflowRun.onLoad completes or add some form of synchronization. - return; - } - FlowExecutionList list = FlowExecutionList.get(); - FlowExecutionOwner owner = e.getOwner(); - if (!list.runningTasks.contains(owner)) { - LOGGER.log(Level.WARNING, "Resuming {0}, which is missing from FlowExecutionList ({1}), so registering it now.", - new Object[] {owner, list.runningTasks.getView()}); - list.register(owner); - } - LOGGER.log(Level.FINE, "Will resume {0}", result); - for (StepExecution se : result) { - se.onResume(); - } - } - - @Override - public void onFailure(@NonNull Throwable t) { - if (t instanceof CancellationException) { - LOGGER.log(Level.FINE, "Cancelled load of " + e, t); - } else { - LOGGER.log(Level.WARNING, "Failed to load " + e, t); - } - } - - }, MoreExecutors.directExecutor()); // TODO: Unclear if we need to run this asynchronously or if StepExecution.onResume has any particular thread requirements. - } - } } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java index 8cf59f30..733a3859 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java @@ -24,35 +24,17 @@ package org.jenkinsci.plugins.workflow.flow; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertNotNull; -import hudson.AbortException; import hudson.model.ParametersAction; import hudson.model.ParametersDefinitionProperty; -import hudson.model.Result; import hudson.model.StringParameterDefinition; import hudson.model.StringParameterValue; -import hudson.model.TaskListener; import hudson.model.queue.QueueTaskFuture; -import java.io.Serializable; -import java.time.Duration; -import java.time.Instant; -import java.util.Collections; -import java.util.Set; -import java.util.function.Supplier; import java.util.logging.Level; -import org.hamcrest.Matcher; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; -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.jenkinsci.plugins.workflow.test.steps.SemaphoreStep; import org.junit.ClassRule; import org.junit.Test; import org.junit.Rule; @@ -60,8 +42,6 @@ import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.JenkinsSessionRule; -import org.jvnet.hudson.test.TestExtension; -import org.kohsuke.stapler.DataBoundConstructor; public class FlowExecutionListTest { @@ -99,113 +79,4 @@ public class FlowExecutionListTest { }); } - @Test public void forceLoadRunningExecutionsAfterRestart() throws Throwable { - logging.capture(50); - sessions.then(r -> { - WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); - p.setDefinition(new CpsFlowDefinition("semaphore('wait')", true)); - WorkflowRun b = p.scheduleBuild2(0).waitForStart(); - SemaphoreStep.waitForStart("wait/1", b); - }); - sessions.then(r -> { - /* - Make sure that the build gets loaded automatically by FlowExecutionList$ItemListenerImpl before we load it explictly. - Expected call stack for resuming a Pipelines and its steps: - at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ResumeStepExecutionListener$1.onSuccess(FlowExecutionList.java:250) - at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ResumeStepExecutionListener$1.onSuccess(FlowExecutionList.java:247) - at com.google.common.util.concurrent.Futures$6.run(Futures.java:975) - at org.jenkinsci.plugins.workflow.flow.DirectExecutor.execute(DirectExecutor.java:33) - ... Guava Futures API internals ... - at com.google.common.util.concurrent.Futures.addCallback(Futures.java:985) - at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ResumeStepExecutionListener.onResumed(FlowExecutionList.java:247) - at org.jenkinsci.plugins.workflow.flow.FlowExecutionListener.fireResumed(FlowExecutionListener.java:84) - at org.jenkinsci.plugins.workflow.job.WorkflowRun.onLoad(WorkflowRun.java:528) - at hudson.model.RunMap.retrieve(RunMap.java:225) - ... RunMap internals ... - at hudson.model.RunMap.getById(RunMap.java:205) - at org.jenkinsci.plugins.workflow.job.WorkflowRun$Owner.run(WorkflowRun.java:937) - at org.jenkinsci.plugins.workflow.job.WorkflowRun$Owner.get(WorkflowRun.java:948) - at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$1.computeNext(FlowExecutionList.java:65) - at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$1.computeNext(FlowExecutionList.java:57) - at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:143) - at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:138) - at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ItemListenerImpl.onLoaded(FlowExecutionList.java:175) - at jenkins.model.Jenkins.(Jenkins.java:1019) - */ - waitFor(logging::getMessages, hasItem(containsString("Will resume [org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep"))); - WorkflowJob p = r.jenkins.getItemByFullName("p", WorkflowJob.class); - SemaphoreStep.success("wait/1", null); - WorkflowRun b = p.getBuildByNumber(1); - r.waitForCompletion(b); - r.assertBuildStatus(Result.SUCCESS, b); - }); - } - - @Issue("JENKINS-67164") - @Test public void resumeStepExecutions() throws Throwable { - sessions.then(r -> { - WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); - p.setDefinition(new CpsFlowDefinition("noResume()", true)); - WorkflowRun b = p.scheduleBuild2(0).waitForStart(); - r.waitForMessage("Starting non-resumable step", b); - // TODO: Unclear how this might happen in practice. - FlowExecutionList.get().unregister(b.asFlowExecutionOwner()); - }); - sessions.then(r -> { - WorkflowJob p = r.jenkins.getItemByFullName("p", WorkflowJob.class); - WorkflowRun b = p.getBuildByNumber(1); - r.waitForCompletion(b); - r.assertBuildStatus(Result.FAILURE, b); - r.assertLogContains("Unable to resume NonResumableStep", b); - }); - } - - public static class NonResumableStep extends Step implements Serializable { - public static final long serialVersionUID = 1L; - @DataBoundConstructor - public NonResumableStep() { } - @Override - public StepExecution start(StepContext sc) { - return new ExecutionImpl(sc); - } - - private static class ExecutionImpl extends StepExecution implements Serializable { - public static final long serialVersionUID = 1L; - private ExecutionImpl(StepContext sc) { - super(sc); - } - @Override - public boolean start() throws Exception { - getContext().get(TaskListener.class).getLogger().println("Starting non-resumable step"); - return false; - } - @Override - public void onResume() { - getContext().onFailure(new AbortException("Unable to resume NonResumableStep")); - } - } - - @TestExtension public static class DescriptorImpl extends StepDescriptor { - @Override - public Set> getRequiredContext() { - return Collections.singleton(TaskListener.class); - } - @Override - public String getFunctionName() { - return "noResume"; - } - } - } - - /** - * Wait up to 5 seconds for the given supplier to return a matching value. - */ - private static void waitFor(Supplier valueSupplier, Matcher matcher) throws InterruptedException { - Instant end = Instant.now().plus(Duration.ofSeconds(5)); - while (!matcher.matches(valueSupplier.get()) && Instant.now().isBefore(end)) { - Thread.sleep(100L); - } - assertThat("Matcher should have matched after 5s", valueSupplier.get(), matcher); - } - } From 3ce403f6fdd2dc2dabc46fce0801c52dcc1ae4f1 Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Fri, 14 Jan 2022 17:38:23 -0500 Subject: [PATCH 073/114] Preserve FlowExecutionList.isResumption for binary compatibility --- .../plugins/workflow/flow/FlowExecutionList.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 68b173d1..b8fa9051 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -159,6 +159,14 @@ public static FlowExecutionList get() { return l; } + /** + * @deprecated Only exists for binary compatibility. + */ + @Deprecated + public boolean isResumptionComplete() { + return false; + } + /** * When Jenkins starts up and everything is loaded, be sure to proactively resurrect * all the ongoing {@link FlowExecution}s so that they start running again. From fa07adf9df7e7b6afb8b4d0d9916ef79ba54c14c Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Fri, 14 Jan 2022 17:46:05 -0500 Subject: [PATCH 074/114] Preserve minor changes that removed Guice and JSR-305 annotations --- .../plugins/workflow/flow/FlowExecutionList.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index b8fa9051..e0543d8e 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -5,7 +5,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.inject.Inject; +import edu.umd.cs.findbugs.annotations.CheckForNull; import hudson.Extension; import hudson.ExtensionList; import hudson.XmlFile; @@ -29,7 +29,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; @@ -173,12 +172,9 @@ public boolean isResumptionComplete() { */ @Extension public static class ItemListenerImpl extends ItemListener { - @Inject - FlowExecutionList list; - @Override public void onLoaded() { - for (final FlowExecution e : list) { + for (final FlowExecution e : FlowExecutionList.get()) { LOGGER.log(Level.FINE, "Eager loading {0}", e); Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { @Override @@ -207,14 +203,11 @@ public void onFailure(Throwable t) { */ @Extension public static class StepExecutionIteratorImpl extends StepExecutionIterator { - @Inject - FlowExecutionList list; - @Override public ListenableFuture apply(final Function f) { List> all = new ArrayList<>(); - for (FlowExecution e : list) { + for (FlowExecution e : FlowExecutionList.get()) { ListenableFuture> execs = e.getCurrentExecutions(false); all.add(execs); Futures.addCallback(execs,new FutureCallback>() { From 7a916f363c86f6f217861713721d634962823325 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 15 Jan 2022 07:08:51 +0100 Subject: [PATCH 075/114] chore: Prepare for icon removal from core (#192) --- .../jenkinsci/plugins/workflow/graph/FlowNode/sidepanel.jelly | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/graph/FlowNode/sidepanel.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/graph/FlowNode/sidepanel.jelly index d3b5d626..2d5fd1ec 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/graph/FlowNode/sidepanel.jelly +++ b/src/main/resources/org/jenkinsci/plugins/workflow/graph/FlowNode/sidepanel.jelly @@ -26,8 +26,8 @@ - - + + From d9236371fa028362ddfccab242f1c9593be3d563 Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Tue, 18 Jan 2022 10:20:16 -0500 Subject: [PATCH 076/114] Preserve explanatory comment and minor change to log message --- .../jenkinsci/plugins/workflow/flow/FlowExecutionList.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index e0543d8e..b76db092 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -174,8 +174,10 @@ public boolean isResumptionComplete() { public static class ItemListenerImpl extends ItemListener { @Override public void onLoaded() { - for (final FlowExecution e : FlowExecutionList.get()) { - LOGGER.log(Level.FINE, "Eager loading {0}", e); + FlowExecutionList list = FlowExecutionList.get(); + for (final FlowExecution e : list) { + // The call to FlowExecutionOwner.get in the implementation of iterator() is sufficent to load the Pipeline. + LOGGER.log(Level.FINE, "Eagerly loaded {0}", e); Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { @Override public void onSuccess(List result) { From 92aa05cb18720814013d84dbc47ffec9e509c1fc Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Tue, 18 Jan 2022 13:31:59 -0500 Subject: [PATCH 077/114] Preserve NonNull annotations --- .../plugins/workflow/flow/FlowExecutionList.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index b76db092..3fab2131 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -6,6 +6,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.ExtensionList; import hudson.XmlFile; @@ -180,7 +181,7 @@ public void onLoaded() { LOGGER.log(Level.FINE, "Eagerly loaded {0}", e); Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { @Override - public void onSuccess(List result) { + public void onSuccess(@NonNull List result) { LOGGER.log(Level.FINE, "Will resume {0}", result); for (StepExecution se : result) { se.onResume(); @@ -188,7 +189,7 @@ public void onSuccess(List result) { } @Override - public void onFailure(Throwable t) { + public void onFailure(@NonNull Throwable t) { if (t instanceof CancellationException) { LOGGER.log(Level.FINE, "Cancelled load of " + e, t); } else { @@ -214,7 +215,7 @@ public ListenableFuture apply(final Function f) { all.add(execs); Futures.addCallback(execs,new FutureCallback>() { @Override - public void onSuccess(List result) { + public void onSuccess(@NonNull List result) { for (StepExecution e : result) { try { f.apply(e); @@ -225,7 +226,7 @@ public void onSuccess(List result) { } @Override - public void onFailure(Throwable t) { + public void onFailure(@NonNull Throwable t) { LOGGER.log(Level.WARNING, null, t); } }, MoreExecutors.directExecutor()); From 2635c1b37d53ee29026e231d2e2dbbd751918d9e Mon Sep 17 00:00:00 2001 From: NotMyFault Date: Fri, 11 Feb 2022 22:43:49 +0100 Subject: [PATCH 078/114] feat: Utilize modern build status --- pom.xml | 4 ++-- src/main/resources/lib/flow/nodeCaption.jelly | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 426777b5..0367913c 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 999999-SNAPSHOT - 2.249.1 + 2.277.4 8 false true @@ -73,7 +73,7 @@ io.jenkins.tools.bom - bom-2.249.x + bom-2.277.x 984.vb5eaac999a7e import pom diff --git a/src/main/resources/lib/flow/nodeCaption.jelly b/src/main/resources/lib/flow/nodeCaption.jelly index dbf40752..da293b7a 100644 --- a/src/main/resources/lib/flow/nodeCaption.jelly +++ b/src/main/resources/lib/flow/nodeCaption.jelly @@ -23,7 +23,7 @@ THE SOFTWARE. --> - + Generate a caption for the main panel that renders the status of the flow node. @@ -31,7 +31,7 @@ THE SOFTWARE.

          - ${node.iconColor.description}

          From 3584cb469d71edd8e8ce4c92939d8e6d9bb0dd66 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 16 Feb 2022 12:54:42 -0500 Subject: [PATCH 079/114] `onLoad(FlowExecutionOwner)` is meant to throw `IOException` --- .../java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java index ca288540..b1fa3af9 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java @@ -109,7 +109,7 @@ public FlowDurabilityHint getDurabilityHint() { /** * Should be called by the flow owner after it is deserialized. */ - public /*abstract*/ void onLoad(FlowExecutionOwner owner) { + public /*abstract*/ void onLoad(FlowExecutionOwner owner) throws IOException { if (Util.isOverridden(FlowExecution.class, getClass(), "onLoad")) { onLoad(); } From 5db16e232318cc54609e5198c9e52c27d5d2f8f5 Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Tue, 1 Mar 2022 09:03:01 +0000 Subject: [PATCH 080/114] Replace handbuilt html with jelly for flow durability --- .../flow/GlobalDefaultFlowDurabilityLevel.java | 18 ++++++++++++++++++ .../global.jelly | 10 +--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java index 73ecdeef..3ceecef5 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java @@ -4,7 +4,11 @@ import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.security.Permission; +import hudson.util.ListBoxModel; import hudson.util.ReflectionUtils; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; @@ -74,6 +78,20 @@ public static FlowDurabilityHint[] getDurabilityHintValues() { return FlowDurabilityHint.values(); } + public ListBoxModel doFillDurabilityHintItems() { + ListBoxModel options = new ListBoxModel(); + + options.add("None: use pipeline default (" + getSuggestedDurabilityHint().name() + ")", "null"); + + List mappedOptions = Arrays.stream(getDurabilityHintValues()) + .map(hint -> new ListBoxModel.Option(hint.getDescription(), hint.name())) + .collect(Collectors.toList()); + + options.addAll(mappedOptions); + + return options; + } + @NonNull // TODO: Add @Override when Jenkins core baseline is 2.222+ public Permission getRequiredGlobalConfigPagePermission() { diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel/global.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel/global.jelly index 2bc1d122..1245378c 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel/global.jelly +++ b/src/main/resources/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel/global.jelly @@ -2,15 +2,7 @@ - +

          From b8634cbfdbc876a43a9cabbff952edf01313ae08 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Tue, 1 Mar 2022 09:46:05 -0800 Subject: [PATCH 081/114] Remove unnecessary use of reflection --- .../GlobalDefaultFlowDurabilityLevel.java | 17 ++-------------- .../workflow/flow/DurabilityBasicsTest.java | 20 +------------------ 2 files changed, 3 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java index 73ecdeef..7aa6b403 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java @@ -4,14 +4,12 @@ import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.security.Permission; -import hudson.util.ReflectionUtils; import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; -import java.lang.reflect.InvocationTargetException; /** * Supports a global default durability level for users @@ -75,20 +73,9 @@ public static FlowDurabilityHint[] getDurabilityHintValues() { } @NonNull - // TODO: Add @Override when Jenkins core baseline is 2.222+ + @Override public Permission getRequiredGlobalConfigPagePermission() { - return getJenkinsManageOrAdmin(); - } - - // TODO: remove when Jenkins core baseline is 2.222+ - Permission getJenkinsManageOrAdmin() { - Permission manage; - try { // Manage is available starting from Jenkins 2.222 (https://jenkins.io/changelog/#v2.222). See JEP-223 for more info - manage = (Permission) ReflectionUtils.getPublicProperty(Jenkins.get(), "MANAGE"); - } catch (IllegalArgumentException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { - manage = Jenkins.ADMINISTER; - } - return manage; + return Jenkins.MANAGE; } } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java index 35d487d6..97ce4bdb 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/DurabilityBasicsTest.java @@ -5,23 +5,18 @@ import hudson.model.User; import hudson.security.ACL; import hudson.security.ACLContext; -import hudson.security.Permission; -import hudson.util.ReflectionUtils; import jenkins.model.Jenkins; import org.junit.Assert; -import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.MockAuthorizationStrategy; import org.jvnet.hudson.test.JenkinsSessionRule; -import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.Optional; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** @@ -66,13 +61,6 @@ public void defaultHandling() throws Throwable { @Test public void managePermissionShouldAccessGlobalConfig() throws Throwable { sessions.then(j -> { - Permission jenkinsManage; - try { - jenkinsManage = getJenkinsManage(); - } catch (Exception e) { - Assume.assumeTrue("Jenkins baseline is too old for this test (requires Jenkins.MANAGE)", false); - return; - } final String USER = "user"; final String MANAGER = "manager"; j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); @@ -82,7 +70,7 @@ public void managePermissionShouldAccessGlobalConfig() throws Throwable { // Read and Manage .grant(Jenkins.READ).everywhere().to(MANAGER) - .grant(jenkinsManage).everywhere().to(MANAGER) + .grant(Jenkins.MANAGE).everywhere().to(MANAGER) ); try (ACLContext c = ACL.as(User.getById(USER, true))) { @@ -96,10 +84,4 @@ public void managePermissionShouldAccessGlobalConfig() throws Throwable { } }); } - - // TODO: remove when Jenkins core baseline is 2.222+ - private Permission getJenkinsManage() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - // Jenkins.MANAGE is available starting from Jenkins 2.222 (https://jenkins.io/changelog/#v2.222). See JEP-223 for more info - return (Permission) ReflectionUtils.getPublicProperty(Jenkins.get(), "MANAGE"); - } } From 35ad9bca6e8a556b49b5bc30fecc119a2ba132cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 01:31:38 +0000 Subject: [PATCH 082/114] Bump actions/setup-java from 2 to 3 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2 to 3. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/cd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 025fac17..8c6ea0ad 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -47,7 +47,7 @@ jobs: with: fetch-depth: 0 - name: Set up JDK 8 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'adopt' java-version: 8 From 61c3180fa03f758198ee97b678ea8adf3cda9818 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 20 Apr 2022 22:18:45 -0700 Subject: [PATCH 083/114] Update plugin parent POM and BOM (#214) --- pom.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 0367913c..e48a2cba 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.jenkins-ci.plugins plugin - 4.33 + 4.40 org.jenkins-ci.plugins.workflow @@ -44,7 +44,7 @@ - scm:git:git://github.com/${gitHubRepo}.git + scm:git:https://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} ${scmTag} @@ -63,8 +63,7 @@ 999999-SNAPSHOT - 2.277.4 - 8 + 2.289.1 false true jenkinsci/${project.artifactId}-plugin @@ -73,8 +72,8 @@ io.jenkins.tools.bom - bom-2.277.x - 984.vb5eaac999a7e + bom-2.289.x + 1289.v5c4b_1c43511b_ import pom From b07a17d68db29d27ce7dc58e6acb60f8ed473298 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Apr 2022 02:21:39 +0000 Subject: [PATCH 084/114] Bump actions/checkout from 2.4.0 to 3.0.2 Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3.0.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2.4.0...v3.0.2) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/cd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 025fac17..fb6e1930 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -43,7 +43,7 @@ jobs: if: needs.validate.outputs.should_release == 'true' steps: - name: Check out - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3.0.2 with: fetch-depth: 0 - name: Set up JDK 8 From 43d733ba809040a870ed7ffdf0bcfd697afbde67 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 29 Apr 2022 14:52:04 -0400 Subject: [PATCH 085/114] Update cd.yaml Use only major version --- .github/workflows/cd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index fb6e1930..d1ceb2a6 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -43,7 +43,7 @@ jobs: if: needs.validate.outputs.should_release == 'true' steps: - name: Check out - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up JDK 8 From ea8e33e127c853ed324437b1f6a23928d6763fcb Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 2 May 2022 13:57:48 -0400 Subject: [PATCH 086/114] [JENKINS-49707] Introduce `ErrorCondition` --- .../plugins/workflow/flow/ErrorCondition.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/java/org/jenkinsci/plugins/workflow/flow/ErrorCondition.java diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/ErrorCondition.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/ErrorCondition.java new file mode 100644 index 00000000..325ff276 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/ErrorCondition.java @@ -0,0 +1,55 @@ +/* + * The MIT License + * + * Copyright 2022 CloudBees, Inc. + * + * 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 org.jenkinsci.plugins.workflow.flow; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.ExtensionPoint; +import hudson.model.AbstractDescribableImpl; +import hudson.model.Describable; +import hudson.model.Descriptor; +import java.io.IOException; +import java.io.Serializable; +import org.jenkinsci.plugins.workflow.steps.StepContext; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.Beta; + +/** + * User-configurable predicate for errors that may occur during a build. + * Implementations could check for agent-related outages, for example. + * Step callers could use a condition to decide whether to ignore or report an error, retry, etc. + */ +@Restricted(Beta.class) +public abstract class ErrorCondition extends AbstractDescribableImpl implements ExtensionPoint, Serializable { + + public abstract boolean test(@NonNull Throwable error, @CheckForNull StepContext context) throws IOException, InterruptedException; + + @Override public ErrorConditionDescriptor getDescriptor() { + return (ErrorConditionDescriptor) super.getDescriptor(); + } + + public static abstract class ErrorConditionDescriptor extends Descriptor {} + +} From bc885fd4869b06c1fa2941210d61502a34be8dce Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 2 May 2022 17:36:38 -0400 Subject: [PATCH 087/114] Introduced `ErrorAction.findOrigin` --- .../plugins/workflow/actions/ErrorAction.java | 38 +++++++++++++++++++ .../plugins/workflow/flow/ErrorCondition.java | 11 +++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java index 4ce7f9cf..37ba3f0b 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java @@ -32,8 +32,11 @@ import org.codehaus.groovy.control.MultipleCompilationErrorsException; import org.jenkinsci.plugins.workflow.graph.FlowNode; import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.Functions; import jenkins.model.Jenkins; import org.apache.commons.io.output.NullOutputStream; +import org.jenkinsci.plugins.workflow.flow.FlowExecution; +import org.jenkinsci.plugins.workflow.graphanalysis.ForkScanner; /** * Attached to {@link FlowNode} that caused an error. @@ -111,4 +114,39 @@ public String getDisplayName() { public String getUrlName() { return null; } + + /** + * Attempts to locate the first node of a build which threw an error. + * Typically an error will be rethrown by enclosing blocks, + * so this will look for the original node with an {@link ErrorAction} + * matching the given stack trace. + * @param error an error thrown at some point during a build + * @param execution the build + * @return the originating node, if one can be located + */ + public static @CheckForNull FlowNode findOrigin(@NonNull Throwable error, @NonNull FlowExecution execution) { + FlowNode candidate = null; + for (FlowNode n : new ForkScanner().allNodes(execution)) { + ErrorAction errorAction = n.getPersistentAction(ErrorAction.class); + if (errorAction != null && equals(error, errorAction.getError())) { + candidate = n; // continue search for earlier one + } + } + return candidate; + } + + /** + * {@link Throwable#equals} might not be reliable if the program has resumed + * and stuff is deserialized. + */ + private static boolean equals(Throwable t1, Throwable t2) { + if (t1 == t2) { + return true; + } else if (t1.getClass() != t2.getClass()) { + return false; + } else { + return Functions.printThrowable(t1).equals(Functions.printThrowable(t2)); + } + } + } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/ErrorCondition.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/ErrorCondition.java index 325ff276..e24f3e1b 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/ErrorCondition.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/ErrorCondition.java @@ -28,10 +28,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; import hudson.ExtensionPoint; import hudson.model.AbstractDescribableImpl; -import hudson.model.Describable; import hudson.model.Descriptor; import java.io.IOException; import java.io.Serializable; +import org.jenkinsci.plugins.workflow.actions.ErrorAction; import org.jenkinsci.plugins.workflow.steps.StepContext; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.Beta; @@ -44,6 +44,15 @@ @Restricted(Beta.class) public abstract class ErrorCondition extends AbstractDescribableImpl implements ExtensionPoint, Serializable { + /** + * Checks whether a given error matches this condition. + * @param error some exception thrown during a build + * @param context the context in which the error is being handled, if available; + * {@link ErrorAction#findOrigin} could be used to determine the part of the build in which the error was thrown + * @return true if the error is recognized + * @throws IOException as per {@link StepContext#get} + * @throws InterruptedException as per {@link StepContext#get} + */ public abstract boolean test(@NonNull Throwable error, @CheckForNull StepContext context) throws IOException, InterruptedException; @Override public ErrorConditionDescriptor getDescriptor() { From 761b214dde22a196d68f17b239baf1cfb65300f7 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 3 May 2022 12:58:34 -0400 Subject: [PATCH 088/114] Note about `BlockEndNode.getStartNode` --- .../org/jenkinsci/plugins/workflow/actions/ErrorAction.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java b/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java index 37ba3f0b..d35f9395 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/actions/ErrorAction.java @@ -36,6 +36,8 @@ import jenkins.model.Jenkins; import org.apache.commons.io.output.NullOutputStream; import org.jenkinsci.plugins.workflow.flow.FlowExecution; +import org.jenkinsci.plugins.workflow.graph.AtomNode; +import org.jenkinsci.plugins.workflow.graph.BlockEndNode; import org.jenkinsci.plugins.workflow.graphanalysis.ForkScanner; /** @@ -122,7 +124,9 @@ public String getUrlName() { * matching the given stack trace. * @param error an error thrown at some point during a build * @param execution the build - * @return the originating node, if one can be located + * @return the originating node, if one can be located; + * typically an {@link AtomNode} or {@link BlockEndNode} + * (in the latter case you may want to use {@link BlockEndNode#getStartNode} to look up actions) */ public static @CheckForNull FlowNode findOrigin(@NonNull Throwable error, @NonNull FlowExecution execution) { FlowNode candidate = null; From 84d6536bf13568df793a8bef0f17dc034addd8ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 May 2022 02:08:57 +0000 Subject: [PATCH 089/114] Bump jenkins-infra/jenkins-maven-cd-action from 1.2.0 to 1.3.0 Bumps [jenkins-infra/jenkins-maven-cd-action](https://github.com/jenkins-infra/jenkins-maven-cd-action) from 1.2.0 to 1.3.0. - [Release notes](https://github.com/jenkins-infra/jenkins-maven-cd-action/releases) - [Commits](https://github.com/jenkins-infra/jenkins-maven-cd-action/compare/v1.2.0...v1.3.0) --- updated-dependencies: - dependency-name: jenkins-infra/jenkins-maven-cd-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/cd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 6ca08545..f6082890 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -52,7 +52,7 @@ jobs: distribution: 'adopt' java-version: 8 - name: Release - uses: jenkins-infra/jenkins-maven-cd-action@v1.2.0 + uses: jenkins-infra/jenkins-maven-cd-action@v1.3.0 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} From 258c8962847849bbb451cad5906d7a503d18c1bd Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 14 Dec 2021 18:22:50 -0500 Subject: [PATCH 090/114] [JENKINS-40161] Handle exceptions from `StepExecution.onResume` (cherry picked from commit 78186291a1e9f5b514c8aac16f4147bfdb53dd24) --- .../jenkinsci/plugins/workflow/flow/FlowExecutionList.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 3fab2131..b04be346 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -184,7 +184,11 @@ public void onLoaded() { public void onSuccess(@NonNull List result) { LOGGER.log(Level.FINE, "Will resume {0}", result); for (StepExecution se : result) { - se.onResume(); + try { + se.onResume(); + } catch (Throwable x) { + se.getContext().onFailure(x); + } } } From 8fba087072667507bfcfba9be18608b5a808d7fc Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 11 May 2022 15:25:20 -0400 Subject: [PATCH 091/114] [JENKINS-67164] Call `StepExecution.onResume` in response to `WorkflowRun.onLoad` not `FlowExecutionList.ItemListenerImpl` --- .../plugins/workflow/flow/FlowExecution.java | 8 ++ .../workflow/flow/FlowExecutionList.java | 91 ++++++++---- .../workflow/flow/FlowExecutionListener.java | 1 + .../workflow/flow/FlowExecutionListTest.java | 129 ++++++++++++++++++ 4 files changed, 204 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java index b1fa3af9..27273754 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecution.java @@ -322,4 +322,12 @@ public Iterable iterateEnclosingBlocks(@NonNull FlowNode node) { protected void notifyShutdown() { // Default is no-op } + + /** + * Called after a restart and any attempts at {@link StepExecution#onResume} have completed. + * This is a signal that it is safe to resume program execution. + * By default, does nothing. + */ + protected void afterStepExecutionsResumed() {} + } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index b04be346..cc556947 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -10,6 +10,7 @@ import hudson.Extension; import hudson.ExtensionList; import hudson.XmlFile; +import hudson.init.InitMilestone; import hudson.init.Terminator; import hudson.model.listeners.ItemListener; import hudson.remoting.SingleLaneExecutorService; @@ -31,6 +32,7 @@ import java.util.logging.Logger; import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.Beta; import org.kohsuke.accmod.restrictions.DoNotUse; /** @@ -44,6 +46,8 @@ public class FlowExecutionList implements Iterable { private final SingleLaneExecutorService executor = new SingleLaneExecutorService(Timer.get()); private XmlFile configFile; + private transient volatile boolean resumptionComplete; + public FlowExecutionList() { load(); } @@ -160,11 +164,17 @@ public static FlowExecutionList get() { } /** - * @deprecated Only exists for binary compatibility. + * Returns true if all executions that were present in this {@link FlowExecutionList} have been loaded. + * + *

          This takes place slightly after {@link InitMilestone#COMPLETED} is reached during Jenkins startup. + * + *

          Useful to avoid resuming Pipelines in contexts that may lead to deadlock. + * + *

          It is not guaranteed that {@link FlowExecution#afterStepExecutionsResumed} has been called at this point. */ - @Deprecated + @Restricted(Beta.class) public boolean isResumptionComplete() { - return false; + return resumptionComplete; } /** @@ -179,29 +189,8 @@ public void onLoaded() { for (final FlowExecution e : list) { // The call to FlowExecutionOwner.get in the implementation of iterator() is sufficent to load the Pipeline. LOGGER.log(Level.FINE, "Eagerly loaded {0}", e); - Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { - @Override - public void onSuccess(@NonNull List result) { - LOGGER.log(Level.FINE, "Will resume {0}", result); - for (StepExecution se : result) { - try { - se.onResume(); - } catch (Throwable x) { - se.getContext().onFailure(x); - } - } - } - - @Override - public void onFailure(@NonNull Throwable t) { - if (t instanceof CancellationException) { - LOGGER.log(Level.FINE, "Cancelled load of " + e, t); - } else { - LOGGER.log(Level.WARNING, "Failed to load " + e, t); - } - } - }, MoreExecutors.directExecutor()); } + list.resumptionComplete = true; } } @@ -256,4 +245,56 @@ public void onFailure(@NonNull Throwable t) { executor.shutdown(); executor.awaitTermination(1, TimeUnit.MINUTES); } + + /** + * Whenever a Pipeline resumes, resume all incomplete steps in its {@link FlowExecution}. + * + *

          Called by {@code WorkflowRun.onLoad}, so guaranteed to run if a Pipeline resumes + * regardless of its presence in {@link FlowExecutionList}. + */ + @Extension + public static class ResumeStepExecutionListener extends FlowExecutionListener { + @Override + public void onResumed(@NonNull FlowExecution e) { + Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { + @Override + public void onSuccess(@NonNull List result) { + try { + if (e.isComplete()) { + // WorkflowRun.onLoad will not fireResumed if the execution was already complete when loaded, + // and CpsFlowExecution should not then complete until afterStepExecutionsResumed, so this is defensive. + return; + } + FlowExecutionList list = FlowExecutionList.get(); + FlowExecutionOwner owner = e.getOwner(); + if (!list.runningTasks.contains(owner)) { + LOGGER.log(Level.WARNING, "Resuming {0}, which is missing from FlowExecutionList ({1}), so registering it now.", new Object[] {owner, list.runningTasks.getView()}); + list.register(owner); + } + LOGGER.log(Level.FINE, "Will resume {0}", result); + for (StepExecution se : result) { + try { + se.onResume(); + } catch (Throwable x) { + se.getContext().onFailure(x); + } + } + } finally { + e.afterStepExecutionsResumed(); + } + } + + @Override + public void onFailure(@NonNull Throwable t) { + if (t instanceof CancellationException) { + LOGGER.log(Level.FINE, "Cancelled load of " + e, t); + } else { + LOGGER.log(Level.WARNING, "Failed to load " + e, t); + } + e.afterStepExecutionsResumed(); + } + + }, Timer.get()); // We always hold RunMap and WorkflowRun locks here, so we resume steps on a different thread to avoid potential deadlocks. See JENKINS-67351. + } + } } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListener.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListener.java index 570cc6f8..bfdc5035 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListener.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListener.java @@ -62,6 +62,7 @@ public void onCompleted(@NonNull FlowExecution execution) { * Fires the {@link #onCreated(FlowExecution)} event. */ public static void fireCreated(@NonNull FlowExecution execution) { + // TODO Jenkins 2.325+ use Listeners.notify for (FlowExecutionListener listener : ExtensionList.lookup(FlowExecutionListener.class)) { listener.onCreated(execution); } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java index 733a3859..8cf59f30 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java @@ -24,17 +24,35 @@ package org.jenkinsci.plugins.workflow.flow; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertNotNull; +import hudson.AbortException; import hudson.model.ParametersAction; import hudson.model.ParametersDefinitionProperty; +import hudson.model.Result; import hudson.model.StringParameterDefinition; import hudson.model.StringParameterValue; +import hudson.model.TaskListener; import hudson.model.queue.QueueTaskFuture; +import java.io.Serializable; +import java.time.Duration; +import java.time.Instant; +import java.util.Collections; +import java.util.Set; +import java.util.function.Supplier; import java.util.logging.Level; +import org.hamcrest.Matcher; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; +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.jenkinsci.plugins.workflow.test.steps.SemaphoreStep; import org.junit.ClassRule; import org.junit.Test; import org.junit.Rule; @@ -42,6 +60,8 @@ import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.JenkinsSessionRule; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.DataBoundConstructor; public class FlowExecutionListTest { @@ -79,4 +99,113 @@ public class FlowExecutionListTest { }); } + @Test public void forceLoadRunningExecutionsAfterRestart() throws Throwable { + logging.capture(50); + sessions.then(r -> { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition("semaphore('wait')", true)); + WorkflowRun b = p.scheduleBuild2(0).waitForStart(); + SemaphoreStep.waitForStart("wait/1", b); + }); + sessions.then(r -> { + /* + Make sure that the build gets loaded automatically by FlowExecutionList$ItemListenerImpl before we load it explictly. + Expected call stack for resuming a Pipelines and its steps: + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ResumeStepExecutionListener$1.onSuccess(FlowExecutionList.java:250) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ResumeStepExecutionListener$1.onSuccess(FlowExecutionList.java:247) + at com.google.common.util.concurrent.Futures$6.run(Futures.java:975) + at org.jenkinsci.plugins.workflow.flow.DirectExecutor.execute(DirectExecutor.java:33) + ... Guava Futures API internals ... + at com.google.common.util.concurrent.Futures.addCallback(Futures.java:985) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ResumeStepExecutionListener.onResumed(FlowExecutionList.java:247) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionListener.fireResumed(FlowExecutionListener.java:84) + at org.jenkinsci.plugins.workflow.job.WorkflowRun.onLoad(WorkflowRun.java:528) + at hudson.model.RunMap.retrieve(RunMap.java:225) + ... RunMap internals ... + at hudson.model.RunMap.getById(RunMap.java:205) + at org.jenkinsci.plugins.workflow.job.WorkflowRun$Owner.run(WorkflowRun.java:937) + at org.jenkinsci.plugins.workflow.job.WorkflowRun$Owner.get(WorkflowRun.java:948) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$1.computeNext(FlowExecutionList.java:65) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$1.computeNext(FlowExecutionList.java:57) + at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:143) + at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:138) + at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ItemListenerImpl.onLoaded(FlowExecutionList.java:175) + at jenkins.model.Jenkins.(Jenkins.java:1019) + */ + waitFor(logging::getMessages, hasItem(containsString("Will resume [org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep"))); + WorkflowJob p = r.jenkins.getItemByFullName("p", WorkflowJob.class); + SemaphoreStep.success("wait/1", null); + WorkflowRun b = p.getBuildByNumber(1); + r.waitForCompletion(b); + r.assertBuildStatus(Result.SUCCESS, b); + }); + } + + @Issue("JENKINS-67164") + @Test public void resumeStepExecutions() throws Throwable { + sessions.then(r -> { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition("noResume()", true)); + WorkflowRun b = p.scheduleBuild2(0).waitForStart(); + r.waitForMessage("Starting non-resumable step", b); + // TODO: Unclear how this might happen in practice. + FlowExecutionList.get().unregister(b.asFlowExecutionOwner()); + }); + sessions.then(r -> { + WorkflowJob p = r.jenkins.getItemByFullName("p", WorkflowJob.class); + WorkflowRun b = p.getBuildByNumber(1); + r.waitForCompletion(b); + r.assertBuildStatus(Result.FAILURE, b); + r.assertLogContains("Unable to resume NonResumableStep", b); + }); + } + + public static class NonResumableStep extends Step implements Serializable { + public static final long serialVersionUID = 1L; + @DataBoundConstructor + public NonResumableStep() { } + @Override + public StepExecution start(StepContext sc) { + return new ExecutionImpl(sc); + } + + private static class ExecutionImpl extends StepExecution implements Serializable { + public static final long serialVersionUID = 1L; + private ExecutionImpl(StepContext sc) { + super(sc); + } + @Override + public boolean start() throws Exception { + getContext().get(TaskListener.class).getLogger().println("Starting non-resumable step"); + return false; + } + @Override + public void onResume() { + getContext().onFailure(new AbortException("Unable to resume NonResumableStep")); + } + } + + @TestExtension public static class DescriptorImpl extends StepDescriptor { + @Override + public Set> getRequiredContext() { + return Collections.singleton(TaskListener.class); + } + @Override + public String getFunctionName() { + return "noResume"; + } + } + } + + /** + * Wait up to 5 seconds for the given supplier to return a matching value. + */ + private static void waitFor(Supplier valueSupplier, Matcher matcher) throws InterruptedException { + Instant end = Instant.now().plus(Duration.ofSeconds(5)); + while (!matcher.matches(valueSupplier.get()) && Instant.now().isBefore(end)) { + Thread.sleep(100L); + } + assertThat("Matcher should have matched after 5s", valueSupplier.get(), matcher); + } + } From ae7a477b433847c5fd4565b766febb8d7e5f57de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 01:25:11 +0000 Subject: [PATCH 092/114] Bump bom-2.289.x from 1289.v5c4b_1c43511b_ to 1382.v7d694476f340 Bumps [bom-2.289.x](https://github.com/jenkinsci/bom) from 1289.v5c4b_1c43511b_ to 1382.v7d694476f340. - [Release notes](https://github.com/jenkinsci/bom/releases) - [Commits](https://github.com/jenkinsci/bom/commits) --- updated-dependencies: - dependency-name: io.jenkins.tools.bom:bom-2.289.x dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e48a2cba..eab36320 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ io.jenkins.tools.bom bom-2.289.x - 1289.v5c4b_1c43511b_ + 1382.v7d694476f340 import pom From 761ccc92d24756c359a85539ae7c8226453d9536 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 16 May 2022 08:28:40 -0400 Subject: [PATCH 093/114] =?UTF-8?q?2.289.1=20=E2=86=92=202.289.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eab36320..a3d92b00 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 999999-SNAPSHOT - 2.289.1 + 2.289.3 false true jenkinsci/${project.artifactId}-plugin From af5fef3cadc41b1fdbb58dcbfbe8a2c7f3cad41c Mon Sep 17 00:00:00 2001 From: NotMyFault Date: Mon, 16 May 2022 18:43:10 +0200 Subject: [PATCH 094/114] feat: Use proper CSS class for spacing between wirdgets --- src/main/resources/lib/flow/nodeCaption.jelly | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/lib/flow/nodeCaption.jelly b/src/main/resources/lib/flow/nodeCaption.jelly index da293b7a..277030ae 100644 --- a/src/main/resources/lib/flow/nodeCaption.jelly +++ b/src/main/resources/lib/flow/nodeCaption.jelly @@ -33,6 +33,8 @@ THE SOFTWARE.

          - + + +

          From b1778a924b2db390a4f4aed23f01d36493ff95f7 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 19 May 2022 17:22:28 -0400 Subject: [PATCH 095/114] Call `StepExecution.onResume` in parallel to the extent possible --- .../workflow/flow/FlowExecutionList.java | 126 ++++++++++++++---- 1 file changed, 103 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index cc556947..28b4665b 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -23,13 +23,20 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import org.jenkinsci.plugins.workflow.graph.FlowNode; +import org.jenkinsci.plugins.workflow.graphanalysis.LinearBlockHoppingScanner; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.Beta; @@ -259,29 +266,19 @@ public void onResumed(@NonNull FlowExecution e) { Futures.addCallback(e.getCurrentExecutions(false), new FutureCallback>() { @Override public void onSuccess(@NonNull List result) { - try { - if (e.isComplete()) { - // WorkflowRun.onLoad will not fireResumed if the execution was already complete when loaded, - // and CpsFlowExecution should not then complete until afterStepExecutionsResumed, so this is defensive. - return; - } - FlowExecutionList list = FlowExecutionList.get(); - FlowExecutionOwner owner = e.getOwner(); - if (!list.runningTasks.contains(owner)) { - LOGGER.log(Level.WARNING, "Resuming {0}, which is missing from FlowExecutionList ({1}), so registering it now.", new Object[] {owner, list.runningTasks.getView()}); - list.register(owner); - } - LOGGER.log(Level.FINE, "Will resume {0}", result); - for (StepExecution se : result) { - try { - se.onResume(); - } catch (Throwable x) { - se.getContext().onFailure(x); - } - } - } finally { - e.afterStepExecutionsResumed(); + if (e.isComplete()) { + // WorkflowRun.onLoad will not fireResumed if the execution was already complete when loaded, + // and CpsFlowExecution should not then complete until afterStepExecutionsResumed, so this is defensive. + return; } + FlowExecutionList list = FlowExecutionList.get(); + FlowExecutionOwner owner = e.getOwner(); + if (!list.runningTasks.contains(owner)) { + LOGGER.log(Level.WARNING, "Resuming {0}, which is missing from FlowExecutionList ({1}), so registering it now.", new Object[] {owner, list.runningTasks.getView()}); + list.register(owner); + } + LOGGER.log(Level.FINE, "Will resume {0}", result); + new ParallelResumer(result, e::afterStepExecutionsResumed).run(); } @Override @@ -294,7 +291,90 @@ public void onFailure(@NonNull Throwable t) { e.afterStepExecutionsResumed(); } - }, Timer.get()); // We always hold RunMap and WorkflowRun locks here, so we resume steps on a different thread to avoid potential deadlocks. See JENKINS-67351. + }, MoreExecutors.directExecutor()); } } + + /** Calls {@link StepExecution#onResume} for each step in a running build. + * Does so in parallel, but always completing enclosing blocks before the enclosed step. + * A simplified version of https://stackoverflow.com/a/67449067/12916, since this should be a tree not a general DAG. + */ + private static final class ParallelResumer { + + private final Runnable onCompletion; + /** Step nodes mapped to the step execution. Entries removed when they are ready to be resumed. */ + private final Map nodes = new HashMap<>(); + /** Step nodes currently being resumed. Removed after resumption completes. */ + private final Set processing = new HashSet<>(); + /** Step nodes mapped to the nearest enclosing step node (no entry if at root). */ + private final Map enclosing = new HashMap<>(); + + ParallelResumer(Collection executions, Runnable onCompletion) { + this.onCompletion = onCompletion; + // First look up positions in the flow graph, so that we can compute dependencies: + for (StepExecution se : executions) { + try { + FlowNode n = se.getContext().get(FlowNode.class); + if (n != null) { + nodes.put(n, se); + } else { + LOGGER.warning(() -> "Could not find FlowNode for " + se + " so it will not be resumed"); + } + } catch (IOException | InterruptedException x) { + LOGGER.log(Level.WARNING, "Could not look up FlowNode for " + se + " so it will not be resumed", x); + } + } + for (FlowNode n : nodes.keySet()) { + LinearBlockHoppingScanner scanner = new LinearBlockHoppingScanner(); + scanner.setup(n); + for (FlowNode parent : scanner) { + if (parent != n && nodes.containsKey(parent)) { + enclosing.put(n, parent); + break; + } + } + } + } + + synchronized void run() { + LOGGER.fine(() -> "Checking status with nodes=" + nodes + " enclosing=" + enclosing + " processing=" + processing); + if (nodes.isEmpty()) { + if (processing.isEmpty()) { + LOGGER.fine("Done"); + onCompletion.run(); + } + return; + } + Map ready = new HashMap<>(); + for (Map.Entry entry : nodes.entrySet()) { + FlowNode n = entry.getKey(); + FlowNode parent = enclosing.get(n); + if (parent == null || !nodes.containsKey(parent)) { + ready.put(n, entry.getValue()); + } + } + LOGGER.fine(() -> "Ready to resume: " + ready); + nodes.keySet().removeAll(ready.keySet()); + for (Map.Entry entry : ready.entrySet()) { + FlowNode n = entry.getKey(); + StepExecution exec = entry.getValue(); + processing.add(n); + Timer.get().submit(() -> { + LOGGER.fine(() -> "About to resume " + n + " ~ " + exec); + try { + exec.onResume(); + } catch (Throwable x) { + exec.getContext().onFailure(x); + } + LOGGER.fine(() -> "Finished resuming " + n + " ~ " + exec); + synchronized (ParallelResumer.this) { + processing.remove(n); + run(); + } + }); + } + } + + } + } From 8034dd22fb209133e3c9160e197c20bd4b8b92da Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 26 May 2022 15:36:40 -0400 Subject: [PATCH 096/114] https://github.com/jenkinsci/workflow-api-plugin/pull/221#discussion_r882853693 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example from `ExecutorStepDynamicContextTest.parallelNodeDisappearance`: ``` "Computer.threadPoolForRemoting [#3]" #89 daemon prio=5 os_prio=0 tid=0x00007f30d0c6a800 nid=0x6d0f9 in Object.wait() [0x00007f31047fe000] java.lang.Thread.State: TIMED_WAITING (on object monitor) at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:460) at hudson.remoting.AsyncFutureImpl.get(AsyncFutureImpl.java:97) - locked <0x00000000f83dd1e8> (a hudson.remoting.AsyncFutureImpl) at org.jenkinsci.plugins.workflow.support.steps.ExecutorStepDynamicContext.resume(ExecutorStepDynamicContext.java:108) at org.jenkinsci.plugins.workflow.support.steps.ExecutorStepExecution.onResume(ExecutorStepExecution.java:201) at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ParallelResumer.lambda$run$5(FlowExecutionList.java:369) at org.jenkinsci.plugins.workflow.flow.FlowExecutionList$ParallelResumer$$Lambda$350/265274739.run(Unknown Source) at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28) at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:68) at … ``` --- .../jenkinsci/plugins/workflow/flow/FlowExecutionList.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 28b4665b..a36e8e61 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -12,6 +12,7 @@ import hudson.XmlFile; import hudson.init.InitMilestone; import hudson.init.Terminator; +import hudson.model.Computer; import hudson.model.listeners.ItemListener; import hudson.remoting.SingleLaneExecutorService; import hudson.util.CopyOnWriteList; @@ -359,7 +360,10 @@ synchronized void run() { FlowNode n = entry.getKey(); StepExecution exec = entry.getValue(); processing.add(n); - Timer.get().submit(() -> { + // Strictly speaking threadPoolForRemoting should be used for agent communications. + // In practice the onResume impl known to block is in ExecutorStepExecution. + // Avoid jenkins.util.Timer since it is capped at 10 threads and should not be used for long tasks. + Computer.threadPoolForRemoting.submit(() -> { LOGGER.fine(() -> "About to resume " + n + " ~ " + exec); try { exec.onResume(); From e10ac664253e36c4c775e55f8cac1e35907039b6 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 2 Jun 2022 20:42:45 -0400 Subject: [PATCH 097/114] Typo in comment Co-authored-by: Devin Nusbaum --- .../org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index a36e8e61..13d4194b 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -361,7 +361,7 @@ synchronized void run() { StepExecution exec = entry.getValue(); processing.add(n); // Strictly speaking threadPoolForRemoting should be used for agent communications. - // In practice the onResume impl known to block is in ExecutorStepExecution. + // In practice the only onResume impl known to block is in ExecutorStepExecution. // Avoid jenkins.util.Timer since it is capped at 10 threads and should not be used for long tasks. Computer.threadPoolForRemoting.submit(() -> { LOGGER.fine(() -> "About to resume " + n + " ~ " + exec); From 4550e8e6b59e34e1c2efa3d68371e95bb4f810d0 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 3 Jun 2022 14:36:50 -0400 Subject: [PATCH 098/114] Require 2.332.x --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a3d92b00..60c8b7cd 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 999999-SNAPSHOT - 2.289.3 + 2.332.1 false true jenkinsci/${project.artifactId}-plugin @@ -72,7 +72,7 @@ io.jenkins.tools.bom - bom-2.289.x + bom-2.332.x 1382.v7d694476f340 import pom From 49f425dd601a05ba124c7a789b409d56f170912d Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Tue, 14 Jun 2022 23:38:26 +0200 Subject: [PATCH 099/114] chore: use jenkins infra maven cd reusable workflow --- .github/workflows/cd.yaml | 55 ++++----------------------------------- 1 file changed, 5 insertions(+), 50 deletions(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index f6082890..0279984d 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -8,53 +8,8 @@ on: - completed jobs: - validate: - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.verify-ci-status.outputs.result == 'success' && steps.interesting-categories.outputs.interesting == 'true' }} - steps: - - name: Verify CI status - uses: jenkins-infra/verify-ci-status-action@v1.2.0 - id: verify-ci-status - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - output_result: true - - - name: Release Drafter - uses: release-drafter/release-drafter@v5 - if: steps.verify-ci-status.outputs.result == 'success' - with: - name: next - tag: next - version: next - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check interesting categories - uses: jenkins-infra/interesting-category-action@v1.0.0 - id: interesting-categories - if: steps.verify-ci-status.outputs.result == 'success' - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - release: - runs-on: ubuntu-latest - needs: [validate] - if: needs.validate.outputs.should_release == 'true' - steps: - - name: Check out - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Set up JDK 8 - uses: actions/setup-java@v3 - with: - distribution: 'adopt' - java-version: 8 - - name: Release - uses: jenkins-infra/jenkins-maven-cd-action@v1.3.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} - MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} - + maven-cd: + uses: jenkins-infra/github-reusable-workflows/.github/workflows/maven-cd.yml@v1 + secrets: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} From 02c3dba6be36f57b4125dd4eac80c30350398497 Mon Sep 17 00:00:00 2001 From: Ted Date: Fri, 1 Jul 2022 05:17:36 +0800 Subject: [PATCH 100/114] allower downstream plugin to adjust the apply order of TaskListenerDecorator created from TaskListenerDecorator.Factory (#166) * introduce isAppliedBeforeMainDecorator in TaskListenerDecorator.Factory so user can adjust apply order * limit to beta usage Co-authored-by: Ted Xiao Co-authored-by: Carroll Chiou --- .../workflow/log/TaskListenerDecorator.java | 21 +- .../log/TaskListenerDecoratorOrderTest.java | 190 ++++++++++++++++++ 2 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 src/test/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecoratorOrderTest.java diff --git a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java index d22033f0..1f167b00 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.java @@ -54,6 +54,8 @@ import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner; import org.jenkinsci.plugins.workflow.steps.BodyInvoker; import org.jenkinsci.plugins.workflow.steps.StepContext; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.Beta; /** * A way of decorating output from a {@link TaskListener}. @@ -131,6 +133,15 @@ public interface Factory extends ExtensionPoint { */ @CheckForNull TaskListenerDecorator of(@NonNull FlowExecutionOwner owner); + /** + * + * @return boolean, false means to apply step decorators first, then TaskListenerDecorator, true means otherwise + * @see #apply(TaskListener, FlowExecutionOwner, TaskListenerDecorator) + */ + @Restricted(Beta.class) + default boolean isAppliedBeforeMainDecorator(){ + return false; + } } /** @@ -145,11 +156,13 @@ public interface Factory extends ExtensionPoint { */ public static BuildListener apply(@NonNull TaskListener listener, @NonNull FlowExecutionOwner owner, @CheckForNull TaskListenerDecorator mainDecorator) { JenkinsJVM.checkJenkinsJVM(); + List decoratorFactories = ExtensionList.lookup(TaskListenerDecorator.Factory.class); List decorators = Stream.concat( - ExtensionList.lookup(TaskListenerDecorator.Factory.class).stream().map(f -> f.of(owner)), - Stream.of(mainDecorator)). - filter(Objects::nonNull). - collect(Collectors.toCollection(ArrayList::new)); + decoratorFactories.stream().filter(f -> !f.isAppliedBeforeMainDecorator()).map(f -> f.of(owner)), + Stream.concat(Stream.of(mainDecorator), + decoratorFactories.stream().filter(f -> f.isAppliedBeforeMainDecorator()).map(f -> f.of(owner)))). + filter(Objects::nonNull). + collect(Collectors.toCollection(ArrayList::new)); if (decorators.isEmpty()) { return CloseableTaskListener.of(BuildListenerAdapter.wrap(listener), listener); } else { diff --git a/src/test/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecoratorOrderTest.java b/src/test/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecoratorOrderTest.java new file mode 100644 index 00000000..8848b0e8 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/workflow/log/TaskListenerDecoratorOrderTest.java @@ -0,0 +1,190 @@ +package org.jenkinsci.plugins.workflow.log; + +import com.google.common.collect.ImmutableSet; +import hudson.console.ConsoleLogFilter; +import hudson.console.LineTransformationOutputStream; +import hudson.model.AbstractBuild; +import hudson.model.Node; +import hudson.model.TaskListener; +import hudson.remoting.Channel; +import jenkins.security.MasterToSlaveCallable; +import jenkins.util.JenkinsJVM; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.jenkinsci.plugins.workflow.steps.*; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.*; +import org.kohsuke.stapler.DataBoundConstructor; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.Collections; +import java.util.Set; + +@For(TaskListenerDecorator.class) +public class TaskListenerDecoratorOrderTest { + @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher(); + + @Rule public JenkinsRule r = new JenkinsRule(); + + @Test public void verifyTaskListenerDecoratorOrder() throws Exception { + r.createSlave("remote", null, null); + WorkflowJob p = r.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition("filter {node('remote') {remotePrint()}}", true)); + WorkflowRun b = r.buildAndAssertSuccess(p); + r.assertLogContains("[ApplyOrderDecoratorFactory: job/p/1/] Started", b); + // TaskListenerDecorator applied before step decorators, modify stream last. pseudo: new StepDecorator(new TaskListenerDecorator(stream)) + r.assertLogContains("[ApplyOrderDecoratorFactory: job/p/1/] [StepLevelDecorator] Running on remote in", b); + r.assertLogContains("[ApplyOrderDecoratorFactory: job/p/1/ via remote] [StepLevelDecorator via remote] printed a message on master=false", b); + // now reverse the order + ApplyOrderDecoratorFactory muteOrderDecoratorFactory=r.jenkins.getExtensionList(TaskListenerDecorator.Factory.class).get(ApplyOrderDecoratorFactory.class); + muteOrderDecoratorFactory.applyFirst=false; + b = r.buildAndAssertSuccess(p); + // TaskListenerDecorator applied after step decorators, modify stream first. pseudo: new TaskListenerDecorator(new StepDecorator(stream)) + r.assertLogContains("[StepLevelDecorator] [ApplyOrderDecoratorFactory: job/p/2/] Running", b); + r.assertLogContains("[StepLevelDecorator via remote] [ApplyOrderDecoratorFactory: job/p/2/ via remote] printed a message on master=false", b); + } + + private static final class DecoratorImpl extends TaskListenerDecorator { + private static final long serialVersionUID = 1L; + + private final String tagName; + DecoratorImpl(String tagName) { + this.tagName = tagName; + } + private Object writeReplace() { + Channel ch = Channel.current(); + return ch != null ? new DecoratorImpl(tagName + " via " + ch.getName()) : this; + } + @Override public OutputStream decorate(OutputStream logger) throws IOException, InterruptedException { + return new LineTransformationOutputStream() { + @Override + protected void eol(byte[] b, int len) throws IOException { + logger.write(("[" + tagName + "] ").getBytes()); + logger.write(b, 0, len ); + } + @Override public void close() throws IOException { + super.close(); + logger.close(); + } + @Override public void flush() throws IOException { + logger.flush(); + } + }; + } + @Override public String toString() { + return "DecoratorImpl[" + tagName + "]"; + } + } + + @TestExtension public static final class ApplyOrderDecoratorFactory implements TaskListenerDecorator.Factory { + private boolean applyFirst=true; + @Override public TaskListenerDecorator of(FlowExecutionOwner owner) { + try { + return new DecoratorImpl("ApplyOrderDecoratorFactory: "+owner.getUrl()); + } catch (IOException x) { + throw new AssertionError(x); + } + } + protected void setApplyFirst(boolean applyFirst){ + this.applyFirst=applyFirst; + } + @Override + public boolean isAppliedBeforeMainDecorator() { + return applyFirst; + } + } + + + public static final class FilterStep extends Step { + @DataBoundConstructor public FilterStep() {} + @Override public StepExecution start(StepContext context) throws Exception { + return new Execution(context); + } + private static final class Execution extends StepExecution { + private static final long serialVersionUID = 1L; + + Execution(StepContext context) { + super(context); + } + @Override public boolean start() throws Exception { + getContext().newBodyInvoker().withContext(new Filter("StepLevelDecorator")).withCallback(BodyExecutionCallback.wrap(getContext())).start(); + return false; + } + } + private static final class Filter extends ConsoleLogFilter implements Serializable { + private static final long serialVersionUID = 1L; + + private final String message; + Filter(String message) { + this.message = message; + } + private Object writeReplace() { + Channel ch = Channel.current(); + return ch != null ? new Filter(message + " via " + ch.getName()) : this; + } + @SuppressWarnings("rawtypes") + @Override public OutputStream decorateLogger(AbstractBuild _ignore, OutputStream logger) throws IOException, InterruptedException { + return new DecoratorImpl(message).decorate(logger); + } + @Override public String toString() { + return "Filter[" + message + "]"; + } + } + @TestExtension public static final class DescriptorImpl extends StepDescriptor { + @Override public Set> getRequiredContext() { + return Collections.emptySet(); + } + @Override public String getFunctionName() { + return "filter"; + } + @Override public boolean takesImplicitBlockArgument() { + return true; + } + } + } + + public static final class RemotePrintStep extends Step { + @DataBoundConstructor public RemotePrintStep() {} + @Override public StepExecution start(StepContext context) throws Exception { + return new Execution(context); + } + private static final class Execution extends SynchronousNonBlockingStepExecution { + private static final long serialVersionUID = 1L; + + Execution(StepContext context) { + super(context); + } + @Override protected Void run() throws Exception { + return getContext().get(Node.class).getChannel().call(new PrintCallable(getContext().get(TaskListener.class))); + } + } + private static final class PrintCallable extends MasterToSlaveCallable { + private static final long serialVersionUID = 1L; + + private final TaskListener listener; + PrintCallable(TaskListener listener) { + this.listener = listener; + } + @Override public Void call() throws RuntimeException { + listener.getLogger().println("printed a message on master=" + JenkinsJVM.isJenkinsJVM()); + listener.getLogger().flush(); + return null; + } + } + @TestExtension public static final class DescriptorImpl extends StepDescriptor { + @Override public Set> getRequiredContext() { + return ImmutableSet.of(Node.class, TaskListener.class); + } + @Override public String getFunctionName() { + return "remotePrint"; + } + } + } + +} From 015fd8680d00dd9d9e57ed5d32f30f1ca4c137e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 01:25:07 +0000 Subject: [PATCH 101/114] Bump bom-2.332.x from 1382.v7d694476f340 to 1466.v85a_616ea_b_87c Bumps [bom-2.332.x](https://github.com/jenkinsci/bom) from 1382.v7d694476f340 to 1466.v85a_616ea_b_87c. - [Release notes](https://github.com/jenkinsci/bom/releases) - [Commits](https://github.com/jenkinsci/bom/commits) --- updated-dependencies: - dependency-name: io.jenkins.tools.bom:bom-2.332.x dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 60c8b7cd..54b78213 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ io.jenkins.tools.bom bom-2.332.x - 1382.v7d694476f340 + 1466.v85a_616ea_b_87c import pom From 5faec006f3241402c955936b17ef8a3bd8773213 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 11:19:41 +0000 Subject: [PATCH 102/114] Bump plugin from 4.40 to 4.42 Bumps [plugin](https://github.com/jenkinsci/plugin-pom) from 4.40 to 4.42. - [Release notes](https://github.com/jenkinsci/plugin-pom/releases) - [Changelog](https://github.com/jenkinsci/plugin-pom/blob/master/CHANGELOG.md) - [Commits](https://github.com/jenkinsci/plugin-pom/compare/plugin-4.40...plugin-4.42) --- updated-dependencies: - dependency-name: org.jenkins-ci.plugins:plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 54b78213..29a1b5c5 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.jenkins-ci.plugins plugin - 4.40 + 4.42 org.jenkins-ci.plugins.workflow From a6f7b4c7de6fe3a64ab37f05f524eabf04447803 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Thu, 7 Jul 2022 20:50:01 +0100 Subject: [PATCH 103/114] Update GlobalDefaultFlowDurabilityLevel.java --- .../plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java index fcdc6c1d..c4ea0085 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java @@ -5,7 +5,6 @@ import hudson.model.Descriptor; import hudson.security.Permission; import hudson.util.ListBoxModel; -import hudson.util.ReflectionUtils; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; From 786bbb37b24c7b0393f72a602547a8a85640d3e8 Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Thu, 7 Jul 2022 23:09:15 +0100 Subject: [PATCH 104/114] Add tooltip via FormValidation --- .../flow/GlobalDefaultFlowDurabilityLevel.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java index c4ea0085..42873f6b 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java @@ -4,12 +4,14 @@ import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.security.Permission; +import hudson.util.FormValidation; import hudson.util.ListBoxModel; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import jenkins.model.Jenkins; import net.sf.json.JSONObject; +import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import edu.umd.cs.findbugs.annotations.CheckForNull; @@ -50,6 +52,15 @@ public void setDurabilityHint(FlowDurabilityHint hint){ save(); } + public FormValidation doCheckDurabilityHint(@QueryParameter("durabilityHint") String durabilityHint) { + FlowDurabilityHint flowDurabilityHint = Arrays.stream(FlowDurabilityHint.values()) + .filter(f -> f.name().equals(durabilityHint)) + .findFirst() + .orElse(FlowDurabilityHint.MAX_SURVIVABILITY); + + return FormValidation.ok(flowDurabilityHint.getTooltip()); + } + @Override public boolean configure(StaplerRequest req, JSONObject json) { // TODO verify if this is covered by permissions checks or we need an explicit check here. From 4eb62370e92d234fe986aecf907b8a34d573d2fa Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Fri, 8 Jul 2022 20:42:02 +0100 Subject: [PATCH 105/114] Cleanup --- .../flow/GlobalDefaultFlowDurabilityLevel.java | 14 +++----------- .../GlobalDefaultFlowDurabilityLevel/global.jelly | 5 ++--- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java index 42873f6b..d07c751f 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel.java @@ -56,7 +56,7 @@ public FormValidation doCheckDurabilityHint(@QueryParameter("durabilityHint") St FlowDurabilityHint flowDurabilityHint = Arrays.stream(FlowDurabilityHint.values()) .filter(f -> f.name().equals(durabilityHint)) .findFirst() - .orElse(FlowDurabilityHint.MAX_SURVIVABILITY); + .orElse(GlobalDefaultFlowDurabilityLevel.SUGGESTED_DURABILITY_HINT); return FormValidation.ok(flowDurabilityHint.getTooltip()); } @@ -79,20 +79,12 @@ public boolean configure(StaplerRequest req, JSONObject json) { return true; } - public static FlowDurabilityHint getSuggestedDurabilityHint() { - return GlobalDefaultFlowDurabilityLevel.SUGGESTED_DURABILITY_HINT; - } - - public static FlowDurabilityHint[] getDurabilityHintValues() { - return FlowDurabilityHint.values(); - } - public ListBoxModel doFillDurabilityHintItems() { ListBoxModel options = new ListBoxModel(); - options.add("None: use pipeline default (" + getSuggestedDurabilityHint().name() + ")", "null"); + options.add("None: use pipeline default (" + GlobalDefaultFlowDurabilityLevel.SUGGESTED_DURABILITY_HINT.name() + ")", "null"); - List mappedOptions = Arrays.stream(getDurabilityHintValues()) + List mappedOptions = Arrays.stream(FlowDurabilityHint.values()) .map(hint -> new ListBoxModel.Option(hint.getDescription(), hint.name())) .collect(Collectors.toList()); diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel/global.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel/global.jelly index 1245378c..49be137a 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel/global.jelly +++ b/src/main/resources/org/jenkinsci/plugins/workflow/flow/GlobalDefaultFlowDurabilityLevel/global.jelly @@ -1,9 +1,8 @@ - - + + -

          \ No newline at end of file From 2dcaddede5441df0b67f7846f80edcc331e9267e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jul 2022 01:42:39 +0000 Subject: [PATCH 106/114] Bump docker-fixtures from 1.11 to 1.12 Bumps [docker-fixtures](https://github.com/jenkinsci/docker-fixtures) from 1.11 to 1.12. - [Release notes](https://github.com/jenkinsci/docker-fixtures/releases) - [Changelog](https://github.com/jenkinsci/docker-fixtures/blob/master/old-changelog.md) - [Commits](https://github.com/jenkinsci/docker-fixtures/compare/docker-fixtures-1.11...docker-fixtures-1.12) --- updated-dependencies: - dependency-name: org.jenkins-ci.test:docker-fixtures dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 29a1b5c5..ad8a52c5 100644 --- a/pom.xml +++ b/pom.xml @@ -118,7 +118,7 @@ org.jenkins-ci.test docker-fixtures - 1.11 + 1.12 test From 7e81e4ea06f80578acd2e918748d189bbe093d2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 01:27:09 +0000 Subject: [PATCH 107/114] Bump git-changelist-maven-extension from 1.3 to 1.4 Bumps [git-changelist-maven-extension](https://github.com/jenkinsci/incrementals-tools) from 1.3 to 1.4. - [Release notes](https://github.com/jenkinsci/incrementals-tools/releases) - [Commits](https://github.com/jenkinsci/incrementals-tools/compare/parent-1.3...parent-1.4) --- updated-dependencies: - dependency-name: io.jenkins.tools.incrementals:git-changelist-maven-extension dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .mvn/extensions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index a65d82e1..9ac2968b 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -2,6 +2,6 @@ io.jenkins.tools.incrementals git-changelist-maven-extension - 1.3 + 1.4 From 2b089d454f064c07ec943c8af1d7d4393c7ff42b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 01:33:29 +0000 Subject: [PATCH 108/114] Bump plugin from 4.42 to 4.45 Bumps [plugin](https://github.com/jenkinsci/plugin-pom) from 4.42 to 4.45. - [Release notes](https://github.com/jenkinsci/plugin-pom/releases) - [Changelog](https://github.com/jenkinsci/plugin-pom/blob/master/CHANGELOG.md) - [Commits](https://github.com/jenkinsci/plugin-pom/compare/plugin-4.42...plugin-4.45) --- updated-dependencies: - dependency-name: org.jenkins-ci.plugins:plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ad8a52c5..34ea651d 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.jenkins-ci.plugins plugin - 4.42 + 4.45 org.jenkins-ci.plugins.workflow From d03c7879bbfcf50b23976a6ac822b55617996657 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 8 Aug 2022 15:09:15 -0400 Subject: [PATCH 109/114] Diagnosis for `NoSuchElementException` from `ForkScanner.setHeads` --- .../plugins/workflow/graphanalysis/ForkScanner.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java index a56fe90d..82d5da13 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/ForkScanner.java @@ -439,6 +439,10 @@ ArrayDeque leastCommonAncestor(@NonNull final Set } } + if (parallelForks.isEmpty()) { + throw new IllegalStateException("No least common ancestor found from " + heads); + } + // If we hit issues with the ordering of blocks by depth, apply a sorting to the parallels by depth return convertForksToBlockStarts(parallelForks); } @@ -450,7 +454,6 @@ protected void setHeads(@NonNull Collection heads) { headIds.add(f.getId()); } parallelBlockStartStack = leastCommonAncestor(new LinkedHashSet<>(heads)); - assert parallelBlockStartStack.size() > 0; currentParallelStart = parallelBlockStartStack.pop(); currentParallelStartNode = currentParallelStart.forkStart; myCurrent = currentParallelStart.unvisited.pop(); From 2d0deb19d212559742ac26851a2c40453af591bf Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 10 Aug 2022 14:00:57 -0400 Subject: [PATCH 110/114] Use `Files.createTempDirectory` (#245) --- .../jenkinsci/plugins/workflow/flow/StashManager.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java index cc9e8b34..996175df 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/StashManager.java @@ -52,6 +52,7 @@ import java.util.TreeMap; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.file.Files; import jenkins.model.ArtifactManager; import jenkins.model.Jenkins; import jenkins.util.BuildListenerAdapter; @@ -362,7 +363,7 @@ public interface StashAwareArtifactManager /* extends ArtifactManager */ { return; } VirtualFile srcroot = original.getArtifactManager().root(); - FilePath dstDir = createTmpDir(); + FilePath dstDir = new FilePath(Files.createTempDirectory("artifact-copy").toFile()); try { Map files = new HashMap<>(); for (String path : srcroot.list("**/*", null, false)) { @@ -382,14 +383,6 @@ public interface StashAwareArtifactManager /* extends ArtifactManager */ { StashManager.copyAll(original, copy); } - private FilePath createTmpDir() throws IOException { - File dir = File.createTempFile("artifact", "copy"); - if (!(dir.delete() && dir.mkdirs())) { - throw new IOException("Failed to create temporary directory " + dir.getPath()); - } - return new FilePath(dir); - } - } } From eae12dae592a9a2c4702149654d019e626ca9944 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 01:30:29 +0000 Subject: [PATCH 111/114] Bump docker-fixtures from 1.12 to 166.v912b_95083ffe Bumps [docker-fixtures](https://github.com/jenkinsci/docker-fixtures) from 1.12 to 166.v912b_95083ffe. - [Release notes](https://github.com/jenkinsci/docker-fixtures/releases) - [Changelog](https://github.com/jenkinsci/docker-fixtures/blob/master/old-changelog.md) - [Commits](https://github.com/jenkinsci/docker-fixtures/commits) --- updated-dependencies: - dependency-name: org.jenkins-ci.test:docker-fixtures dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 34ea651d..b7c2a06b 100644 --- a/pom.xml +++ b/pom.xml @@ -118,7 +118,7 @@ org.jenkins-ci.test docker-fixtures - 1.12 + 166.v912b_95083ffe test From 9ea53b67bcc8a94339460f2418f909b9fccb453a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 01:30:35 +0000 Subject: [PATCH 112/114] Bump plugin from 4.45 to 4.48 Bumps [plugin](https://github.com/jenkinsci/plugin-pom) from 4.45 to 4.48. - [Release notes](https://github.com/jenkinsci/plugin-pom/releases) - [Changelog](https://github.com/jenkinsci/plugin-pom/blob/master/CHANGELOG.md) - [Commits](https://github.com/jenkinsci/plugin-pom/compare/plugin-4.45...plugin-4.48) --- updated-dependencies: - dependency-name: org.jenkins-ci.plugins:plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 34ea651d..1ac71176 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.jenkins-ci.plugins plugin - 4.45 + 4.48 org.jenkins-ci.plugins.workflow From 597ffe744637d84c22ac254120043f000967e52f Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 10 Oct 2022 18:06:49 -0400 Subject: [PATCH 113/114] `FlowExecutionList.ParallelResumer` should wait until Jenkins startup is complete --- .../plugins/workflow/flow/FlowExecutionList.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java index 13d4194b..220a2272 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java @@ -338,6 +338,15 @@ private static final class ParallelResumer { } synchronized void run() { + if (Jenkins.get().isTerminating()) { + LOGGER.fine("Skipping step resumption during shutdown"); + return; + } + if (Jenkins.get().getInitLevel() != InitMilestone.COMPLETED || Jenkins.get().isQuietingDown()) { + LOGGER.fine("Waiting to resume step until Jenkins completes startup and is not in quiet mode"); + Timer.get().schedule(this::run, 100, TimeUnit.MILLISECONDS); + return; + } LOGGER.fine(() -> "Checking status with nodes=" + nodes + " enclosing=" + enclosing + " processing=" + processing); if (nodes.isEmpty()) { if (processing.isEmpty()) { From 4bfe059b22e0850265466c86f3026c01bdf691bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jan 2023 17:00:56 +0000 Subject: [PATCH 114/114] Bump bom-2.332.x from 1466.v85a_616ea_b_87c to 1763.v092b_8980a_f5e Bumps [bom-2.332.x](https://github.com/jenkinsci/bom) from 1466.v85a_616ea_b_87c to 1763.v092b_8980a_f5e. - [Release notes](https://github.com/jenkinsci/bom/releases) - [Commits](https://github.com/jenkinsci/bom/commits) --- updated-dependencies: - dependency-name: io.jenkins.tools.bom:bom-2.332.x dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0454b41d..82517d1e 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ io.jenkins.tools.bom bom-2.332.x - 1466.v85a_616ea_b_87c + 1763.v092b_8980a_f5e import pom