Skip to content
Permalink
Browse files

[FIXED JENKINS-17417] Expand Branch Specifier

Expand Branch Specifier with build environment variables.

Note that the Branch Specifier is not expanded for repository polling
because no build environment is available at that time.
  • Loading branch information
hrubi committed Oct 29, 2013
1 parent dc73e25 commit 4fc7f4447c990082e8322c42ebafd1f33a2e60aa
@@ -1,5 +1,6 @@
package hudson.plugins.git;

import hudson.EnvVars;
import hudson.Extension;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
@@ -27,7 +28,6 @@
private static final long serialVersionUID = -6177158367915899356L;

private String name;
private transient Pattern pattern;

public String getName() {
return name;
@@ -48,54 +48,69 @@ public BranchSpec(String name) {
}

public String toString() {
return pattern + " (" + name + ")";
return name;
}

public boolean matches(String item) {
return getPattern().matcher(item).matches();
EnvVars env = new EnvVars();
return matches(item, env);
}

public boolean matches(String item, EnvVars env) {
return getPattern(env).matcher(item).matches();
}

public List<String> filterMatching(Collection<String> branches) {
EnvVars env = new EnvVars();
return filterMatching(branches, env);
}

public List<String> filterMatching(Collection<String> branches, EnvVars env) {
List<String> items = new ArrayList<String>();

for(String b : branches) {
if(matches(b))
if(matches(b, env))
items.add(b);
}

return items;
}

public List<Branch> filterMatchingBranches(Collection<Branch> branches) {
EnvVars env = new EnvVars();
return filterMatchingBranches(branches, env);
}

public List<Branch> filterMatchingBranches(Collection<Branch> branches, EnvVars env) {
List<Branch> items = new ArrayList<Branch>();

for(Branch b : branches) {
if(matches(b.getName()))
if(matches(b.getName(), env))
items.add(b);
}

return items;
}

private String getExpandedName(EnvVars env) {
return env.expand(name);
}

private Pattern getPattern() {
// return the saved pattern if available
if (pattern != null)
return pattern;

private Pattern getPattern(EnvVars env) {
String expandedName = getExpandedName(env);
// use regex syntax directly if name starts with colon
if (name.startsWith(":") && name.length() > 1) {
String regexSubstring = name.substring(1, name.length());
pattern = Pattern.compile(regexSubstring);
return pattern;
if (expandedName.startsWith(":") && expandedName.length() > 1) {
String regexSubstring = expandedName.substring(1, expandedName.length());
return Pattern.compile(regexSubstring);
}

// if an unqualified branch was given add a "*/" so it will match branches
// from remote repositories as the user probably intended
String qualifiedName;
if (!name.contains("**") && !name.contains("/"))
qualifiedName = "*/" + name;
if (!expandedName.contains("**") && !expandedName.contains("/"))
qualifiedName = "*/" + expandedName;
else
qualifiedName = name;
qualifiedName = expandedName;

// build a pattern into this builder
StringBuilder builder = new StringBuilder();
@@ -140,10 +155,7 @@ private Pattern getPattern() {
builder.append("[^/]*");
}

// save the pattern
pattern = Pattern.compile(builder.toString());

return pattern;
return Pattern.compile(builder.toString());
}

@Extension
@@ -1,6 +1,7 @@
package hudson.plugins.git.util;

import hudson.Extension;
import hudson.EnvVars;
import hudson.model.TaskListener;
import hudson.plugins.git.*;
import hudson.remoting.VirtualChannel;
@@ -48,7 +49,7 @@ public DefaultBuildChooser() {
// if the branch name contains more wildcards then the simple usecase
// does not apply and we need to skip to the advanced usecase
if (singleBranch == null || singleBranch.contains("*"))
return getAdvancedCandidateRevisions(isPollCall,listener,new GitUtils(listener,git),data);
return getAdvancedCandidateRevisions(isPollCall,listener,new GitUtils(listener,git),data, context);

// check if we're trying to build a specific commit
// this only makes sense for a build, there is no
@@ -167,7 +168,9 @@ private Revision objectId2Revision(String singleBranch, ObjectId sha1) {
* @throws IOException
* @throws GitException
*/
private List<Revision> getAdvancedCandidateRevisions(boolean isPollCall, TaskListener listener, GitUtils utils, BuildData data) throws GitException, IOException, InterruptedException {
private List<Revision> getAdvancedCandidateRevisions(boolean isPollCall, TaskListener listener, GitUtils utils, BuildData data, BuildChooserContext context) throws GitException, IOException, InterruptedException {
EnvVars env = context.getBuild().getEnvironment();

// 1. Get all the (branch) revisions that exist
List<Revision> revs = new ArrayList<Revision>(utils.getAllBranchRevisions());
verbose(listener, "Starting with all the branches: {0}", revs);
@@ -182,7 +185,7 @@ private Revision objectId2Revision(String singleBranch, ObjectId sha1) {
Branch b = j.next();
boolean keep = false;
for (BranchSpec bspec : gitSCM.getBranches()) {
if (bspec.matches(b.getName())) {
if (bspec.matches(b.getName(), env)) {
keep = true;
break;
}
@@ -198,7 +201,7 @@ private Revision objectId2Revision(String singleBranch, ObjectId sha1) {
if (r.getBranches().size() > 1) {
for (Iterator<Branch> j = r.getBranches().iterator(); j.hasNext();) {
Branch b = j.next();
if (HEAD.matches(b.getName())) {
if (HEAD.matches(b.getName(), env)) {
verbose(listener, "Ignoring {0} because there''s named branch for this revision", b.getName());
j.remove();
}
@@ -241,7 +244,7 @@ private Revision objectId2Revision(String singleBranch, ObjectId sha1) {
// with fast-forward merges between branches
if (!isPollCall && revs.isEmpty() && lastBuiltRevision != null) {
verbose(listener, "Nothing seems worth building, so falling back to the previously built revision: {0}", data.getLastBuiltRevision());
return Collections.singletonList(utils.sortBranchesForRevision(lastBuiltRevision, gitSCM.getBranches()));
return Collections.singletonList(utils.sortBranchesForRevision(lastBuiltRevision, gitSCM.getBranches(), env));
}

// 5. sort them by the date of commit, old to new
@@ -82,13 +82,18 @@ public Revision getRevisionForSHA1(ObjectId sha1) throws GitException, IOExcepti
}

public Revision sortBranchesForRevision(Revision revision, List<BranchSpec> branchOrder) {
EnvVars env = new EnvVars();
return sortBranchesForRevision(revision, branchOrder, env);
}

public Revision sortBranchesForRevision(Revision revision, List<BranchSpec> branchOrder, EnvVars env) {
ArrayList<Branch> orderedBranches = new ArrayList<Branch>(revision.getBranches().size());
ArrayList<Branch> revisionBranches = new ArrayList<Branch>(revision.getBranches());

for(BranchSpec branchSpec : branchOrder) {
for (Iterator<Branch> i = revisionBranches.iterator(); i.hasNext();) {
Branch b = i.next();
if (branchSpec.matches(b.getName())) {
if (branchSpec.matches(b.getName(), env)) {
i.remove();
orderedBranches.add(b);
}
@@ -1,6 +1,7 @@
package hudson.plugins.git.util;

import hudson.Extension;
import hudson.EnvVars;
import hudson.model.TaskListener;
import hudson.plugins.git.*;
import hudson.remoting.VirtualChannel;
@@ -41,6 +42,7 @@ public InverseBuildChooser() {
String singleBranch, GitClient git, TaskListener listener,
BuildData buildData, BuildChooserContext context) throws GitException, IOException, InterruptedException {

EnvVars env = context.getBuild().getEnvironment();
GitUtils utils = new GitUtils(listener, git);
List<Revision> branchRevs = new ArrayList<Revision>(utils.getAllBranchRevisions());
List<BranchSpec> specifiedBranches = gitSCM.getBranches();
@@ -56,7 +58,7 @@ public InverseBuildChooser() {
// Check whether this branch matches a branch spec from the job config
for (BranchSpec spec : specifiedBranches) {
// If the branch matches, throw it away as we do *not* want to build it
if (spec.matches(branch.getName()) || HEAD.matches(branch.getName())) {
if (spec.matches(branch.getName(), env) || HEAD.matches(branch.getName(), env)) {
j.remove();
break;
}
@@ -1,5 +1,8 @@
package hudson.plugins.git;

import hudson.EnvVars;
import java.util.HashMap;

import junit.framework.Assert;
import junit.framework.TestCase;

@@ -46,6 +49,58 @@ public void testMatch() {
Assert.assertFalse(p.matches("origin/my-branch/b1"));
}

public void testMatchEnv() {
HashMap<String, String> envMap = new HashMap<String, String>();
envMap.put("master", "master");
envMap.put("origin", "origin");
envMap.put("dev", "dev");
envMap.put("magnayn", "magnayn");
envMap.put("mybranch", "my.branch");
envMap.put("anyLong", "**");
envMap.put("anyShort", "*");
EnvVars env = new EnvVars(envMap);

BranchSpec l = new BranchSpec("${master}");
Assert.assertTrue(l.matches("origin/master", env));
Assert.assertFalse(l.matches("origin/something/master", env));
Assert.assertFalse(l.matches("master", env));
Assert.assertFalse(l.matches("dev", env));


BranchSpec est = new BranchSpec("${origin}/*/${dev}");

Assert.assertFalse(est.matches("origintestdev", env));
Assert.assertTrue(est.matches("origin/test/dev", env));
Assert.assertFalse(est.matches("origin/test/release", env));
Assert.assertFalse(est.matches("origin/test/somthing/release", env));

BranchSpec s = new BranchSpec("${origin}/*");

Assert.assertTrue(s.matches("origin/master", env));

BranchSpec m = new BranchSpec("**/${magnayn}/*");

Assert.assertTrue(m.matches("origin/magnayn/b1", env));
Assert.assertTrue(m.matches("remote/origin/magnayn/b1", env));

BranchSpec n = new BranchSpec("*/${mybranch}/*");

Assert.assertTrue(n.matches("origin/my.branch/b1", env));
Assert.assertFalse(n.matches("origin/my-branch/b1", env));
Assert.assertFalse(n.matches("remote/origin/my.branch/b1", env));

BranchSpec o = new BranchSpec("${anyLong}");

Assert.assertTrue(o.matches("origin/my.branch/b1", env));
Assert.assertTrue(o.matches("origin/my-branch/b1", env));
Assert.assertTrue(o.matches("remote/origin/my.branch/b1", env));

BranchSpec p = new BranchSpec("${anyShort}");

Assert.assertTrue(p.matches("origin/x", env));
Assert.assertFalse(p.matches("origin/my-branch/b1", env));
}

public void testEmptyName() {
BranchSpec branchSpec = new BranchSpec("");
assertEquals("**",branchSpec.getName());

1 comment on commit 4fc7f44

@MarkEWaite

This comment has been minimized.

Copy link

MarkEWaite commented on 4fc7f44 Feb 3, 2014

Was the change to the toString() method really helpful?

I think that toString() output was useful for me in the past when i could see both the branch specification of the job definition (the pattern) and the branch selected by the pattern (name).

Please sign in to comment.
You can’t perform that action at this time.