Skip to content

Commit 768f251

Browse files
committed
LibraryConfiguration: defaultedVersion(): for WorkflowRun check getSCMs() and handle if it is GitSCM with branches, before looking at envvars [JENKINS-69731]
1 parent 2197764 commit 768f251

File tree

1 file changed

+99
-1
lines changed

1 file changed

+99
-1
lines changed

src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package org.jenkinsci.plugins.workflow.libs;
2626

2727
import hudson.AbortException;
28+
import hudson.EnvVars;
2829
import hudson.Extension;
2930
import hudson.ExtensionList;
3031
import hudson.Util;
@@ -34,8 +35,10 @@
3435
import hudson.model.Item;
3536
import hudson.model.Run;
3637
import hudson.model.TaskListener;
38+
import hudson.scm.SCM;
3739
import hudson.util.FormValidation;
3840
import jenkins.model.Jenkins;
41+
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
3942
import org.kohsuke.accmod.Restricted;
4043
import org.kohsuke.accmod.restrictions.NoExternalUse;
4144
import org.kohsuke.stapler.AncestorInPath;
@@ -48,6 +51,8 @@
4851

4952
import edu.umd.cs.findbugs.annotations.CheckForNull;
5053
import edu.umd.cs.findbugs.annotations.NonNull;
54+
import java.lang.reflect.Method;
55+
import java.util.List;
5156
import java.util.Collection;
5257

5358
/**
@@ -184,12 +189,105 @@ public LibraryCachingConfiguration getCachingConfiguration() {
184189
if (run != null && listener != null) {
185190
try {
186191
runParent = run.getParent();
187-
runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null);
188192
} catch (Exception x) {
189193
// no-op, keep null
190194
}
191195
}
192196

197+
// without a runParent we can't validateVersion() anyway
198+
if (runParent != null) {
199+
// First ask SCM source of the pipeline (if any),
200+
// as the most authoritative source of the branch
201+
// name we want:
202+
try {
203+
if (run instanceof WorkflowRun) {
204+
// This covers both "Multibranch Pipeline"
205+
// and "Pipeline script from SCM" jobs;
206+
// it also covers "inline" pipeline scripts
207+
// but throws a hudson.AbortException since
208+
// there is no SCM attached.
209+
WorkflowRun wfRun = (WorkflowRun) run;
210+
SCM scm0 = wfRun.getSCMs().get(0);
211+
if (scm0 != null) {
212+
// Avoid importing GitSCM and so requiring that
213+
// it is always installed even if not used by
214+
// particular Jenkins deployment (using e.g.
215+
// SVN, Gerritt, etc.). Our aim is to query this:
216+
// runVersion = scm0.getBranches().first().getExpandedName(run.getEnvironment(listener));
217+
// https://mkyong.com/java/how-to-use-reflection-to-call-java-method-at-runtime/
218+
Class noparams[] = {};
219+
Class[] paramEnvVars = new Class[1];
220+
paramEnvVars[0] = EnvVars.class;
221+
if ("hudson.plugins.git.GitSCM".equals(scm0.getClass().getName())) {
222+
// https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/GitSCM.html#getBranches() =>
223+
// https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/BranchSpec.html#toString()
224+
Method methodGetBranches = null;
225+
try {
226+
methodGetBranches = scm0.getClass().getDeclaredMethod("getBranches", noparams);
227+
} catch (Exception x) {
228+
// NoSuchMethodException | SecurityException | NullPointerException
229+
methodGetBranches = null;
230+
}
231+
if (methodGetBranches != null) {
232+
Object branchList = methodGetBranches.invoke(scm0);
233+
if (branchList instanceof List) {
234+
Object branch0 = ((List<Object>) branchList).get(0);
235+
if ("hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) {
236+
Method methodGetExpandedName = null;
237+
try {
238+
methodGetExpandedName = branch0.getClass().getDeclaredMethod("getExpandedName", paramEnvVars);
239+
} catch (Exception x) {
240+
methodGetExpandedName = null;
241+
}
242+
if (methodGetExpandedName != null) {
243+
// Handle possible shell-templated branch specs:
244+
Object expandedBranchName = methodGetExpandedName.invoke(branch0, run.getEnvironment(listener));
245+
if (expandedBranchName != null) {
246+
runVersion = expandedBranchName.toString();
247+
}
248+
}
249+
if (runVersion == null || "".equals(runVersion)) {
250+
runVersion = branch0.toString();
251+
}
252+
} // else unknown class, make no blind guesses
253+
}
254+
} // else not really the GitSCM we know?
255+
} // else SVN, Gerritt or some other SCM -
256+
// add handling when needed and known how
257+
// or rely on BRANCH_NAME (if set) below...
258+
}
259+
260+
// Still alive? Chop off leading '*/'
261+
// (if any) from single-branch MBP and
262+
// plain "Pipeline" job definitions.
263+
if (runVersion != null) {
264+
runVersion = runVersion.replaceFirst("^\\*/", "");
265+
}
266+
}
267+
} catch (Exception x) {
268+
// no-op, keep null
269+
}
270+
271+
if (runVersion == null) {
272+
// Probably not in a multibranch pipeline workflow
273+
// type of job? Is envvar BRANCH_NAME defined?
274+
try {
275+
runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null);
276+
} catch (Exception x) {
277+
// no-op, keep null
278+
}
279+
}
280+
281+
// Note: if runVersion remains null (unresolved -
282+
// with other job types and/or SCMs maybe setting
283+
// other envvar names), we might drill into names
284+
// like GIT_BRANCH, GERRIT_BRANCH etc. but it would
285+
// not be too scalable. So gotta stop somewhere.
286+
// We would however look into (MBP-defined for PRs)
287+
// CHANGE_BRANCH and CHANGE_TARGET as other fallbacks
288+
// below.
289+
}
290+
193291
if (runParent == null || runVersion == null || "".equals(runVersion)) {
194292
// Current build does not know a BRANCH_NAME envvar,
195293
// or it's an empty string, or this request has null

0 commit comments

Comments
 (0)