Skip to content

Commit e472c0a

Browse files
committed
Merge remote-tracking branch 'origin/master' into verified-as-of-2.39
Conflicts: src/test/java/org/jenkinsci/plugins/workflow/cps/CpsFlowDefinition2Test.java
2 parents 5984664 + 59c88c7 commit e472c0a

25 files changed

+436
-180
lines changed

Jenkinsfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
buildPlugin(jenkinsVersions: [null, '2.60.1'])
1+
buildPlugin(jenkinsVersions: [null, '2.60.3', '2.73.1'])

pom.xml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@
2828
<parent>
2929
<groupId>org.jenkins-ci.plugins</groupId>
3030
<artifactId>plugin</artifactId>
31-
<version>2.31</version>
31+
<version>2.35</version>
3232
<relativePath />
3333
</parent>
3434
<groupId>org.jenkins-ci.plugins.workflow</groupId>
3535
<artifactId>workflow-cps</artifactId>
36-
<version>2.40-SNAPSHOT</version>
36+
<version>2.42-SNAPSHOT</version>
3737
<packaging>hpi</packaging>
3838
<name>Pipeline: Groovy</name>
3939
<url>https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Groovy+Plugin</url>
@@ -67,13 +67,13 @@
6767
<git-plugin.version>3.1.0</git-plugin.version>
6868
<workflow-support-plugin.version>2.14</workflow-support-plugin.version>
6969
<scm-api-plugin.version>2.0.8</scm-api-plugin.version>
70-
<groovy-cps.version>1.18</groovy-cps.version>
70+
<groovy-cps.version>1.20</groovy-cps.version>
7171
</properties>
7272
<dependencies>
7373
<dependency>
7474
<groupId>org.jenkins-ci.plugins.workflow</groupId>
7575
<artifactId>workflow-step-api</artifactId>
76-
<version>2.10</version>
76+
<version>2.13</version>
7777
</dependency>
7878
<dependency>
7979
<groupId>org.jenkins-ci.plugins.workflow</groupId>
@@ -93,7 +93,7 @@
9393
<dependency>
9494
<groupId>org.jenkins-ci.plugins</groupId>
9595
<artifactId>script-security</artifactId>
96-
<version>1.31</version>
96+
<version>1.34</version>
9797
</dependency>
9898
<dependency>
9999
<groupId>org.jenkins-ci.plugins</groupId>
@@ -139,7 +139,7 @@
139139
<dependency>
140140
<groupId>org.jenkins-ci.plugins.workflow</groupId>
141141
<artifactId>workflow-job</artifactId>
142-
<version>2.10</version>
142+
<version>2.11.2</version>
143143
<scope>test</scope>
144144
</dependency>
145145
<dependency>
@@ -298,6 +298,13 @@
298298
<compatibleSinceVersion>2.18</compatibleSinceVersion>
299299
</configuration>
300300
</plugin>
301+
<plugin>
302+
<groupId>org.apache.maven.plugins</groupId>
303+
<artifactId>maven-surefire-plugin</artifactId>
304+
<configuration>
305+
<reuseForks>false</reuseForks> <!-- TODO reuse seems to cause problems in CpsFlowExecutionMemoryTest -->
306+
</configuration>
307+
</plugin>
301308
</plugins>
302309
</build>
303310
</project>

src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecution.java

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
import org.acegisecurity.Authentication;
138138
import org.acegisecurity.userdetails.UsernameNotFoundException;
139139
import org.apache.commons.io.Charsets;
140+
import org.codehaus.groovy.GroovyBugError;
140141
import org.jboss.marshalling.reflect.SerializableClassRegistry;
141142

142143
import static org.jenkinsci.plugins.workflow.cps.persistence.PersistenceContext.*;
@@ -656,7 +657,7 @@ public void onFailure(Throwable t) {
656657
}
657658
});
658659

659-
} catch (IOException e) {
660+
} catch (Exception | GroovyBugError e) {
660661
loadProgramFailed(e, result);
661662
}
662663
}
@@ -1035,6 +1036,7 @@ synchronized void onProgramEnd(Outcome outcome) {
10351036
}
10361037

10371038
void cleanUpHeap() {
1039+
LOGGER.log(Level.FINE, "cleanUpHeap on {0}", owner);
10381040
shell = null;
10391041
trusted = null;
10401042
if (scriptClass != null) {
@@ -1044,6 +1046,8 @@ void cleanUpHeap() {
10441046
LOGGER.log(Level.WARNING, "failed to clean up memory from " + owner, x);
10451047
}
10461048
scriptClass = null;
1049+
} else {
1050+
LOGGER.fine("no scriptClass");
10471051
}
10481052
// perhaps also set programPromise to null or a precompleted failure?
10491053
}
@@ -1054,6 +1058,7 @@ private static void cleanUpLoader(ClassLoader loader, Set<ClassLoader> encounter
10541058
return;
10551059
}
10561060
if (!(loader instanceof GroovyClassLoader)) {
1061+
LOGGER.log(Level.FINER, "ignoring {0}", loader);
10571062
return;
10581063
}
10591064
if (!encounteredLoaders.add(loader)) {
@@ -1079,12 +1084,7 @@ private static void cleanUpLoader(ClassLoader loader, Set<ClassLoader> encounter
10791084
private static void cleanUpGlobalClassValue(@Nonnull ClassLoader loader) throws Exception {
10801085
Class<?> classInfoC = Class.forName("org.codehaus.groovy.reflection.ClassInfo");
10811086
// TODO switch to MethodHandle for speed
1082-
Field globalClassValueF;
1083-
try {
1084-
globalClassValueF = classInfoC.getDeclaredField("globalClassValue");
1085-
} catch (NoSuchFieldException x) {
1086-
return; // Groovy 1, fine
1087-
}
1087+
Field globalClassValueF = classInfoC.getDeclaredField("globalClassValue");
10881088
globalClassValueF.setAccessible(true);
10891089
Object globalClassValue = globalClassValueF.get(null);
10901090
Class<?> groovyClassValuePreJava7C = Class.forName("org.codehaus.groovy.reflection.GroovyClassValuePreJava7");
@@ -1135,33 +1135,28 @@ private static void cleanUpGlobalClassSet(@Nonnull Class<?> clazz) throws Except
11351135
Field globalClassSetF = classInfoC.getDeclaredField("globalClassSet");
11361136
globalClassSetF.setAccessible(true);
11371137
Object globalClassSet = globalClassSetF.get(null);
1138-
try { // Groovy 1
1139-
globalClassSet.getClass().getMethod("remove", Object.class).invoke(globalClassSet, clazz); // like Map but not
1140-
LOGGER.log(Level.FINER, "cleaning up {0} from GlobalClassSet", clazz.getName());
1141-
} catch (NoSuchMethodException x) { // Groovy 2
1142-
try {
1143-
classInfoC.getDeclaredField("classRef");
1144-
return; // 2.4.8+, nothing to do here (classRef is weak anyway)
1145-
} catch (NoSuchFieldException x2) {} // 2.4.7-
1146-
// Cannot just call .values() since that returns a copy.
1147-
Field itemsF = globalClassSet.getClass().getDeclaredField("items");
1148-
itemsF.setAccessible(true);
1149-
Object items = itemsF.get(globalClassSet);
1150-
Method iteratorM = items.getClass().getMethod("iterator");
1151-
Field klazzF = classInfoC.getDeclaredField("klazz");
1152-
klazzF.setAccessible(true);
1153-
synchronized (items) {
1154-
Iterator<?> iterator = (Iterator) iteratorM.invoke(items);
1155-
while (iterator.hasNext()) {
1156-
Object classInfo = iterator.next();
1157-
if (classInfo == null) {
1158-
LOGGER.finer("JENKINS-41945: ignoring null ClassInfo from ManagedLinkedList.Iter.next");
1159-
continue;
1160-
}
1161-
if (klazzF.get(classInfo) == clazz) {
1162-
iterator.remove();
1163-
LOGGER.log(Level.FINER, "cleaning up {0} from GlobalClassSet", clazz.getName());
1164-
}
1138+
try {
1139+
classInfoC.getDeclaredField("classRef");
1140+
return; // 2.4.8+, nothing to do here (classRef is weak anyway)
1141+
} catch (NoSuchFieldException x2) {} // 2.4.7-
1142+
// Cannot just call .values() since that returns a copy.
1143+
Field itemsF = globalClassSet.getClass().getDeclaredField("items");
1144+
itemsF.setAccessible(true);
1145+
Object items = itemsF.get(globalClassSet);
1146+
Method iteratorM = items.getClass().getMethod("iterator");
1147+
Field klazzF = classInfoC.getDeclaredField("klazz");
1148+
klazzF.setAccessible(true);
1149+
synchronized (items) {
1150+
Iterator<?> iterator = (Iterator) iteratorM.invoke(items);
1151+
while (iterator.hasNext()) {
1152+
Object classInfo = iterator.next();
1153+
if (classInfo == null) {
1154+
LOGGER.finer("JENKINS-41945: ignoring null ClassInfo from ManagedLinkedList.Iter.next");
1155+
continue;
1156+
}
1157+
if (klazzF.get(classInfo) == clazz) {
1158+
iterator.remove();
1159+
LOGGER.log(Level.FINER, "cleaning up {0} from GlobalClassSet", clazz.getName());
11651160
}
11661161
}
11671162
}

src/main/java/org/jenkinsci/plugins/workflow/cps/CpsThreadGroup.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,13 @@ private Object readResolve() {
152152
if (scripts != null) { // compatibility: the field will be null in old programs
153153
GroovyShell shell = execution.getShell();
154154
assert shell.getContext().getVariables().isEmpty();
155-
assert !scripts.isEmpty();
156-
// Take the canonical bindings from the main script and relink that object with that of the shell and all other loaded scripts which kept the same bindings.
157-
shell.getContext().getVariables().putAll(scripts.get(0).getBinding().getVariables());
158-
for (Script script : scripts) {
159-
if (script.getBinding().getVariables().equals(shell.getContext().getVariables())) {
160-
script.setBinding(shell.getContext());
155+
if (!scripts.isEmpty()) {
156+
// Take the canonical bindings from the main script and relink that object with that of the shell and all other loaded scripts which kept the same bindings.
157+
shell.getContext().getVariables().putAll(scripts.get(0).getBinding().getVariables());
158+
for (Script script : scripts) {
159+
if (script.getBinding().getVariables().equals(shell.getContext().getVariables())) {
160+
script.setBinding(shell.getContext());
161+
}
161162
}
162163
}
163164
}

src/main/java/org/jenkinsci/plugins/workflow/cps/EnvActionImpl.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import javax.annotation.Nonnull;
4343
import jenkins.model.RunAction2;
4444
import org.jenkinsci.plugins.workflow.flow.FlowCopier;
45+
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
4546
import org.jenkinsci.plugins.workflow.steps.EnvironmentExpander;
4647
import org.jenkinsci.plugins.workflow.support.actions.EnvironmentAction;
4748
import org.kohsuke.accmod.Restricted;
@@ -56,19 +57,25 @@ public class EnvActionImpl extends GroovyObjectSupport implements EnvironmentAct
5657
private static final long serialVersionUID = 1;
5758

5859
private final Map<String,String> env;
59-
private transient EnvVars ownerEnvironment; // cache
6060
private transient Run<?,?> owner;
6161

6262
private EnvActionImpl() {
6363
this.env = new TreeMap<String,String>();
6464
}
6565

6666
@Override public EnvVars getEnvironment() throws IOException, InterruptedException {
67-
if (ownerEnvironment == null) {
68-
// TODO call FlowExecutionOwner.getListener
69-
ownerEnvironment = owner.getEnvironment(new LogTaskListener(LOGGER, Level.INFO));
67+
TaskListener listener;
68+
if (owner instanceof FlowExecutionOwner.Executable) {
69+
FlowExecutionOwner executionOwner = ((FlowExecutionOwner.Executable) owner).asFlowExecutionOwner();
70+
if (executionOwner != null) {
71+
listener = executionOwner.getListener();
72+
} else {
73+
listener = new LogTaskListener(LOGGER, Level.INFO);
74+
}
75+
} else {
76+
listener = new LogTaskListener(LOGGER, Level.INFO);
7077
}
71-
EnvVars e = new EnvVars(ownerEnvironment);
78+
EnvVars e = owner.getEnvironment(listener);
7279
e.putAll(env);
7380
return e;
7481
}

src/main/java/org/jenkinsci/plugins/workflow/cps/RunningFlowActions.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@
2626

2727
import hudson.Extension;
2828
import hudson.model.Action;
29-
import java.util.Arrays;
29+
import hudson.model.Item;
30+
import hudson.security.AccessControlled;
31+
import java.util.ArrayList;
3032
import java.util.Collection;
3133
import java.util.Collections;
34+
import java.util.List;
3235
import jenkins.model.TransientActionFactory;
3336
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
3437
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
@@ -48,7 +51,13 @@
4851
FlowExecution exec = owner.getOrNull();
4952
if (exec instanceof CpsFlowExecution && !exec.isComplete()) {
5053
CpsFlowExecution e = (CpsFlowExecution) exec;
51-
return Arrays.asList(new CpsThreadDumpAction(e), new PauseUnpauseAction(e));
54+
List<Action> actions = new ArrayList<>();
55+
actions.add(new CpsThreadDumpAction(e));
56+
// TODO cf. comment in CpsFlowExecution#pause
57+
if (!(executable instanceof AccessControlled) || ((AccessControlled) executable).hasPermission(Item.CANCEL)) {
58+
actions.add(new PauseUnpauseAction(e));
59+
}
60+
return actions;
5261
}
5362
}
5463
return Collections.emptySet();

src/main/java/org/jenkinsci/plugins/workflow/cps/persistence/IteratorHack.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.AbstractList;
3030
import java.util.AbstractMap;
3131
import java.util.ArrayList;
32+
import java.util.Arrays;
3233
import java.util.Collection;
3334
import java.util.ConcurrentModificationException;
3435
import java.util.Iterator;
@@ -97,6 +98,11 @@ public static <E> Iterator<E> iterator(Set<E> set) {
9798
return new Itr<>(new ArrayList<>(set));
9899
}
99100

101+
public static <E> Iterator<E> iterator(E[] array) {
102+
// TODO as above
103+
return new Itr<>(Arrays.asList(array));
104+
}
105+
100106
private static final class ListItr<E> extends Itr<E> implements ListIterator<E> {
101107
private static final long serialVersionUID = 1;
102108
ListItr(List<E> list, int idx) {

src/main/java/org/jenkinsci/plugins/workflow/cps/replay/ReplayAction.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import javax.annotation.CheckForNull;
5959
import javax.annotation.Nonnull;
6060
import javax.servlet.ServletException;
61+
62+
import hudson.util.HttpResponses;
6163
import jenkins.model.Jenkins;
6264
import jenkins.model.ParameterizedJobMixIn;
6365
import jenkins.model.TransientActionFactory;
@@ -76,6 +78,8 @@
7678
import org.kohsuke.stapler.StaplerResponse;
7779
import org.kohsuke.stapler.interceptor.RequirePOST;
7880

81+
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
82+
7983
/**
8084
* Attached to a {@link Run} when it could be replayed with script edits.
8185
*/
@@ -115,6 +119,11 @@ private ReplayAction(Run run) {
115119
if (!run.hasPermission(REPLAY)) {
116120
return false;
117121
}
122+
123+
if (!run.getParent().isBuildable()) {
124+
return false;
125+
}
126+
118127
CpsFlowExecution exec = getExecution();
119128
if (exec == null) {
120129
return false;
@@ -163,7 +172,10 @@ public void doRun(StaplerRequest req, StaplerResponse rsp) throws ServletExcepti
163172
// optString since you might be replaying a running build, which might have loaded a script after the page load but before submission.
164173
replacementLoadedScripts.put(entry.getKey(), form.optString(entry.getKey().replace('.', '_'), entry.getValue()));
165174
}
166-
run(form.getString("mainScript"), replacementLoadedScripts);
175+
if (run(form.getString("mainScript"), replacementLoadedScripts) == null) {
176+
throw HttpResponses.error(SC_CONFLICT, new IOException(run.getParent().getFullName() + " is not buildable"));
177+
178+
}
167179
rsp.sendRedirect("../.."); // back to WorkflowJob; new build might not start instantly so cannot redirect to it
168180
}
169181

src/main/java/org/jenkinsci/plugins/workflow/cps/steps/LoadStepExecution.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,6 @@ public boolean start() throws Exception {
5454
return false;
5555
}
5656

57-
@Override
58-
public void stop(Throwable cause) throws Exception {
59-
// noop
60-
//
61-
// the head of the CPS thread that's executing the body should stop and that's all we need to do.
62-
}
63-
6457
private static final long serialVersionUID = 1L;
6558

6659
}

src/main/java/org/jenkinsci/plugins/workflow/cps/steps/ParallelStepExecution.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public boolean start() throws Exception {
6262

6363
@Override
6464
public void stop(Throwable cause) throws Exception {
65+
// Despite suggestion in JENKINS-26148, super.stop does not work here, even accounting for the direct call from checkAllDone.
6566
for (BodyExecution body : bodies) {
6667
body.cancel(cause);
6768
}

0 commit comments

Comments
 (0)