Skip to content

Commit 7fd3dd2

Browse files
authored
Merge branch 'master' into logging-exMTL
2 parents 1ea2ffe + 90b1b9e commit 7fd3dd2

File tree

17 files changed

+274
-178
lines changed

17 files changed

+274
-178
lines changed

lib/src/main/java/com/cloudbees/groovy/cps/impl/CallEnv.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,10 @@ public void buildStackTraceElements(List<StackTraceElement> stack, int depth) {
132132
public int getDepth() {
133133
return depth;
134134
}
135+
136+
@Override
137+
public String toString() {
138+
return callSiteLoc != null ? super.toString() + " @" + callSiteLoc : super.toString();
139+
}
140+
135141
}

plugin/pom.xml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
</license>
4242
</licenses>
4343
<properties>
44+
<!-- TODO until in parent -->
45+
<jenkins-test-harness.version>2403.v256947ecb_c8a_</jenkins-test-harness.version>
4446
<no-test-jar>false</no-test-jar>
4547
<node.version>18.15.0</node.version>
4648
<yarn.version>1.22.19</yarn.version>
@@ -54,6 +56,17 @@
5456
<scope>import</scope>
5557
<type>pom</type>
5658
</dependency>
59+
<!-- TODO until in BOM -->
60+
<dependency>
61+
<groupId>org.jenkins-ci.plugins</groupId>
62+
<artifactId>support-core</artifactId>
63+
<version>1575.v49b_01a_460e3b_</version>
64+
</dependency>
65+
<dependency>
66+
<groupId>org.jenkins-ci.plugins.workflow</groupId>
67+
<artifactId>workflow-api</artifactId>
68+
<version>1366.vf1fb_e1a_f6b_22</version>
69+
</dependency>
5770
</dependencies>
5871
</dependencyManagement>
5972
<dependencies>
@@ -226,7 +239,7 @@
226239
<dependency>
227240
<groupId>org.testcontainers</groupId>
228241
<artifactId>testcontainers</artifactId>
229-
<version>1.20.4</version>
242+
<version>1.20.6</version>
230243
<scope>test</scope>
231244
<exclusions>
232245
<!-- Provided by Jenkins core -->
@@ -239,7 +252,7 @@
239252
<dependency>
240253
<groupId>org.awaitility</groupId>
241254
<artifactId>awaitility</artifactId>
242-
<version>4.2.2</version>
255+
<version>4.3.0</version>
243256
<scope>test</scope>
244257
</dependency>
245258
<dependency>

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,6 @@
145145
import edu.umd.cs.findbugs.annotations.NonNull;
146146
import net.jcip.annotations.GuardedBy;
147147

148-
import org.acegisecurity.Authentication;
149-
import org.acegisecurity.userdetails.UsernameNotFoundException;
150148
import java.nio.charset.StandardCharsets;
151149
import jenkins.util.SystemProperties;
152150
import org.codehaus.groovy.GroovyBugError;
@@ -158,6 +156,8 @@
158156
import org.jenkinsci.plugins.workflow.flow.FlowExecutionList;
159157
import org.jenkinsci.plugins.workflow.graph.FlowGraphWalker;
160158
import org.kohsuke.accmod.restrictions.DoNotUse;
159+
import org.springframework.security.core.Authentication;
160+
import org.springframework.security.core.userdetails.UsernameNotFoundException;
161161

162162
/**
163163
* {@link FlowExecution} implemented with Groovy CPS.
@@ -425,8 +425,8 @@ public CpsFlowExecution(@NonNull String script, boolean sandbox, @NonNull FlowE
425425
this.script = script;
426426
this.sandbox = sandbox;
427427
this.durabilityHint = durabilityHint;
428-
Authentication auth = Jenkins.getAuthentication();
429-
this.user = auth.equals(ACL.SYSTEM) ? null : auth.getName();
428+
Authentication auth = Jenkins.getAuthentication2();
429+
this.user = auth.equals(ACL.SYSTEM2) ? null : auth.getName();
430430
this.storage = createStorage();
431431
this.storage.setAvoidAtomicWrite(!this.getDurabilityHint().isAtomicWrite());
432432
}
@@ -1872,21 +1872,21 @@ void notifyListeners(List<FlowNode> nodes, boolean synchronous) {
18721872
}
18731873
}
18741874

1875-
@Override public Authentication getAuthentication() {
1875+
@Override public Authentication getAuthentication2() {
18761876
if (user == null) {
1877-
return ACL.SYSTEM;
1877+
return ACL.SYSTEM2;
18781878
}
18791879
try {
18801880
User u = User.getById(user, true);
18811881
if (u == null) {
1882-
return Jenkins.ANONYMOUS;
1882+
return Jenkins.ANONYMOUS2;
18831883
} else {
1884-
return u.impersonate();
1884+
return u.impersonate2();
18851885
}
18861886
} catch (UsernameNotFoundException x) {
18871887
LOGGER.log(Level.WARNING, "could not restore authentication", x);
18881888
// Should not expose this to callers.
1889-
return Jenkins.ANONYMOUS;
1889+
return Jenkins.ANONYMOUS2;
18901890
}
18911891
}
18921892

plugin/src/main/java/org/jenkinsci/plugins/workflow/cps/CpsVmExecutorService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ void restore() {
169169

170170
private ThreadContext setUp() {
171171
CpsFlowExecution execution = cpsThreadGroup.getExecution();
172-
ACL.impersonate(execution.getAuthentication());
172+
ACL.impersonate2(execution.getAuthentication2());
173173
CURRENT.set(cpsThreadGroup);
174174
cpsThreadGroup.busy = true;
175175
Thread t = Thread.currentThread();

plugin/src/main/java/org/jenkinsci/plugins/workflow/cps/LoggingInvoker.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.cloudbees.groovy.cps.sandbox.SandboxInvoker;
3131
import edu.umd.cs.findbugs.annotations.CheckForNull;
3232
import edu.umd.cs.findbugs.annotations.NonNull;
33+
import groovy.lang.Closure;
3334
import groovy.lang.GroovyClassLoader;
3435
import hudson.ExtensionList;
3536
import java.util.Set;
@@ -133,10 +134,55 @@ private void maybeRecord(Class<?> clazz, Supplier<String> message) {
133134
return delegate.getProperty(lhs, name);
134135
}
135136

137+
private static final Set<String> CLOSURE_METAPROPS = Set.of("delegate", "directive", "resolveStrategy");
138+
136139
@Override public void setProperty(Object lhs, String name, Object value) throws Throwable {
137140
Class<?> clazz = classOf(lhs);
138141
maybeRecord(clazz, () -> clazz.getName() + "." + name);
139142
delegate.setProperty(lhs, name, value);
143+
if (SystemProperties.getBoolean(LoggingInvoker.class.getName() + ".fieldSetWarning", true)) {
144+
if (value != null && !CLOSURE_METAPROPS.contains(name)) {
145+
var receiver = findReceiver(lhs);
146+
if (receiver instanceof CpsScript) {
147+
try {
148+
receiver.getClass().getDeclaredField(name);
149+
// OK, @Field, ignore
150+
} catch (NoSuchFieldException x) {
151+
var g = CpsThreadGroup.current();
152+
if (g != null) {
153+
var e = g.getExecution();
154+
if (e != null) {
155+
e.getOwner().getListener().getLogger().println(Messages.LoggingInvoker_field_set(receiver.getClass().getSimpleName(), name, value.getClass().getSimpleName()));
156+
}
157+
}
158+
}
159+
}
160+
}
161+
}
162+
}
163+
164+
private Object findReceiver(Object o) {
165+
if (o instanceof Closure<?> c) {
166+
// c.f. https://github.com/apache/groovy/blob/41b990d0a20e442f29247f0e04cbed900f3dcad4/src/main/groovy/lang/Closure.java#L344
167+
return switch (c.getResolveStrategy()) {
168+
case Closure.DELEGATE_ONLY -> findReceiver(c.getDelegate());
169+
case Closure.OWNER_ONLY -> findReceiver(c.getOwner());
170+
case Closure.TO_SELF -> c;
171+
case Closure.DELEGATE_FIRST -> {
172+
var delegate = c.getDelegate();
173+
if (delegate == null) {
174+
yield findReceiver(c.getOwner());
175+
}
176+
// TODO: Groovy will actually try the delegate first and then the owner if needed, but it is
177+
// difficult for us to know what will happen in advance.
178+
yield findReceiver(delegate);
179+
}
180+
default ->
181+
// TODO: Groovy will actually try the owner first and then the delegate if needed.
182+
findReceiver(c.getOwner());
183+
};
184+
}
185+
return o;
140186
}
141187

142188
@Override public Object getAttribute(Object lhs, String name) throws Throwable {

plugin/src/main/resources/org/jenkinsci/plugins/workflow/cps/Messages.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ SnippetizerLink.OnlineDocsLink.displayName=Online Documentation
66
SnippetizerLink.ExamplesLink.displayName=Examples Reference
77
SnippetizerLink.GDSLLink.displayName=IntelliJ IDEA GDSL
88
Pipeline.Syntax=Pipeline Syntax
9+
# TODO perhaps create a https://jenkins.io/redirect/pipeline-field-set/
10+
LoggingInvoker.field_set=\
11+
Did you forget the `def` keyword? \
12+
{0} seems to be setting a field named {1} (to a value of type {2}) \
13+
which could lead to memory leaks or other issues.

plugin/src/main/resources/org/jenkinsci/plugins/workflow/cps/Snippetizer/globals.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ l.layout(title:_("Pipeline Syntax: Global Variable Reference"), norefresh: true)
3030
}
3131
}
3232
dd{
33-
def rd = request.getView(v, "help");
33+
def rd = request2.getView(v, "help");
3434
div(class:"help", style:"display: block") {
3535
if (rd != null) {
3636
st.include(page: "help", it: v)

plugin/src/main/resources/org/jenkinsci/plugins/workflow/cps/Snippetizer/index.jelly

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ THE SOFTWARE.
4040
</f:block>
4141
<f:section title="${%Steps}"/>
4242
<!-- Similar to f:dropdownDescriptorSelector, but adds fallback content to block, and JENKINS-25130 adds per-selection help: -->
43-
<j:set var="item" value="${it.getItem(request)}"/>
43+
<j:set var="item" value="${it.getItem(request2)}"/>
4444
<d:taglib uri="local">
4545
<d:tag name="listSteps">
4646
<j:forEach var="quasiDescriptor" items="${quasiDescriptors}">

plugin/src/test/java/org/jenkinsci/plugins/workflow/Workflow2Test.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ private static void stage2(JenkinsRule r) throws Throwable {
8888
inputStepExecution.proceed(null);
8989
r.assertBuildStatusSuccess(r.waitForCompletion(b));
9090
var b2 = await().until(() -> p.getBuildByNumber(2), Matchers.notNullValue());
91-
assertThat("Expecting the replayed build to use sandbox", ((CpsFlowExecution) b2.getExecution()).isSandbox(), is(true));
91+
var execution = await().until(b2::getExecution, Matchers.notNullValue());
92+
assertThat("Expecting the replayed build to use sandbox", ((CpsFlowExecution) execution).isSandbox(), is(true));
93+
b2.doStop();
94+
r.waitForCompletion(b2);
9295
}
9396
}

0 commit comments

Comments
 (0)