Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add 'Inverse' choosing strategy which ignores specified branches #45

Merged
merged 4 commits into from
@orrc
Collaborator

For feature branches etc, I wanted a way to build everything except for master (and also someone asked about it on StackOverflow recently). Seems like a relatively common/useful feature.

So here's a choosing strategy which looks at "Branches to build" and promptly ignores them all -- only branches which do not match the configured branch specifiers will be built.

I've given this a reasonable test, and half the code is based on the DefaultBuildChooser anyway, so hopefully this implementation makes sense.

I also updated the original inline help to be a bit more explanatory.

@jeyb

+1

@pelle

+1

@orrc
Collaborator

Since you Doximity types are so keen, here's a build of the plugin including this feature:
https://github.com/downloads/orrc/git-plugin/git.hpi

@blakefrost

Would it be possible for the build chooser to accept regex? Seems like that would solve the same problem as this, but in a different manner that may be more flexible. Just a thought.

@orrc
Collaborator

"Now you have two problems."
Writing inversions as regular expressions is a bit annoying.

But if you wanted a feature like that, you'd have to ask the core developers -- probably it would have a number of knock-on effects in how these BuildChooser implementations function, and so on.

The git plugin will likely get a rewrite at some point, so I probably wouldn't hold my breath unless you tackle it yourself :)

@blakefrost

Yeah, I suppose it could be more trouble then it's worth. Essentially, what you've built will get the job done for what we need. Thanks.

@stisti
Collaborator

Heh, just today I was saying at work how great it would be if the git plugin could be configured to ignore the master branch and work on all the other branches. +1

@maletor

This didn't work :(

@orrc
Collaborator

This does work :)

I just took a clean Jenkins installation, created a job, entered the Git URL, entered "**/master" as the branch, chose this "Inverse" strategy, hit Save and started some builds. All branches except for master were built.

Can you be more specific?

@maletor
orrc added some commits
@orrc
Collaborator

I fixed a possible NPE and improved the log messages shown.

Updated build is here:
https://github.com/downloads/orrc/git-plugin/git.hpi

@steel

+1

@ndeloof ndeloof merged commit ae23c65 into from
@erikackermann

How can I use this to ignore two branches? (e.g. build every branch except master and release)

@vvo
vvo commented

@erikackermann I am looking for the exact same thing, maybe @orrc knows how to do that?

**/master|otherbranch ?

@orrc
Collaborator

You'd have to modify the plugin to do that, or write another choosing strategy.
As far as I recall, the change I added here does an exact match (well, plus wildcards).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 26, 2011
  1. @orrc

    Add 'Inverse' choosing strategy which ignores specified branches.

    orrc authored
    Also add documentation and improve the existing strategy documentation.
  2. @orrc
Commits on Oct 6, 2011
  1. @orrc

    Ensure 'all branches excluded' message is shown only when that is true.

    orrc authored
    Also add a message when there's nothing new to build, so the last-built branch is selected.
  2. @orrc
This page is out of date. Refresh to see the latest.
View
3  src/main/java/hudson/plugins/git/util/DefaultBuildChooser.java
@@ -7,6 +7,7 @@
import hudson.plugins.git.GitException;
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.IGitAPI;
+import hudson.plugins.git.Messages;
import hudson.plugins.git.Revision;
import org.kohsuke.stapler.DataBoundConstructor;
import org.eclipse.jgit.lib.ObjectId;
@@ -227,7 +228,7 @@ private void verbose(TaskListener listener, String format, Object... args) {
public static final class DescriptorImpl extends BuildChooserDescriptor {
@Override
public String getDisplayName() {
- return "Default";
+ return Messages.BuildChooser_Default();
}
@Override
View
115 src/main/java/hudson/plugins/git/util/InverseBuildChooser.java
@@ -0,0 +1,115 @@
+package hudson.plugins.git.util;
+
+import hudson.Extension;
+import hudson.model.TaskListener;
+import hudson.plugins.git.Branch;
+import hudson.plugins.git.BranchSpec;
+import hudson.plugins.git.GitException;
+import hudson.plugins.git.IGitAPI;
+import hudson.plugins.git.Messages;
+import hudson.plugins.git.Revision;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.kohsuke.stapler.DataBoundConstructor;
+
+/**
+ * Git build chooser which will select all branches <b>except</b> for those which match the
+ * configured branch specifiers.
+ * <p>
+ * e.g. If <tt>&#x2a;&#x2a;/master</tt> and <tt>&#x2a;&#x2a;/release-&#x2a;</tt> are configured as
+ * "Branches to build" then any branches matching those patterns <b>will not</b> be built, unless
+ * another branch points to the same revision.
+ * <p>
+ * This is useful, for example, when you have jobs building your <tt>master</tt> and various
+ * <tt>release</tt> branches and you want a second job which builds all new feature branches &mdash;
+ * i.e. branches which do not match these patterns &mdash; without redundantly building
+ * <tt>master</tt> and the release branches again each time they change.
+ *
+ * @author Christopher Orr
+ */
+public class InverseBuildChooser extends BuildChooser {
+
+ /* Ignore symbolic default branch ref. */
+ private static final BranchSpec HEAD = new BranchSpec("*/HEAD");
+
+ @DataBoundConstructor
+ public InverseBuildChooser() {
+ }
+
+ @Override
+ public Collection<Revision> getCandidateRevisions(boolean isPollCall,
+ String singleBranch, IGitAPI git, TaskListener listener,
+ BuildData buildData) throws GitException, IOException {
+
+ GitUtils utils = new GitUtils(listener, git);
+ List<Revision> branchRevs = new ArrayList<Revision>(utils.getAllBranchRevisions());
+ List<BranchSpec> specifiedBranches = gitSCM.getBranches();
+
+ // Iterate over all the revisions pointed to by branches in the repository
+ for (Iterator<Revision> i = branchRevs.iterator(); i.hasNext(); ) {
+ Revision revision = i.next();
+
+ // Iterate over each branch for this revision
+ for (Iterator<Branch> j = revision.getBranches().iterator(); j.hasNext(); ) {
+ Branch branch = j.next();
+
+ // 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())) {
+ j.remove();
+ break;
+ }
+ }
+ }
+
+ // If we discarded all branches for this revision, ignore the whole revision
+ if (revision.getBranches().isEmpty()) {
+ i.remove();
+ }
+ }
+
+ // Filter out branch revisions that aren't leaves
+ branchRevs = utils.filterTipBranches(branchRevs);
+
+ // Warn the user that they've done something crazy such as excluding all branches
+ if (branchRevs.isEmpty()) {
+ listener.getLogger().println(Messages.BuildChooser_Inverse_EverythingExcluded());
+ }
+
+ // Filter out branch revisions that have already been built
+ for (Iterator<Revision> i = branchRevs.iterator(); i.hasNext(); ) {
+ Revision r = i.next();
+ if (buildData.hasBeenBuilt(r.getSha1())) {
+ i.remove();
+ }
+ }
+
+ // If we're in a build (not an SCM poll) and nothing new was found, run the last build again
+ if (!isPollCall && branchRevs.isEmpty() && buildData.getLastBuiltRevision() != null) {
+ listener.getLogger().println(Messages.BuildChooser_BuildingLastRevision());
+ return Collections.singletonList(buildData.getLastBuiltRevision());
+ }
+
+ // Sort revisions by the date of commit, old to new, to ensure fairness in scheduling
+ Collections.sort(branchRevs, new CommitTimeComparator(utils.git.getRepository()));
+ return branchRevs;
+ }
+
+ @Extension
+ public static final class DescriptorImpl extends BuildChooserDescriptor {
+ @Override
+ public String getDisplayName() {
+ return Messages.BuildChooser_Inverse();
+ }
+ }
+
+ private static final long serialVersionUID = 1L;
+
+}
View
4 src/main/resources/hudson/plugins/git/Messages.properties
@@ -0,0 +1,4 @@
+BuildChooser_Default=Default
+BuildChooser_Inverse=Inverse
+BuildChooser_Inverse_EverythingExcluded=All current git branches were excluded from being built. Either your branch specifiers are too broad or you should be using the "Default" choosing strategy.
+BuildChooser_BuildingLastRevision=No new revisions were found; the most-recently built branch will be built again.
View
21 src/main/webapp/choosingStrategy.html
@@ -1,4 +1,21 @@
<div>
- Which strategy to use when selecting revision for building.
- Default will search HEADs for different branches.
+ Specifies the strategy Jenkins will use to decide which revision to build.
+ <dl>
+ <dt>Default</dt>
+ <dd>
+ For each branch which matches the "Branches to build" (or all branches, if none are listed),
+ this strategy will select those whose most recent commit has not already been built.<br>
+ If multiple branches match these criteria, the oldest will be selected.
+ </dd>
+ <dt>Inverse</dt>
+ <dd>
+ This does the opposite of the "Default" strategy &mdash; any branches listed under "Branches
+ to build" will <strong>not</strong> be built. For example, entering the pattern
+ <tt>*/master</tt> will cause Jenkins to build any changes found on any branch, so long as the
+ branch name does <em>not</em> match this pattern.<br>
+ If multiple branches match these criteria, the oldest will be selected.
+ </dd>
+ </dl>
+ For details on any other strategies that may be listed in the drop-down box above, refer to the
+ respective plugin's documentation.
</div>
Something went wrong with that request. Please try again.