From a937477f843af05cedef627e73d218c9cd162ed0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 28 Sep 2022 17:12:57 +0200 Subject: [PATCH 01/70] LibraryConfiguration: extend with allowBRANCH_NAME for literal use of @Library('libname@${BRANCH_NAME}') [JENKINS-69731] To simplify co-existence of feature-branched pipeline scripts with feature-branched Jenkins Shared Libraries, allow literal use of @Library('libname@${BRANCH_NAME}') _ lines to load a variant of a trusted (global) library with one case of arbitrary branch naming. This should be permitted by allowBRANCH_NAME checkbox (independent of generic allowVersionOverride setting) in the first place. If enabled, the value of BRANCH_NAME environment variable set by the current build's Run object would be queried, and if resolvable - verified with retriever.validateVersion() to match the backend (Legacy SCM, Modern SCM, and other retrievers like HTTP/ZIP from workflow-cps-global-lib-http-plugin). --- .../plugins/workflow/libs/LibraryAdder.java | 2 +- .../workflow/libs/LibraryConfiguration.java | 90 ++++++++++++++++++- .../libs/LibraryConfiguration/config.jelly | 3 + .../help-allowBRANCH_NAME.html | 9 ++ 4 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME.html diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryAdder.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryAdder.java index 9ff64777..3653038d 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryAdder.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryAdder.java @@ -126,7 +126,7 @@ listener.getLogger().println("Only using first definition of library " + name); continue; } - String version = cfg.defaultedVersion(libraryVersions.remove(name)); + String version = cfg.defaultedVersion(libraryVersions.remove(name), build, listener); Boolean changelog = cfg.defaultedChangelogs(libraryChangelogs.remove(name)); String source = kind.getClass().getName(); if (cfg instanceof LibraryResolver.ResolvedLibraryConfiguration) { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index bc7a67d6..fe2c42d3 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -32,6 +32,8 @@ import hudson.model.Descriptor; import hudson.model.DescriptorVisibilityFilter; import hudson.model.Item; +import hudson.model.Run; +import hudson.model.TaskListener; import hudson.util.FormValidation; import jenkins.model.Jenkins; import org.kohsuke.accmod.Restricted; @@ -58,6 +60,7 @@ public class LibraryConfiguration extends AbstractDescribableImpl run, TaskListener listener) throws AbortException { if (version == null) { if (defaultVersion == null) { throw new AbortException("No version specified for library " + name); } else { return defaultVersion; } - } else if (allowVersionOverride) { + } else if (allowVersionOverride && !"${BRANCH_NAME}".equals(version)) { return version; + } else if (allowBRANCH_NAME && "${BRANCH_NAME}".equals(version)) { + String runVersion = null; + Item runParent = null; + if (run != null && listener != null) { + try { + runParent = run.getParent(); + runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null); + } catch (Exception x) { + // no-op, keep null + } + } + + if (runParent == null || runVersion == null || "".equals(runVersion)) { + // Current build does not know a BRANCH_NAME envvar, + // or it's an empty string, or this request has null + // args for run/listener needed for validateVersion() + // below, or some other problem occurred. + // Fall back if we can: + if (defaultVersion == null) { + throw new AbortException("No version specified for library " + name); + } else { + return defaultVersion; + } + } + + // Check if runVersion is resolvable by LibraryRetriever + // implementation (SCM, HTTP, etc.); fall back if not: + if (retriever != null) { + FormValidation fv = retriever.validateVersion(name, runVersion, runParent); + + if (fv != null && fv.kind == FormValidation.Kind.OK) { + return runVersion; + } + } + + // No retriever, or its validateVersion() did not confirm + // usability of BRANCH_NAME string value as the version... + if (defaultVersion == null) { + throw new AbortException("BRANCH_NAME version " + runVersion + + " was not found, and no default version specified, for library " + name); + } else { + return defaultVersion; + } } else { throw new AbortException("Version override not permitted for library " + name); } @@ -172,16 +236,38 @@ public FormValidation doCheckName(@QueryParameter String name) { } @RequirePOST - public FormValidation doCheckDefaultVersion(@AncestorInPath Item context, @QueryParameter String defaultVersion, @QueryParameter boolean implicit, @QueryParameter boolean allowVersionOverride, @QueryParameter String name) { + public FormValidation doCheckDefaultVersion(@AncestorInPath Item context, @QueryParameter String defaultVersion, @QueryParameter boolean implicit, @QueryParameter boolean allowVersionOverride, @QueryParameter boolean allowBRANCH_NAME, @QueryParameter String name) { if (defaultVersion.isEmpty()) { if (implicit) { return FormValidation.error("If you load a library implicitly, you must specify a default version."); } + if (allowBRANCH_NAME) { + return FormValidation.error("If you allow use of literal '@${BRANCH_NAME}' for overriding a default version, you must define that version as fallback."); + } if (!allowVersionOverride) { return FormValidation.error("If you deny overriding a default version, you must define that version."); } return FormValidation.ok(); } else { + if ("${BRANCH_NAME}".equals(defaultVersion)) { + if (!allowBRANCH_NAME) { + return FormValidation.error("Use of literal '@${BRANCH_NAME}' not allowed in this configuration."); + } + + // The context is not a particular Run (might be a Job) + // so we can't detect which BRANCH_NAME is relevant: + String msg = "Cannot validate default version: " + + "literal '@${BRANCH_NAME}' is reserved " + + "for pipeline files from SCM"; + if (implicit) { + // Someone might want to bind feature branches of + // job definitions and implicit libs by default?.. + return FormValidation.warning(msg); + } else { + return FormValidation.error(msg); + } + } + for (LibraryResolver resolver : ExtensionList.lookup(LibraryResolver.class)) { for (LibraryConfiguration config : resolver.fromConfiguration(Stapler.getCurrentRequest())) { if (config.getName().equals(name)) { diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly index 336a96e5..24e710a2 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly @@ -37,6 +37,9 @@ THE SOFTWARE. + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME.html b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME.html new file mode 100644 index 00000000..c486426a --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME.html @@ -0,0 +1,9 @@ +
+ If checked, scripts may select a custom version of the library + by appending literally @${BRANCH_NAME} in the + @Library annotation, to use same SCM branch name + of the library codebase as that of the pipeline being built.
+ If such branch name is not resolvable as an environment variable + or not present in library storage (SCM, release artifacts...), + it would fall back to default version you select here. +
From 15a252ae7eb2c8fa6d54dcbe5e09dfaf89bd67bc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 28 Sep 2022 18:31:22 +0200 Subject: [PATCH 02/70] LibraryConfigurationTest.java: add tests for LibraryConfiguration.defaultedVersion() for JENKINS-69731 related behaviors --- .../libs/LibraryConfigurationTest.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java index c91a4627..36a4f975 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java @@ -26,9 +26,11 @@ import java.util.Collections; +import hudson.AbortException; import hudson.plugins.git.GitSCM; import org.hamcrest.Matchers; import org.junit.Test; +import org.junit.Assert; import static org.junit.Assert.*; import org.junit.Rule; import org.jvnet.hudson.test.Issue; @@ -92,6 +94,105 @@ public class LibraryConfigurationTest { assertNull(cfg.getDefaultVersion()); } + @Issue("JENKINS-69731") + @Test public void nullPresentDefaultedVersion() { + String libraryName = "valid-name"; + String defaultVersion = "master"; + + LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); + cfg.setDefaultVersion(defaultVersion); + + assertEquals("master", cfg.getDefaultVersion()); + try { + assertEquals("master", cfg.defaultedVersion(null)); + } catch(AbortException ae) { + Assert.fail("LibraryConfiguration.defaultedVersion() threw an AbortException when it was not expected: " + ae.getMessage()); + } + } + + @Issue("JENKINS-69731") + @Test public void nullAbsentDefaultedVersion() { + String libraryName = "valid-name"; + + LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); + + assertEquals(null, cfg.getDefaultVersion()); + assertThrows(AbortException.class, () -> cfg.defaultedVersion(null)); + } + + @Issue("JENKINS-69731") + @Test public void forbiddenOverrideDefaultedVersion() { + String libraryName = "valid-name"; + + LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); + cfg.setAllowVersionOverride(false); + + assertEquals(false, cfg.isAllowVersionOverride()); + assertThrows(AbortException.class, () -> cfg.defaultedVersion("branchname")); + } + + @Issue("JENKINS-69731") + @Test public void allowedOverrideDefaultedVersion() { + String libraryName = "valid-name"; + + LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); + cfg.setAllowVersionOverride(true); + + assertEquals(true, cfg.isAllowVersionOverride()); + try { + assertEquals("branchname", cfg.defaultedVersion("branchname")); + } catch(AbortException ae) { + Assert.fail("LibraryConfiguration.defaultedVersion() threw an AbortException when it was not expected: " + ae.getMessage()); + } + } + + @Issue("JENKINS-69731") + @Test public void notAllowedOverrideDefaultedVersionWhenBRANCH_NAME() { + String libraryName = "valid-name"; + + LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); + cfg.setAllowVersionOverride(true); + cfg.setAllowBRANCH_NAME(false); + + assertEquals(true, cfg.isAllowVersionOverride()); + assertEquals(false, cfg.isAllowBRANCH_NAME()); + assertThrows(AbortException.class, () -> cfg.defaultedVersion("${BRANCH_NAME}")); + /* This SHOULD NOT return a version string that literally remains '${BRANCH_NAME}'! */ + } + + @Issue("JENKINS-69731") + @Test public void allowedBRANCH_NAMEnoRunPresentDefaultedVersion() { + String libraryName = "valid-name"; + String defaultVersion = "master"; + + LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); + cfg.setDefaultVersion(defaultVersion); + cfg.setAllowBRANCH_NAME(true); + + assertEquals(true, cfg.isAllowBRANCH_NAME()); + try { + assertEquals("master", cfg.defaultedVersion("${BRANCH_NAME}", null, null)); + } catch(AbortException ae) { + Assert.fail("LibraryConfiguration.defaultedVersion() threw an AbortException when it was not expected: " + ae.getMessage()); + } + } + + @Issue("JENKINS-69731") + @Test public void allowedBRANCH_NAMEnoRunAbsentDefaultedVersion() { + String libraryName = "valid-name"; + + LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); + cfg.setAllowBRANCH_NAME(true); + + assertEquals(true, cfg.isAllowBRANCH_NAME()); + assertThrows(AbortException.class, () -> cfg.defaultedVersion("${BRANCH_NAME}", null, null)); + } + /* Note: further tests for JENKINS-69731 behaviors with allowBRANCH_NAME + * would rely on having a Run with or without a BRANCH_NAME envvar, and + * a TaskListener, and a (mock?) LibraryRetriever that would confirm or + * deny existence of a requested "version" (e.g. Git branch) of the lib. + * For examples, see e.g. SCMSourceRetrieverTest codebase. + */ } From bf228e4c95965ef53119298d6f94d5974dbe2f06 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 28 Sep 2022 20:34:46 +0200 Subject: [PATCH 03/70] SCMSourceRetrieverTest.java: add checkDefaultVersion() for interactions with BRANCH_NAME [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index e1b1553a..8e6ca67a 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -218,6 +218,54 @@ public class SCMSourceRetrieverTest { assertThat("foo\\..\\bar", matchesPattern(PROHIBITED_DOUBLE_DOT)); } + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion() throws Exception { + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowBRANCH_NAME(true); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Basename "libname" notation => use specified default branch + WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); + p0.setDefinition(new CpsFlowDefinition("@Library('branchylib') import myecho; myecho()", true)); + WorkflowRun b0 = r.buildAndAssertSuccess(p0); + r.assertLogContains("something special", b0); + + // Use specified branch + WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@master') import myecho; myecho()", true)); + WorkflowRun b1 = r.buildAndAssertSuccess(p1); + r.assertLogContains("something special", b1); + + // Use another specified branch + WorkflowJob p2 = r.jenkins.createProject(WorkflowJob.class, "p2"); + p2.setDefinition(new CpsFlowDefinition("@Library('branchylib@feature') import myecho; myecho()", true)); + WorkflowRun b2 = r.buildAndAssertSuccess(p2); + r.assertLogContains("something very special", b2); + + // Branch context for job not set - fall back to default + WorkflowJob p3 = r.jenkins.createProject(WorkflowJob.class, "p3"); + p3.setDefinition(new CpsFlowDefinition("@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()", true)); + WorkflowRun b3 = r.buildAndAssertSuccess(p3); + r.assertLogContains("something special", b3); + + // TODO: create a job instantiated from Git (or fooled into + // thinking it is - injecting BRANCH_NAME envvar via Java) + // and check behaviors with BRANCH_NAME="master", + // BRANCH_NAME="feature", BRANCH_NAME="bogus" and + // BRANCH_NAME="" + } + @Issue("JENKINS-43802") @Test public void owner() throws Exception { GlobalLibraries.get().setLibraries(Collections.singletonList( From 61db728b4a53a2485a7ed3916dd81d05acd7dc3e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 28 Sep 2022 23:56:09 +0200 Subject: [PATCH 04/70] SCMSourceRetrieverTest: checkDefaultVersion(): also test non-checkout of an absent libname@bogusbranch --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 8e6ca67a..f742f3dc 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -259,6 +259,14 @@ public class SCMSourceRetrieverTest { WorkflowRun b3 = r.buildAndAssertSuccess(p3); r.assertLogContains("something special", b3); + // Use a specified but missing branch + WorkflowJob p4 = r.jenkins.createProject(WorkflowJob.class, "p4"); + p4.setDefinition(new CpsFlowDefinition("@Library('branchylib@bogus') import myecho; myecho()", true)); + WorkflowRun b4 = r.buildAndAssertStatus(Result.FAILURE, p4); + r.assertLogContains("ERROR: No version bogus found for library branchylib", b4); + r.assertLogContains("org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:", b4); + r.assertLogContains("WorkflowScript: Loading libraries failed", b4); + // TODO: create a job instantiated from Git (or fooled into // thinking it is - injecting BRANCH_NAME envvar via Java) // and check behaviors with BRANCH_NAME="master", From 2e3c8558bdac58307a256ca05584ba3cd2883a9d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 29 Sep 2022 13:31:15 +0200 Subject: [PATCH 05/70] LibraryConfiguration: extend with allowBRANCH_NAME_PR to let CHANGE_BRANCH and CHANGE_TARGET be considered as fallbacks [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 55 ++++++++++++++++++- .../libs/LibraryConfiguration/config.jelly | 3 + .../help-allowBRANCH_NAME_PR.html | 10 ++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME_PR.html diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index fe2c42d3..c26af47d 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -61,6 +61,7 @@ public class LibraryConfiguration extends AbstractDescribableImpl + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME_PR.html b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME_PR.html new file mode 100644 index 00000000..815b3adc --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME_PR.html @@ -0,0 +1,10 @@ +
+ If checked, and if scripts are allowed to select a custom version + of the library by appending literally @${BRANCH_NAME} + in the @Library annotation, then for pull request + builds additional fall-back library branches would include the + names used as CHANGE_BRANCH (name of source branch + of pipeline pull request) and CHANGE_TARGET (name + of target branch of pipeline pull request), if such branch names + already exist in the trusted shared library repository. +
From 0f89b227886341d92c532a9d09d2fb1ecd09ab52 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 29 Sep 2022 13:32:40 +0200 Subject: [PATCH 06/70] LibraryConfiguration: defaultedVersion(): for WorkflowRun check getSCMs() and handle if it is GitSCM with branches, before looking at envvars [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 100 +++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index c26af47d..7fc30a0e 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -25,6 +25,7 @@ package org.jenkinsci.plugins.workflow.libs; import hudson.AbortException; +import hudson.EnvVars; import hudson.Extension; import hudson.ExtensionList; import hudson.Util; @@ -34,8 +35,10 @@ import hudson.model.Item; import hudson.model.Run; import hudson.model.TaskListener; +import hudson.scm.SCM; import hudson.util.FormValidation; import jenkins.model.Jenkins; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.AncestorInPath; @@ -48,6 +51,8 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import java.lang.reflect.Method; +import java.util.List; import java.util.Collection; /** @@ -184,12 +189,105 @@ public LibraryCachingConfiguration getCachingConfiguration() { if (run != null && listener != null) { try { runParent = run.getParent(); - runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null); } catch (Exception x) { // no-op, keep null } } + // without a runParent we can't validateVersion() anyway + if (runParent != null) { + // First ask SCM source of the pipeline (if any), + // as the most authoritative source of the branch + // name we want: + try { + if (run instanceof WorkflowRun) { + // This covers both "Multibranch Pipeline" + // and "Pipeline script from SCM" jobs; + // it also covers "inline" pipeline scripts + // but throws a hudson.AbortException since + // there is no SCM attached. + WorkflowRun wfRun = (WorkflowRun) run; + SCM scm0 = wfRun.getSCMs().get(0); + if (scm0 != null) { + // Avoid importing GitSCM and so requiring that + // it is always installed even if not used by + // particular Jenkins deployment (using e.g. + // SVN, Gerritt, etc.). Our aim is to query this: + // runVersion = scm0.getBranches().first().getExpandedName(run.getEnvironment(listener)); + // https://mkyong.com/java/how-to-use-reflection-to-call-java-method-at-runtime/ + Class noparams[] = {}; + Class[] paramEnvVars = new Class[1]; + paramEnvVars[0] = EnvVars.class; + if ("hudson.plugins.git.GitSCM".equals(scm0.getClass().getName())) { + // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/GitSCM.html#getBranches() => + // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/BranchSpec.html#toString() + Method methodGetBranches = null; + try { + methodGetBranches = scm0.getClass().getDeclaredMethod("getBranches", noparams); + } catch (Exception x) { + // NoSuchMethodException | SecurityException | NullPointerException + methodGetBranches = null; + } + if (methodGetBranches != null) { + Object branchList = methodGetBranches.invoke(scm0); + if (branchList instanceof List) { + Object branch0 = ((List) branchList).get(0); + if ("hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) { + Method methodGetExpandedName = null; + try { + methodGetExpandedName = branch0.getClass().getDeclaredMethod("getExpandedName", paramEnvVars); + } catch (Exception x) { + methodGetExpandedName = null; + } + if (methodGetExpandedName != null) { + // Handle possible shell-templated branch specs: + Object expandedBranchName = methodGetExpandedName.invoke(branch0, run.getEnvironment(listener)); + if (expandedBranchName != null) { + runVersion = expandedBranchName.toString(); + } + } + if (runVersion == null || "".equals(runVersion)) { + runVersion = branch0.toString(); + } + } // else unknown class, make no blind guesses + } + } // else not really the GitSCM we know? + } // else SVN, Gerritt or some other SCM - + // add handling when needed and known how + // or rely on BRANCH_NAME (if set) below... + } + + // Still alive? Chop off leading '*/' + // (if any) from single-branch MBP and + // plain "Pipeline" job definitions. + if (runVersion != null) { + runVersion = runVersion.replaceFirst("^\\*/", ""); + } + } + } catch (Exception x) { + // no-op, keep null + } + + if (runVersion == null) { + // Probably not in a multibranch pipeline workflow + // type of job? Is envvar BRANCH_NAME defined? + try { + runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null); + } catch (Exception x) { + // no-op, keep null + } + } + + // Note: if runVersion remains null (unresolved - + // with other job types and/or SCMs maybe setting + // other envvar names), we might drill into names + // like GIT_BRANCH, GERRIT_BRANCH etc. but it would + // not be too scalable. So gotta stop somewhere. + // We would however look into (MBP-defined for PRs) + // CHANGE_BRANCH and CHANGE_TARGET as other fallbacks + // below. + } + if (runParent == null || runVersion == null || "".equals(runVersion)) { // Current build does not know a BRANCH_NAME envvar, // or it's an empty string, or this request has null From cf3d140d2c0e47ae3cd768e1baca4251f39a43b8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 29 Sep 2022 16:01:37 +0200 Subject: [PATCH 07/70] LibraryConfiguration: defaultedVersion(): check for BRANCH_NAME first; and then it is safer to check getSCMs() not for WorkflowRun, but for the FlowDefinition from WorkflowJob [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 168 ++++++++++++------ 1 file changed, 109 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 7fc30a0e..93a345dd 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -38,6 +38,8 @@ import hudson.scm.SCM; import hudson.util.FormValidation; import jenkins.model.Jenkins; +import org.jenkinsci.plugins.workflow.flow.FlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -196,66 +198,126 @@ public LibraryCachingConfiguration getCachingConfiguration() { // without a runParent we can't validateVersion() anyway if (runParent != null) { - // First ask SCM source of the pipeline (if any), - // as the most authoritative source of the branch - // name we want: + // First, check if envvar BRANCH_NAME is defined? + // Trust the plugins and situations where it is set. try { - if (run instanceof WorkflowRun) { + runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null); + } catch (Exception x) { + // no-op, keep null + } + + if (runVersion == null) { + // Probably not in a multibranch pipeline workflow + // type of job? + // Ask for SCM source of the pipeline (if any), + // as the most authoritative source of the branch + // name we want: + SCM scm0 = null; + + if (runParent instanceof WorkflowJob) { + // This covers both "Multibranch Pipeline" + // and "Pipeline script from SCM" jobs; + // it also covers "inline" pipeline scripts + // but should return an empty Collection of + // SCMs since there is no SCM attached to + // the "static source". + // TODO: If there are "pre-loaded libraries" + // in a Jenkins deployment, can they interfere? + FlowDefinition fd = ((WorkflowJob)runParent).getDefinition(); + if (fd != null) { + for (SCM scmN : fd.getSCMs()) { + if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { + // The best we can do here is accept + // the first seen SCM (with branch + // support which we know how to query). + scm0 = scmN; + break; + } + } + } + } + +/* + // NOTE: the list of SCMs used by the run does not + // seem trustworthy: if an "inline" pipeline script + // (not from SCM) is used, there is no "relevant" + // branch name to request; however the list of SCMs + // would be populated as @Library lines are processed + // and some SCM sources get checked out. + if (scm0 == null && run instanceof WorkflowRun) { // This covers both "Multibranch Pipeline" // and "Pipeline script from SCM" jobs; // it also covers "inline" pipeline scripts // but throws a hudson.AbortException since // there is no SCM attached. - WorkflowRun wfRun = (WorkflowRun) run; - SCM scm0 = wfRun.getSCMs().get(0); - if (scm0 != null) { - // Avoid importing GitSCM and so requiring that - // it is always installed even if not used by - // particular Jenkins deployment (using e.g. - // SVN, Gerritt, etc.). Our aim is to query this: - // runVersion = scm0.getBranches().first().getExpandedName(run.getEnvironment(listener)); - // https://mkyong.com/java/how-to-use-reflection-to-call-java-method-at-runtime/ - Class noparams[] = {}; - Class[] paramEnvVars = new Class[1]; - paramEnvVars[0] = EnvVars.class; - if ("hudson.plugins.git.GitSCM".equals(scm0.getClass().getName())) { - // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/GitSCM.html#getBranches() => - // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/BranchSpec.html#toString() - Method methodGetBranches = null; + try { + WorkflowRun wfRun = (WorkflowRun) run; + scm0 = wfRun.getSCMs().get(0); + } catch (Exception x) { + // no-op, keep null + } + } +*/ + + if (scm0 != null) { + // Avoid importing GitSCM and so requiring that + // it is always installed even if not used by + // particular Jenkins deployment (using e.g. + // SVN, Gerritt, etc.). Our aim is to query this: + // runVersion = scm0.getBranches().first().getExpandedName(run.getEnvironment(listener)); + // https://mkyong.com/java/how-to-use-reflection-to-call-java-method-at-runtime/ + Class noparams[] = {}; + Class[] paramEnvVars = new Class[1]; + paramEnvVars[0] = EnvVars.class; + if ("hudson.plugins.git.GitSCM".equals(scm0.getClass().getName())) { + // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/GitSCM.html#getBranches() => + // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/BranchSpec.html#toString() + Method methodGetBranches = null; + try { + methodGetBranches = scm0.getClass().getDeclaredMethod("getBranches", noparams); + } catch (Exception x) { + // NoSuchMethodException | SecurityException | NullPointerException + methodGetBranches = null; + } + if (methodGetBranches != null) { + Object branchList = null; try { - methodGetBranches = scm0.getClass().getDeclaredMethod("getBranches", noparams); + branchList = methodGetBranches.invoke(scm0); } catch (Exception x) { - // NoSuchMethodException | SecurityException | NullPointerException - methodGetBranches = null; + // InvocationTargetException | IllegalAccessException + branchList = null; } - if (methodGetBranches != null) { - Object branchList = methodGetBranches.invoke(scm0); - if (branchList instanceof List) { - Object branch0 = ((List) branchList).get(0); - if ("hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) { - Method methodGetExpandedName = null; + if (branchList != null && branchList instanceof List) { + Object branch0 = ((List) branchList).get(0); + if ("hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) { + Method methodGetExpandedName = null; + try { + methodGetExpandedName = branch0.getClass().getDeclaredMethod("getExpandedName", paramEnvVars); + } catch (Exception x) { + methodGetExpandedName = null; + } + if (methodGetExpandedName != null) { + // Handle possible shell-templated branch specs: + Object expandedBranchName = null; try { - methodGetExpandedName = branch0.getClass().getDeclaredMethod("getExpandedName", paramEnvVars); + expandedBranchName = methodGetExpandedName.invoke(branch0, run.getEnvironment(listener)); } catch (Exception x) { - methodGetExpandedName = null; + // IllegalAccessException | IOException + expandedBranchName = null; } - if (methodGetExpandedName != null) { - // Handle possible shell-templated branch specs: - Object expandedBranchName = methodGetExpandedName.invoke(branch0, run.getEnvironment(listener)); - if (expandedBranchName != null) { - runVersion = expandedBranchName.toString(); - } + if (expandedBranchName != null) { + runVersion = expandedBranchName.toString(); } - if (runVersion == null || "".equals(runVersion)) { - runVersion = branch0.toString(); - } - } // else unknown class, make no blind guesses - } - } // else not really the GitSCM we know? - } // else SVN, Gerritt or some other SCM - - // add handling when needed and known how - // or rely on BRANCH_NAME (if set) below... - } + } + if (runVersion == null || "".equals(runVersion)) { + runVersion = branch0.toString(); + } + } // else unknown class, make no blind guesses + } + } // else not really the GitSCM we know? + } // else SVN, Gerritt or some other SCM - + // add handling when needed and known how + // or rely on BRANCH_NAME (if set) below... // Still alive? Chop off leading '*/' // (if any) from single-branch MBP and @@ -264,18 +326,6 @@ public LibraryCachingConfiguration getCachingConfiguration() { runVersion = runVersion.replaceFirst("^\\*/", ""); } } - } catch (Exception x) { - // no-op, keep null - } - - if (runVersion == null) { - // Probably not in a multibranch pipeline workflow - // type of job? Is envvar BRANCH_NAME defined? - try { - runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null); - } catch (Exception x) { - // no-op, keep null - } } // Note: if runVersion remains null (unresolved - From eb73ce4e2ef315efb8af0b10cfd31b37c1e7e731 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 29 Sep 2022 16:46:55 +0200 Subject: [PATCH 08/70] SCMSourceRetrieverTest: rename checkDefaultVersion() to checkDefaultVersionStaticStrings() and limit influence of caller-defined BRANCH_NAME [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index f742f3dc..02cb626c 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -219,7 +219,7 @@ public class SCMSourceRetrieverTest { } @Issue("JENKINS-69731") - @Test public void checkDefaultVersion() throws Exception { + @Test public void checkDefaultVersionStaticStrings() throws Exception { sampleRepo.init(); sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); sampleRepo.git("add", "vars"); @@ -239,25 +239,35 @@ public class SCMSourceRetrieverTest { WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); p0.setDefinition(new CpsFlowDefinition("@Library('branchylib') import myecho; myecho()", true)); WorkflowRun b0 = r.buildAndAssertSuccess(p0); + r.assertLogContains("Loading library branchylib@master", b0); r.assertLogContains("something special", b0); // Use specified branch WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@master') import myecho; myecho()", true)); WorkflowRun b1 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@master", b1); r.assertLogContains("something special", b1); // Use another specified branch WorkflowJob p2 = r.jenkins.createProject(WorkflowJob.class, "p2"); p2.setDefinition(new CpsFlowDefinition("@Library('branchylib@feature') import myecho; myecho()", true)); WorkflowRun b2 = r.buildAndAssertSuccess(p2); + r.assertLogContains("Loading library branchylib@feature", b2); r.assertLogContains("something very special", b2); - // Branch context for job not set - fall back to default - WorkflowJob p3 = r.jenkins.createProject(WorkflowJob.class, "p3"); - p3.setDefinition(new CpsFlowDefinition("@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()", true)); - WorkflowRun b3 = r.buildAndAssertSuccess(p3); - r.assertLogContains("something special", b3); + // Do not let caller-provided BRANCH_NAME interfere here + if (System.getenv("BRANCH_NAME") == null) { + // Branch context for job not set - fall back to default + WorkflowJob p3 = r.jenkins.createProject(WorkflowJob.class, "p3"); + p3.setDefinition(new CpsFlowDefinition("@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()", true)); + WorkflowRun b3 = r.buildAndAssertSuccess(p3); + r.assertLogContains("Loading library branchylib@master", b3); + r.assertLogContains("something special", b3); + } else { + System.out.println("SKIPPED test case with @Library('branchylib@${BRANCH_NAME}') " + + "because BRANCH_NAME is defined among system environment variables"); + } // Use a specified but missing branch WorkflowJob p4 = r.jenkins.createProject(WorkflowJob.class, "p4"); From 3ef66aee94046e1726ff7c0bdb779f0a39d4d0b6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 29 Sep 2022 16:54:48 +0200 Subject: [PATCH 09/70] SCMSourceRetrieverTest: add checkDefaultVersion_BRANCH_NAME_MBP() [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 02cb626c..9a5628b0 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -43,6 +43,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import jenkins.branch.BranchSource; import jenkins.plugins.git.GitSCMSource; import jenkins.plugins.git.GitSampleRepoRule; import jenkins.scm.api.SCMHead; @@ -58,6 +59,7 @@ 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.multibranch.*; import static hudson.ExtensionList.lookupSingleton; import static org.hamcrest.Matchers.contains; @@ -87,6 +89,7 @@ public class SCMSourceRetrieverTest { @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher(); @Rule public JenkinsRule r = new JenkinsRule(); @Rule public GitSampleRepoRule sampleRepo = new GitSampleRepoRule(); + @Rule public GitSampleRepoRule sampleRepo2 = new GitSampleRepoRule(); @Rule public SubversionSampleRepoRule sampleRepoSvn = new SubversionSampleRepoRule(); @Issue("JENKINS-40408") @@ -276,12 +279,64 @@ public class SCMSourceRetrieverTest { r.assertLogContains("ERROR: No version bogus found for library branchylib", b4); r.assertLogContains("org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:", b4); r.assertLogContains("WorkflowScript: Loading libraries failed", b4); + } - // TODO: create a job instantiated from Git (or fooled into - // thinking it is - injecting BRANCH_NAME envvar via Java) + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_BRANCH_NAME_MBP() throws Exception { + // Create a MultiBranch Pipeline job instantiated from Git // and check behaviors with BRANCH_NAME="master", - // BRANCH_NAME="feature", BRANCH_NAME="bogus" and - // BRANCH_NAME="" + // BRANCH_NAME="feature", and BRANCH_NAME="bogus" + // TODO? BRANCH_NAME="" + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowBRANCH_NAME(true); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2.init(); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=init"); + sampleRepo2.git("branch", "feature"); + sampleRepo2.git("branch", "bogus"); + + WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); + BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo2.toString(), "", "*", "", false)); + mbp.getSourcesList().add(branchSource); + sampleRepo2.notifyCommit(r); + + WorkflowJob p1 = mbp.getItem("master"); + WorkflowRun b1 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@master", b1); + r.assertLogContains("something special", b1); + + WorkflowJob p2 = mbp.getItem("feature"); + WorkflowRun b2 = r.buildAndAssertSuccess(p2); + r.assertLogContains("Loading library branchylib@feature", b2); + r.assertLogContains("something very special", b2); + + // library branch "bogus" does not exist => fall back to default (master) + WorkflowJob p3 = mbp.getItem("bogus"); + WorkflowRun b3 = r.buildAndAssertSuccess(p3); + r.assertLogContains("Loading library branchylib@master", b3); + r.assertLogContains("something special", b3); + + // TODO: test that lc.setAllowBRANCH_NAME(false) causes + // fallbacks always + + // TODO: test lc.setAllowBRANCH_NAME_PR(true) for PR builds } @Issue("JENKINS-43802") From 7ddf8e6033369379fe177931b4164f75d90d4be3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 29 Sep 2022 20:11:12 +0200 Subject: [PATCH 10/70] SCMSourceRetrieverTest.java: add checkDefaultVersion_MBP() and checkDefaultVersion_BRANCH_NAME_notAllowed() cases [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 115 +++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 9a5628b0..5a1b669e 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -333,12 +333,121 @@ public class SCMSourceRetrieverTest { r.assertLogContains("Loading library branchylib@master", b3); r.assertLogContains("something special", b3); - // TODO: test that lc.setAllowBRANCH_NAME(false) causes - // fallbacks always - // TODO: test lc.setAllowBRANCH_NAME_PR(true) for PR builds } + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_MBP() throws Exception { + // Test that lc.setAllowBRANCH_NAME(false) does not + // preclude fixed branch names (they should work), + // like @Library('branchylib@master') + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(true); + lc.setAllowBRANCH_NAME(false); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2.init(); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@master') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=master"); + sampleRepo2.git("checkout", "-b", "feature"); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@feature') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=feature"); + sampleRepo2.git("checkout", "-b", "bogus"); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@bogus') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=bogus"); + + WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); + BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo2.toString(), "", "*", "", false)); + mbp.getSourcesList().add(branchSource); + sampleRepo2.notifyCommit(r); + + WorkflowJob p1 = mbp.getItem("master"); + WorkflowRun b1 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@master", b1); + r.assertLogContains("something special", b1); + + WorkflowJob p2 = mbp.getItem("feature"); + WorkflowRun b2 = r.buildAndAssertSuccess(p2); + r.assertLogContains("Loading library branchylib@feature", b2); + r.assertLogContains("something very special", b2); + + WorkflowJob p3 = mbp.getItem("bogus"); + WorkflowRun b3 = r.buildAndAssertStatus(Result.FAILURE, p3); + r.assertLogContains("ERROR: Could not resolve bogus", b3); + r.assertLogContains("ambiguous argument 'bogus^{commit}': unknown revision or path not in the working tree", b3); + r.assertLogContains("ERROR: No version bogus found for library branchylib", b3); + r.assertLogContains("WorkflowScript: Loading libraries failed", b3); + } + + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_BRANCH_NAME_notAllowed() throws Exception { + // Test that lc.setAllowBRANCH_NAME(false) causes + // @Library('libname@${BRANCH_NAME}') to always + // fail, while fixed branch names should work. + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(true); + lc.setAllowBRANCH_NAME(false); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2.init(); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=init"); + sampleRepo2.git("branch", "feature"); + sampleRepo2.git("branch", "bogus"); + + WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); + BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo2.toString(), "", "*", "", false)); + mbp.getSourcesList().add(branchSource); + sampleRepo2.notifyCommit(r); + + WorkflowJob p1 = mbp.getItem("master"); + WorkflowRun b1 = r.buildAndAssertStatus(Result.FAILURE, p1); + r.assertLogContains("ERROR: Version override not permitted for library branchylib", b1); + r.assertLogContains("WorkflowScript: Loading libraries failed", b1); + + WorkflowJob p2 = mbp.getItem("feature"); + WorkflowRun b2 = r.buildAndAssertStatus(Result.FAILURE, p2); + r.assertLogContains("ERROR: Version override not permitted for library branchylib", b2); + r.assertLogContains("WorkflowScript: Loading libraries failed", b2); + + WorkflowJob p3 = mbp.getItem("bogus"); + WorkflowRun b3 = r.buildAndAssertStatus(Result.FAILURE, p3); + r.assertLogContains("ERROR: Version override not permitted for library branchylib", b3); + r.assertLogContains("WorkflowScript: Loading libraries failed", b3); + } + @Issue("JENKINS-43802") @Test public void owner() throws Exception { GlobalLibraries.get().setLibraries(Collections.singletonList( From 4f792f6ab120f755139725d7c5faa0cfd94473aa Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 29 Sep 2022 20:31:23 +0200 Subject: [PATCH 11/70] SCMSourceRetrieverTest.java: optimize tests using MBP to not build same jobs twice [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 5a1b669e..b966a306 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -315,21 +315,35 @@ public class SCMSourceRetrieverTest { WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo2.toString(), "", "*", "", false)); mbp.getSourcesList().add(branchSource); + // Note: this notification causes discovery of branches, + // definition of MBP "leaf" jobs, and launch of builds, + // so below we just make sure they complete and analyze + // the outcomes. sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); WorkflowJob p1 = mbp.getItem("master"); - WorkflowRun b1 = r.buildAndAssertSuccess(p1); + WorkflowRun b1 = p1.getLastBuild(); + r.waitForCompletion(b1); + assertFalse(p1.isBuilding()); + r.assertBuildStatusSuccess(b1); r.assertLogContains("Loading library branchylib@master", b1); r.assertLogContains("something special", b1); WorkflowJob p2 = mbp.getItem("feature"); - WorkflowRun b2 = r.buildAndAssertSuccess(p2); + WorkflowRun b2 = p2.getLastBuild(); + r.waitForCompletion(b2); + assertFalse(p2.isBuilding()); + r.assertBuildStatusSuccess(b2); r.assertLogContains("Loading library branchylib@feature", b2); r.assertLogContains("something very special", b2); // library branch "bogus" does not exist => fall back to default (master) WorkflowJob p3 = mbp.getItem("bogus"); - WorkflowRun b3 = r.buildAndAssertSuccess(p3); + WorkflowRun b3 = p3.getLastBuild(); + r.waitForCompletion(b3); + assertFalse(p3.isBuilding()); + r.assertBuildStatusSuccess(b3); r.assertLogContains("Loading library branchylib@master", b3); r.assertLogContains("something special", b3); @@ -376,20 +390,34 @@ public class SCMSourceRetrieverTest { WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo2.toString(), "", "*", "", false)); mbp.getSourcesList().add(branchSource); + // Note: this notification causes discovery of branches, + // definition of MBP "leaf" jobs, and launch of builds, + // so below we just make sure they complete and analyze + // the outcomes. sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); WorkflowJob p1 = mbp.getItem("master"); - WorkflowRun b1 = r.buildAndAssertSuccess(p1); + WorkflowRun b1 = p1.getLastBuild(); + r.waitForCompletion(b1); + assertFalse(p1.isBuilding()); + r.assertBuildStatusSuccess(b1); r.assertLogContains("Loading library branchylib@master", b1); r.assertLogContains("something special", b1); WorkflowJob p2 = mbp.getItem("feature"); - WorkflowRun b2 = r.buildAndAssertSuccess(p2); + WorkflowRun b2 = p2.getLastBuild(); + r.waitForCompletion(b2); + assertFalse(p2.isBuilding()); + r.assertBuildStatusSuccess(b2); r.assertLogContains("Loading library branchylib@feature", b2); r.assertLogContains("something very special", b2); WorkflowJob p3 = mbp.getItem("bogus"); - WorkflowRun b3 = r.buildAndAssertStatus(Result.FAILURE, p3); + WorkflowRun b3 = p3.getLastBuild(); + r.waitForCompletion(b3); + assertFalse(p3.isBuilding()); + r.assertBuildStatus(Result.FAILURE, b3); r.assertLogContains("ERROR: Could not resolve bogus", b3); r.assertLogContains("ambiguous argument 'bogus^{commit}': unknown revision or path not in the working tree", b3); r.assertLogContains("ERROR: No version bogus found for library branchylib", b3); @@ -430,20 +458,34 @@ public class SCMSourceRetrieverTest { WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo2.toString(), "", "*", "", false)); mbp.getSourcesList().add(branchSource); + // Note: this notification causes discovery of branches, + // definition of MBP "leaf" jobs, and launch of builds, + // so below we just make sure they complete and analyze + // the outcomes. sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); WorkflowJob p1 = mbp.getItem("master"); - WorkflowRun b1 = r.buildAndAssertStatus(Result.FAILURE, p1); + WorkflowRun b1 = p1.getLastBuild(); + r.waitForCompletion(b1); + assertFalse(p1.isBuilding()); + r.assertBuildStatus(Result.FAILURE, b1); r.assertLogContains("ERROR: Version override not permitted for library branchylib", b1); r.assertLogContains("WorkflowScript: Loading libraries failed", b1); WorkflowJob p2 = mbp.getItem("feature"); - WorkflowRun b2 = r.buildAndAssertStatus(Result.FAILURE, p2); + WorkflowRun b2 = p2.getLastBuild(); + r.waitForCompletion(b2); + assertFalse(p2.isBuilding()); + r.assertBuildStatus(Result.FAILURE, b2); r.assertLogContains("ERROR: Version override not permitted for library branchylib", b2); r.assertLogContains("WorkflowScript: Loading libraries failed", b2); WorkflowJob p3 = mbp.getItem("bogus"); - WorkflowRun b3 = r.buildAndAssertStatus(Result.FAILURE, p3); + WorkflowRun b3 = p3.getLastBuild(); + r.waitForCompletion(b3); + assertFalse(p3.isBuilding()); + r.assertBuildStatus(Result.FAILURE, b3); r.assertLogContains("ERROR: Version override not permitted for library branchylib", b3); r.assertLogContains("WorkflowScript: Loading libraries failed", b3); } From 9d95881fd92ac22543664dd5ca00a47cf90bb12e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 29 Sep 2022 20:39:07 +0200 Subject: [PATCH 12/70] SCMSourceRetrieverTest.java: adjust naming of tests using MBP [JENKINS-69731] --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index b966a306..3ce1b3d0 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -282,7 +282,7 @@ public class SCMSourceRetrieverTest { } @Issue("JENKINS-69731") - @Test public void checkDefaultVersion_BRANCH_NAME_MBP() throws Exception { + @Test public void checkDefaultVersion_MBP_BRANCH_NAME() throws Exception { // Create a MultiBranch Pipeline job instantiated from Git // and check behaviors with BRANCH_NAME="master", // BRANCH_NAME="feature", and BRANCH_NAME="bogus" @@ -425,10 +425,11 @@ public class SCMSourceRetrieverTest { } @Issue("JENKINS-69731") - @Test public void checkDefaultVersion_BRANCH_NAME_notAllowed() throws Exception { + @Test public void checkDefaultVersion_MBP_BRANCH_NAME_notAllowed() throws Exception { // Test that lc.setAllowBRANCH_NAME(false) causes - // @Library('libname@${BRANCH_NAME}') to always - // fail, while fixed branch names should work. + // @Library('libname@${BRANCH_NAME}') to always fail + // (not treated as a "version override" for funny + // branch name that is literally "${BRANCH_NAME}"). sampleRepo.init(); sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); From 0694dd19ff8ce8fde6500778a623ec37698e4c99 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 29 Sep 2022 22:11:19 +0200 Subject: [PATCH 13/70] SCMSourceRetrieverTest: add checkDefaultVersion_singleBranch() [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 3ce1b3d0..0eecf67f 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -32,6 +32,8 @@ import hudson.model.Item; import hudson.model.Result; import hudson.model.TaskListener; +import hudson.plugins.git.GitSCM; +import hudson.plugins.git.BranchSpec; import hudson.scm.ChangeLogSet; import hudson.scm.SCM; import hudson.slaves.WorkspaceList; @@ -57,6 +59,7 @@ import jenkins.scm.impl.subversion.SubversionSampleRepoRule; import org.apache.commons.io.FileUtils; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.jenkinsci.plugins.workflow.multibranch.*; @@ -491,6 +494,64 @@ public class SCMSourceRetrieverTest { r.assertLogContains("WorkflowScript: Loading libraries failed", b3); } + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_singleBranch() throws Exception { + // Test that lc.setAllowBRANCH_NAME(false) does not + // preclude fixed branch names (they should work), + // like @Library('branchylib@master') when used for + // a simple "Pipeline" job with static SCM source. + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(true); + lc.setAllowBRANCH_NAME(false); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2.init(); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@master') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=master"); + sampleRepo2.git("checkout", "-b", "feature"); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@feature') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=feature"); + sampleRepo2.git("checkout", "-b", "bogus"); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@bogus') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=bogus"); + + //GitSCM gitSCM = new GitSCM(sampleRepo2.toString()); + GitSCM gitSCM = new GitSCM( + GitSCM.createRepoList(sampleRepo2.toString(), null), + Collections.singletonList(new BranchSpec("*/feature")), + null, null, Collections.emptyList()); + + WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); + p0.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); + // Note: this notification causes discovery of branches, + // definition of MBP "leaf" jobs, and launch of builds, + // so below we just make sure they complete and analyze + // the outcomes. + sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); + + WorkflowRun b0 = r.buildAndAssertSuccess(p0); + r.assertLogContains("Loading library branchylib@feature", b0); + r.assertLogContains("something very special", b0); + } + @Issue("JENKINS-43802") @Test public void owner() throws Exception { GlobalLibraries.get().setLibraries(Collections.singletonList( From 824a204f22ebc51b080e4b1752b321e96f4714ee Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 11:33:57 +0200 Subject: [PATCH 14/70] SCMSourceRetrieverTest.java: report "Jobs generated by MBP" tests [JENKINS-69731] --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 0eecf67f..7b1485b0 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -324,6 +324,7 @@ public class SCMSourceRetrieverTest { // the outcomes. sampleRepo2.notifyCommit(r); r.waitUntilNoActivity(); + System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); WorkflowJob p1 = mbp.getItem("master"); WorkflowRun b1 = p1.getLastBuild(); @@ -399,6 +400,7 @@ public class SCMSourceRetrieverTest { // the outcomes. sampleRepo2.notifyCommit(r); r.waitUntilNoActivity(); + System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); WorkflowJob p1 = mbp.getItem("master"); WorkflowRun b1 = p1.getLastBuild(); @@ -468,6 +470,7 @@ public class SCMSourceRetrieverTest { // the outcomes. sampleRepo2.notifyCommit(r); r.waitUntilNoActivity(); + System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); WorkflowJob p1 = mbp.getItem("master"); WorkflowRun b1 = p1.getLastBuild(); From 553aba3cfa2c7241fabbea4670990a0b8594a64d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 11:35:50 +0200 Subject: [PATCH 15/70] SCMSourceRetrieverTest.java: add PoC checkDefaultVersion_MBP_singleBranch() - Need help setting up MBP+SingleSCMSource [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 7b1485b0..96cc12f7 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -55,6 +55,7 @@ import jenkins.scm.api.SCMSource; import jenkins.scm.api.SCMSourceCriteria; import jenkins.scm.api.SCMSourceDescriptor; +import jenkins.scm.impl.SingleSCMSource; import jenkins.scm.impl.subversion.SubversionSCMSource; import jenkins.scm.impl.subversion.SubversionSampleRepoRule; import org.apache.commons.io.FileUtils; @@ -497,6 +498,95 @@ public class SCMSourceRetrieverTest { r.assertLogContains("WorkflowScript: Loading libraries failed", b3); } + @Issue("JENKINS-69731") + //@Ignore("Need help setting up MBP+SingleSCMSource") + @Test public void checkDefaultVersion_MBP_singleBranch() throws Exception { + // FAILS: Setup below does not instantiate any jobs! + // (expected one for "feature") + + // Test that lc.setAllowBRANCH_NAME(false) does not + // preclude fixed branch names (they should work), + // like @Library('branchylib@master') when used + // for MBP with "Single repository and branch" as + // the SCM source. + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(true); + lc.setAllowBRANCH_NAME(false); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2.init(); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@master') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=master"); + sampleRepo2.git("checkout", "-b", "feature"); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@feature') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=feature"); + sampleRepo2.git("checkout", "-b", "bogus"); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@bogus') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=bogus"); + + // TODO: Test also another MBP where we would + // set options "List of branches to build" and + // "Branch Specifier" to a different value than + // the "name" of branchSource. While the "name" + // becomes "BRANCH_NAME" envvar (and MBP job name + // generated for the branch), the specifier is + // what gets placed into SCMs list. + + // FAILS: Setup below does not instantiate any jobs! + // (expected one for "feature") + WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); + //GitSCM gitSCM = new GitSCM(sampleRepo2.toString()); + GitSCM gitSCM = new GitSCM( + GitSCM.createRepoList(sampleRepo2.toString(), null), + Collections.singletonList(new BranchSpec("*/feature")), + null, null, Collections.emptyList()); + BranchSource branchSource = new BranchSource(new SingleSCMSource("feature-id", "feature", gitSCM)); + mbp.getSourcesList().add(branchSource); + // Note: this notification causes discovery of branches, + // definition of MBP "leaf" jobs, and launch of builds, + // so below we just make sure they complete and analyze + // the outcomes. + sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); + System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); + assumeFalse("MBP should have generated 'feature' pipeline job", mbp.getItems().isEmpty()); + + // In case of MBP with "Single repository and branch" + // it only defines one job, so those for other branches + // should be null: + WorkflowJob p1 = mbp.getItem("master"); + assertNull(p1); + + WorkflowJob p2 = mbp.getItem("source-id"); + assertNotNull(p2); + WorkflowRun b2 = p2.getLastBuild(); + r.waitForCompletion(b2); + assertFalse(p2.isBuilding()); + r.assertBuildStatusSuccess(b2); + r.assertLogContains("Loading library branchylib@feature", b2); + r.assertLogContains("something very special", b2); + + WorkflowJob p3 = mbp.getItem("bogus"); + assertNull(p3); + } + @Issue("JENKINS-69731") @Test public void checkDefaultVersion_singleBranch() throws Exception { // Test that lc.setAllowBRANCH_NAME(false) does not From bcba44503825425f247f51c0037e63170e4fd0b3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 12:14:46 +0200 Subject: [PATCH 16/70] LibraryConfiguration: add optional traceBRANCH_NAME toggle, and lots of trace info about decision making [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 174 ++++++++++++++++-- .../libs/LibraryConfiguration/config.jelly | 3 + .../help-traceBRANCH_NAME.html | 5 + 3 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-traceBRANCH_NAME.html diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 93a345dd..ecc7e24c 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -53,6 +53,8 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; + +import java.io.PrintStream; import java.lang.reflect.Method; import java.util.List; import java.util.Collection; @@ -69,6 +71,11 @@ public class LibraryConfiguration extends AbstractDescribableImpl run, TaskListener listener) throws AbortException { + PrintStream logger = null; + if (traceBRANCH_NAME && listener != null) { + logger = listener.getLogger(); + } + if (logger != null) { + logger.println("defaultedVersion(): Resolving '" + version + "'"); + } + if (version == null) { if (defaultVersion == null) { throw new AbortException("No version specified for library " + name); @@ -196,14 +219,30 @@ public LibraryCachingConfiguration getCachingConfiguration() { } } + if (logger != null) { + logger.println("defaultedVersion(): Resolving BRANCH_NAME; " + + (runParent == null ? "without" : "have") + + " a runParent object"); + } + // without a runParent we can't validateVersion() anyway if (runParent != null) { // First, check if envvar BRANCH_NAME is defined? // Trust the plugins and situations where it is set. try { runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null); + if (logger != null) { + if (runVersion != null) { + logger.println("defaultedVersion(): Resolved envvar BRANCH_NAME='" + runVersion + "'"); + } else { + logger.println("defaultedVersion(): Did not resolve envvar BRANCH_NAME: not in env"); + } + } } catch (Exception x) { - // no-op, keep null + runVersion = null; + if (logger != null) { + logger.println("defaultedVersion(): Did not resolve envvar BRANCH_NAME: " + x.getMessage()); + } } if (runVersion == null) { @@ -223,15 +262,37 @@ public LibraryCachingConfiguration getCachingConfiguration() { // the "static source". // TODO: If there are "pre-loaded libraries" // in a Jenkins deployment, can they interfere? + if (logger != null) { + logger.println("defaultedVersion(): inspecting WorkflowJob for a FlowDefinition"); + } FlowDefinition fd = ((WorkflowJob)runParent).getDefinition(); if (fd != null) { - for (SCM scmN : fd.getSCMs()) { - if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { - // The best we can do here is accept - // the first seen SCM (with branch - // support which we know how to query). - scm0 = scmN; - break; + Collection fdscms = (Collection) fd.getSCMs(); + if (fdscms.isEmpty()) { + if (logger != null) { + logger.println("defaultedVersion(): FlowDefinition '" + + fd.getClass().getName() + + "' is not associated with any SCMs"); + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): inspecting FlowDefinition '" + + fd.getClass().getName() + + "' for SCMs it might use"); + } + for (SCM scmN : fdscms) { + if (logger != null) { + logger.println("defaultedVersion(): inspecting SCM '" + + scmN.getClass().getName() + + "': " + scmN.toString()); + } + if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { + // The best we can do here is accept + // the first seen SCM (with branch + // support which we know how to query). + scm0 = scmN; + break; + } } } } @@ -250,11 +311,18 @@ public LibraryCachingConfiguration getCachingConfiguration() { // it also covers "inline" pipeline scripts // but throws a hudson.AbortException since // there is no SCM attached. + if (logger != null) { + logger.println("defaultedVersion(): inspecting WorkflowRun"); + } try { WorkflowRun wfRun = (WorkflowRun) run; scm0 = wfRun.getSCMs().get(0); } catch (Exception x) { - // no-op, keep null + if (logger != null) { + logger.println("defaultedVersion(): " + + "Did not get first listed SCM: " + + x.getMessage()); + } } } */ @@ -266,6 +334,12 @@ public LibraryCachingConfiguration getCachingConfiguration() { // SVN, Gerritt, etc.). Our aim is to query this: // runVersion = scm0.getBranches().first().getExpandedName(run.getEnvironment(listener)); // https://mkyong.com/java/how-to-use-reflection-to-call-java-method-at-runtime/ + if (logger != null) { + logger.println("defaultedVersion(): " + + "inspecting first listed SCM: " + + scm0.toString()); + } + Class noparams[] = {}; Class[] paramEnvVars = new Class[1]; paramEnvVars[0] = EnvVars.class; @@ -289,7 +363,7 @@ public LibraryCachingConfiguration getCachingConfiguration() { } if (branchList != null && branchList instanceof List) { Object branch0 = ((List) branchList).get(0); - if ("hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) { + if (branch0 != null && "hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) { Method methodGetExpandedName = null; try { methodGetExpandedName = branch0.getClass().getDeclaredMethod("getExpandedName", paramEnvVars); @@ -308,22 +382,63 @@ public LibraryCachingConfiguration getCachingConfiguration() { if (expandedBranchName != null) { runVersion = expandedBranchName.toString(); } + } else { + if (logger != null) { + logger.println("defaultedVersion(): " + + "did not find method BranchSpec.getExpandedName()"); + } } if (runVersion == null || "".equals(runVersion)) { runVersion = branch0.toString(); } - } // else unknown class, make no blind guesses + } else { + // unknown branchspec class, make no blind guesses + if (logger != null) { + logger.println("defaultedVersion(): " + + "list of branches did not return a " + + "BranchSpec class instance, but " + + (branch0 == null ? "null" : + branch0.getClass().getName())); + } + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): " + + "getBranches() did not return a " + + "list of branches: " + + (branchList == null ? "null" : + branchList.getClass().getName())); + } } - } // else not really the GitSCM we know? - } // else SVN, Gerritt or some other SCM - - // add handling when needed and known how - // or rely on BRANCH_NAME (if set) below... + } else { + // not really the GitSCM we know? + if (logger != null) { + logger.println("defaultedVersion(): " + + "did not find method GitSCM.getBranches()"); + } + } + } else { + // else SVN, Gerritt or some other SCM - + // add handling when needed and known how + // or rely on BRANCH_NAME (if set) below... + if (logger != null) { + logger.println("defaultedVersion(): " + + "the first listed SCM was not of currently " + + "supported class with recognized branch support: " + + scm0.getClass().getName()); + } + } // Still alive? Chop off leading '*/' // (if any) from single-branch MBP and // plain "Pipeline" job definitions. if (runVersion != null) { runVersion = runVersion.replaceFirst("^\\*/", ""); + if (logger != null) { + logger.println("defaultedVersion(): " + + "Discovered runVersion '" + runVersion + + "' in SCM source of the pipeline"); + } } } } @@ -336,6 +451,11 @@ public LibraryCachingConfiguration getCachingConfiguration() { // We would however look into (MBP-defined for PRs) // CHANGE_BRANCH and CHANGE_TARGET as other fallbacks // below. + } else { + if (logger != null) { + logger.println("defaultedVersion(): Trying to default: " + + "without a runParent we can't validateVersion() anyway"); + } } if (runParent == null || runVersion == null || "".equals(runVersion)) { @@ -344,6 +464,12 @@ public LibraryCachingConfiguration getCachingConfiguration() { // args for run/listener needed for validateVersion() // below, or some other problem occurred. // Fall back if we can: + if (logger != null) { + logger.println("defaultedVersion(): Trying to default: " + + "runVersion is " + + (runVersion == null ? "null" : + ("".equals(runVersion) ? "empty" : runVersion))); + } if (defaultVersion == null) { throw new AbortException("No version specified for library " + name); } else { @@ -354,6 +480,10 @@ public LibraryCachingConfiguration getCachingConfiguration() { // Check if runVersion is resolvable by LibraryRetriever // implementation (SCM, HTTP, etc.); fall back if not: if (retriever != null) { + if (logger != null) { + logger.println("defaultedVersion(): Trying to validate runVersion: " + runVersion); + } + FormValidation fv = retriever.validateVersion(name, runVersion, runParent); if (fv != null && fv.kind == FormValidation.Kind.OK) { @@ -377,6 +507,9 @@ public LibraryCachingConfiguration getCachingConfiguration() { runVersion = null; } if (runVersion != null && !("".equals(runVersion))) { + if (logger != null) { + logger.println("defaultedVersion(): Trying to validate CHANGE_BRANCH: " + runVersion); + } fv = retriever.validateVersion(name, runVersion, runParent); if (fv != null && fv.kind == FormValidation.Kind.OK) { @@ -393,17 +526,26 @@ public LibraryCachingConfiguration getCachingConfiguration() { runVersion = null; } if (runVersion != null && !("".equals(runVersion))) { + if (logger != null) { + logger.println("defaultedVersion(): Trying to validate CHANGE_TARGET: " + runVersion); + } fv = retriever.validateVersion(name, runVersion, runParent); if (fv != null && fv.kind == FormValidation.Kind.OK) { return runVersion; } } - } + } // else not a PR or not allowBRANCH_NAME_PR } // No retriever, or its validateVersion() did not confirm // usability of BRANCH_NAME string value as the version... + if (logger != null) { + logger.println("defaultedVersion(): Trying to default: " + + "could not resolve runVersion which is " + + (runVersion == null ? "null" : + ("".equals(runVersion) ? "empty" : runVersion))); + } if (defaultVersion == null) { throw new AbortException("BRANCH_NAME version " + runVersion + " was not found, and no default version specified, for library " + name); diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly index b26f62d9..daf6d6a9 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly @@ -43,6 +43,9 @@ THE SOFTWARE. + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-traceBRANCH_NAME.html b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-traceBRANCH_NAME.html new file mode 100644 index 00000000..3a0dc8dd --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-traceBRANCH_NAME.html @@ -0,0 +1,5 @@ +
+ If checked, the defaultedVersion method would print + its progress trying to resolve @${BRANCH_NAME} in the + @Library annotation into the build log. +
From 6b5255cf4b2f394b35793deb4ddd10926ac80831 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 12:17:31 +0200 Subject: [PATCH 17/70] LibraryConfigurationTest: enable traceBRANCH_NAME for test runs [JENKINS-69731] --- .../plugins/workflow/libs/LibraryConfigurationTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java index 36a4f975..ccd5ff9d 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java @@ -153,9 +153,11 @@ public class LibraryConfigurationTest { LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); cfg.setAllowVersionOverride(true); cfg.setAllowBRANCH_NAME(false); + cfg.setTraceBRANCH_NAME(true); assertEquals(true, cfg.isAllowVersionOverride()); assertEquals(false, cfg.isAllowBRANCH_NAME()); + assertEquals(true, cfg.isTraceBRANCH_NAME()); assertThrows(AbortException.class, () -> cfg.defaultedVersion("${BRANCH_NAME}")); /* This SHOULD NOT return a version string that literally remains '${BRANCH_NAME}'! */ } @@ -168,6 +170,7 @@ public class LibraryConfigurationTest { LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); cfg.setDefaultVersion(defaultVersion); cfg.setAllowBRANCH_NAME(true); + cfg.setTraceBRANCH_NAME(true); assertEquals(true, cfg.isAllowBRANCH_NAME()); try { @@ -183,6 +186,7 @@ public class LibraryConfigurationTest { LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); cfg.setAllowBRANCH_NAME(true); + cfg.setTraceBRANCH_NAME(true); assertEquals(true, cfg.isAllowBRANCH_NAME()); assertThrows(AbortException.class, () -> cfg.defaultedVersion("${BRANCH_NAME}", null, null)); From a661a13f1934c9c1a0a68711ee11177568c47e00 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 12:18:03 +0200 Subject: [PATCH 18/70] SCMSourceRetrieverTest: enable traceBRANCH_NAME for test runs [JENKINS-69731] --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 96cc12f7..0dbba2d3 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -240,6 +240,7 @@ public class SCMSourceRetrieverTest { lc.setDefaultVersion("master"); lc.setIncludeInChangesets(false); lc.setAllowBRANCH_NAME(true); + lc.setTraceBRANCH_NAME(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Basename "libname" notation => use specified default branch @@ -305,6 +306,7 @@ public class SCMSourceRetrieverTest { lc.setDefaultVersion("master"); lc.setIncludeInChangesets(false); lc.setAllowBRANCH_NAME(true); + lc.setTraceBRANCH_NAME(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -375,6 +377,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(false); + lc.setTraceBRANCH_NAME(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -451,6 +454,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(false); + lc.setTraceBRANCH_NAME(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -524,6 +528,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(false); + lc.setTraceBRANCH_NAME(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -608,6 +613,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(false); + lc.setTraceBRANCH_NAME(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like From 9700565ba2aaa1e888c450a9918e94c8a08c4aab Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 13:43:17 +0200 Subject: [PATCH 19/70] SCMSourceRetrieverTest: update comments [JENKINS-69731] --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 0dbba2d3..5274459b 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -292,6 +292,9 @@ public class SCMSourceRetrieverTest { // and check behaviors with BRANCH_NAME="master", // BRANCH_NAME="feature", and BRANCH_NAME="bogus" // TODO? BRANCH_NAME="" + // Note: An externally provided BRANCH_NAME envvar + // does not interfere with tested logic, since MBP + // sets the value for launched builds. sampleRepo.init(); sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); @@ -639,10 +642,6 @@ public class SCMSourceRetrieverTest { WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); p0.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); - // Note: this notification causes discovery of branches, - // definition of MBP "leaf" jobs, and launch of builds, - // so below we just make sure they complete and analyze - // the outcomes. sampleRepo2.notifyCommit(r); r.waitUntilNoActivity(); From 30d21b92164f0216af16d221e27b12164485f677 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 20:37:16 +0200 Subject: [PATCH 20/70] SCMSourceRetrieverTest: rename some tests to follow a common pattern [JENKINS-69731] --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 5274459b..19e9b930 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -226,7 +226,7 @@ public class SCMSourceRetrieverTest { } @Issue("JENKINS-69731") - @Test public void checkDefaultVersionStaticStrings() throws Exception { + @Test public void checkDefaultVersion_inline_staticStrings() throws Exception { sampleRepo.init(); sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); sampleRepo.git("add", "vars"); @@ -361,7 +361,7 @@ public class SCMSourceRetrieverTest { } @Issue("JENKINS-69731") - @Test public void checkDefaultVersion_MBP() throws Exception { + @Test public void checkDefaultVersion_MBP_staticStrings() throws Exception { // Test that lc.setAllowBRANCH_NAME(false) does not // preclude fixed branch names (they should work), // like @Library('branchylib@master') @@ -507,7 +507,7 @@ public class SCMSourceRetrieverTest { @Issue("JENKINS-69731") //@Ignore("Need help setting up MBP+SingleSCMSource") - @Test public void checkDefaultVersion_MBP_singleBranch() throws Exception { + @Test public void checkDefaultVersion_MBPsingleBranch_staticStrings() throws Exception { // FAILS: Setup below does not instantiate any jobs! // (expected one for "feature") @@ -596,7 +596,7 @@ public class SCMSourceRetrieverTest { } @Issue("JENKINS-69731") - @Test public void checkDefaultVersion_singleBranch() throws Exception { + @Test public void checkDefaultVersion_singleBranch_staticStrings() throws Exception { // Test that lc.setAllowBRANCH_NAME(false) does not // preclude fixed branch names (they should work), // like @Library('branchylib@master') when used for From 34718a4617b711d53c853d73dc1714c021d0289a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 20:44:03 +0200 Subject: [PATCH 21/70] SCMSourceRetrieverTest: separate checkDefaultVersion_inline_BRANCH_NAME() from checkDefaultVersion_inline_staticStrings() [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 90 ++++++++++++++----- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 19e9b930..bca58d58 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -42,6 +42,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -244,38 +245,25 @@ public class SCMSourceRetrieverTest { GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Basename "libname" notation => use specified default branch - WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); - p0.setDefinition(new CpsFlowDefinition("@Library('branchylib') import myecho; myecho()", true)); - WorkflowRun b0 = r.buildAndAssertSuccess(p0); - r.assertLogContains("Loading library branchylib@master", b0); - r.assertLogContains("something special", b0); - - // Use specified branch WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); - p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@master') import myecho; myecho()", true)); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib') import myecho; myecho()", true)); WorkflowRun b1 = r.buildAndAssertSuccess(p1); r.assertLogContains("Loading library branchylib@master", b1); r.assertLogContains("something special", b1); - // Use another specified branch + // Use specified branch WorkflowJob p2 = r.jenkins.createProject(WorkflowJob.class, "p2"); - p2.setDefinition(new CpsFlowDefinition("@Library('branchylib@feature') import myecho; myecho()", true)); + p2.setDefinition(new CpsFlowDefinition("@Library('branchylib@master') import myecho; myecho()", true)); WorkflowRun b2 = r.buildAndAssertSuccess(p2); - r.assertLogContains("Loading library branchylib@feature", b2); - r.assertLogContains("something very special", b2); + r.assertLogContains("Loading library branchylib@master", b2); + r.assertLogContains("something special", b2); - // Do not let caller-provided BRANCH_NAME interfere here - if (System.getenv("BRANCH_NAME") == null) { - // Branch context for job not set - fall back to default - WorkflowJob p3 = r.jenkins.createProject(WorkflowJob.class, "p3"); - p3.setDefinition(new CpsFlowDefinition("@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()", true)); - WorkflowRun b3 = r.buildAndAssertSuccess(p3); - r.assertLogContains("Loading library branchylib@master", b3); - r.assertLogContains("something special", b3); - } else { - System.out.println("SKIPPED test case with @Library('branchylib@${BRANCH_NAME}') " + - "because BRANCH_NAME is defined among system environment variables"); - } + // Use another specified branch + WorkflowJob p3 = r.jenkins.createProject(WorkflowJob.class, "p3"); + p3.setDefinition(new CpsFlowDefinition("@Library('branchylib@feature') import myecho; myecho()", true)); + WorkflowRun b3 = r.buildAndAssertSuccess(p3); + r.assertLogContains("Loading library branchylib@feature", b3); + r.assertLogContains("something very special", b3); // Use a specified but missing branch WorkflowJob p4 = r.jenkins.createProject(WorkflowJob.class, "p4"); @@ -286,6 +274,60 @@ public class SCMSourceRetrieverTest { r.assertLogContains("WorkflowScript: Loading libraries failed", b4); } + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_inline_BRANCH_NAME() throws Exception { + // Test that @Library('branchylib@${BRANCH_NAME}') + // falls back to default for "Pipeline script" which + // is not "from SCM", even when we try to confuse it + // by having some checkouts (and so list of SCMs). + + // Do not let caller-provided BRANCH_NAME interfere here + assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + System.getenv("BRANCH_NAME") != null); + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(true); + lc.setAllowBRANCH_NAME(true); + lc.setTraceBRANCH_NAME(true); + + sampleRepo2.init(); + sampleRepo2.write("vars/myecho2.groovy", "def call() {echo 'something weird'}"); + sampleRepo2.git("add", "vars"); + sampleRepo2.git("commit", "--message=init"); + sampleRepo2.git("checkout", "-b", "feature"); + sampleRepo2.write("vars/myecho2.groovy", "def call() {echo 'something wonderful'}"); + sampleRepo2.git("add", "vars"); + sampleRepo2.git("commit", "--message=init"); + SCMSourceRetriever scm2 = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo2.toString(), "", "*", "", true)); + LibraryConfiguration lc2 = new LibraryConfiguration("branchylib2", scm2); + lc2.setDefaultVersion("master"); + lc2.setIncludeInChangesets(false); + lc2.setAllowVersionOverride(true); + lc2.setAllowBRANCH_NAME(true); + lc2.setTraceBRANCH_NAME(true); + + // Configure two libs to make a mess :) + GlobalLibraries.get().setLibraries(Arrays.asList(lc, lc2)); + + // Branch context for job not set - fall back to default + WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); + p0.setDefinition(new CpsFlowDefinition("@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()", true)); + WorkflowRun b0 = r.buildAndAssertSuccess(p0); + r.assertLogContains("Loading library branchylib@master", b0); + r.assertLogContains("something special", b0); + } + @Issue("JENKINS-69731") @Test public void checkDefaultVersion_MBP_BRANCH_NAME() throws Exception { // Create a MultiBranch Pipeline job instantiated from Git From 8e92f64ba1e6094142844a9c73633ae67f03e3c7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 20:50:25 +0200 Subject: [PATCH 22/70] SCMSourceRetrieverTest: add checkDefaultVersion_MBPsingleBranch_BRANCH_NAME() [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index bca58d58..330f2734 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -624,7 +624,89 @@ public class SCMSourceRetrieverTest { WorkflowJob p1 = mbp.getItem("master"); assertNull(p1); - WorkflowJob p2 = mbp.getItem("source-id"); + WorkflowJob p2 = mbp.getItem("feature-id"); + assertNotNull(p2); + WorkflowRun b2 = p2.getLastBuild(); + r.waitForCompletion(b2); + assertFalse(p2.isBuilding()); + r.assertBuildStatusSuccess(b2); + r.assertLogContains("Loading library branchylib@feature", b2); + r.assertLogContains("something very special", b2); + + WorkflowJob p3 = mbp.getItem("bogus"); + assertNull(p3); + } + + @Issue("JENKINS-69731") + //@Ignore("Need help setting up MBP+SingleSCMSource") + @Test public void checkDefaultVersion_MBPsingleBranch_BRANCH_NAME() throws Exception { + // FAILS: Setup below does not instantiate any jobs! + // (expected one for "feature") + + // Test that @Library('branchylib@${BRANCH_NAME}') works + // also for MBP with "Single repository and branch" as + // the SCM source. + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(true); + lc.setAllowBRANCH_NAME(false); + lc.setTraceBRANCH_NAME(true); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2.init(); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=init"); + sampleRepo2.git("branch", "feature"); + sampleRepo2.git("branch", "bogus"); + + // TODO: Test also another MBP where we would + // set options "List of branches to build" and + // "Branch Specifier" to a different value than + // the "name" of branchSource. While the "name" + // becomes "BRANCH_NAME" envvar (and MBP job name + // generated for the branch), the specifier is + // what gets placed into SCMs list. + + // FAILS: Setup below does not instantiate any jobs! + // (expected one for "feature") + WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); + //GitSCM gitSCM = new GitSCM(sampleRepo2.toString()); + GitSCM gitSCM = new GitSCM( + GitSCM.createRepoList(sampleRepo2.toString(), null), + Collections.singletonList(new BranchSpec("*/feature")), + null, null, Collections.emptyList()); + BranchSource branchSource = new BranchSource(new SingleSCMSource("feature", "feature", gitSCM)); + mbp.getSourcesList().add(branchSource); + // Note: this notification causes discovery of branches, + // definition of MBP "leaf" jobs, and launch of builds, + // so below we just make sure they complete and analyze + // the outcomes. + sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); + System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); + assumeFalse("MBP should have generated 'feature' pipeline job", mbp.getItems().isEmpty()); + + // In case of MBP with "Single repository and branch" + // it only defines one job, so those for other branches + // should be null: + WorkflowJob p1 = mbp.getItem("master"); + assertNull(p1); + + WorkflowJob p2 = mbp.getItem("feature"); assertNotNull(p2); WorkflowRun b2 = p2.getLastBuild(); r.waitForCompletion(b2); From 5dc85dd953597a2d8d0f06d2adcb6e21051c62b4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 21:32:41 +0200 Subject: [PATCH 23/70] LibraryConfiguration: defaultedVersion(): unblock WorkflowRun handling [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index ecc7e24c..87cf3444 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -298,13 +298,19 @@ public LibraryCachingConfiguration getCachingConfiguration() { } } -/* // NOTE: the list of SCMs used by the run does not // seem trustworthy: if an "inline" pipeline script // (not from SCM) is used, there is no "relevant" - // branch name to request; however the list of SCMs - // would be populated as @Library lines are processed - // and some SCM sources get checked out. + // branch name to request; however during work on + // JENKINS-69731 I was concerned that the list of + // SCMs might get populated as @Library lines are + // processed and some SCM sources get checked out. + // Experimentally it seems this is not happening, + // and whole pipeline script source is pre-processed + // first (calling this method for many @Library + // lines), and checkouts happen later (populating + // list of SCMs). This is specifically tested by + // checkDefaultVersion_inline_BRANCH_NAME() case. if (scm0 == null && run instanceof WorkflowRun) { // This covers both "Multibranch Pipeline" // and "Pipeline script from SCM" jobs; @@ -325,7 +331,6 @@ public LibraryCachingConfiguration getCachingConfiguration() { } } } -*/ if (scm0 != null) { // Avoid importing GitSCM and so requiring that From a9aa8f114ebc979e7acbea649e33e3712e15d1a8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 21:33:50 +0200 Subject: [PATCH 24/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_BRANCH_NAME(): test that many @Library lines do not confuse list of SCMs for BRANCH_NAME resolution [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 330f2734..6ef1c2f6 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -326,6 +326,20 @@ public class SCMSourceRetrieverTest { WorkflowRun b0 = r.buildAndAssertSuccess(p0); r.assertLogContains("Loading library branchylib@master", b0); r.assertLogContains("something special", b0); + + // Branch context for second lib might be confused as "feature" + // because the first loaded lib would become part of SCMs list + // for this build, and there are no other SCMs in the list (an + // inline pipeline). In fact the second lib should fall back to + // "master" because the pipeline script is not from Git so there + // is no "BRANCH_NAME" of its own. + WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@feature') import myecho; myecho(); @Library('branchylib2@${BRANCH_NAME}') import myecho2; myecho2()", true)); + WorkflowRun b1 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@feature", b1); + r.assertLogContains("Loading library branchylib2@master", b1); + r.assertLogContains("something very special", b1); + r.assertLogContains("something weird", b1); } @Issue("JENKINS-69731") From 418c572d2599c9c5ac25b468e5b5e8aab8ce0cd4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 21:36:33 +0200 Subject: [PATCH 25/70] SCMSourceRetrieverTest: add checkDefaultVersion_singleBranch_BRANCH_NAME() [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 6ef1c2f6..0f1c4094 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -788,6 +788,58 @@ public class SCMSourceRetrieverTest { r.assertLogContains("something very special", b0); } + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_singleBranch_BRANCH_NAME() throws Exception { + // Test that lc.setAllowBRANCH_NAME(true) enables + // @Library('branchylib@${BRANCH_NAME}') also for + // a simple "Pipeline" job with static SCM source, + // and that even lc.setAllowVersionOverride(false) + // does not intervene here. + assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + System.getenv("BRANCH_NAME") != null); + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(false); + lc.setAllowBRANCH_NAME(true); + lc.setTraceBRANCH_NAME(true); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2.init(); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=init"); + sampleRepo2.git("branch", "feature"); + sampleRepo2.git("branch", "bogus"); + + // Get a non-default branch loaded for this single-branch build: + GitSCM gitSCM = new GitSCM( + GitSCM.createRepoList(sampleRepo2.toString(), null), + Collections.singletonList(new BranchSpec("*/feature")), + null, null, Collections.emptyList()); + + WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); + p0.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); + sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); + + WorkflowRun b0 = r.buildAndAssertSuccess(p0); + r.assertLogContains("Loading library branchylib@feature", b0); + r.assertLogContains("something very special", b0); + } + @Issue("JENKINS-43802") @Test public void owner() throws Exception { GlobalLibraries.get().setLibraries(Collections.singletonList( From 54bb3e9af37b98771a648e6db738d1caa39bf124 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 22:14:37 +0200 Subject: [PATCH 26/70] LibraryConfiguration: defaultedVersion(): add allowVersionEnvvar for @Library('libname@${env.VARNAME}') support [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 125 +++++++++++++++++- .../libs/LibraryConfiguration/config.jelly | 3 + .../help-allowVersionEnvvar.html | 10 ++ 3 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowVersionEnvvar.html diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 87cf3444..fa63e881 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -69,6 +69,7 @@ public class LibraryConfiguration extends AbstractDescribableImpl + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowVersionEnvvar.html b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowVersionEnvvar.html new file mode 100644 index 00000000..cad02363 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowVersionEnvvar.html @@ -0,0 +1,10 @@ +
+ If checked, scripts may select a custom version of the library + by appending literally @${env.VARNAME} pattern in + the @Library annotation, to use current value of + chosen environment variable named VARNAME if it + is defined in job properties or on the build agent.
+ If such branch name is not resolvable as an environment variable + or not present in library storage (SCM, release artifacts...), + it would fall back to default version you select here. +
From d47dad5a22fc745a0e510fe6c1bcba94896617a0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 30 Sep 2022 22:47:35 +0200 Subject: [PATCH 27/70] SCMSourceRetrieverTest: add PoC for checkDefaultVersion_inline_allowVersionEnvvar() [JENKINS-69731] --- pom.xml | 6 +++ .../workflow/libs/SCMSourceRetrieverTest.java | 50 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/pom.xml b/pom.xml index aac4038c..c465fc48 100644 --- a/pom.xml +++ b/pom.xml @@ -203,5 +203,11 @@ 1.10.7 test + + org.jenkins-ci.plugins + envinject + 2.4.0 + test + diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 0f1c4094..50046907 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -46,6 +46,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.TreeMap; import jenkins.branch.BranchSource; import jenkins.plugins.git.GitSCMSource; import jenkins.plugins.git.GitSampleRepoRule; @@ -60,6 +61,7 @@ import jenkins.scm.impl.subversion.SubversionSCMSource; import jenkins.scm.impl.subversion.SubversionSampleRepoRule; import org.apache.commons.io.FileUtils; +import org.jenkinsci.plugins.envinject.EnvInjectPluginAction; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; @@ -73,6 +75,7 @@ import static org.hamcrest.Matchers.not; import static org.junit.Assert.*; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.BuildWatcher; @@ -840,6 +843,53 @@ public class SCMSourceRetrieverTest { r.assertLogContains("something very special", b0); } + @Issue("JENKINS-69731") + @Ignore("Need help with environment manipulation for the build") + @Test public void checkDefaultVersion_inline_allowVersionEnvvar() throws Exception { + // Test that @Library('branchylib@${env.TEST_VAR_NAME}') + // is resolved with the TEST_VAR_NAME="feature" in environment. + + // Do not let caller-provided BRANCH_NAME interfere here + assumeFalse("An externally provided TEST_VAR_NAME envvar interferes with tested logic", + System.getenv("TEST_VAR_NAME") != null); + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(true); + lc.setAllowVersionEnvvar(true); + lc.setTraceBRANCH_NAME(true); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // TEST_VAR_NAME for job not set - fall back to default + WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); + p0.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho()", true)); + WorkflowRun b0 = r.buildAndAssertSuccess(p0); + r.assertLogContains("Loading library branchylib@master", b0); + r.assertLogContains("something special", b0); + + // TEST_VAR_NAME injected into env, use its value for library checkout + // https://github.com/jenkinsci/envinject-plugin/blob/master/src/test/java/org/jenkinsci/plugins/envinject/EnvInjectPluginActionTest.java + TreeMap testEnv = new TreeMap(); + testEnv.put("TEST_VAR_NAME", "feature"); + EnvInjectPluginAction ea = new EnvInjectPluginAction(testEnv); + WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho()", true)); + p1.addAction(ea); + WorkflowRun b1 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@feature", b1); + r.assertLogContains("something very special", b1); + } + @Issue("JENKINS-43802") @Test public void owner() throws Exception { GlobalLibraries.get().setLibraries(Collections.singletonList( From 811fb822e8ea7a239693b1fae0bc7d7bd594b003 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 1 Oct 2022 14:38:43 +0200 Subject: [PATCH 28/70] help-allowVersionEnvvar.html, help-allowBRANCH_NAME.html: stress that "Tokens spelled in @Library annotation are not Groovy variables!" [JENKINS-69731] ...however for simplicity of "naive use" without knowledge of the feature added with JENKINS-69731, the markup used for these semi-variable version specifications is intentionally similar to what "real" Groovy coders would use in their pipeline scripts. --- .../LibraryConfiguration/help-allowBRANCH_NAME.html | 10 +++++++++- .../LibraryConfiguration/help-allowVersionEnvvar.html | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME.html b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME.html index c486426a..06b4b58b 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME.html +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowBRANCH_NAME.html @@ -5,5 +5,13 @@ of the library codebase as that of the pipeline being built.
If such branch name is not resolvable as an environment variable or not present in library storage (SCM, release artifacts...), - it would fall back to default version you select here. + it would fall back to default version you select here.
+ Keep in mind that while the markup for such variable version + specification is intentionally similar to what you would use + in pipeline Groovy code, for simpler use and maintenance, the + actual strings are expanded by the plugin as it pre-processes + the pipeline script before compilation. Tokens spelled in the + @Library annotation are not Groovy variables! + The values substituted here are not influenced by run-time + interpretation of the pipeline script source text! diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowVersionEnvvar.html b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowVersionEnvvar.html index cad02363..a4c50cbd 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowVersionEnvvar.html +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-allowVersionEnvvar.html @@ -6,5 +6,13 @@ is defined in job properties or on the build agent.
If such branch name is not resolvable as an environment variable or not present in library storage (SCM, release artifacts...), - it would fall back to default version you select here. + it would fall back to default version you select here.
+ Keep in mind that while the markup for such variable version + specification is intentionally similar to what you would use + in pipeline Groovy code, for simpler use and maintenance, the + actual strings are expanded by the plugin as it pre-processes + the pipeline script before compilation. Tokens spelled in the + @Library annotation are not Groovy variables! + The values substituted here are not influenced by run-time + interpretation of the pipeline script source text! From 43d45b8a3f75d2aa99a1d817f2fcca3dca50d341 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 1 Oct 2022 15:19:16 +0200 Subject: [PATCH 29/70] SCMSourceRetrieverTest: add checkDefaultVersion_singleBranch_BRANCH_NAME_doubleQuotes() [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 50046907..9c93080b 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -843,6 +843,83 @@ public class SCMSourceRetrieverTest { r.assertLogContains("something very special", b0); } + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_singleBranch_BRANCH_NAME_doubleQuotes() throws Exception { + // Similar to above, the goal of this test is to + // verify that substitution of ${BRANCH_NAME} is + // not impacted by content of Groovy variables. + // The @Library annotation version resolution + // happens before Groovy->Java->... compilation. + // Note that at this time LibraryDecorator.java + // forbids use of non-constant strings; so this + // test would be dynamically skipped as long as + // this behavior happens. + // TODO: If this behavior does change, extend + // the test to also try ${env.VARNAME} confusion. + + assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + System.getenv("BRANCH_NAME") != null); + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(false); + lc.setAllowBRANCH_NAME(true); + lc.setTraceBRANCH_NAME(true); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2.init(); + sampleRepo2.write("Jenkinsfile", "BRANCH_NAME='whatever'; @Library(\"branchylib@${BRANCH_NAME}\") import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile"); + sampleRepo2.git("commit", "--message=init"); + sampleRepo2.git("branch", "feature"); + sampleRepo2.git("branch", "bogus"); + + // Get a non-default branch loaded for this single-branch build: + GitSCM gitSCM = new GitSCM( + GitSCM.createRepoList(sampleRepo2.toString(), null), + Collections.singletonList(new BranchSpec("*/feature")), + null, null, Collections.emptyList()); + + WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); + p1.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); + sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); + p1.scheduleBuild2(0); + r.waitUntilNoActivity(); + WorkflowRun b1 = p1.getLastBuild(); + r.waitForCompletion(b1); + assertFalse(p1.isBuilding()); + + // LibraryDecorator may forbid use of double-quotes + try { + r.assertBuildStatus(Result.FAILURE, b1); + r.assertLogContains("WorkflowScript: @Library value", b1); + r.assertLogContains("was not a constant; did you mean to use the", b1); + r.assertLogContains("step instead?", b1); + // assertions survived, skip the test + assumeFalse("LibraryDecorator forbids use of double-quotes for @Library annotation", true); + } catch(AssertionError x) { + // Chosen library version should not be "whatever" + // causing fallback to "master", but "feature" per + // pipeline SCM branch name. + r.assertBuildStatusSuccess(b1); + r.assertLogContains("Loading library branchylib@feature", b1); + r.assertLogContains("something very special", b1); + } + } + @Issue("JENKINS-69731") @Ignore("Need help with environment manipulation for the build") @Test public void checkDefaultVersion_inline_allowVersionEnvvar() throws Exception { From 38f380f492f46c3215d7518cbd22c182354b33be Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 1 Oct 2022 16:35:55 +0200 Subject: [PATCH 30/70] LibraryConfiguration/config.jelly: fix markup [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration/config.jelly | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly index ec834ae9..0db5175a 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly @@ -37,16 +37,16 @@ THE SOFTWARE. - + - + - + - + From 68902fd5dc345422488798952fe2aa859d7ce220 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 1 Oct 2022 16:42:39 +0200 Subject: [PATCH 31/70] LibraryConfiguration: doCheckDefaultVersion(): fix markup [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index fa63e881..a2557c60 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -695,28 +695,28 @@ public FormValidation doCheckDefaultVersion(@AncestorInPath Item context, @Query return FormValidation.error("If you load a library implicitly, you must specify a default version."); } if (allowBRANCH_NAME) { - return FormValidation.error("If you allow use of literal '@${BRANCH_NAME}' for overriding a default version, you must define that version as fallback."); + return FormValidation.error("If you allow use of literal '${BRANCH_NAME}' for overriding a default version, you must define that version as fallback."); } if (allowVersionEnvvar) { - return FormValidation.error("If you allow use of literal '@${env.VARNAME}' pattern for overriding a default version, you must define that version as fallback."); + return FormValidation.error("If you allow use of literal '${env.VARNAME}' pattern for overriding a default version, you must define that version as fallback."); } if (!allowVersionOverride) { return FormValidation.error("If you deny overriding a default version, you must define that version."); } if (allowBRANCH_NAME_PR) { - return FormValidation.warning("This setting has no effect when you do not allow use of literal '@${BRANCH_NAME}' for overriding a default version"); + return FormValidation.warning("This setting has no effect when you do not allow use of literal '${BRANCH_NAME}' for overriding a default version"); } return FormValidation.ok(); } else { if ("${BRANCH_NAME}".equals(defaultVersion)) { if (!allowBRANCH_NAME) { - return FormValidation.error("Use of literal '@${BRANCH_NAME}' not allowed in this configuration."); + return FormValidation.error("Use of literal '${BRANCH_NAME}' not allowed in this configuration."); } // The context is not a particular Run (might be a Job) // so we can't detect which BRANCH_NAME is relevant: String msg = "Cannot validate default version: " + - "literal '@${BRANCH_NAME}' is reserved " + + "literal '${BRANCH_NAME}' is reserved " + "for pipeline files from SCM"; if (implicit) { // Someone might want to bind feature branches of @@ -729,11 +729,18 @@ public FormValidation doCheckDefaultVersion(@AncestorInPath Item context, @Query if (defaultVersion.startsWith("${env.") && defaultVersion.endsWith("}")) { if (!allowVersionEnvvar) { - return FormValidation.error("Use of literal '@${env.VARNAME}' pattern not allowed in this configuration."); + return FormValidation.error("Use of literal '${env.VARNAME}' pattern not allowed in this configuration."); } String msg = "Cannot set default version to " + - "literal '@${env.VARNAME}' pattern"; + "literal '${env.VARNAME}' pattern"; + // TOTHINK: Should this be an error? + // What if users intentionally want the (implicit?) + // library version to depend on envvars without a + // fallback? and what about git clones with no + // specified "version" to use preference of GitHub + // or similar platform's project/org settings as + // the final sensible fallback? return FormValidation.error(msg); } From dddd3ac0c23f7e861a3b3033602b1773a0776681 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 1 Oct 2022 18:54:13 +0200 Subject: [PATCH 32/70] SCMSourceRetrieverTest: make checkDefaultVersion_inline_allowVersionEnvvar() useful for at least one realistic use-case [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 9c93080b..b4fdfce4 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -27,6 +27,7 @@ import com.cloudbees.hudson.plugins.folder.Folder; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.AbortException; +import hudson.EnvVars; import hudson.FilePath; import hudson.Functions; import hudson.model.Item; @@ -36,6 +37,9 @@ import hudson.plugins.git.BranchSpec; import hudson.scm.ChangeLogSet; import hudson.scm.SCM; +import hudson.slaves.EnvironmentVariablesNodeProperty; +import hudson.slaves.NodeProperty; +import hudson.slaves.NodePropertyDescriptor; import hudson.slaves.WorkspaceList; import java.io.File; import java.io.IOException; @@ -47,6 +51,7 @@ import java.util.Iterator; import java.util.List; import java.util.TreeMap; +import hudson.util.DescribableList; import jenkins.branch.BranchSource; import jenkins.plugins.git.GitSCMSource; import jenkins.plugins.git.GitSampleRepoRule; @@ -921,7 +926,7 @@ public class SCMSourceRetrieverTest { } @Issue("JENKINS-69731") - @Ignore("Need help with environment manipulation for the build") + //@Ignore("Need help with environment manipulation for the build") @Test public void checkDefaultVersion_inline_allowVersionEnvvar() throws Exception { // Test that @Library('branchylib@${env.TEST_VAR_NAME}') // is resolved with the TEST_VAR_NAME="feature" in environment. @@ -938,6 +943,10 @@ public class SCMSourceRetrieverTest { sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); sampleRepo.git("add", "vars"); sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "stable"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something reliable'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -956,15 +965,47 @@ public class SCMSourceRetrieverTest { // TEST_VAR_NAME injected into env, use its value for library checkout // https://github.com/jenkinsci/envinject-plugin/blob/master/src/test/java/org/jenkinsci/plugins/envinject/EnvInjectPluginActionTest.java + WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho()", true)); + + // Inject envvar to server global settings: + DescribableList, NodePropertyDescriptor> globalNodeProperties = r.jenkins.getGlobalNodeProperties(); + List envVarsNodePropertyList = globalNodeProperties.getAll(EnvironmentVariablesNodeProperty.class); + + EnvironmentVariablesNodeProperty newEnvVarsNodeProperty = null; + EnvVars envVars = null; + + if (envVarsNodePropertyList == null || envVarsNodePropertyList.isEmpty()) { + newEnvVarsNodeProperty = new EnvironmentVariablesNodeProperty(); + globalNodeProperties.add(newEnvVarsNodeProperty); + envVars = newEnvVarsNodeProperty.getEnvVars(); + } else { + envVars = envVarsNodePropertyList.get(0).getEnvVars(); + } + envVars.put("TEST_VAR_NAME", "stable"); + r.jenkins.save(); + + WorkflowRun b1 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@stable", b1); + r.assertLogContains("something reliable", b1); + + // TODO: similar trick with build-agent settings + // to check they override global server settings? + +/* + // TODO: Make sense of envinject or similar way to set + // envvars into the job or build before it starts. + // Look at how workflow or git plugins do it?.. + + // Same job, different value of envvar, nearer in scope: TreeMap testEnv = new TreeMap(); testEnv.put("TEST_VAR_NAME", "feature"); EnvInjectPluginAction ea = new EnvInjectPluginAction(testEnv); - WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); - p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho()", true)); p1.addAction(ea); - WorkflowRun b1 = r.buildAndAssertSuccess(p1); - r.assertLogContains("Loading library branchylib@feature", b1); - r.assertLogContains("something very special", b1); + WorkflowRun b2 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@feature", b2); + r.assertLogContains("something very special", b2); + */ } @Issue("JENKINS-43802") From 48ad0fe03c63ce45af882f4d4c4daaf7fdedbe3a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 1 Oct 2022 20:00:30 +0200 Subject: [PATCH 33/70] LibraryConfiguration: defaultedVersion(): if there were no SCMs associated with FlowDefinition of a WorkflowJob, ask in the WorkflowJob itself for completeness [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index a2557c60..03a8e7e5 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -404,6 +404,39 @@ public LibraryCachingConfiguration getCachingConfiguration() { } } } + + if (scm0 == null) { + Collection wjscms = (Collection) ((WorkflowJob)runParent).getSCMs();; + if (wjscms.isEmpty()) { + if (logger != null) { + logger.println("defaultedVersion(): " + + "WorkflowJob '" + + runParent.getClass().getName() + + "' is not associated with any SCMs"); + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): " + + "inspecting WorkflowJob '" + + runParent.getClass().getName() + + "' for SCMs it might use"); + } + for (SCM scmN : wjscms) { + if (logger != null) { + logger.println("defaultedVersion(): inspecting SCM '" + + scmN.getClass().getName() + + "': " + scmN.toString()); + } + if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { + // The best we can do here is accept + // the first seen SCM (with branch + // support which we know how to query). + scm0 = scmN; + break; + } + } + } + } } // NOTE: the list of SCMs used by the run does not From eeba94caf5f7870e2a5f20b2ca6cc7e84dc3fa78 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 1 Oct 2022 20:03:31 +0200 Subject: [PATCH 34/70] SCMSourceRetrieverTest: extend checkDefaultVersion_inline_allowVersionEnvvar() attempts for better test coverage [JENKINS-69731] --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index b4fdfce4..e3ad67a4 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -31,6 +31,7 @@ import hudson.FilePath; import hudson.Functions; import hudson.model.Item; +import hudson.model.Node; import hudson.model.Result; import hudson.model.TaskListener; import hudson.plugins.git.GitSCM; @@ -991,6 +992,7 @@ public class SCMSourceRetrieverTest { // TODO: similar trick with build-agent settings // to check they override global server settings? + //p1.getEnvironment(r.jenkins.getNode("built-in"), null).put("TEST_VAR_NAME", "feature"); /* // TODO: Make sense of envinject or similar way to set @@ -1002,7 +1004,13 @@ public class SCMSourceRetrieverTest { testEnv.put("TEST_VAR_NAME", "feature"); EnvInjectPluginAction ea = new EnvInjectPluginAction(testEnv); p1.addAction(ea); - WorkflowRun b2 = r.buildAndAssertSuccess(p1); + p1.save(); + p1.scheduleBuild2(0, ea); + r.waitUntilNoActivity(); + WorkflowRun b2 = p1.getLastBuild(); + r.waitForCompletion(b2); + assertFalse(p1.isBuilding()); + r.assertBuildStatusSuccess(b2); r.assertLogContains("Loading library branchylib@feature", b2); r.assertLogContains("something very special", b2); */ From ee36d663cd8b504f44ebbb403db37e3aca5e86f3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 1 Oct 2022 20:32:42 +0200 Subject: [PATCH 35/70] LibraryConfiguration: defaultedVersion(): first check if WorkflowJob has a BranchJobProperty, then envvar and then associated SCMs [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 03a8e7e5..47393163 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -41,6 +41,7 @@ import org.jenkinsci.plugins.workflow.flow.FlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.AncestorInPath; @@ -335,21 +336,51 @@ public LibraryCachingConfiguration getCachingConfiguration() { // without a runParent we can't validateVersion() anyway if (runParent != null) { - // First, check if envvar BRANCH_NAME is defined? - // Trust the plugins and situations where it is set. - try { - runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null); + // For a first shot, ask if the job says anything? + // If not, we have more complex SCM-dependent queries + // for WorkflowJob to try below... + if (runParent instanceof WorkflowJob) { if (logger != null) { - if (runVersion != null) { - logger.println("defaultedVersion(): Resolved envvar BRANCH_NAME='" + runVersion + "'"); - } else { - logger.println("defaultedVersion(): Did not resolve envvar BRANCH_NAME: not in env"); + logger.println("defaultedVersion(): inspecting WorkflowJob for BranchJobProperty"); + } + BranchJobProperty property = ((WorkflowJob)runParent).getProperty(BranchJobProperty.class); + if (property != null) { + try { + runVersion = property.getBranch().getName(); + if (logger != null) { + logger.println("defaultedVersion(): WorkflowJob BranchJobProperty refers to " + runVersion); + } + } catch (Exception x) { + runVersion = null; + if (logger != null) { + logger.println("defaultedVersion(): WorkflowJob BranchJobProperty " + + "does not refer to a runVersion: " + x.getMessage()); + } + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): WorkflowJob is not associated with a BranchJobProperty"); } } - } catch (Exception x) { - runVersion = null; - if (logger != null) { - logger.println("defaultedVersion(): Did not resolve envvar BRANCH_NAME: " + x.getMessage()); + } + + // Next, check if envvar BRANCH_NAME is defined? + // Trust the plugins and situations where it is set. + if (runVersion == null) { + try { + runVersion = run.getEnvironment(listener).get("BRANCH_NAME", null); + if (logger != null) { + if (runVersion != null) { + logger.println("defaultedVersion(): Resolved envvar BRANCH_NAME='" + runVersion + "'"); + } else { + logger.println("defaultedVersion(): Did not resolve envvar BRANCH_NAME: not in env"); + } + } + } catch (Exception x) { + runVersion = null; + if (logger != null) { + logger.println("defaultedVersion(): Did not resolve envvar BRANCH_NAME: " + x.getMessage()); + } } } From 83cea0760725b2c98bfdee3a4ac05d486983981e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 2 Oct 2022 11:28:20 +0200 Subject: [PATCH 36/70] SCMSourceRetrieverTest: checkDefaultVersion_MBPsingleBranch_*(): try mbp.save() [JENKINS-69731] --- .../jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index e3ad67a4..8878c812 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -632,6 +632,7 @@ public class SCMSourceRetrieverTest { null, null, Collections.emptyList()); BranchSource branchSource = new BranchSource(new SingleSCMSource("feature-id", "feature", gitSCM)); mbp.getSourcesList().add(branchSource); + mbp.save(); // Note: this notification causes discovery of branches, // definition of MBP "leaf" jobs, and launch of builds, // so below we just make sure they complete and analyze @@ -714,6 +715,7 @@ public class SCMSourceRetrieverTest { null, null, Collections.emptyList()); BranchSource branchSource = new BranchSource(new SingleSCMSource("feature", "feature", gitSCM)); mbp.getSourcesList().add(branchSource); + mbp.save(); // Note: this notification causes discovery of branches, // definition of MBP "leaf" jobs, and launch of builds, // so below we just make sure they complete and analyze From cf2c0691cf508ce803ec9c9be07bacfdb94b3aa5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 2 Oct 2022 11:30:35 +0200 Subject: [PATCH 37/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): let devs easily tinker on currently failing part of test setup [JENKINS-69731] This keeps build requirements and imports in the loop, so compilers and IDEs do not complain or optimize away "unused" lines. --- .../workflow/libs/SCMSourceRetrieverTest.java | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 8878c812..73c75d17 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -929,7 +929,6 @@ public class SCMSourceRetrieverTest { } @Issue("JENKINS-69731") - //@Ignore("Need help with environment manipulation for the build") @Test public void checkDefaultVersion_inline_allowVersionEnvvar() throws Exception { // Test that @Library('branchylib@${env.TEST_VAR_NAME}') // is resolved with the TEST_VAR_NAME="feature" in environment. @@ -996,26 +995,32 @@ public class SCMSourceRetrieverTest { // to check they override global server settings? //p1.getEnvironment(r.jenkins.getNode("built-in"), null).put("TEST_VAR_NAME", "feature"); -/* - // TODO: Make sense of envinject or similar way to set - // envvars into the job or build before it starts. - // Look at how workflow or git plugins do it?.. - - // Same job, different value of envvar, nearer in scope: - TreeMap testEnv = new TreeMap(); - testEnv.put("TEST_VAR_NAME", "feature"); - EnvInjectPluginAction ea = new EnvInjectPluginAction(testEnv); - p1.addAction(ea); - p1.save(); - p1.scheduleBuild2(0, ea); - r.waitUntilNoActivity(); - WorkflowRun b2 = p1.getLastBuild(); - r.waitForCompletion(b2); - assertFalse(p1.isBuilding()); - r.assertBuildStatusSuccess(b2); - r.assertLogContains("Loading library branchylib@feature", b2); - r.assertLogContains("something very special", b2); - */ + // This part of the test is optionally fenced away - + // so developers tinkering on the fix can do so and + // run it, but for default case it is so far ignored. + // $ ENABLE_TEST_VAR_NAME_JOBLEVEL=true mvn test -Dtest='SCMSourceRetrieverTest#checkDefaultVersion_inline_allowVersionEnvvar' + if ("true".equals(System.getenv("ENABLE_TEST_VAR_NAME_JOBLEVEL"))) { + //@Ignore("Need help with environment manipulation for the build") + + // TODO: Make sense of envinject or similar way to set + // envvars into the job or build before it starts. + // Look at how workflow or git plugins do it?.. + + // Same job, different value of envvar, nearer in scope: + TreeMap testEnv = new TreeMap(); + testEnv.put("TEST_VAR_NAME", "feature"); + EnvInjectPluginAction ea = new EnvInjectPluginAction(testEnv); + p1.addAction(ea); + p1.save(); + p1.scheduleBuild2(0, ea); + r.waitUntilNoActivity(); + WorkflowRun b2 = p1.getLastBuild(); + r.waitForCompletion(b2); + assertFalse(p1.isBuilding()); + r.assertBuildStatusSuccess(b2); + r.assertLogContains("Loading library branchylib@feature", b2); + r.assertLogContains("something very special", b2); + } } @Issue("JENKINS-43802") From 6d74aea9c6c3b6339e88168464ba9902b0887b55 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 2 Oct 2022 12:06:38 +0200 Subject: [PATCH 38/70] LibraryConfiguration: refactor irky defaultedVersionSCM() helper out of defaultedVersion() [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 489 ++++++++++-------- 1 file changed, 260 insertions(+), 229 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 47393163..3638c4e5 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -204,6 +204,264 @@ public LibraryCachingConfiguration getCachingConfiguration() { } } + /* This helper is separated as a good target for refactoring + * away a large part of this code eventually (dealing directly + * with "hudson.plugins.git.GitSCM"); for more context see + * https://github.com/jenkinsci/pipeline-groovy-lib-plugin/pull/19#discussion_r985097304 + * + * Its goal is to inspect the WorkflowRun or its WorkflowJob parent, + * and possibly its FlowDefinition of the pipeline script, look into + * associated SCMs, and pick out those SCM classes (GitSCM so far) + * that we can query for info to deduce branch name of the pipeline + * script's source. + * This goes too intimately into other classes, so if the general idea + * of "version" evaluation in defaultedVersion() is okay, the logic + * better be inverted - to stem from the base SCM class/interface + * and have those SCMs tell us what we want here. But that's a job + * for another day. + */ + private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener listener, PrintStream logger) { + String runVersion = null; + Item runParent = run.getParent(); + + // Ask for SCM source of the pipeline (if any), + // as the most authoritative source of the branch + // name we want: + SCM scm0 = null; + if (runParent != null && runParent instanceof WorkflowJob) { + // This covers both "Multibranch Pipeline" + // and "Pipeline script from SCM" jobs; + // it also covers "inline" pipeline scripts + // but should return an empty Collection of + // SCMs since there is no SCM attached to + // the "static source". + // TODO: If there are "pre-loaded libraries" + // in a Jenkins deployment, can they interfere? + if (logger != null) { + logger.println("defaultedVersion(): inspecting WorkflowJob for a FlowDefinition"); + } + FlowDefinition fd = ((WorkflowJob)runParent).getDefinition(); + if (fd != null) { + Collection fdscms = (Collection) fd.getSCMs(); + if (fdscms.isEmpty()) { + if (logger != null) { + logger.println("defaultedVersion(): FlowDefinition '" + + fd.getClass().getName() + + "' is not associated with any SCMs"); + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): inspecting FlowDefinition '" + + fd.getClass().getName() + + "' for SCMs it might use"); + } + for (SCM scmN : fdscms) { + if (logger != null) { + logger.println("defaultedVersion(): inspecting SCM '" + + scmN.getClass().getName() + + "': " + scmN.toString()); + } + if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { + // The best we can do here is accept + // the first seen SCM (with branch + // support which we know how to query). + scm0 = scmN; + break; + } + } + } + } + + if (scm0 == null) { + Collection wjscms = (Collection) ((WorkflowJob)runParent).getSCMs();; + if (wjscms.isEmpty()) { + if (logger != null) { + logger.println("defaultedVersion(): " + + "WorkflowJob '" + + runParent.getClass().getName() + + "' is not associated with any SCMs"); + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): " + + "inspecting WorkflowJob '" + + runParent.getClass().getName() + + "' for SCMs it might use"); + } + for (SCM scmN : wjscms) { + if (logger != null) { + logger.println("defaultedVersion(): inspecting SCM '" + + scmN.getClass().getName() + + "': " + scmN.toString()); + } + if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { + // The best we can do here is accept + // the first seen SCM (with branch + // support which we know how to query). + scm0 = scmN; + break; + } + } + } + } + } // if (runParent != null && runParent instanceof WorkflowJob) + + // If no hit with runParent, look into the run itself: + if (scm0 == null && run instanceof WorkflowRun) { + // This covers both "Multibranch Pipeline" + // and "Pipeline script from SCM" jobs; + // it also covers "inline" pipeline scripts + // but throws a hudson.AbortException since + // there is no SCM attached. + + // NOTE: the list of SCMs used by the run does not + // seem trustworthy: if an "inline" pipeline script + // (not from SCM) is used, there is no "relevant" + // branch name to request; however during work on + // JENKINS-69731 I was concerned that the list of + // SCMs might get populated as @Library lines are + // processed and some SCM sources get checked out. + // Experimentally it seems this is not happening, + // and whole pipeline script source is pre-processed + // first (calling this method for many @Library + // lines), and checkouts happen later (populating + // list of SCMs). This is specifically tested by + // checkDefaultVersion_inline_BRANCH_NAME() case. + + if (logger != null) { + logger.println("defaultedVersion(): inspecting WorkflowRun"); + } + try { + WorkflowRun wfRun = (WorkflowRun) run; + scm0 = wfRun.getSCMs().get(0); + } catch (Exception x) { + if (logger != null) { + logger.println("defaultedVersion(): " + + "Did not get first listed SCM: " + + x.getMessage()); + } + } + } // if (scm0 == null && run instanceof WorkflowRun) + + // Got some hit? Drill deeper! + if (scm0 != null) { + // Avoid importing GitSCM and so requiring that + // it is always installed even if not used by + // particular Jenkins deployment (using e.g. + // SVN, Gerritt, etc.). Our aim is to query this: + // runVersion = scm0.getBranches().first().getExpandedName(run.getEnvironment(listener)); + // https://mkyong.com/java/how-to-use-reflection-to-call-java-method-at-runtime/ + if (logger != null) { + logger.println("defaultedVersion(): " + + "inspecting first listed SCM: " + + scm0.toString()); + } + + Class noparams[] = {}; + Class[] paramEnvVars = new Class[1]; + paramEnvVars[0] = EnvVars.class; + if ("hudson.plugins.git.GitSCM".equals(scm0.getClass().getName())) { + // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/GitSCM.html#getBranches() => + // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/BranchSpec.html#toString() + Method methodGetBranches = null; + try { + methodGetBranches = scm0.getClass().getDeclaredMethod("getBranches", noparams); + } catch (Exception x) { + // NoSuchMethodException | SecurityException | NullPointerException + methodGetBranches = null; + } + if (methodGetBranches != null) { + Object branchList = null; + try { + branchList = methodGetBranches.invoke(scm0); + } catch (Exception x) { + // InvocationTargetException | IllegalAccessException + branchList = null; + } + if (branchList != null && branchList instanceof List) { + Object branch0 = ((List) branchList).get(0); + if (branch0 != null && "hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) { + Method methodGetExpandedName = null; + try { + methodGetExpandedName = branch0.getClass().getDeclaredMethod("getExpandedName", paramEnvVars); + } catch (Exception x) { + methodGetExpandedName = null; + } + if (methodGetExpandedName != null) { + // Handle possible shell-templated branch specs: + Object expandedBranchName = null; + try { + expandedBranchName = methodGetExpandedName.invoke(branch0, run.getEnvironment(listener)); + } catch (Exception x) { + // IllegalAccessException | IOException + expandedBranchName = null; + } + if (expandedBranchName != null) { + runVersion = expandedBranchName.toString(); + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): " + + "did not find method BranchSpec.getExpandedName()"); + } + } + if (runVersion == null || "".equals(runVersion)) { + runVersion = branch0.toString(); + } + } else { + // unknown branchspec class, make no blind guesses + if (logger != null) { + logger.println("defaultedVersion(): " + + "list of branches did not return a " + + "BranchSpec class instance, but " + + (branch0 == null ? "null" : + branch0.getClass().getName())); + } + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): " + + "getBranches() did not return a " + + "list of branches: " + + (branchList == null ? "null" : + branchList.getClass().getName())); + } + } + } else { + // not really the GitSCM we know? + if (logger != null) { + logger.println("defaultedVersion(): " + + "did not find method GitSCM.getBranches()"); + } + } + + // Still alive? Chop off leading '*/' + // (if any) from single-branch MBP and + // plain "Pipeline" job definitions. + if (runVersion != null) { + runVersion = runVersion.replaceFirst("^\\*/", ""); + if (logger != null) { + logger.println("defaultedVersion(): " + + "Discovered runVersion '" + runVersion + + "' in SCM source of the pipeline"); + } + } + } else { + // else SVN, Gerritt or some other SCM - + // add handling when needed and known how + // or rely on BRANCH_NAME (if set) below... + if (logger != null) { + logger.println("defaultedVersion(): " + + "the first listed SCM was not of currently " + + "supported class with recognized branch support: " + + scm0.getClass().getName()); + } + } + } // if (scm0 != null) + + return runVersion; + } + @NonNull String defaultedVersion(@CheckForNull String version) throws AbortException { return defaultedVersion(version, null, null); } @@ -389,235 +647,8 @@ public LibraryCachingConfiguration getCachingConfiguration() { // type of job? // Ask for SCM source of the pipeline (if any), // as the most authoritative source of the branch - // name we want: - SCM scm0 = null; - - if (runParent instanceof WorkflowJob) { - // This covers both "Multibranch Pipeline" - // and "Pipeline script from SCM" jobs; - // it also covers "inline" pipeline scripts - // but should return an empty Collection of - // SCMs since there is no SCM attached to - // the "static source". - // TODO: If there are "pre-loaded libraries" - // in a Jenkins deployment, can they interfere? - if (logger != null) { - logger.println("defaultedVersion(): inspecting WorkflowJob for a FlowDefinition"); - } - FlowDefinition fd = ((WorkflowJob)runParent).getDefinition(); - if (fd != null) { - Collection fdscms = (Collection) fd.getSCMs(); - if (fdscms.isEmpty()) { - if (logger != null) { - logger.println("defaultedVersion(): FlowDefinition '" + - fd.getClass().getName() + - "' is not associated with any SCMs"); - } - } else { - if (logger != null) { - logger.println("defaultedVersion(): inspecting FlowDefinition '" + - fd.getClass().getName() + - "' for SCMs it might use"); - } - for (SCM scmN : fdscms) { - if (logger != null) { - logger.println("defaultedVersion(): inspecting SCM '" + - scmN.getClass().getName() + - "': " + scmN.toString()); - } - if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { - // The best we can do here is accept - // the first seen SCM (with branch - // support which we know how to query). - scm0 = scmN; - break; - } - } - } - } - - if (scm0 == null) { - Collection wjscms = (Collection) ((WorkflowJob)runParent).getSCMs();; - if (wjscms.isEmpty()) { - if (logger != null) { - logger.println("defaultedVersion(): " + - "WorkflowJob '" + - runParent.getClass().getName() + - "' is not associated with any SCMs"); - } - } else { - if (logger != null) { - logger.println("defaultedVersion(): " + - "inspecting WorkflowJob '" + - runParent.getClass().getName() + - "' for SCMs it might use"); - } - for (SCM scmN : wjscms) { - if (logger != null) { - logger.println("defaultedVersion(): inspecting SCM '" + - scmN.getClass().getName() + - "': " + scmN.toString()); - } - if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { - // The best we can do here is accept - // the first seen SCM (with branch - // support which we know how to query). - scm0 = scmN; - break; - } - } - } - } - } - - // NOTE: the list of SCMs used by the run does not - // seem trustworthy: if an "inline" pipeline script - // (not from SCM) is used, there is no "relevant" - // branch name to request; however during work on - // JENKINS-69731 I was concerned that the list of - // SCMs might get populated as @Library lines are - // processed and some SCM sources get checked out. - // Experimentally it seems this is not happening, - // and whole pipeline script source is pre-processed - // first (calling this method for many @Library - // lines), and checkouts happen later (populating - // list of SCMs). This is specifically tested by - // checkDefaultVersion_inline_BRANCH_NAME() case. - if (scm0 == null && run instanceof WorkflowRun) { - // This covers both "Multibranch Pipeline" - // and "Pipeline script from SCM" jobs; - // it also covers "inline" pipeline scripts - // but throws a hudson.AbortException since - // there is no SCM attached. - if (logger != null) { - logger.println("defaultedVersion(): inspecting WorkflowRun"); - } - try { - WorkflowRun wfRun = (WorkflowRun) run; - scm0 = wfRun.getSCMs().get(0); - } catch (Exception x) { - if (logger != null) { - logger.println("defaultedVersion(): " + - "Did not get first listed SCM: " + - x.getMessage()); - } - } - } - - if (scm0 != null) { - // Avoid importing GitSCM and so requiring that - // it is always installed even if not used by - // particular Jenkins deployment (using e.g. - // SVN, Gerritt, etc.). Our aim is to query this: - // runVersion = scm0.getBranches().first().getExpandedName(run.getEnvironment(listener)); - // https://mkyong.com/java/how-to-use-reflection-to-call-java-method-at-runtime/ - if (logger != null) { - logger.println("defaultedVersion(): " + - "inspecting first listed SCM: " + - scm0.toString()); - } - - Class noparams[] = {}; - Class[] paramEnvVars = new Class[1]; - paramEnvVars[0] = EnvVars.class; - if ("hudson.plugins.git.GitSCM".equals(scm0.getClass().getName())) { - // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/GitSCM.html#getBranches() => - // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/BranchSpec.html#toString() - Method methodGetBranches = null; - try { - methodGetBranches = scm0.getClass().getDeclaredMethod("getBranches", noparams); - } catch (Exception x) { - // NoSuchMethodException | SecurityException | NullPointerException - methodGetBranches = null; - } - if (methodGetBranches != null) { - Object branchList = null; - try { - branchList = methodGetBranches.invoke(scm0); - } catch (Exception x) { - // InvocationTargetException | IllegalAccessException - branchList = null; - } - if (branchList != null && branchList instanceof List) { - Object branch0 = ((List) branchList).get(0); - if (branch0 != null && "hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) { - Method methodGetExpandedName = null; - try { - methodGetExpandedName = branch0.getClass().getDeclaredMethod("getExpandedName", paramEnvVars); - } catch (Exception x) { - methodGetExpandedName = null; - } - if (methodGetExpandedName != null) { - // Handle possible shell-templated branch specs: - Object expandedBranchName = null; - try { - expandedBranchName = methodGetExpandedName.invoke(branch0, run.getEnvironment(listener)); - } catch (Exception x) { - // IllegalAccessException | IOException - expandedBranchName = null; - } - if (expandedBranchName != null) { - runVersion = expandedBranchName.toString(); - } - } else { - if (logger != null) { - logger.println("defaultedVersion(): " + - "did not find method BranchSpec.getExpandedName()"); - } - } - if (runVersion == null || "".equals(runVersion)) { - runVersion = branch0.toString(); - } - } else { - // unknown branchspec class, make no blind guesses - if (logger != null) { - logger.println("defaultedVersion(): " + - "list of branches did not return a " + - "BranchSpec class instance, but " + - (branch0 == null ? "null" : - branch0.getClass().getName())); - } - } - } else { - if (logger != null) { - logger.println("defaultedVersion(): " + - "getBranches() did not return a " + - "list of branches: " + - (branchList == null ? "null" : - branchList.getClass().getName())); - } - } - } else { - // not really the GitSCM we know? - if (logger != null) { - logger.println("defaultedVersion(): " + - "did not find method GitSCM.getBranches()"); - } - } - } else { - // else SVN, Gerritt or some other SCM - - // add handling when needed and known how - // or rely on BRANCH_NAME (if set) below... - if (logger != null) { - logger.println("defaultedVersion(): " + - "the first listed SCM was not of currently " + - "supported class with recognized branch support: " + - scm0.getClass().getName()); - } - } - - // Still alive? Chop off leading '*/' - // (if any) from single-branch MBP and - // plain "Pipeline" job definitions. - if (runVersion != null) { - runVersion = runVersion.replaceFirst("^\\*/", ""); - if (logger != null) { - logger.println("defaultedVersion(): " + - "Discovered runVersion '" + runVersion + - "' in SCM source of the pipeline"); - } - } - } + // name we want, if they know something: + runVersion = defaultedVersionSCM(run, listener, logger); } // Note: if runVersion remains null (unresolved - From 6097f6aea1b6e4bf1bf0a6bc889ff514d5936369 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 2 Oct 2022 12:10:20 +0200 Subject: [PATCH 39/70] LibraryConfiguration and tests: rename traceBRANCH_NAME to more generic and camel-cased traceDefaultedVersion [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 19 ++++++++------- .../libs/LibraryConfiguration/config.jelly | 2 +- ...E.html => help-traceDefaultedVersion.html} | 0 .../libs/LibraryConfigurationTest.java | 8 +++---- .../workflow/libs/SCMSourceRetrieverTest.java | 24 +++++++++---------- 5 files changed, 27 insertions(+), 26 deletions(-) rename src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/{help-traceBRANCH_NAME.html => help-traceDefaultedVersion.html} (100%) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 3638c4e5..62619bfd 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -74,10 +74,11 @@ public class LibraryConfiguration extends AbstractDescribableImpl run, @NonNull TaskListener @NonNull String defaultedVersion(@CheckForNull String version, Run run, TaskListener listener) throws AbortException { PrintStream logger = null; - if (traceBRANCH_NAME && listener != null) { + if (traceDefaultedVersion && listener != null) { logger = listener.getLogger(); } if (logger != null) { diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly index 0db5175a..addb3ca8 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly @@ -46,7 +46,7 @@ THE SOFTWARE. - + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-traceBRANCH_NAME.html b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-traceDefaultedVersion.html similarity index 100% rename from src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-traceBRANCH_NAME.html rename to src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-traceDefaultedVersion.html diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java index ccd5ff9d..9ad3e0f7 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryConfigurationTest.java @@ -153,11 +153,11 @@ public class LibraryConfigurationTest { LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); cfg.setAllowVersionOverride(true); cfg.setAllowBRANCH_NAME(false); - cfg.setTraceBRANCH_NAME(true); + cfg.setTraceDefaultedVersion(true); assertEquals(true, cfg.isAllowVersionOverride()); assertEquals(false, cfg.isAllowBRANCH_NAME()); - assertEquals(true, cfg.isTraceBRANCH_NAME()); + assertEquals(true, cfg.isTraceDefaultedVersion()); assertThrows(AbortException.class, () -> cfg.defaultedVersion("${BRANCH_NAME}")); /* This SHOULD NOT return a version string that literally remains '${BRANCH_NAME}'! */ } @@ -170,7 +170,7 @@ public class LibraryConfigurationTest { LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); cfg.setDefaultVersion(defaultVersion); cfg.setAllowBRANCH_NAME(true); - cfg.setTraceBRANCH_NAME(true); + cfg.setTraceDefaultedVersion(true); assertEquals(true, cfg.isAllowBRANCH_NAME()); try { @@ -186,7 +186,7 @@ public class LibraryConfigurationTest { LibraryConfiguration cfg = new LibraryConfiguration(libraryName, new SCMRetriever(new GitSCM("https://phony.jenkins.io/bar.git"))); cfg.setAllowBRANCH_NAME(true); - cfg.setTraceBRANCH_NAME(true); + cfg.setTraceDefaultedVersion(true); assertEquals(true, cfg.isAllowBRANCH_NAME()); assertThrows(AbortException.class, () -> cfg.defaultedVersion("${BRANCH_NAME}", null, null)); diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 73c75d17..62d2932b 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -250,7 +250,7 @@ public class SCMSourceRetrieverTest { lc.setDefaultVersion("master"); lc.setIncludeInChangesets(false); lc.setAllowBRANCH_NAME(true); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Basename "libname" notation => use specified default branch @@ -308,7 +308,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(true); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); sampleRepo2.init(); sampleRepo2.write("vars/myecho2.groovy", "def call() {echo 'something weird'}"); @@ -324,7 +324,7 @@ public class SCMSourceRetrieverTest { lc2.setIncludeInChangesets(false); lc2.setAllowVersionOverride(true); lc2.setAllowBRANCH_NAME(true); - lc2.setTraceBRANCH_NAME(true); + lc2.setTraceDefaultedVersion(true); // Configure two libs to make a mess :) GlobalLibraries.get().setLibraries(Arrays.asList(lc, lc2)); @@ -374,7 +374,7 @@ public class SCMSourceRetrieverTest { lc.setDefaultVersion("master"); lc.setIncludeInChangesets(false); lc.setAllowBRANCH_NAME(true); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -445,7 +445,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(false); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -522,7 +522,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(false); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -596,7 +596,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(false); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -685,7 +685,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(false); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -765,7 +765,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowBRANCH_NAME(false); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -823,7 +823,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(false); lc.setAllowBRANCH_NAME(true); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -882,7 +882,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(false); lc.setAllowBRANCH_NAME(true); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // Inspired in part by tests like @@ -955,7 +955,7 @@ public class SCMSourceRetrieverTest { lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); lc.setAllowVersionEnvvar(true); - lc.setTraceBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); // TEST_VAR_NAME for job not set - fall back to default From 85e96e32ed25cf2ab554067d2a523fe17be7a2de Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 2 Oct 2022 12:28:21 +0200 Subject: [PATCH 40/70] SCMSourceRetrieverTest: remove currently unused imports [JENKINS-69731] --- .../jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 62d2932b..19ffdfbe 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -31,7 +31,6 @@ import hudson.FilePath; import hudson.Functions; import hudson.model.Item; -import hudson.model.Node; import hudson.model.Result; import hudson.model.TaskListener; import hudson.plugins.git.GitSCM; @@ -81,7 +80,6 @@ import static org.hamcrest.Matchers.not; import static org.junit.Assert.*; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.BuildWatcher; From 4ce1164e6ed9c9e0bf6689e07f73110789f1c099 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 2 Oct 2022 16:26:31 +0200 Subject: [PATCH 41/70] SCMSourceRetrieverTest: complete the checkDefaultVersion_MBPsingleBranch_*() cases [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 150 ++++++++++++------ 1 file changed, 99 insertions(+), 51 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 19ffdfbe..4fb1d281 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -52,7 +52,9 @@ import java.util.List; import java.util.TreeMap; import hudson.util.DescribableList; +import jenkins.branch.BranchProperty; import jenkins.branch.BranchSource; +import jenkins.branch.DefaultBranchPropertyStrategy; import jenkins.plugins.git.GitSCMSource; import jenkins.plugins.git.GitSampleRepoRule; import jenkins.scm.api.SCMHead; @@ -569,14 +571,10 @@ public class SCMSourceRetrieverTest { } @Issue("JENKINS-69731") - //@Ignore("Need help setting up MBP+SingleSCMSource") @Test public void checkDefaultVersion_MBPsingleBranch_staticStrings() throws Exception { - // FAILS: Setup below does not instantiate any jobs! - // (expected one for "feature") - // Test that lc.setAllowBRANCH_NAME(false) does not // preclude fixed branch names (they should work), - // like @Library('branchylib@master') when used + // like @Library('branchylib@feature') when used // for MBP with "Single repository and branch" as // the SCM source. @@ -612,41 +610,45 @@ public class SCMSourceRetrieverTest { sampleRepo2.git("add", "Jenkinsfile"); sampleRepo2.git("commit", "--message=bogus"); - // TODO: Test also another MBP where we would - // set options "List of branches to build" and - // "Branch Specifier" to a different value than - // the "name" of branchSource. While the "name" - // becomes "BRANCH_NAME" envvar (and MBP job name - // generated for the branch), the specifier is - // what gets placed into SCMs list. - - // FAILS: Setup below does not instantiate any jobs! - // (expected one for "feature") WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); - //GitSCM gitSCM = new GitSCM(sampleRepo2.toString()); GitSCM gitSCM = new GitSCM( GitSCM.createRepoList(sampleRepo2.toString(), null), Collections.singletonList(new BranchSpec("*/feature")), null, null, Collections.emptyList()); - BranchSource branchSource = new BranchSource(new SingleSCMSource("feature-id", "feature", gitSCM)); - mbp.getSourcesList().add(branchSource); + // We test an MBP with two leaf jobs where we + // set options "List of branches to build"/ + // "Branch Specifier" to a same and a different + // value than the "name" of branchSource. + // While the "name" becomes "BRANCH_NAME" envvar + // (and MBP job name generated for the branch), + // the specifier is what gets placed into SCMs list. + BranchSource branchSource1 = new BranchSource( + new SingleSCMSource("feature-id1", "feature", gitSCM), + new DefaultBranchPropertyStrategy(new BranchProperty[0])); + BranchSource branchSource2 = new BranchSource( + new SingleSCMSource("feature-id2", "featurette", gitSCM), + new DefaultBranchPropertyStrategy(new BranchProperty[0])); + mbp.getSourcesList().add(branchSource1); + mbp.getSourcesList().add(branchSource2); mbp.save(); - // Note: this notification causes discovery of branches, - // definition of MBP "leaf" jobs, and launch of builds, - // so below we just make sure they complete and analyze - // the outcomes. + // Rescan to actually define leaf jobs: + mbp.scheduleBuild(0); sampleRepo2.notifyCommit(r); r.waitUntilNoActivity(); + System.out.println("All Jenkins items: " + r.jenkins.getItems().toString()); + System.out.println("MBP sources: " + mbp.getSourcesList().toString()); + System.out.println("MBP source 0: " + mbp.getSourcesList().get(0).getSource().toString()); + System.out.println("MBP source 1: " + mbp.getSourcesList().get(1).getSource().toString()); System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); - assumeFalse("MBP should have generated 'feature' pipeline job", mbp.getItems().isEmpty()); + assumeFalse("MBP should have generated 'feature' and 'featurette' pipeline job", mbp.getItems().size() != 2); // In case of MBP with "Single repository and branch" - // it only defines one job, so those for other branches - // should be null: + // it only defines one job (per single-branch source), + // so those for other known branches should be null: WorkflowJob p1 = mbp.getItem("master"); assertNull(p1); - WorkflowJob p2 = mbp.getItem("feature-id"); + WorkflowJob p2 = mbp.getItem("feature"); assertNotNull(p2); WorkflowRun b2 = p2.getLastBuild(); r.waitForCompletion(b2); @@ -657,14 +659,28 @@ public class SCMSourceRetrieverTest { WorkflowJob p3 = mbp.getItem("bogus"); assertNull(p3); + + // For fixed branch in @Library spec, we see the + // SingleSCMSource checkout out the "*/feature" + // specified in its GitSCM and so request the + // @Library('branchylib@feature') spelled there. + // And then MBP sets BRANCH_NAME='featurette' + // (and leaf job) per SingleSCMSource "name". + WorkflowJob p4 = mbp.getItem("featurette"); + assertNotNull(p4); + WorkflowRun b4 = p4.getLastBuild(); + r.waitForCompletion(b4); + assertFalse(p4.isBuilding()); + r.assertBuildStatusSuccess(b4); + System.out.println("Envvar BRANCH_NAME set into 'featurette' job: " + b4.getEnvironment().get("BRANCH_NAME")); + // We use same gitSCM source, and so same static + // version of the library: + r.assertLogContains("Loading library branchylib@feature", b4); + r.assertLogContains("something very special", b4); } @Issue("JENKINS-69731") - //@Ignore("Need help setting up MBP+SingleSCMSource") @Test public void checkDefaultVersion_MBPsingleBranch_BRANCH_NAME() throws Exception { - // FAILS: Setup below does not instantiate any jobs! - // (expected one for "feature") - // Test that @Library('branchylib@${BRANCH_NAME}') works // also for MBP with "Single repository and branch" as // the SCM source. @@ -682,7 +698,7 @@ public class SCMSourceRetrieverTest { lc.setDefaultVersion("master"); lc.setIncludeInChangesets(false); lc.setAllowVersionOverride(true); - lc.setAllowBRANCH_NAME(false); + lc.setAllowBRANCH_NAME(true); lc.setTraceDefaultedVersion(true); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); @@ -695,37 +711,41 @@ public class SCMSourceRetrieverTest { sampleRepo2.git("branch", "feature"); sampleRepo2.git("branch", "bogus"); - // TODO: Test also another MBP where we would - // set options "List of branches to build" and - // "Branch Specifier" to a different value than - // the "name" of branchSource. While the "name" - // becomes "BRANCH_NAME" envvar (and MBP job name - // generated for the branch), the specifier is - // what gets placed into SCMs list. - - // FAILS: Setup below does not instantiate any jobs! - // (expected one for "feature") WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); - //GitSCM gitSCM = new GitSCM(sampleRepo2.toString()); GitSCM gitSCM = new GitSCM( GitSCM.createRepoList(sampleRepo2.toString(), null), Collections.singletonList(new BranchSpec("*/feature")), null, null, Collections.emptyList()); - BranchSource branchSource = new BranchSource(new SingleSCMSource("feature", "feature", gitSCM)); - mbp.getSourcesList().add(branchSource); + // We test an MBP with two leaf jobs where we + // set options "List of branches to build"/ + // "Branch Specifier" to a same and a different + // value than the "name" of branchSource. + // While the "name" becomes "BRANCH_NAME" envvar + // (and MBP job name generated for the branch), + // the specifier is what gets placed into SCMs list. + BranchSource branchSource1 = new BranchSource( + new SingleSCMSource("feature-id1", "feature", gitSCM), + new DefaultBranchPropertyStrategy(new BranchProperty[0])); + BranchSource branchSource2 = new BranchSource( + new SingleSCMSource("feature-id2", "featurette", gitSCM), + new DefaultBranchPropertyStrategy(new BranchProperty[0])); + mbp.getSourcesList().add(branchSource1); + mbp.getSourcesList().add(branchSource2); mbp.save(); - // Note: this notification causes discovery of branches, - // definition of MBP "leaf" jobs, and launch of builds, - // so below we just make sure they complete and analyze - // the outcomes. + // Rescan to actually define leaf jobs: + mbp.scheduleBuild(0); sampleRepo2.notifyCommit(r); r.waitUntilNoActivity(); + System.out.println("All Jenkins items: " + r.jenkins.getItems().toString()); + System.out.println("MBP sources: " + mbp.getSourcesList().toString()); + System.out.println("MBP source 0: " + mbp.getSourcesList().get(0).getSource().toString()); + System.out.println("MBP source 1: " + mbp.getSourcesList().get(1).getSource().toString()); System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); - assumeFalse("MBP should have generated 'feature' pipeline job", mbp.getItems().isEmpty()); + assumeFalse("MBP should have generated 'feature' and 'featurette' pipeline job", mbp.getItems().size() != 2); // In case of MBP with "Single repository and branch" - // it only defines one job, so those for other branches - // should be null: + // it only defines one job (per single-branch source), + // so those for other known branches should be null: WorkflowJob p1 = mbp.getItem("master"); assertNull(p1); @@ -740,6 +760,34 @@ public class SCMSourceRetrieverTest { WorkflowJob p3 = mbp.getItem("bogus"); assertNull(p3); + + // For fixed branch in @Library spec, we see the + // SingleSCMSource checkout out the "*/feature" + // specified in its GitSCM and so evaluate the + // @Library('branchylib@${BRANCH_NAME}')... + // And then MBP sets BRANCH_NAME='featurette' + // (and leaf job) per SingleSCMSource "name". + WorkflowJob p4 = mbp.getItem("featurette"); + assertNotNull(p4); + WorkflowRun b4 = p4.getLastBuild(); + r.waitForCompletion(b4); + assertFalse(p4.isBuilding()); + r.assertBuildStatusSuccess(b4); + System.out.println("Envvar BRANCH_NAME set into 'featurette' job: " + b4.getEnvironment().get("BRANCH_NAME")); + // Library does not have a "featurette" branch, + // so if that source were tried according to the + // single-branch source name, should fall back + // to "master". But if it were tried according + // to the actual branch of pipeline script, it + // should use "feature". + // For now, I've sided with the MBP plugin which + // goes to great lengths to make-believe that + // the "name" specified in config is the branch + // name (also setting it into the WorkflowJob + // BranchJobProperty), even if it does not exist + // in actual SCM. + r.assertLogContains("Loading library branchylib@master", b4); + r.assertLogContains("something special", b4); } @Issue("JENKINS-69731") From ce8b8207f545d9df531147a1c5ce8d2cd80029eb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 9 Oct 2022 15:41:14 +0200 Subject: [PATCH 42/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): add some debug traces [JENKINS-69731] --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 4fb1d281..dc368bf7 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1014,7 +1014,7 @@ public class SCMSourceRetrieverTest { // TEST_VAR_NAME injected into env, use its value for library checkout // https://github.com/jenkinsci/envinject-plugin/blob/master/src/test/java/org/jenkinsci/plugins/envinject/EnvInjectPluginActionTest.java WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); - p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho()", true)); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho(); echo \"Groovy TEST_VAR_NAME='${TEST_VAR_NAME}'\"; echo \"env.TEST_VAR_NAME='${env.TEST_VAR_NAME}'\"", true)); // Inject envvar to server global settings: DescribableList, NodePropertyDescriptor> globalNodeProperties = r.jenkins.getGlobalNodeProperties(); @@ -1064,6 +1064,11 @@ public class SCMSourceRetrieverTest { r.waitForCompletion(b2); assertFalse(p1.isBuilding()); r.assertBuildStatusSuccess(b2); + + System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); + System.out.println("[DEBUG:EXT] wfRun env: " + b2.getEnvironment()); + System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b2.getActions(EnvironmentContributingAction.class)); + r.assertLogContains("Loading library branchylib@feature", b2); r.assertLogContains("something very special", b2); } From 2d4bd48a9aabeb8bcad0d271a3cb07e24effa59e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 9 Oct 2022 15:42:06 +0200 Subject: [PATCH 43/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): add FreeStyle test line for ideas about building on an agent [JENKINS-69731] --- .../jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index dc368bf7..2d298aa6 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1039,6 +1039,7 @@ public class SCMSourceRetrieverTest { // TODO: similar trick with build-agent settings // to check they override global server settings? + //p1.setAssignedNode(r.createSlave()); //p1.getEnvironment(r.jenkins.getNode("built-in"), null).put("TEST_VAR_NAME", "feature"); // This part of the test is optionally fenced away - From 6767dc2521ab115db890f761c353f06cb59bcec3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 9 Oct 2022 16:22:54 +0200 Subject: [PATCH 44/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): add envvar injection tests with EnvironmentContributor [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 2d298aa6..38eb6859 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -28,10 +28,16 @@ import edu.umd.cs.findbugs.annotations.NonNull; import hudson.AbortException; import hudson.EnvVars; +import hudson.ExtensionList; import hudson.FilePath; import hudson.Functions; +import hudson.model.BuildVariableContributor; +import hudson.model.EnvironmentContributingAction; +import hudson.model.EnvironmentContributor; import hudson.model.Item; +import hudson.model.Job; import hudson.model.Result; +import hudson.model.Run; import hudson.model.TaskListener; import hudson.plugins.git.GitSCM; import hudson.plugins.git.BranchSpec; @@ -46,6 +52,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; @@ -1073,6 +1080,57 @@ public class SCMSourceRetrieverTest { r.assertLogContains("Loading library branchylib@feature", b2); r.assertLogContains("something very special", b2); } + + if ("true".equals(System.getenv("ENABLE_TEST_VAR_NAME_JOBLEVEL"))) { + // General override idea was lifted from + // https://github.com/jenkinsci/subversion-plugin/blob/master/src/test/java/hudson/scm/SubversionSCMTest.java#L1383 + // test-case recursiveEnvironmentVariables() + + // Per https://github.com/jenkinsci/jenkins/blob/031f40c50899ec4e5fa4d886a1c006a5330f2627/core/src/main/java/hudson/ExtensionList.java#L296 + // in implementation of `add(index, T)` the index is ignored. + // So we save a copy in same order, drop the list, add our + // override as the only entry (hence highest priority) + // and re-add the original list contents. + ExtensionList ecList = EnvironmentContributor.all(); + List ecOrig = new ArrayList(); + for (EnvironmentContributor ec : ecList) { + ecOrig.add(ec); + } + ecList.removeAll(ecOrig); + assumeFalse("EnvironmentContributor.all() should be empty now", !ecList.isEmpty()); + + ecList.add(new EnvironmentContributor() { + @Override public void buildEnvironmentFor(Run run, EnvVars ev, TaskListener tl) throws IOException, InterruptedException { + if (tl != null) + tl.getLogger().println("[DEBUG:RUN] Injecting TEST_VAR_NAME='feature' to EnvVars"); + ev.put("TEST_VAR_NAME", "feature"); + } + @Override public void buildEnvironmentFor(Job run, EnvVars ev, TaskListener tl) throws IOException, InterruptedException { + if (tl != null) + tl.getLogger().println("[DEBUG:JOB] Injecting TEST_VAR_NAME='feature' to EnvVars"); + ev.put("TEST_VAR_NAME", "feature"); + } + }); +/* + for (EnvironmentContributor ec : ecOrig) { + ecList.add(ec); + } +*/ + + p1.scheduleBuild2(0); + r.waitUntilNoActivity(); + WorkflowRun b2 = p1.getLastBuild(); + r.waitForCompletion(b2); + assertFalse(p1.isBuilding()); + r.assertBuildStatusSuccess(b2); + + System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); + System.out.println("[DEBUG:EXT] wfRun env: " + b2.getEnvironment()); + System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b2.getActions(EnvironmentContributingAction.class)); + + r.assertLogContains("Loading library branchylib@feature", b2); + r.assertLogContains("something very special", b2); + } } @Issue("JENKINS-43802") From edd1ddbf95d17ba083f9dd9ef22f24309815a4e3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 9 Oct 2022 16:25:03 +0200 Subject: [PATCH 45/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): update comments [JENKINS-69731] --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 38eb6859..0e913dd6 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1054,6 +1054,8 @@ public class SCMSourceRetrieverTest { // run it, but for default case it is so far ignored. // $ ENABLE_TEST_VAR_NAME_JOBLEVEL=true mvn test -Dtest='SCMSourceRetrieverTest#checkDefaultVersion_inline_allowVersionEnvvar' if ("true".equals(System.getenv("ENABLE_TEST_VAR_NAME_JOBLEVEL"))) { + // See research commented at + // https://github.com/jenkinsci/pipeline-groovy-lib-plugin/pull/19#discussion_r990792561 //@Ignore("Need help with environment manipulation for the build") // TODO: Make sense of envinject or similar way to set @@ -1082,6 +1084,12 @@ public class SCMSourceRetrieverTest { } if ("true".equals(System.getenv("ENABLE_TEST_VAR_NAME_JOBLEVEL"))) { + // Try a more direct way to inject environment + // variables into a Job/Run without extra plugins: + + // See research commented at + // https://github.com/jenkinsci/pipeline-groovy-lib-plugin/pull/19#discussion_r990781686 + // General override idea was lifted from // https://github.com/jenkinsci/subversion-plugin/blob/master/src/test/java/hudson/scm/SubversionSCMTest.java#L1383 // test-case recursiveEnvironmentVariables() From 519021479b066642431ef6d04289979863128714 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 9 Oct 2022 16:25:40 +0200 Subject: [PATCH 46/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): drop attempts with envinject [JENKINS-69731] --- pom.xml | 6 ---- .../workflow/libs/SCMSourceRetrieverTest.java | 31 ------------------- 2 files changed, 37 deletions(-) diff --git a/pom.xml b/pom.xml index c465fc48..aac4038c 100644 --- a/pom.xml +++ b/pom.xml @@ -203,11 +203,5 @@ 1.10.7 test - - org.jenkins-ci.plugins - envinject - 2.4.0 - test - diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 0e913dd6..22e15ae5 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -75,7 +75,6 @@ import jenkins.scm.impl.subversion.SubversionSCMSource; import jenkins.scm.impl.subversion.SubversionSampleRepoRule; import org.apache.commons.io.FileUtils; -import org.jenkinsci.plugins.envinject.EnvInjectPluginAction; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; @@ -1053,36 +1052,6 @@ public class SCMSourceRetrieverTest { // so developers tinkering on the fix can do so and // run it, but for default case it is so far ignored. // $ ENABLE_TEST_VAR_NAME_JOBLEVEL=true mvn test -Dtest='SCMSourceRetrieverTest#checkDefaultVersion_inline_allowVersionEnvvar' - if ("true".equals(System.getenv("ENABLE_TEST_VAR_NAME_JOBLEVEL"))) { - // See research commented at - // https://github.com/jenkinsci/pipeline-groovy-lib-plugin/pull/19#discussion_r990792561 - //@Ignore("Need help with environment manipulation for the build") - - // TODO: Make sense of envinject or similar way to set - // envvars into the job or build before it starts. - // Look at how workflow or git plugins do it?.. - - // Same job, different value of envvar, nearer in scope: - TreeMap testEnv = new TreeMap(); - testEnv.put("TEST_VAR_NAME", "feature"); - EnvInjectPluginAction ea = new EnvInjectPluginAction(testEnv); - p1.addAction(ea); - p1.save(); - p1.scheduleBuild2(0, ea); - r.waitUntilNoActivity(); - WorkflowRun b2 = p1.getLastBuild(); - r.waitForCompletion(b2); - assertFalse(p1.isBuilding()); - r.assertBuildStatusSuccess(b2); - - System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); - System.out.println("[DEBUG:EXT] wfRun env: " + b2.getEnvironment()); - System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b2.getActions(EnvironmentContributingAction.class)); - - r.assertLogContains("Loading library branchylib@feature", b2); - r.assertLogContains("something very special", b2); - } - if ("true".equals(System.getenv("ENABLE_TEST_VAR_NAME_JOBLEVEL"))) { // Try a more direct way to inject environment // variables into a Job/Run without extra plugins: From 06a3e09efc40a41efa40a902208ca7cb6987694a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 9 Oct 2022 22:20:07 +0200 Subject: [PATCH 47/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): add back ecOrig to ecList [JENKINS-69731] --- .../jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 22e15ae5..267a7290 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1088,11 +1088,9 @@ public class SCMSourceRetrieverTest { ev.put("TEST_VAR_NAME", "feature"); } }); -/* for (EnvironmentContributor ec : ecOrig) { ecList.add(ec); } -*/ p1.scheduleBuild2(0); r.waitUntilNoActivity(); From f4c53c747a1988cf3eb770759abd7e6530bd0d68 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 9 Oct 2022 22:20:49 +0200 Subject: [PATCH 48/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): WorkflowRun only accepts injected envvars if global config does not override them [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 267a7290..951e4ec9 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1103,8 +1103,28 @@ public class SCMSourceRetrieverTest { System.out.println("[DEBUG:EXT] wfRun env: " + b2.getEnvironment()); System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b2.getActions(EnvironmentContributingAction.class)); - r.assertLogContains("Loading library branchylib@feature", b2); - r.assertLogContains("something very special", b2); + // Our first try is expected to fail currently, since + // WorkflowRun::buildEnvironmentFor() takes "env" from + // super-class, and overlays with envvars from global + // configuration. However, if in the future behavior + // of workflow changes, it is not consequential - just + // something to adjust "correct" expectations for. + r.assertLogContains("Loading library branchylib@stable", b2); + r.assertLogContains("something reliable", b2); + + // For the next try, however, we remove global config + // part and expect the injected envvar to take hold: + envVars.remove("TEST_VAR_NAME"); + r.jenkins.save(); + + WorkflowRun b3 = r.buildAndAssertSuccess(p1); + + System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); + System.out.println("[DEBUG:EXT] wfRun env: " + b3.getEnvironment()); + System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b3.getActions(EnvironmentContributingAction.class)); + + r.assertLogContains("Loading library branchylib@feature", b3); + r.assertLogContains("something very special", b3); } } From d5294217a7f7767c6f87e015e6c6fff49d4c0ae6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 9 Oct 2022 22:26:55 +0200 Subject: [PATCH 49/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): un-block test with injected envvars [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 157 +++++++++--------- 1 file changed, 77 insertions(+), 80 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 951e4ec9..13f508db 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1043,89 +1043,86 @@ public class SCMSourceRetrieverTest { r.assertLogContains("Loading library branchylib@stable", b1); r.assertLogContains("something reliable", b1); - // TODO: similar trick with build-agent settings - // to check they override global server settings? - //p1.setAssignedNode(r.createSlave()); - //p1.getEnvironment(r.jenkins.getNode("built-in"), null).put("TEST_VAR_NAME", "feature"); - - // This part of the test is optionally fenced away - - // so developers tinkering on the fix can do so and - // run it, but for default case it is so far ignored. - // $ ENABLE_TEST_VAR_NAME_JOBLEVEL=true mvn test -Dtest='SCMSourceRetrieverTest#checkDefaultVersion_inline_allowVersionEnvvar' - if ("true".equals(System.getenv("ENABLE_TEST_VAR_NAME_JOBLEVEL"))) { - // Try a more direct way to inject environment - // variables into a Job/Run without extra plugins: - - // See research commented at - // https://github.com/jenkinsci/pipeline-groovy-lib-plugin/pull/19#discussion_r990781686 - - // General override idea was lifted from - // https://github.com/jenkinsci/subversion-plugin/blob/master/src/test/java/hudson/scm/SubversionSCMTest.java#L1383 - // test-case recursiveEnvironmentVariables() - - // Per https://github.com/jenkinsci/jenkins/blob/031f40c50899ec4e5fa4d886a1c006a5330f2627/core/src/main/java/hudson/ExtensionList.java#L296 - // in implementation of `add(index, T)` the index is ignored. - // So we save a copy in same order, drop the list, add our - // override as the only entry (hence highest priority) - // and re-add the original list contents. - ExtensionList ecList = EnvironmentContributor.all(); - List ecOrig = new ArrayList(); - for (EnvironmentContributor ec : ecList) { - ecOrig.add(ec); + // Now try a more direct way to inject environment + // variables into a Job/Run without extra plugins: + + // See research commented at + // https://github.com/jenkinsci/pipeline-groovy-lib-plugin/pull/19#discussion_r990781686 + + // General override idea was lifted from + // https://github.com/jenkinsci/subversion-plugin/blob/master/src/test/java/hudson/scm/SubversionSCMTest.java#L1383 + // test-case recursiveEnvironmentVariables() + + // Per https://github.com/jenkinsci/jenkins/blob/031f40c50899ec4e5fa4d886a1c006a5330f2627/core/src/main/java/hudson/ExtensionList.java#L296 + // in implementation of `add(index, T)` the index is ignored. + // So we save a copy in same order, drop the list, add our + // override as the only entry (hence highest priority) + // and re-add the original list contents. + ExtensionList ecList = EnvironmentContributor.all(); + List ecOrig = new ArrayList(); + for (EnvironmentContributor ec : ecList) { + ecOrig.add(ec); + } + ecList.removeAll(ecOrig); + assumeFalse("EnvironmentContributor.all() should be empty now", !ecList.isEmpty()); + + ecList.add(new EnvironmentContributor() { + @Override public void buildEnvironmentFor(Run run, EnvVars ev, TaskListener tl) throws IOException, InterruptedException { + if (tl != null) + tl.getLogger().println("[DEBUG:RUN] Injecting TEST_VAR_NAME='feature' to EnvVars"); + ev.put("TEST_VAR_NAME", "feature"); } - ecList.removeAll(ecOrig); - assumeFalse("EnvironmentContributor.all() should be empty now", !ecList.isEmpty()); - - ecList.add(new EnvironmentContributor() { - @Override public void buildEnvironmentFor(Run run, EnvVars ev, TaskListener tl) throws IOException, InterruptedException { - if (tl != null) - tl.getLogger().println("[DEBUG:RUN] Injecting TEST_VAR_NAME='feature' to EnvVars"); - ev.put("TEST_VAR_NAME", "feature"); - } - @Override public void buildEnvironmentFor(Job run, EnvVars ev, TaskListener tl) throws IOException, InterruptedException { - if (tl != null) - tl.getLogger().println("[DEBUG:JOB] Injecting TEST_VAR_NAME='feature' to EnvVars"); - ev.put("TEST_VAR_NAME", "feature"); - } - }); - for (EnvironmentContributor ec : ecOrig) { - ecList.add(ec); + @Override public void buildEnvironmentFor(Job run, EnvVars ev, TaskListener tl) throws IOException, InterruptedException { + if (tl != null) + tl.getLogger().println("[DEBUG:JOB] Injecting TEST_VAR_NAME='feature' to EnvVars"); + ev.put("TEST_VAR_NAME", "feature"); } - - p1.scheduleBuild2(0); - r.waitUntilNoActivity(); - WorkflowRun b2 = p1.getLastBuild(); - r.waitForCompletion(b2); - assertFalse(p1.isBuilding()); - r.assertBuildStatusSuccess(b2); - - System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); - System.out.println("[DEBUG:EXT] wfRun env: " + b2.getEnvironment()); - System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b2.getActions(EnvironmentContributingAction.class)); - - // Our first try is expected to fail currently, since - // WorkflowRun::buildEnvironmentFor() takes "env" from - // super-class, and overlays with envvars from global - // configuration. However, if in the future behavior - // of workflow changes, it is not consequential - just - // something to adjust "correct" expectations for. - r.assertLogContains("Loading library branchylib@stable", b2); - r.assertLogContains("something reliable", b2); - - // For the next try, however, we remove global config - // part and expect the injected envvar to take hold: - envVars.remove("TEST_VAR_NAME"); - r.jenkins.save(); - - WorkflowRun b3 = r.buildAndAssertSuccess(p1); - - System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); - System.out.println("[DEBUG:EXT] wfRun env: " + b3.getEnvironment()); - System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b3.getActions(EnvironmentContributingAction.class)); - - r.assertLogContains("Loading library branchylib@feature", b3); - r.assertLogContains("something very special", b3); + }); + for (EnvironmentContributor ec : ecOrig) { + ecList.add(ec); } + + p1.scheduleBuild2(0); + r.waitUntilNoActivity(); + WorkflowRun b2 = p1.getLastBuild(); + r.waitForCompletion(b2); + assertFalse(p1.isBuilding()); + r.assertBuildStatusSuccess(b2); + + System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); + System.out.println("[DEBUG:EXT] wfRun env: " + b2.getEnvironment()); + System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b2.getActions(EnvironmentContributingAction.class)); + + // Our first try is expected to fail currently, since + // WorkflowRun::getEnvironment() takes "env" from + // super-class, and overlays with envvars from global + // configuration. However, if in the future behavior + // of workflow changes, it is not consequential - just + // something to adjust "correct" expectations for. + r.assertLogContains("Loading library branchylib@stable", b2); + r.assertLogContains("something reliable", b2); + + // For the next try, however, we remove global config + // part and expect the injected envvar to take hold: + envVars.remove("TEST_VAR_NAME"); + r.jenkins.save(); + + WorkflowRun b3 = r.buildAndAssertSuccess(p1); + + System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); + System.out.println("[DEBUG:EXT] wfRun env: " + b3.getEnvironment()); + System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b3.getActions(EnvironmentContributingAction.class)); + + r.assertLogContains("Loading library branchylib@feature", b3); + r.assertLogContains("something very special", b3); + + // TODO: similar trick with built-in agent settings + // which has lowest priority behind global and injected + // envvars (see Run::getEnvironment()). Check with the + // override above, and with ecList contents restored to + // ecOrig state only. + //p1.setAssignedNode(r.createSlave()); + //p1.getEnvironment(r.jenkins.getNode("built-in"), null).put("TEST_VAR_NAME", "feature"); } @Issue("JENKINS-43802") From 71c51c85fbf07fac55fbb1b6764796f101bab415 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 10 Oct 2022 13:08:25 +0200 Subject: [PATCH 50/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): tag debug printouts with build number [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 13f508db..3ff5b769 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1089,9 +1089,9 @@ public class SCMSourceRetrieverTest { assertFalse(p1.isBuilding()); r.assertBuildStatusSuccess(b2); - System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); - System.out.println("[DEBUG:EXT] wfRun env: " + b2.getEnvironment()); - System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b2.getActions(EnvironmentContributingAction.class)); + System.out.println("[DEBUG:EXT:p1b2] wfJob env: " + p1.getEnvironment(null, null)); + System.out.println("[DEBUG:EXT:p1b2] wfRun env: " + b2.getEnvironment()); + System.out.println("[DEBUG:EXT:p1b2] wfRun envContribActions: " + b2.getActions(EnvironmentContributingAction.class)); // Our first try is expected to fail currently, since // WorkflowRun::getEnvironment() takes "env" from @@ -1109,9 +1109,9 @@ public class SCMSourceRetrieverTest { WorkflowRun b3 = r.buildAndAssertSuccess(p1); - System.out.println("[DEBUG:EXT] wfJob env: " + p1.getEnvironment(null, null)); - System.out.println("[DEBUG:EXT] wfRun env: " + b3.getEnvironment()); - System.out.println("[DEBUG:EXT] wfRun envContribActions: " + b3.getActions(EnvironmentContributingAction.class)); + System.out.println("[DEBUG:EXT:p1b3] wfJob env: " + p1.getEnvironment(null, null)); + System.out.println("[DEBUG:EXT:p1b3] wfRun env: " + b3.getEnvironment()); + System.out.println("[DEBUG:EXT:p1b3] wfRun envContribActions: " + b3.getActions(EnvironmentContributingAction.class)); r.assertLogContains("Loading library branchylib@feature", b3); r.assertLogContains("something very special", b3); From 9e20d7864d9b7c2a3ed92db1785aa9c134c8b0c1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 10 Oct 2022 19:36:09 +0200 Subject: [PATCH 51/70] SCMSourceRetrieverTest: checkDefaultVersion_inline_allowVersionEnvvar(): play with "built-in" node envvars [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 180 +++++++++++++++++- 1 file changed, 170 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 3ff5b769..85415ea2 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -31,24 +31,29 @@ import hudson.ExtensionList; import hudson.FilePath; import hudson.Functions; -import hudson.model.BuildVariableContributor; +import hudson.model.Computer; import hudson.model.EnvironmentContributingAction; import hudson.model.EnvironmentContributor; import hudson.model.Item; import hudson.model.Job; +import hudson.model.Node; import hudson.model.Result; import hudson.model.Run; import hudson.model.TaskListener; import hudson.plugins.git.GitSCM; import hudson.plugins.git.BranchSpec; +import hudson.remoting.VirtualChannel; import hudson.scm.ChangeLogSet; import hudson.scm.SCM; import hudson.slaves.EnvironmentVariablesNodeProperty; import hudson.slaves.NodeProperty; import hudson.slaves.NodePropertyDescriptor; +import hudson.slaves.OfflineCause; +import hudson.slaves.WorkspaceList; import hudson.slaves.WorkspaceList; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -57,11 +62,11 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.TreeMap; import hudson.util.DescribableList; import jenkins.branch.BranchProperty; import jenkins.branch.BranchSource; import jenkins.branch.DefaultBranchPropertyStrategy; +import jenkins.model.Jenkins; import jenkins.plugins.git.GitSCMSource; import jenkins.plugins.git.GitSampleRepoRule; import jenkins.scm.api.SCMHead; @@ -1020,7 +1025,7 @@ public class SCMSourceRetrieverTest { // TEST_VAR_NAME injected into env, use its value for library checkout // https://github.com/jenkinsci/envinject-plugin/blob/master/src/test/java/org/jenkinsci/plugins/envinject/EnvInjectPluginActionTest.java WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); - p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho(); echo \"Groovy TEST_VAR_NAME='${TEST_VAR_NAME}'\"; echo \"env.TEST_VAR_NAME='${env.TEST_VAR_NAME}'\"", true)); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho(); try { echo \"Groovy TEST_VAR_NAME='${TEST_VAR_NAME}'\"; } catch (groovy.lang.MissingPropertyException mpe) { echo \"Groovy TEST_VAR_NAME missing: ${mpe.getMessage()}\"; } ; echo \"env.TEST_VAR_NAME='${env.TEST_VAR_NAME}'\"", true)); // Inject envvar to server global settings: DescribableList, NodePropertyDescriptor> globalNodeProperties = r.jenkins.getGlobalNodeProperties(); @@ -1116,13 +1121,168 @@ public class SCMSourceRetrieverTest { r.assertLogContains("Loading library branchylib@feature", b3); r.assertLogContains("something very special", b3); - // TODO: similar trick with built-in agent settings - // which has lowest priority behind global and injected - // envvars (see Run::getEnvironment()). Check with the - // override above, and with ecList contents restored to - // ecOrig state only. - //p1.setAssignedNode(r.createSlave()); - //p1.getEnvironment(r.jenkins.getNode("built-in"), null).put("TEST_VAR_NAME", "feature"); + // Below we do a similar trick with built-in agent settings + // which (as a Computer=>Node) has lowest priority behind + // global and injected envvars (see Run::getEnvironment()). + // Check with the injected envvars (they override) like above + // first, and with ecList contents restored to ecOrig state + // only (so only Computer envvars are applied). + // Trick here is that the "jenkins.model.Jenkins" is inherited + // from Node and its toComputer() returns the "built-in" (nee + // "master"), instance of "hudson.model.Hudson$MasterComputer" + // whose String getName() is actually empty. + // Pipeline scripts are initially processed only by this node, + // further run on the controller, and then the actual work is + // distributed to agents (often via remoting proxy methods) + // if any are defined. + Computer builtInComputer = r.jenkins.toComputer(); + Node builtInNode = builtInComputer.getNode(); +/* + EnvironmentVariablesNodeProperty builtInEnvProp = builtInNode.getNodeProperty(EnvironmentVariablesNodeProperty.class); + + if (builtInEnvProp == null) { + builtInEnvProp = new EnvironmentVariablesNodeProperty(); + //builtInEnvProp.setNode(builtInNode); + builtInNode.getNodeProperties().add(builtInEnvProp); + } + EnvVars builtInEnvVars = builtInEnvProp.getEnvVars(); + builtInEnvVars.put("TEST_VAR_NAME", "stable"); + builtInEnvProp.buildEnvVars(new EnvVars("TEST_VAR_NAME", "stable"), null); + */ + builtInNode.getNodeProperties().add(new EnvironmentVariablesNodeProperty(new EnvironmentVariablesNodeProperty.Entry("TEST_VAR_NAME", "stable"))); + r.jenkins.save(); + builtInNode.save(); + + System.out.println("[DEBUG] Restart the 'built-in' Computer connection to clear its cachedEnvironment and recognize added envvar"); + builtInComputer.setTemporarilyOffline(true, new OfflineCause.ByCLI("Restart built-in to reread envvars config")); + builtInComputer.waitUntilOffline(); + builtInComputer.disconnect(new OfflineCause.ByCLI("Restart built-in to reread envvars config")); + r.waitUntilNoActivity(); + Thread.sleep(3000); + builtInComputer.setTemporarilyOffline(false, null); + builtInComputer.connect(true); + builtInComputer.waitUntilOnline(); + + System.out.println("[DEBUG] builtIn node env: " + builtInComputer.getEnvironment()); + + // Both injected var and build node envvar setting present; + // injected var wins: + WorkflowRun b4 = r.buildAndAssertSuccess(p1); + System.out.println("[DEBUG:EXT:p1b4] wfJob env: " + p1.getEnvironment(null, null)); + System.out.println("[DEBUG:EXT:p1b4] wfRun env: " + b4.getEnvironment()); + System.out.println("[DEBUG:EXT:p1b4] wfRun envContribActions: " + b4.getActions(EnvironmentContributingAction.class)); + r.assertLogContains("Loading library branchylib@feature", b4); + r.assertLogContains("something very special", b4); + r.assertLogContains("Groovy TEST_VAR_NAME='feature'", b4); + r.assertLogContains("env.TEST_VAR_NAME='feature'", b4); + + // Only build agent envvars are present: drop all, + // add back original (before our mock above): + List ecCurr = new ArrayList(); + for (EnvironmentContributor ec : ecList) { + ecCurr.add(ec); + } + ecList.removeAll(ecCurr); + for (EnvironmentContributor ec : ecOrig) { + ecList.add(ec); + } + + System.out.println("[DEBUG:EXT:p1b5] EnvironmentContributor.all(): " + EnvironmentContributor.all()); + System.out.println("[DEBUG:EXT:p1b5] builtIn node env: " + builtInComputer.getEnvironment()); + System.out.println("[DEBUG:EXT:p1b5] wfJob env in builtIn: " + p1.getEnvironment(builtInNode, null)); + System.out.println("[DEBUG:EXT:p1b5] wfJob env (without node): " + p1.getEnvironment(null, null)); + WorkflowRun b5 = r.buildAndAssertSuccess(p1); + System.out.println("[DEBUG:EXT:p1b5] wfRun env: " + b5.getEnvironment()); + System.out.println("[DEBUG:EXT:p1b5] wfRun envContribActions: " + b5.getActions(EnvironmentContributingAction.class)); + r.assertLogContains("Loading library branchylib@stable", b5); + r.assertLogContains("something reliable", b5); + // Why oh why is the build agent's (cached) envvar not resolved + // even after reconnect?.. Probably a burden of "built-in" until + // Jenkins restart?.. + r.assertLogContains("Groovy TEST_VAR_NAME missing", b5); + r.assertLogContains("env.TEST_VAR_NAME='null'", b5); + + // Let's try just that - restart Jenkins to fully reinit the + // built-in node with its config: + r.jenkins.reload(); + r.waitUntilNoActivity(); + + WorkflowRun b6 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@stable", b6); + r.assertLogContains("something reliable", b6); + r.assertLogContains("Groovy TEST_VAR_NAME missing", b6); + r.assertLogContains("env.TEST_VAR_NAME='null'", b6); + + Thread.sleep(160000); + } + + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_inline_allowVersionEnvvar_builtIn() throws Exception { + // Test that @Library('branchylib@${env.TEST_VAR_NAME}') + // is resolved with the TEST_VAR_NAME="feature" in the + // "built-in" node environment settings. + + // Do not let caller-provided BRANCH_NAME interfere here + assumeFalse("An externally provided TEST_VAR_NAME envvar interferes with tested logic", + System.getenv("TEST_VAR_NAME") != null); + + // First apply the "built-in node", then touch Jenkins jobs: + Computer builtInComputer = r.jenkins.toComputer(); + Node builtInNode = builtInComputer.getNode(); + builtInNode.getNodeProperties().add(new EnvironmentVariablesNodeProperty(new EnvironmentVariablesNodeProperty.Entry("TEST_VAR_NAME", "stable"))); + r.jenkins.save(); + builtInNode.save(); + + System.out.println("[DEBUG] Restart the 'built-in' Computer connection to clear its cachedEnvironment and recognize added envvar"); + builtInComputer.setTemporarilyOffline(true, new OfflineCause.ByCLI("Restart built-in to reread envvars config")); + builtInComputer.disconnect(new OfflineCause.ByCLI("Restart built-in to reread envvars config")); + builtInComputer.waitUntilOffline(); + r.waitUntilNoActivity(); + builtInComputer.getChannel().close(); + Thread.sleep(3000); + r.jenkins.reload(); + builtInComputer.setTemporarilyOffline(false, null); + builtInComputer.connect(true); + builtInComputer.waitUntilOnline(); + r.waitUntilNoActivity(); + + // Feed it to Java reflection, to clear the internal cache... + Field ccc = Computer.class.getDeclaredField("cachedEnvironment"); + ccc.setAccessible(true); + ccc.set(builtInComputer, null); + + System.out.println("[DEBUG] builtIn node env: " + builtInComputer.getEnvironment()); + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "stable"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something reliable'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(true); + lc.setAllowVersionEnvvar(true); + lc.setTraceDefaultedVersion(true); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho(); try { echo \"Groovy TEST_VAR_NAME='${TEST_VAR_NAME}'\"; } catch (groovy.lang.MissingPropertyException mpe) { echo \"Groovy TEST_VAR_NAME missing: ${mpe.getMessage()}\"; } ; echo \"env.TEST_VAR_NAME='${env.TEST_VAR_NAME}'\"", true)); + + WorkflowRun b6 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@stable", b6); + r.assertLogContains("something reliable", b6); + r.assertLogContains("Groovy TEST_VAR_NAME missing", b6); + r.assertLogContains("env.TEST_VAR_NAME='null'", b6); + } @Issue("JENKINS-43802") From 3cb60c0d30e0792644d7562046b1b5690edd9607 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 10 Oct 2022 19:43:08 +0200 Subject: [PATCH 52/70] SCMSourceRetrieverTest: drop separate checkDefaultVersion_inline_allowVersionEnvvar_builtIn() [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 74 ++----------------- 1 file changed, 8 insertions(+), 66 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 85415ea2..36218551 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1207,82 +1207,24 @@ public class SCMSourceRetrieverTest { r.jenkins.reload(); r.waitUntilNoActivity(); - WorkflowRun b6 = r.buildAndAssertSuccess(p1); - r.assertLogContains("Loading library branchylib@stable", b6); - r.assertLogContains("something reliable", b6); - r.assertLogContains("Groovy TEST_VAR_NAME missing", b6); - r.assertLogContains("env.TEST_VAR_NAME='null'", b6); - - Thread.sleep(160000); - } - - @Issue("JENKINS-69731") - @Test public void checkDefaultVersion_inline_allowVersionEnvvar_builtIn() throws Exception { - // Test that @Library('branchylib@${env.TEST_VAR_NAME}') - // is resolved with the TEST_VAR_NAME="feature" in the - // "built-in" node environment settings. - - // Do not let caller-provided BRANCH_NAME interfere here - assumeFalse("An externally provided TEST_VAR_NAME envvar interferes with tested logic", - System.getenv("TEST_VAR_NAME") != null); - - // First apply the "built-in node", then touch Jenkins jobs: - Computer builtInComputer = r.jenkins.toComputer(); - Node builtInNode = builtInComputer.getNode(); - builtInNode.getNodeProperties().add(new EnvironmentVariablesNodeProperty(new EnvironmentVariablesNodeProperty.Entry("TEST_VAR_NAME", "stable"))); - r.jenkins.save(); - builtInNode.save(); - - System.out.println("[DEBUG] Restart the 'built-in' Computer connection to clear its cachedEnvironment and recognize added envvar"); - builtInComputer.setTemporarilyOffline(true, new OfflineCause.ByCLI("Restart built-in to reread envvars config")); - builtInComputer.disconnect(new OfflineCause.ByCLI("Restart built-in to reread envvars config")); - builtInComputer.waitUntilOffline(); - r.waitUntilNoActivity(); - builtInComputer.getChannel().close(); - Thread.sleep(3000); - r.jenkins.reload(); - builtInComputer.setTemporarilyOffline(false, null); - builtInComputer.connect(true); - builtInComputer.waitUntilOnline(); - r.waitUntilNoActivity(); - // Feed it to Java reflection, to clear the internal cache... Field ccc = Computer.class.getDeclaredField("cachedEnvironment"); ccc.setAccessible(true); ccc.set(builtInComputer, null); - System.out.println("[DEBUG] builtIn node env: " + builtInComputer.getEnvironment()); - - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "stable"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something reliable'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); - LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); - lc.setDefaultVersion("master"); - lc.setIncludeInChangesets(false); - lc.setAllowVersionOverride(true); - lc.setAllowVersionEnvvar(true); - lc.setTraceDefaultedVersion(true); - GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); - - WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); - p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@${env.TEST_VAR_NAME}') import myecho; myecho(); try { echo \"Groovy TEST_VAR_NAME='${TEST_VAR_NAME}'\"; } catch (groovy.lang.MissingPropertyException mpe) { echo \"Groovy TEST_VAR_NAME missing: ${mpe.getMessage()}\"; } ; echo \"env.TEST_VAR_NAME='${env.TEST_VAR_NAME}'\"", true)); - + // Still, "built-in" node's envvars are ignored (technically they - + // now that the cache is cleared - reload global config values for + // the "MasterComputer" as its own). Checked on standalone instance + // configured interactively that even a complete Jenkins restart + // does not let configured "built-in" node envvars become recognized. + // Loosely related to https://github.com/jenkinsci/jenkins/pull/1728 + // So we keep this test here as a way to notice if core functionality + // ever changes. WorkflowRun b6 = r.buildAndAssertSuccess(p1); r.assertLogContains("Loading library branchylib@stable", b6); r.assertLogContains("something reliable", b6); r.assertLogContains("Groovy TEST_VAR_NAME missing", b6); r.assertLogContains("env.TEST_VAR_NAME='null'", b6); - } @Issue("JENKINS-43802") From ba0112844d185b6b3bc41be64aac4eefd43b48d1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Oct 2022 19:50:35 +0200 Subject: [PATCH 53/70] SCMSourceRetrieverTest: add checkDefaultVersion_singleBranch_BRANCH_NAME_after_staticStrings() for some TDD [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 36218551..fa434ee9 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -908,6 +908,143 @@ public class SCMSourceRetrieverTest { r.assertLogContains("something very special", b0); } + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_singleBranch_BRANCH_NAME_after_staticStrings() throws Exception { + // Test that using @Library('branchylib@static') + // in one build of a job definition, and then a + // @Library('branchylib@${BRANCH_NAME}') next, + // both behave well. + // For context see e.g. WorkflowJob.getSCMs(): + // https://github.com/jonsten/workflow-job-plugin/blob/master/src/main/java/org/jenkinsci/plugins/workflow/job/WorkflowJob.java#L539 + // https://issues.jenkins.io/browse/JENKINS-40255 + // how it looks at a history of EARLIER builds + // (preferring successes) and not at the current + // job definition. + // Note: being a piece of test-driven development, + // this test does not fail as soon as it gets an + // "unexpected" log message (so far expected due + // to the bug being hunted), but counts the faults + // and asserts in the end whether there were none. + assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + System.getenv("BRANCH_NAME") != null); + + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "stable"); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something reliable'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(false); + lc.setAllowBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2.init(); + sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); + sampleRepo2.write("Jenkinsfile-static", "@Library('branchylib@stable') import myecho; myecho()"); + sampleRepo2.git("add", "Jenkinsfile*"); + sampleRepo2.git("commit", "--message=init"); + sampleRepo2.git("branch", "feature"); + sampleRepo2.git("branch", "bogus"); + + // Get a non-default branch loaded for this single-branch build: + GitSCM gitSCM = new GitSCM( + GitSCM.createRepoList(sampleRepo2.toString(), null), + Collections.singletonList(new BranchSpec("*/feature")), + null, null, Collections.emptyList()); + + sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); + + // First run a job definition with a fixed library version, + // e.g. like a custom Replay might in the field, or before + // redefining an "inline" pipeline to one coming from SCM. + // Pepper job history with successes and faults: + long failCount = 0; + WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@stable') import myecho; myecho()", true)); + WorkflowRun b1 = r.buildAndAssertStatus(Result.FAILURE, p1); + r.assertLogContains("ERROR: Version override not permitted for library branchylib", b1); + r.assertLogContains("WorkflowScript: Loading libraries failed", b1); + + // Use default version: + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib') import myecho; myecho()", true)); + WorkflowRun b2 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@master", b2); + r.assertLogContains("something special", b2); + + // Now redefine the same job to come from SCM and use a + // run-time resolved library version (WorkflowJob getSCMs + // behavior should not be a problem): + p1.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); + WorkflowRun b3 = r.buildAndAssertSuccess(p1); + try { + // In case of misbehavior this loads "master" version: + r.assertLogContains("Loading library branchylib@feature", b3); + r.assertLogContains("something very special", b3); + } catch (AssertionError ae) { + failCount++; + // Make sure it was not some other problem: + r.assertLogContains("Loading library branchylib@master", b3); + r.assertLogContains("something special", b3); + } + + // Override with a static version: + lc.setAllowVersionOverride(true); + p1.setDefinition(new CpsFlowDefinition("@Library('branchylib@stable') import myecho; myecho()", true)); + WorkflowRun b4 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@stable", b4); + r.assertLogContains("something reliable", b4); + + // Dynamic version again: + p1.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); + WorkflowRun b5 = r.buildAndAssertSuccess(p1); + try { + // In case of misbehavior this loads "stable" version: + r.assertLogContains("Loading library branchylib@feature", b5); + r.assertLogContains("something very special", b5); + } catch (AssertionError ae) { + failCount++; + // Make sure it was not some other problem: + r.assertLogContains("Loading library branchylib@stable", b5); + r.assertLogContains("something reliable", b5); + } + + // SCM source pointing at static version + p1.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile-static")); + WorkflowRun b6 = r.buildAndAssertSuccess(p1); + r.assertLogContains("Loading library branchylib@stable", b6); + r.assertLogContains("something reliable", b6); + + // Dynamic version again; seems with the change of filename it works okay: + p1.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); + WorkflowRun b7 = r.buildAndAssertSuccess(p1); + try { + // In case of misbehavior this loads "stable" version: + r.assertLogContains("Loading library branchylib@feature", b7); + r.assertLogContains("something very special", b7); + } catch (AssertionError ae) { + failCount++; + // Make sure it was not some other problem: + r.assertLogContains("Loading library branchylib@stable", b7); + r.assertLogContains("something reliable", b7); + } + + assertEquals("All BRANCH_NAME resolutions are expected to checkout feature",0, failCount); + } + @Issue("JENKINS-69731") @Test public void checkDefaultVersion_singleBranch_BRANCH_NAME_doubleQuotes() throws Exception { // Similar to above, the goal of this test is to From 863b17063380b722c5b1ef44c62ea4da2f89e90b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 13 Oct 2022 08:54:14 +0200 Subject: [PATCH 54/70] LibraryConfiguration: defaultedVersionSCM(): avoid WorkflowJob.getSCMs(), it does not give the answers we are looking for --- .../workflow/libs/LibraryConfiguration.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 62619bfd..a6373bd7 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -37,7 +37,9 @@ import hudson.model.TaskListener; import hudson.scm.SCM; import hudson.util.FormValidation; +import jenkins.branch.Branch; import jenkins.model.Jenkins; +import jenkins.scm.api.SCMSourceOwner; import org.jenkinsci.plugins.workflow.flow.FlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; @@ -274,8 +276,12 @@ private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener } if (scm0 == null) { - Collection wjscms = (Collection) ((WorkflowJob)runParent).getSCMs();; - if (wjscms.isEmpty()) { + // WARNING: the WorkflowJob.getSCMs() does not return SCMs + // associated with the current build configuration, but + // rather those that were associated with previous runs + // for different branches (with lastSuccessfulBuild on top)! + BranchJobProperty property = ((WorkflowJob)runParent).getProperty(BranchJobProperty.class); + if (property == null) { // && !(runParent.getParent() instanceof SCMSourceOwner)) { if (logger != null) { logger.println("defaultedVersion(): " + "WorkflowJob '" + @@ -283,25 +289,28 @@ private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener "' is not associated with any SCMs"); } } else { + Branch pipelineBranch = property.getBranch(); if (logger != null) { logger.println("defaultedVersion(): " + "inspecting WorkflowJob '" + runParent.getClass().getName() + "' for SCMs it might use"); } - for (SCM scmN : wjscms) { + if (pipelineBranch != null) { if (logger != null) { - logger.println("defaultedVersion(): inspecting SCM '" + - scmN.getClass().getName() + - "': " + scmN.toString()); + logger.println("defaultedVersion(): inspecting pipelineBranch '" + + pipelineBranch.getClass().getName() + + "': " + pipelineBranch.toString()); } - if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { +/* + if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { // The best we can do here is accept // the first seen SCM (with branch // support which we know how to query). scm0 = scmN; break; } +*/ } } } From dea552f5aa5128f06a801ee495c654d222725a56 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 13 Oct 2022 10:00:17 +0200 Subject: [PATCH 55/70] LibraryConfiguration: defaultedVersionSCM(): drop fallback WorkflowJob handling --- .../workflow/libs/LibraryConfiguration.java | 46 +++---------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index a6373bd7..25cd50a2 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -275,45 +275,13 @@ private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener } } - if (scm0 == null) { - // WARNING: the WorkflowJob.getSCMs() does not return SCMs - // associated with the current build configuration, but - // rather those that were associated with previous runs - // for different branches (with lastSuccessfulBuild on top)! - BranchJobProperty property = ((WorkflowJob)runParent).getProperty(BranchJobProperty.class); - if (property == null) { // && !(runParent.getParent() instanceof SCMSourceOwner)) { - if (logger != null) { - logger.println("defaultedVersion(): " + - "WorkflowJob '" + - runParent.getClass().getName() + - "' is not associated with any SCMs"); - } - } else { - Branch pipelineBranch = property.getBranch(); - if (logger != null) { - logger.println("defaultedVersion(): " + - "inspecting WorkflowJob '" + - runParent.getClass().getName() + - "' for SCMs it might use"); - } - if (pipelineBranch != null) { - if (logger != null) { - logger.println("defaultedVersion(): inspecting pipelineBranch '" + - pipelineBranch.getClass().getName() + - "': " + pipelineBranch.toString()); - } -/* - if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { - // The best we can do here is accept - // the first seen SCM (with branch - // support which we know how to query). - scm0 = scmN; - break; - } -*/ - } - } - } + // WARNING: the WorkflowJob.getSCMs() does not return SCMs + // associated with the current build configuration, but + // rather those that were associated with previous runs + // for different branches (with lastSuccessfulBuild on + // top), so we should not fall back looking at those! + // And we inspect (MBP) BranchJobProperty early in the + // main defaultedVersion() method. } // if (runParent != null && runParent instanceof WorkflowJob) // If no hit with runParent, look into the run itself: From 70b029cf9aa4181368f2cdad127b854297ddfc2a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Oct 2022 16:03:57 +0200 Subject: [PATCH 56/70] LibraryConfiguration/config.jelly: update comment for allowVersionOverride field --- .../plugins/workflow/libs/LibraryConfiguration/config.jelly | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly index addb3ca8..9520bfc6 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly @@ -34,7 +34,7 @@ THE SOFTWARE. - + From 786092de55d97efc357eaa7aea88ff041d839103 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Oct 2022 16:44:28 +0200 Subject: [PATCH 57/70] SCMSourceRetrieverTest: refactor baseline with sampleRepo1ContentMaster*() helpers --- .../workflow/libs/SCMSourceRetrieverTest.java | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index fa434ee9..c71334fc 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -117,12 +117,35 @@ public class SCMSourceRetrieverTest { @Rule public GitSampleRepoRule sampleRepo2 = new GitSampleRepoRule(); @Rule public SubversionSampleRepoRule sampleRepoSvn = new SubversionSampleRepoRule(); - @Issue("JENKINS-40408") - @Test public void lease() throws Exception { + // Repetitive helpers for test cases + private void sampleRepo1ContentMaster() throws Exception { + sampleRepo1ContentMaster(null); + } + + private void sampleRepo1ContentMaster(String subdir) throws Exception { + if (subdir != null && !(subdir.endsWith("/"))) subdir += "/"; + if (subdir == null) subdir = ""; sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); + sampleRepo.write(subdir + "vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", subdir + "vars"); sampleRepo.git("commit", "--message=init"); + } + + private void sampleRepo1ContentMasterAddLibraryCommit() throws Exception { + sampleRepo1ContentMasterAddLibraryCommit(null); + } + + private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exception { + if (subdir != null && !(subdir.endsWith("/"))) subdir += "/"; + if (subdir == null) subdir = ""; + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something even more special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=library_commit"); + } + + @Issue("JENKINS-40408") + @Test public void lease() throws Exception { + sampleRepo1ContentMaster(); GlobalLibraries.get().setLibraries(Collections.singletonList( new LibraryConfiguration("echoing", new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true))))); @@ -143,10 +166,7 @@ public class SCMSourceRetrieverTest { @Issue("JENKINS-41497") @Test public void includeChanges() throws Exception { - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMaster(); GlobalLibraries.get().setLibraries(Collections.singletonList( new LibraryConfiguration("include_changes", new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true))))); @@ -157,9 +177,7 @@ public class SCMSourceRetrieverTest { WorkflowRun a = r.buildAndAssertSuccess(p); r.assertLogContains("something special", a); } - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something even more special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=library_commit"); + sampleRepo1ContentMasterAddLibraryCommit(); try (WorkspaceList.Lease lease = r.jenkins.toComputer().getWorkspaceList().acquire(base)) { WorkflowRun b = r.buildAndAssertSuccess(p); List> changeSets = b.getChangeSets(); @@ -177,10 +195,7 @@ public class SCMSourceRetrieverTest { @Issue("JENKINS-41497") @Test public void dontIncludeChanges() throws Exception { - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMaster(); LibraryConfiguration lc = new LibraryConfiguration("dont_include_changes", new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true))); lc.setIncludeInChangesets(false); GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); @@ -190,9 +205,7 @@ public class SCMSourceRetrieverTest { try (WorkspaceList.Lease lease = r.jenkins.toComputer().getWorkspaceList().acquire(base)) { WorkflowRun a = r.buildAndAssertSuccess(p); } - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something even more special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=library_commit"); + sampleRepo1ContentMasterAddLibraryCommit(); try (WorkspaceList.Lease lease = r.jenkins.toComputer().getWorkspaceList().acquire(base)) { WorkflowRun b = r.buildAndAssertSuccess(p); List> changeSets = b.getChangeSets(); @@ -203,10 +216,7 @@ public class SCMSourceRetrieverTest { @Issue("JENKINS-38609") @Test public void libraryPath() throws Exception { - sampleRepo.init(); - sampleRepo.write("sub/path/vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "sub"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMaster("sub/path"); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("root_sub_path", scm); lc.setIncludeInChangesets(false); @@ -220,10 +230,7 @@ public class SCMSourceRetrieverTest { @Issue("JENKINS-38609") @Test public void libraryPathSecurity() throws Exception { - sampleRepo.init(); - sampleRepo.write("sub/path/vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "sub"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMaster("sub/path"); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("root_sub_path", scm); lc.setIncludeInChangesets(false); @@ -1471,10 +1478,7 @@ public static class BasicSCMSource extends SCMSource { @Issue("JENKINS-66629") @Test public void renameDeletesOldLibsWorkspace() throws Exception { - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMaster(); GlobalLibraries.get().setLibraries(Collections.singletonList( new LibraryConfiguration("delete_removes_libs_workspace", new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true))))); @@ -1495,10 +1499,7 @@ public static class BasicSCMSource extends SCMSource { @Issue("JENKINS-66629") @Test public void deleteRemovesLibsWorkspace() throws Exception { - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMaster(); GlobalLibraries.get().setLibraries(Collections.singletonList( new LibraryConfiguration("delete_removes_libs_workspace", new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true))))); From be0c051e4f046acb4af8f257a6cea72803d3f481 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Oct 2022 16:47:42 +0200 Subject: [PATCH 58/70] SCMSourceRetrieverTest: refactor new tests with sampleRepo1Content*() helpers [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 311 +++++++----------- 1 file changed, 126 insertions(+), 185 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index c71334fc..dae9eb7d 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -117,7 +117,7 @@ public class SCMSourceRetrieverTest { @Rule public GitSampleRepoRule sampleRepo2 = new GitSampleRepoRule(); @Rule public SubversionSampleRepoRule sampleRepoSvn = new SubversionSampleRepoRule(); - // Repetitive helpers for test cases + // Repetitive helpers for test cases dealing with @Issue("JENKINS-69731") and others private void sampleRepo1ContentMaster() throws Exception { sampleRepo1ContentMaster(null); } @@ -143,6 +143,110 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce sampleRepo.git("commit", "--message=library_commit"); } + private void sampleRepo1ContentMasterFeature() throws Exception { + sampleRepo1ContentMasterFeature(null); + } + + private void sampleRepo1ContentMasterFeature(String subdir) throws Exception { + if (subdir != null && !(subdir.endsWith("/"))) subdir += "/"; + if (subdir == null) subdir = ""; + sampleRepo.init(); + sampleRepo.write(subdir + "vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", subdir + "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write(subdir + "vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", subdir + "vars"); + sampleRepo.git("commit", "--message=init"); + } + + private void sampleRepo1ContentMasterFeatureStable() throws Exception { + sampleRepo1ContentMasterFeatureStable(null); + } + + private void sampleRepo1ContentMasterFeatureStable(String subdir) throws Exception { + if (subdir != null && !(subdir.endsWith("/"))) subdir += "/"; + if (subdir == null) subdir = ""; + sampleRepo.init(); + sampleRepo.write(subdir + "vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", subdir + "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "feature"); + sampleRepo.write(subdir + "vars/myecho.groovy", "def call() {echo 'something very special'}"); + sampleRepo.git("add", subdir + "vars"); + sampleRepo.git("commit", "--message=init"); + sampleRepo.git("checkout", "-b", "stable"); + sampleRepo.write(subdir + "vars/myecho.groovy", "def call() {echo 'something reliable'}"); + sampleRepo.git("add", subdir + "vars"); + sampleRepo.git("commit", "--message=init"); + } + + private void sampleRepo2ContentMasterFeature() throws Exception { + sampleRepo2ContentMasterFeature(null); + } + + private void sampleRepo2ContentMasterFeature(String subdir) throws Exception { + if (subdir != null && !(subdir.endsWith("/"))) subdir += "/"; + if (subdir == null) subdir = ""; + sampleRepo2.init(); + sampleRepo2.write(subdir + "vars/myecho2.groovy", "def call() {echo 'something weird'}"); + sampleRepo2.git("add", subdir + "vars"); + sampleRepo2.git("commit", "--message=init"); + sampleRepo2.git("checkout", "-b", "feature"); + sampleRepo2.write(subdir + "vars/myecho2.groovy", "def call() {echo 'something wonderful'}"); + sampleRepo2.git("add", subdir + "vars"); + sampleRepo2.git("commit", "--message=init"); + } + + private void sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME() throws Exception { + sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(null, false); + } + + private void sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(Boolean addJenkinsfileStatic) throws Exception { + sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(null, addJenkinsfileStatic); + } + + private void sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(String subdir) throws Exception { + sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(subdir, false); + } + + private void sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(String subdir, Boolean addJenkinsfileStatic) throws Exception { + if (subdir != null && !(subdir.endsWith("/"))) subdir += "/"; + if (subdir == null) subdir = ""; + sampleRepo2.init(); + sampleRepo2.write(subdir + "Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); + if (addJenkinsfileStatic) { + sampleRepo2.write("Jenkinsfile-static", "@Library('branchylib@stable') import myecho; myecho()"); + sampleRepo2.git("add", subdir + "Jenkinsfile*"); + } else { + sampleRepo2.git("add", subdir + "Jenkinsfile"); + } + sampleRepo2.git("commit", "--message=init"); + sampleRepo2.git("branch", "feature"); + sampleRepo2.git("branch", "bogus"); + } + + private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings() throws Exception { + sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(null); + } + + private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String subdir) throws Exception { + if (subdir != null && !(subdir.endsWith("/"))) subdir += "/"; + if (subdir == null) subdir = ""; + sampleRepo2.init(); + sampleRepo2.write(subdir + "Jenkinsfile", "@Library('branchylib@master') import myecho; myecho()"); + sampleRepo2.git("add", subdir + "Jenkinsfile"); + sampleRepo2.git("commit", "--message=master"); + sampleRepo2.git("checkout", "-b", "feature"); + sampleRepo2.write(subdir + "Jenkinsfile", "@Library('branchylib@feature') import myecho; myecho()"); + sampleRepo2.git("add", subdir + "Jenkinsfile"); + sampleRepo2.git("commit", "--message=feature"); + sampleRepo2.git("checkout", "-b", "bogus"); + sampleRepo2.write(subdir + "Jenkinsfile", "@Library('branchylib@bogus') import myecho; myecho()"); + sampleRepo2.git("add", subdir + "Jenkinsfile"); + sampleRepo2.git("commit", "--message=bogus"); + } + @Issue("JENKINS-40408") @Test public void lease() throws Exception { sampleRepo1ContentMaster(); @@ -255,14 +359,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce @Issue("JENKINS-69731") @Test public void checkDefaultVersion_inline_staticStrings() throws Exception { - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -312,14 +409,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", System.getenv("BRANCH_NAME") != null); - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -328,14 +418,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce lc.setAllowBRANCH_NAME(true); lc.setTraceDefaultedVersion(true); - sampleRepo2.init(); - sampleRepo2.write("vars/myecho2.groovy", "def call() {echo 'something weird'}"); - sampleRepo2.git("add", "vars"); - sampleRepo2.git("commit", "--message=init"); - sampleRepo2.git("checkout", "-b", "feature"); - sampleRepo2.write("vars/myecho2.groovy", "def call() {echo 'something wonderful'}"); - sampleRepo2.git("add", "vars"); - sampleRepo2.git("commit", "--message=init"); + sampleRepo2ContentMasterFeature(); SCMSourceRetriever scm2 = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo2.toString(), "", "*", "", true)); LibraryConfiguration lc2 = new LibraryConfiguration("branchylib2", scm2); lc2.setDefaultVersion("master"); @@ -379,14 +462,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // does not interfere with tested logic, since MBP // sets the value for launched builds. - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -397,12 +473,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // Inspired in part by tests like // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 - sampleRepo2.init(); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=init"); - sampleRepo2.git("branch", "feature"); - sampleRepo2.git("branch", "bogus"); + sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(); WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo2.toString(), "", "*", "", false)); @@ -449,14 +520,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // preclude fixed branch names (they should work), // like @Library('branchylib@master') - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -468,19 +532,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // Inspired in part by tests like // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 - sampleRepo2.init(); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@master') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=master"); - sampleRepo2.git("checkout", "-b", "feature"); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@feature') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=feature"); - sampleRepo2.git("checkout", "-b", "bogus"); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@bogus') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=bogus"); - + sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(); WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo2.toString(), "", "*", "", false)); mbp.getSourcesList().add(branchSource); @@ -526,14 +578,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // (not treated as a "version override" for funny // branch name that is literally "${BRANCH_NAME}"). - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -545,13 +590,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // Inspired in part by tests like // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 - sampleRepo2.init(); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=init"); - sampleRepo2.git("branch", "feature"); - sampleRepo2.git("branch", "bogus"); - + sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(); WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo2.toString(), "", "*", "", false)); mbp.getSourcesList().add(branchSource); @@ -596,14 +635,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // for MBP with "Single repository and branch" as // the SCM source. - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -615,19 +647,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // Inspired in part by tests like // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 - sampleRepo2.init(); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@master') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=master"); - sampleRepo2.git("checkout", "-b", "feature"); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@feature') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=feature"); - sampleRepo2.git("checkout", "-b", "bogus"); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@bogus') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=bogus"); - + sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(); WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); GitSCM gitSCM = new GitSCM( GitSCM.createRepoList(sampleRepo2.toString(), null), @@ -703,14 +723,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // also for MBP with "Single repository and branch" as // the SCM source. - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -722,13 +735,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // Inspired in part by tests like // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 - sampleRepo2.init(); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=init"); - sampleRepo2.git("branch", "feature"); - sampleRepo2.git("branch", "bogus"); - + sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(); WorkflowMultiBranchProject mbp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "mbp"); GitSCM gitSCM = new GitSCM( GitSCM.createRepoList(sampleRepo2.toString(), null), @@ -815,14 +822,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // like @Library('branchylib@master') when used for // a simple "Pipeline" job with static SCM source. - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -834,19 +834,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // Inspired in part by tests like // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 - sampleRepo2.init(); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@master') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=master"); - sampleRepo2.git("checkout", "-b", "feature"); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@feature') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=feature"); - sampleRepo2.git("checkout", "-b", "bogus"); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@bogus') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=bogus"); - + sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(); //GitSCM gitSCM = new GitSCM(sampleRepo2.toString()); GitSCM gitSCM = new GitSCM( GitSCM.createRepoList(sampleRepo2.toString(), null), @@ -873,14 +861,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", System.getenv("BRANCH_NAME") != null); - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -892,12 +873,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // Inspired in part by tests like // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 - sampleRepo2.init(); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile"); - sampleRepo2.git("commit", "--message=init"); - sampleRepo2.git("branch", "feature"); - sampleRepo2.git("branch", "bogus"); + sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(); // Get a non-default branch loaded for this single-branch build: GitSCM gitSCM = new GitSCM( @@ -935,18 +911,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", System.getenv("BRANCH_NAME") != null); - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "stable"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something reliable'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeatureStable(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -958,13 +923,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce // Inspired in part by tests like // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 - sampleRepo2.init(); - sampleRepo2.write("Jenkinsfile", "@Library('branchylib@${BRANCH_NAME}') import myecho; myecho()"); - sampleRepo2.write("Jenkinsfile-static", "@Library('branchylib@stable') import myecho; myecho()"); - sampleRepo2.git("add", "Jenkinsfile*"); - sampleRepo2.git("commit", "--message=init"); - sampleRepo2.git("branch", "feature"); - sampleRepo2.git("branch", "bogus"); + sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(true); // Get a non-default branch loaded for this single-branch build: GitSCM gitSCM = new GitSCM( @@ -1069,14 +1028,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", System.getenv("BRANCH_NAME") != null); - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeature(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); @@ -1138,18 +1090,7 @@ private void sampleRepo1ContentMasterAddLibraryCommit(String subdir) throws Exce assumeFalse("An externally provided TEST_VAR_NAME envvar interferes with tested logic", System.getenv("TEST_VAR_NAME") != null); - sampleRepo.init(); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "feature"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something very special'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); - sampleRepo.git("checkout", "-b", "stable"); - sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something reliable'}"); - sampleRepo.git("add", "vars"); - sampleRepo.git("commit", "--message=init"); + sampleRepo1ContentMasterFeatureStable(); SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); lc.setDefaultVersion("master"); From d67c2d1fd2c53b7059b99f3fb9eafd6ebf174bf0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Oct 2022 20:25:21 +0200 Subject: [PATCH 59/70] SCMSourceRetrieverTest: clarify in surefire log that checkoutDirectoriesAreNotReusedByDifferentScms() is skipped on Windows --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index dae9eb7d..76ac3252 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1496,7 +1496,9 @@ public static class BasicSCMSource extends SCMSource { @Issue("SECURITY-2463") @Test public void checkoutDirectoriesAreNotReusedByDifferentScms() throws Exception { - assumeFalse(Functions.isWindows()); // Checkout hook is not cross-platform. + assumeFalse("checkoutDirectoriesAreNotReusedByDifferentScms() is " + + "skipped on Windows: Checkout hook is not cross-platform", + Functions.isWindows()); sampleRepo.init(); sampleRepo.write("vars/foo.groovy", "def call() { echo('using global lib') }"); sampleRepo.git("add", "vars"); From db744312d84ba0eb6ad5874bf533e2d9749c989d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Oct 2022 21:18:36 +0200 Subject: [PATCH 60/70] SCMSourceRetrieverTest.java: cleanup imports --- .../plugins/workflow/libs/SCMSourceRetrieverTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 76ac3252..7fe55e09 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -42,7 +42,6 @@ import hudson.model.TaskListener; import hudson.plugins.git.GitSCM; import hudson.plugins.git.BranchSpec; -import hudson.remoting.VirtualChannel; import hudson.scm.ChangeLogSet; import hudson.scm.SCM; import hudson.slaves.EnvironmentVariablesNodeProperty; @@ -50,7 +49,7 @@ import hudson.slaves.NodePropertyDescriptor; import hudson.slaves.OfflineCause; import hudson.slaves.WorkspaceList; -import hudson.slaves.WorkspaceList; + import java.io.File; import java.io.IOException; import java.lang.reflect.Field; @@ -66,7 +65,6 @@ import jenkins.branch.BranchProperty; import jenkins.branch.BranchSource; import jenkins.branch.DefaultBranchPropertyStrategy; -import jenkins.model.Jenkins; import jenkins.plugins.git.GitSCMSource; import jenkins.plugins.git.GitSampleRepoRule; import jenkins.scm.api.SCMHead; From 812d12d0bf7f7f03e876d6e8ec05df96d4765374 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Oct 2022 21:18:58 +0200 Subject: [PATCH 61/70] SCMSourceRetrieverTest.java: add checkDefaultVersion_singleBranch_BRANCH_NAME_lightweight() [JENKINS-69731] --- .../workflow/libs/SCMSourceRetrieverTest.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 7fe55e09..08ecb49f 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -1079,6 +1079,46 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub } } + @Issue("JENKINS-69731") + @Test public void checkDefaultVersion_singleBranch_BRANCH_NAME_lightweight() throws Exception { + // Test that lightweight checkouts from SCM allow + // @Library('branchylib@${BRANCH_NAME}') to see + // sufficient SCM context to determine the branch. + assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + System.getenv("BRANCH_NAME") != null); + + sampleRepo1ContentMasterFeature(); + SCMSourceRetriever scm = new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)); + LibraryConfiguration lc = new LibraryConfiguration("branchylib", scm); + lc.setDefaultVersion("master"); + lc.setIncludeInChangesets(false); + lc.setAllowVersionOverride(false); + lc.setAllowBRANCH_NAME(true); + lc.setTraceDefaultedVersion(true); + GlobalLibraries.get().setLibraries(Collections.singletonList(lc)); + + // Inspired in part by tests like + // https://github.com/jenkinsci/workflow-multibranch-plugin/blob/master/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java#L132 + sampleRepo2ContentSameMasterFeatureBogus_BRANCH_NAME(); + + // Get a non-default branch loaded for this single-branch build: + GitSCM gitSCM = new GitSCM( + GitSCM.createRepoList(sampleRepo2.toString(), null), + Collections.singletonList(new BranchSpec("*/feature")), + null, null, Collections.emptyList()); + + CpsScmFlowDefinition csfd = new CpsScmFlowDefinition(gitSCM, "Jenkinsfile"); + csfd.setLightweight(true); + WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); + p0.setDefinition(csfd); + sampleRepo2.notifyCommit(r); + r.waitUntilNoActivity(); + + WorkflowRun b0 = r.buildAndAssertSuccess(p0); + r.assertLogContains("Loading library branchylib@feature", b0); + r.assertLogContains("something very special", b0); + } + @Issue("JENKINS-69731") @Test public void checkDefaultVersion_inline_allowVersionEnvvar() throws Exception { // Test that @Library('branchylib@${env.TEST_VAR_NAME}') From 6ab574a8766a883da12da388c319ed404edb9eb2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Oct 2022 21:20:01 +0200 Subject: [PATCH 62/70] LibraryConfiguration.java: defaultedVersionSCM(): avoid get()ing from empty array [JENKINS-69731] --- .../plugins/workflow/libs/LibraryConfiguration.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 25cd50a2..3970b006 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -311,7 +311,17 @@ private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener } try { WorkflowRun wfRun = (WorkflowRun) run; - scm0 = wfRun.getSCMs().get(0); + Collection wfrscms = (Collection) wfRun.getSCMs(); + if (wfrscms.isEmpty()) { + if (logger != null) { + logger.println("defaultedVersion(): WorkflowRun '" + + wfRun.getClass().getName() + + "' is not associated with any SCMs"); + } + } else { + // Somewhat a guess in the dark... + scm0 = wfRun.getSCMs().get(0); + } } catch (Exception x) { if (logger != null) { logger.println("defaultedVersion(): " + From 2d098a5ed9b7562bc3186c3be8addc17f020c5ae Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Oct 2022 21:25:11 +0200 Subject: [PATCH 63/70] LibraryConfiguration.java: defaultedVersionSCM(): check for nuances of CpsScmFlowDefinition [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 71 +++++++++++++------ 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 3970b006..ca79e39e 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -40,6 +40,7 @@ import jenkins.branch.Branch; import jenkins.model.Jenkins; import jenkins.scm.api.SCMSourceOwner; +import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition; import org.jenkinsci.plugins.workflow.flow.FlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; @@ -245,31 +246,61 @@ private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener } FlowDefinition fd = ((WorkflowJob)runParent).getDefinition(); if (fd != null) { - Collection fdscms = (Collection) fd.getSCMs(); - if (fdscms.isEmpty()) { + if (fd instanceof CpsScmFlowDefinition) { + CpsScmFlowDefinition csfd = (CpsScmFlowDefinition)fd; if (logger != null) { - logger.println("defaultedVersion(): FlowDefinition '" + - fd.getClass().getName() + - "' is not associated with any SCMs"); + logger.println("defaultedVersion(): inspecting CpsScmFlowDefinition '" + + csfd.getClass().getName() + + "' for an SCM it might use (with" + + (csfd.isLightweight() ? "" : "out") + + " lightweight checkout)"); } - } else { - if (logger != null) { - logger.println("defaultedVersion(): inspecting FlowDefinition '" + - fd.getClass().getName() + - "' for SCMs it might use"); + scm0 = csfd.getScm(); + + if (scm0 == null) { + if (logger != null) { + logger.println("defaultedVersion(): CpsScmFlowDefinition '" + + csfd.getClass().getName() + + "' is not associated with an SCM"); + } + } else if (!("hudson.plugins.git.GitSCM".equals(scm0.getClass().getName()))) { + if (logger != null) { + logger.println("defaultedVersion(): CpsScmFlowDefinition '" + + csfd.getClass().getName() + + "' is associated with an SCM we can not query: " + + scm0.toString()); + } + scm0 = null; } - for (SCM scmN : fdscms) { + } + + if (scm0 == null) { + Collection fdscms = (Collection) fd.getSCMs(); + if (fdscms.isEmpty()) { + if (logger != null) { + logger.println("defaultedVersion(): generic FlowDefinition '" + + fd.getClass().getName() + + "' is not associated with any SCMs"); + } + } else { if (logger != null) { - logger.println("defaultedVersion(): inspecting SCM '" + - scmN.getClass().getName() + - "': " + scmN.toString()); + logger.println("defaultedVersion(): inspecting generic FlowDefinition '" + + fd.getClass().getName() + + "' for SCMs it might use"); } - if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { - // The best we can do here is accept - // the first seen SCM (with branch - // support which we know how to query). - scm0 = scmN; - break; + for (SCM scmN : fdscms) { + if (logger != null) { + logger.println("defaultedVersion(): inspecting SCM '" + + scmN.getClass().getName() + + "': " + scmN.toString()); + } + if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { + // The best we can do here is accept + // the first seen SCM (with branch + // support which we know how to query). + scm0 = scmN; + break; + } } } } From 6379716491f0eab86b1fdd77148ef4de783df52e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 18 Oct 2022 16:15:33 +0200 Subject: [PATCH 64/70] LibraryConfiguration.java: further refactor defaultedVersionSCM() to separate extractDefaultedVersionGitSCM() hack in a scalable manner [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 270 ++++++++++-------- 1 file changed, 151 insertions(+), 119 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index ca79e39e..5587c5f6 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -224,14 +224,157 @@ public LibraryCachingConfiguration getCachingConfiguration() { * and have those SCMs tell us what we want here. But that's a job * for another day. */ - private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener listener, PrintStream logger) { + private boolean isDefaultedVersionSCMSupported(SCM scm) { + // Return "true" if we can extractDefaultedVersionSCM() from this SCM + return ("hudson.plugins.git.GitSCM".equals(scm.getClass().getName())); + } + + + private String extractDefaultedVersionGitSCM(@NonNull SCM scm, @NonNull Run run, @NonNull TaskListener listener, PrintStream logger) { + if (!("hudson.plugins.git.GitSCM".equals(scm.getClass().getName()))) + return null; + String runVersion = null; - Item runParent = run.getParent(); + // Avoid importing GitSCM and so requiring that + // it is always installed even if not used by + // particular Jenkins deployment (using e.g. + // SVN, Gerritt, etc.). Our aim is to query this: + // runVersion = scm0.getBranches().first().getExpandedName(run.getEnvironment(listener)); + // https://mkyong.com/java/how-to-use-reflection-to-call-java-method-at-runtime/ + Class noparams[] = {}; + Class[] paramEnvVars = new Class[1]; + paramEnvVars[0] = EnvVars.class; + + // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/GitSCM.html#getBranches() => + // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/BranchSpec.html#toString() + Method methodGetBranches = null; + try { + methodGetBranches = scm.getClass().getDeclaredMethod("getBranches", noparams); + } catch (Exception x) { + // NoSuchMethodException | SecurityException | NullPointerException + methodGetBranches = null; + } + if (methodGetBranches != null) { + Object branchList = null; + try { + branchList = methodGetBranches.invoke(scm); + } catch (Exception x) { + // InvocationTargetException | IllegalAccessException + branchList = null; + } + if (branchList != null && branchList instanceof List) { + Object branch0 = ((List) branchList).get(0); + if (branch0 != null && "hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) { + Method methodGetExpandedName = null; + try { + methodGetExpandedName = branch0.getClass().getDeclaredMethod("getExpandedName", paramEnvVars); + } catch (Exception x) { + methodGetExpandedName = null; + } + if (methodGetExpandedName != null) { + // Handle possible shell-templated branch specs: + Object expandedBranchName = null; + try { + expandedBranchName = methodGetExpandedName.invoke(branch0, run.getEnvironment(listener)); + } catch (Exception x) { + // IllegalAccessException | IOException + expandedBranchName = null; + } + if (expandedBranchName != null) { + runVersion = expandedBranchName.toString(); + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): " + + "did not find method BranchSpec.getExpandedName()"); + } + } + if (runVersion == null || "".equals(runVersion)) { + runVersion = branch0.toString(); + } + } else { + // unknown branchspec class, make no blind guesses + if (logger != null) { + logger.println("defaultedVersion(): " + + "list of branches did not return a " + + "BranchSpec class instance, but " + + (branch0 == null ? "null" : + branch0.getClass().getName())); + } + } + } else { + if (logger != null) { + logger.println("defaultedVersion(): " + + "getBranches() did not return a " + + "list of branches: " + + (branchList == null ? "null" : + branchList.getClass().getName())); + } + } + } else { + // not really the GitSCM we know? + if (logger != null) { + logger.println("defaultedVersion(): " + + "did not find method GitSCM.getBranches()"); + } + } + + // Still alive? Chop off leading '*/' + // (if any) from single-branch MBP and + // plain "Pipeline" job definitions. + if (runVersion != null) { + runVersion = runVersion.replaceFirst("^\\*/", ""); + if (logger != null) { + logger.println("defaultedVersion(): " + + "Discovered runVersion '" + runVersion + + "' in GitSCM source of the pipeline"); + } + } + + return runVersion; + } + + private String extractDefaultedVersionSCM(@NonNull SCM scm, @NonNull Run run, @NonNull TaskListener listener, PrintStream logger) { + String runVersion = null; + + if (logger != null) { + logger.println("defaultedVersion(): " + + "inspecting first listed SCM: " + + scm.toString()); + } + + // TODO: If this hack gets traction, try available methods + // until a non-null result. + // Ideally SCM API itself would have all classes return this + // value (or null if branch concept is not supported there): + runVersion = extractDefaultedVersionGitSCM(scm, run, listener, logger); + + if (runVersion == null) { + // got SVN, Gerritt or some other SCM - + // add handling when needed and known how + // or rely on BRANCH_NAME (if set) below... + if (logger != null) { + logger.println("defaultedVersion(): " + + "the first listed SCM was not of currently " + + "supported class with recognized branch support: " + + scm.getClass().getName()); + } + } + + return runVersion; + } + + private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener listener, PrintStream logger) { // Ask for SCM source of the pipeline (if any), // as the most authoritative source of the branch - // name we want: + // name we want. If we get an SCM class we can + // query deeper (supports branch concept), then + // extract the branch name of the script source. SCM scm0 = null; + String runVersion = null; + Item runParent = run.getParent(); + if (runParent != null && runParent instanceof WorkflowJob) { // This covers both "Multibranch Pipeline" // and "Pipeline script from SCM" jobs; @@ -263,11 +406,11 @@ private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener csfd.getClass().getName() + "' is not associated with an SCM"); } - } else if (!("hudson.plugins.git.GitSCM".equals(scm0.getClass().getName()))) { + } else if (!isDefaultedVersionSCMSupported(scm0)) { if (logger != null) { logger.println("defaultedVersion(): CpsScmFlowDefinition '" + csfd.getClass().getName() + - "' is associated with an SCM we can not query: " + + "' is associated with an SCM class we can not query for branches: " + scm0.toString()); } scm0 = null; @@ -294,7 +437,7 @@ private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener scmN.getClass().getName() + "': " + scmN.toString()); } - if ("hudson.plugins.git.GitSCM".equals(scmN.getClass().getName())) { + if (isDefaultedVersionSCMSupported(scmN)) { // The best we can do here is accept // the first seen SCM (with branch // support which we know how to query). @@ -364,119 +507,8 @@ private String defaultedVersionSCM(@NonNull Run run, @NonNull TaskListener // Got some hit? Drill deeper! if (scm0 != null) { - // Avoid importing GitSCM and so requiring that - // it is always installed even if not used by - // particular Jenkins deployment (using e.g. - // SVN, Gerritt, etc.). Our aim is to query this: - // runVersion = scm0.getBranches().first().getExpandedName(run.getEnvironment(listener)); - // https://mkyong.com/java/how-to-use-reflection-to-call-java-method-at-runtime/ - if (logger != null) { - logger.println("defaultedVersion(): " + - "inspecting first listed SCM: " + - scm0.toString()); - } - - Class noparams[] = {}; - Class[] paramEnvVars = new Class[1]; - paramEnvVars[0] = EnvVars.class; - if ("hudson.plugins.git.GitSCM".equals(scm0.getClass().getName())) { - // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/GitSCM.html#getBranches() => - // https://javadoc.jenkins.io/plugin/git/hudson/plugins/git/BranchSpec.html#toString() - Method methodGetBranches = null; - try { - methodGetBranches = scm0.getClass().getDeclaredMethod("getBranches", noparams); - } catch (Exception x) { - // NoSuchMethodException | SecurityException | NullPointerException - methodGetBranches = null; - } - if (methodGetBranches != null) { - Object branchList = null; - try { - branchList = methodGetBranches.invoke(scm0); - } catch (Exception x) { - // InvocationTargetException | IllegalAccessException - branchList = null; - } - if (branchList != null && branchList instanceof List) { - Object branch0 = ((List) branchList).get(0); - if (branch0 != null && "hudson.plugins.git.BranchSpec".equals(branch0.getClass().getName())) { - Method methodGetExpandedName = null; - try { - methodGetExpandedName = branch0.getClass().getDeclaredMethod("getExpandedName", paramEnvVars); - } catch (Exception x) { - methodGetExpandedName = null; - } - if (methodGetExpandedName != null) { - // Handle possible shell-templated branch specs: - Object expandedBranchName = null; - try { - expandedBranchName = methodGetExpandedName.invoke(branch0, run.getEnvironment(listener)); - } catch (Exception x) { - // IllegalAccessException | IOException - expandedBranchName = null; - } - if (expandedBranchName != null) { - runVersion = expandedBranchName.toString(); - } - } else { - if (logger != null) { - logger.println("defaultedVersion(): " + - "did not find method BranchSpec.getExpandedName()"); - } - } - if (runVersion == null || "".equals(runVersion)) { - runVersion = branch0.toString(); - } - } else { - // unknown branchspec class, make no blind guesses - if (logger != null) { - logger.println("defaultedVersion(): " + - "list of branches did not return a " + - "BranchSpec class instance, but " + - (branch0 == null ? "null" : - branch0.getClass().getName())); - } - } - } else { - if (logger != null) { - logger.println("defaultedVersion(): " + - "getBranches() did not return a " + - "list of branches: " + - (branchList == null ? "null" : - branchList.getClass().getName())); - } - } - } else { - // not really the GitSCM we know? - if (logger != null) { - logger.println("defaultedVersion(): " + - "did not find method GitSCM.getBranches()"); - } - } - - // Still alive? Chop off leading '*/' - // (if any) from single-branch MBP and - // plain "Pipeline" job definitions. - if (runVersion != null) { - runVersion = runVersion.replaceFirst("^\\*/", ""); - if (logger != null) { - logger.println("defaultedVersion(): " + - "Discovered runVersion '" + runVersion + - "' in SCM source of the pipeline"); - } - } - } else { - // else SVN, Gerritt or some other SCM - - // add handling when needed and known how - // or rely on BRANCH_NAME (if set) below... - if (logger != null) { - logger.println("defaultedVersion(): " + - "the first listed SCM was not of currently " + - "supported class with recognized branch support: " + - scm0.getClass().getName()); - } - } - } // if (scm0 != null) + runVersion = extractDefaultedVersionSCM(scm0, run, listener, logger); + } return runVersion; } From ac9f40aeeb1844c73de5c98130627a3b5923d845 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 18 Oct 2022 17:41:19 +0200 Subject: [PATCH 65/70] LibraryConfiguration.java: attempt to first extractDefaultedVersionSCMFS() [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 5587c5f6..984633ba 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -39,6 +39,9 @@ import hudson.util.FormValidation; import jenkins.branch.Branch; import jenkins.model.Jenkins; +import jenkins.scm.api.SCMFileSystem; +import jenkins.scm.api.SCMHead; +import jenkins.scm.api.SCMRevision; import jenkins.scm.api.SCMSourceOwner; import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition; import org.jenkinsci.plugins.workflow.flow.FlowDefinition; @@ -335,6 +338,59 @@ private String extractDefaultedVersionGitSCM(@NonNull SCM scm, @NonNull Run run, @NonNull TaskListener listener, PrintStream logger) { + String runVersion = null; + Item runParent = run.getParent(); + if (runParent == null) + return null; + + SCMFileSystem fs; + try { + fs = SCMFileSystem.of(runParent, scm); + if (fs == null && logger != null) { + logger.println("defaultedVersion(): " + + "got no SCMFileSystem: " + + "method of() returned null"); + } + } catch (Exception x) { + fs = null; + if (logger != null) { + logger.println("defaultedVersion(): " + + "failed to get SCMFileSystem: " + + x.getMessage()); + } + } + if (fs == null) + return null; + + SCMRevision rev = fs.getRevision(); + if (rev == null) { + if (logger != null) { + logger.println("defaultedVersion(): " + + "got no SCMRevision from SCMFileSystem"); + } + return null; + } + + SCMHead head = rev.getHead(); + if (head == null) { + if (logger != null) { + logger.println("defaultedVersion(): " + + "got no SCMHead of SCMRevision from SCMFileSystem"); + } + return null; + } + + if (logger != null) { + logger.println("defaultedVersion(): " + + "got SCMHead of SCMRevision from SCMFileSystem: " + + "name='" + head.getName() + "' " + + "toString='" + head.toString() + "'"); + } + + return runVersion; + } + private String extractDefaultedVersionSCM(@NonNull SCM scm, @NonNull Run run, @NonNull TaskListener listener, PrintStream logger) { String runVersion = null; @@ -348,7 +404,10 @@ private String extractDefaultedVersionSCM(@NonNull SCM scm, @NonNull Run r // until a non-null result. // Ideally SCM API itself would have all classes return this // value (or null if branch concept is not supported there): - runVersion = extractDefaultedVersionGitSCM(scm, run, listener, logger); + runVersion = extractDefaultedVersionSCMFS(scm, run, listener, logger); + if (runVersion == null) { + runVersion = extractDefaultedVersionGitSCM(scm, run, listener, logger); + } if (runVersion == null) { // got SVN, Gerritt or some other SCM - From b95c46a94115998c77525bf0e5811165167f8c39 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 18 Oct 2022 18:07:02 +0200 Subject: [PATCH 66/70] LibraryConfiguration.java: drop currently unnecessary imports --- .../jenkinsci/plugins/workflow/libs/LibraryConfiguration.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 984633ba..89a971cc 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -37,12 +37,10 @@ import hudson.model.TaskListener; import hudson.scm.SCM; import hudson.util.FormValidation; -import jenkins.branch.Branch; import jenkins.model.Jenkins; import jenkins.scm.api.SCMFileSystem; import jenkins.scm.api.SCMHead; import jenkins.scm.api.SCMRevision; -import jenkins.scm.api.SCMSourceOwner; import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition; import org.jenkinsci.plugins.workflow.flow.FlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; From a943713dab711bbcf35378779fd7ed53bef63650 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 19 Oct 2022 09:03:44 +0200 Subject: [PATCH 67/70] LibraryConfiguration.java: extractDefaultedVersionSCMFS(): avoid redundant null-checks (spotbugs) [JENKINS-69731] --- .../workflow/libs/LibraryConfiguration.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 89a971cc..3ba903e7 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -338,9 +338,7 @@ private String extractDefaultedVersionGitSCM(@NonNull SCM scm, @NonNull Run run, @NonNull TaskListener listener, PrintStream logger) { String runVersion = null; - Item runParent = run.getParent(); - if (runParent == null) - return null; + Item runParent = run.getParent(); // never returns null SCMFileSystem fs; try { @@ -370,15 +368,7 @@ private String extractDefaultedVersionSCMFS(@NonNull SCM scm, @NonNull Run return null; } - SCMHead head = rev.getHead(); - if (head == null) { - if (logger != null) { - logger.println("defaultedVersion(): " + - "got no SCMHead of SCMRevision from SCMFileSystem"); - } - return null; - } - + SCMHead head = rev.getHead(); // never returns null if (logger != null) { logger.println("defaultedVersion(): " + "got SCMHead of SCMRevision from SCMFileSystem: " + From d039e50979d6da3acc4ddad976db541482c51950 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 19 Oct 2022 09:04:17 +0200 Subject: [PATCH 68/70] LibraryConfiguration.java: extractDefaultedVersionSCMFS(): placeholder to get runVersion from SCMHead (needs practical confirmation) [JENKINS-69731] --- .../jenkinsci/plugins/workflow/libs/LibraryConfiguration.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java index 3ba903e7..4ce592db 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java @@ -376,6 +376,10 @@ private String extractDefaultedVersionSCMFS(@NonNull SCM scm, @NonNull Run "toString='" + head.toString() + "'"); } + // TODO: Never saw this succeed getting an SCMRevision, + // so currently not blindly assigning runVersion: + //runVersion = head.getName(); + return runVersion; } From 6668195642f7e9ec51b74b96aadae7d1b136a485 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 26 Aug 2023 19:07:57 +0200 Subject: [PATCH 69/70] SCMSourceRetrieverTest: clarify messages for "SKIP by pre-test assumption" tests --- .../workflow/libs/SCMSourceRetrieverTest.java | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index 627e7ec4..aa6ca1a5 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -415,7 +415,8 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub // by having some checkouts (and so list of SCMs). // Do not let caller-provided BRANCH_NAME interfere here - assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + assumeFalse("SKIP by pre-test assumption: " + + "An externally provided BRANCH_NAME envvar interferes with tested logic", System.getenv("BRANCH_NAME") != null); sampleRepo1ContentMasterFeature(); @@ -687,7 +688,8 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub System.out.println("MBP source 0: " + mbp.getSourcesList().get(0).getSource().toString()); System.out.println("MBP source 1: " + mbp.getSourcesList().get(1).getSource().toString()); System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); - assumeFalse("MBP should have generated 'feature' and 'featurette' pipeline job", mbp.getItems().size() != 2); + assumeFalse("SKIP by pre-test assumption: " + + "MBP should have generated 'feature' and 'featurette' pipeline job", mbp.getItems().size() != 2); // In case of MBP with "Single repository and branch" // it only defines one job (per single-branch source), @@ -775,7 +777,8 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub System.out.println("MBP source 0: " + mbp.getSourcesList().get(0).getSource().toString()); System.out.println("MBP source 1: " + mbp.getSourcesList().get(1).getSource().toString()); System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); - assumeFalse("MBP should have generated 'feature' and 'featurette' pipeline job", mbp.getItems().size() != 2); + assumeFalse("SKIP by pre-test assumption: " + + "MBP should have generated 'feature' and 'featurette' pipeline job", mbp.getItems().size() != 2); // In case of MBP with "Single repository and branch" // it only defines one job (per single-branch source), @@ -867,7 +870,8 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub // a simple "Pipeline" job with static SCM source, // and that even lc.setAllowVersionOverride(false) // does not intervene here. - assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + assumeFalse("SKIP by pre-test assumption: " + + "An externally provided BRANCH_NAME envvar interferes with tested logic", System.getenv("BRANCH_NAME") != null); sampleRepo1ContentMasterFeature(); @@ -917,7 +921,8 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub // "unexpected" log message (so far expected due // to the bug being hunted), but counts the faults // and asserts in the end whether there were none. - assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + assumeFalse("SKIP by pre-test assumption: " + + "An externally provided BRANCH_NAME envvar interferes with tested logic", System.getenv("BRANCH_NAME") != null); sampleRepo1ContentMasterFeatureStable(); @@ -1034,7 +1039,8 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub // TODO: If this behavior does change, extend // the test to also try ${env.VARNAME} confusion. - assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + assumeFalse("SKIP by pre-test assumption: " + + "An externally provided BRANCH_NAME envvar interferes with tested logic", System.getenv("BRANCH_NAME") != null); sampleRepo1ContentMasterFeature(); @@ -1079,7 +1085,9 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub r.assertLogContains("was not a constant; did you mean to use the", b1); r.assertLogContains("step instead?", b1); // assertions survived, skip the test - assumeFalse("LibraryDecorator forbids use of double-quotes for @Library annotation", true); + // not exactly "pre-test" but oh well + assumeFalse("SKIP by pre-test assumption: " + + "LibraryDecorator forbids use of double-quotes for @Library annotation", true); } catch(AssertionError x) { // Chosen library version should not be "whatever" // causing fallback to "master", but "feature" per @@ -1095,7 +1103,8 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub // Test that lightweight checkouts from SCM allow // @Library('branchylib@${BRANCH_NAME}') to see // sufficient SCM context to determine the branch. - assumeFalse("An externally provided BRANCH_NAME envvar interferes with tested logic", + assumeFalse("SKIP by pre-test assumption: " + + "An externally provided BRANCH_NAME envvar interferes with tested logic", System.getenv("BRANCH_NAME") != null); sampleRepo1ContentMasterFeature(); @@ -1136,7 +1145,8 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub // is resolved with the TEST_VAR_NAME="feature" in environment. // Do not let caller-provided BRANCH_NAME interfere here - assumeFalse("An externally provided TEST_VAR_NAME envvar interferes with tested logic", + assumeFalse("SKIP by pre-test assumption: " + + "An externally provided TEST_VAR_NAME envvar interferes with tested logic", System.getenv("TEST_VAR_NAME") != null); sampleRepo1ContentMasterFeatureStable(); @@ -1203,7 +1213,8 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub ecOrig.add(ec); } ecList.removeAll(ecOrig); - assumeFalse("EnvironmentContributor.all() should be empty now", !ecList.isEmpty()); + assumeFalse("SKIP by pre-test assumption: " + + "EnvironmentContributor.all() should be empty now", !ecList.isEmpty()); ecList.add(new EnvironmentContributor() { @Override public void buildEnvironmentFor(Run run, EnvVars ev, TaskListener tl) throws IOException, InterruptedException { @@ -1655,7 +1666,8 @@ public static class BasicSCMSource extends SCMSource { @Issue("SECURITY-2463") @Test public void checkoutDirectoriesAreNotReusedByDifferentScms() throws Exception { - assumeFalse("checkoutDirectoriesAreNotReusedByDifferentScms() is " + + assumeFalse("SKIP by pre-test assumption: " + + "checkoutDirectoriesAreNotReusedByDifferentScms() is " + "skipped on Windows: Checkout hook is not cross-platform", Functions.isWindows()); sampleRepo.init(); From c1082b969f87fa4673261fa24ef969bb14416c50 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 26 Aug 2023 19:20:41 +0200 Subject: [PATCH 70/70] SCMSourceRetrieverTest: provide directly a sampleRepoNotifyCommit() to work around some GitSampleRepoRule.notifyCommit() issue --- .../workflow/libs/SCMSourceRetrieverTest.java | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java index aa6ca1a5..ef6c5fb6 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/SCMSourceRetrieverTest.java @@ -40,8 +40,9 @@ import hudson.model.Result; import hudson.model.Run; import hudson.model.TaskListener; -import hudson.plugins.git.GitSCM; +import hudson.plugins.git.ApiTokenPropertyConfiguration; import hudson.plugins.git.BranchSpec; +import hudson.plugins.git.GitSCM; import hudson.scm.ChangeLogSet; import hudson.scm.SCM; import hudson.slaves.EnvironmentVariablesNodeProperty; @@ -79,6 +80,7 @@ import jenkins.scm.impl.subversion.SubversionSCMSource; import jenkins.scm.impl.subversion.SubversionSampleRepoRule; import org.apache.commons.io.FileUtils; +import org.htmlunit.WebResponse; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; @@ -127,6 +129,28 @@ public class SCMSourceRetrieverTest { @Rule public LoggerRule logging = new LoggerRule().record(SCMBasedRetriever.class, Level.FINE); // Repetitive helpers for test cases dealing with @Issue("JENKINS-69731") and others + private void sampleRepoNotifyCommit(GitSampleRepoRule sampleRepo) throws Exception { + try { + sampleRepo.notifyCommit(r); + } catch(java.lang.NoSuchMethodError ignored) { + // Some versions of git-plugin maybe mix up "WebClient" + // from "JenkinsRule" and from "htmlunit" packages, while + // using the former implicitly. Maybe something else; says: + // java.lang.NoSuchMethodError: 'com.gargoylesoftware.htmlunit.Page + // org.jvnet.hudson.test.JenkinsRule$WebClient.goTo(java.lang.String, java.lang.String)' + // The catch-code below is an adapted copy of + // GitSampleRepoRule.notifyCommit() as of git-plugin-5.0.0: + + // SKIPPED: protected code: sampleRepo.synchronousPolling(r); + String notifyCommitToken = ApiTokenPropertyConfiguration.get().generateApiToken("notifyCommit").getString("value"); + JenkinsRule.WebClient wc = r.createWebClient(); + WebResponse webResponse = + wc.goTo("git/notifyCommit?url=" + sampleRepo.bareUrl() + "&token=" + notifyCommitToken, "text/plain").getWebResponse(); + // SKIPPED logging of the response + r.waitUntilNoActivity(); + } + } + private void sampleRepo1ContentMaster() throws Exception { sampleRepo1ContentMaster(null); } @@ -492,7 +516,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub // definition of MBP "leaf" jobs, and launch of builds, // so below we just make sure they complete and analyze // the outcomes. - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); @@ -550,7 +574,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub // definition of MBP "leaf" jobs, and launch of builds, // so below we just make sure they complete and analyze // the outcomes. - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); @@ -608,7 +632,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub // definition of MBP "leaf" jobs, and launch of builds, // so below we just make sure they complete and analyze // the outcomes. - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); System.out.println("Jobs generated by MBP: " + mbp.getItems().toString()); @@ -681,7 +705,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub mbp.save(); // Rescan to actually define leaf jobs: mbp.scheduleBuild(0); - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); System.out.println("All Jenkins items: " + r.jenkins.getItems().toString()); System.out.println("MBP sources: " + mbp.getSourcesList().toString()); @@ -770,7 +794,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub mbp.save(); // Rescan to actually define leaf jobs: mbp.scheduleBuild(0); - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); System.out.println("All Jenkins items: " + r.jenkins.getItems().toString()); System.out.println("MBP sources: " + mbp.getSourcesList().toString()); @@ -855,7 +879,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); p0.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); WorkflowRun b0 = r.buildAndAssertSuccess(p0); @@ -896,7 +920,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); p0.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); WorkflowRun b0 = r.buildAndAssertSuccess(p0); @@ -945,7 +969,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub Collections.singletonList(new BranchSpec("*/feature")), null, null, Collections.emptyList()); - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); // First run a job definition with a fixed library version, @@ -1070,7 +1094,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub WorkflowJob p1 = r.jenkins.createProject(WorkflowJob.class, "p1"); p1.setDefinition(new CpsScmFlowDefinition(gitSCM, "Jenkinsfile")); - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); p1.scheduleBuild2(0); r.waitUntilNoActivity(); @@ -1131,7 +1155,7 @@ private void sampleRepo2ContentUniqueMasterFeatureBogus_staticStrings(String sub csfd.setLightweight(true); WorkflowJob p0 = r.jenkins.createProject(WorkflowJob.class, "p0"); p0.setDefinition(csfd); - sampleRepo2.notifyCommit(r); + sampleRepoNotifyCommit(sampleRepo2); r.waitUntilNoActivity(); WorkflowRun b0 = r.buildAndAssertSuccess(p0);