Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

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

Merged
merged 4 commits into from over 2 years ago

11 participants

Christopher Orr Ellis Berner Jey Balachandran Shannon McPherson Blake Taylor Pelle Braendgaard Sami Tikka Steel Fu Erik Ackermann Vincent Voyer Nicolas De loof
Christopher Orr
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.

Jey Balachandran

+1

Pelle Braendgaard

+1

Christopher Orr
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

Blake Taylor

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.

Christopher Orr
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 :)

Blake Taylor

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.

Sami Tikka
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

Ellis Berner

This didn't work :(

Christopher Orr
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?

Ellis Berner
added some commits October 06, 2011
Christopher Orr
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 Fu

+1

Nicolas De loof ndeloof merged commit ae23c65 into from October 25, 2011
Nicolas De loof ndeloof closed this October 25, 2011
Erik Ackermann

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

Vincent Voyer

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

**/master|otherbranch ?

Christopher Orr
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

Showing 4 unique commits by 1 author.

Sep 26, 2011
Christopher Orr Add 'Inverse' choosing strategy which ignores specified branches.
Also add documentation and improve the existing strategy documentation.
e3a33d1
Christopher Orr Internationalise DefaultBuildChooser display name. 6bd7851
Oct 06, 2011
Christopher Orr Ensure 'all branches excluded' message is shown only when that is true.
Also add a message when there's nothing new to build, so the last-built branch is selected.
1a219eb
Christopher Orr Ensure HEAD BranchSpec reference is retained, rather than being trans…
…ient.
2b8d120
This page is out of date. Refresh to see the latest.
3  src/main/java/hudson/plugins/git/util/DefaultBuildChooser.java
@@ -7,6 +7,7 @@
7 7
 import hudson.plugins.git.GitException;
8 8
 import hudson.plugins.git.GitSCM;
9 9
 import hudson.plugins.git.IGitAPI;
  10
+import hudson.plugins.git.Messages;
10 11
 import hudson.plugins.git.Revision;
11 12
 import org.kohsuke.stapler.DataBoundConstructor;
12 13
 import org.eclipse.jgit.lib.ObjectId;
@@ -227,7 +228,7 @@ private void verbose(TaskListener listener, String format, Object... args) {
227 228
     public static final class DescriptorImpl extends BuildChooserDescriptor {
228 229
         @Override
229 230
         public String getDisplayName() {
230  
-            return "Default";
  231
+            return Messages.BuildChooser_Default();
231 232
         }
232 233
 
233 234
         @Override
115  src/main/java/hudson/plugins/git/util/InverseBuildChooser.java
... ...
@@ -0,0 +1,115 @@
  1
+package hudson.plugins.git.util;
  2
+
  3
+import hudson.Extension;
  4
+import hudson.model.TaskListener;
  5
+import hudson.plugins.git.Branch;
  6
+import hudson.plugins.git.BranchSpec;
  7
+import hudson.plugins.git.GitException;
  8
+import hudson.plugins.git.IGitAPI;
  9
+import hudson.plugins.git.Messages;
  10
+import hudson.plugins.git.Revision;
  11
+
  12
+import java.io.IOException;
  13
+import java.util.ArrayList;
  14
+import java.util.Collection;
  15
+import java.util.Collections;
  16
+import java.util.Iterator;
  17
+import java.util.List;
  18
+
  19
+import org.kohsuke.stapler.DataBoundConstructor;
  20
+
  21
+/**
  22
+ * Git build chooser which will select all branches <b>except</b> for those which match the
  23
+ * configured branch specifiers.
  24
+ * <p>
  25
+ * e.g. If <tt>&#x2a;&#x2a;/master</tt> and <tt>&#x2a;&#x2a;/release-&#x2a;</tt> are configured as
  26
+ * "Branches to build" then any branches matching those patterns <b>will not</b> be built, unless
  27
+ * another branch points to the same revision.
  28
+ * <p>
  29
+ * This is useful, for example, when you have jobs building your <tt>master</tt> and various
  30
+ * <tt>release</tt> branches and you want a second job which builds all new feature branches &mdash;
  31
+ * i.e. branches which do not match these patterns &mdash; without redundantly building
  32
+ * <tt>master</tt> and the release branches again each time they change.
  33
+ *
  34
+ * @author Christopher Orr
  35
+ */
  36
+public class InverseBuildChooser extends BuildChooser {
  37
+
  38
+    /* Ignore symbolic default branch ref. */
  39
+    private static final BranchSpec HEAD = new BranchSpec("*/HEAD");
  40
+
  41
+    @DataBoundConstructor
  42
+    public InverseBuildChooser() {
  43
+    }
  44
+
  45
+    @Override
  46
+    public Collection<Revision> getCandidateRevisions(boolean isPollCall,
  47
+            String singleBranch, IGitAPI git, TaskListener listener,
  48
+            BuildData buildData) throws GitException, IOException {
  49
+
  50
+        GitUtils utils = new GitUtils(listener, git);
  51
+        List<Revision> branchRevs = new ArrayList<Revision>(utils.getAllBranchRevisions());
  52
+        List<BranchSpec> specifiedBranches = gitSCM.getBranches();
  53
+
  54
+        // Iterate over all the revisions pointed to by branches in the repository
  55
+        for (Iterator<Revision> i = branchRevs.iterator(); i.hasNext(); ) {
  56
+            Revision revision = i.next();
  57
+
  58
+            // Iterate over each branch for this revision
  59
+            for (Iterator<Branch> j = revision.getBranches().iterator(); j.hasNext(); ) {
  60
+                Branch branch = j.next();
  61
+
  62
+                // Check whether this branch matches a branch spec from the job config
  63
+                for (BranchSpec spec : specifiedBranches) {
  64
+                    // If the branch matches, throw it away as we do *not* want to build it
  65
+                    if (spec.matches(branch.getName()) || HEAD.matches(branch.getName())) {
  66
+                        j.remove();
  67
+                        break;
  68
+                    }
  69
+                }
  70
+            }
  71
+
  72
+            // If we discarded all branches for this revision, ignore the whole revision
  73
+            if (revision.getBranches().isEmpty()) {
  74
+                i.remove();
  75
+            }
  76
+        }
  77
+
  78
+        // Filter out branch revisions that aren't leaves
  79
+        branchRevs = utils.filterTipBranches(branchRevs);
  80
+
  81
+        // Warn the user that they've done something crazy such as excluding all branches
  82
+        if (branchRevs.isEmpty()) {
  83
+            listener.getLogger().println(Messages.BuildChooser_Inverse_EverythingExcluded());
  84
+        }
  85
+
  86
+        // Filter out branch revisions that have already been built
  87
+        for (Iterator<Revision> i = branchRevs.iterator(); i.hasNext(); ) {
  88
+            Revision r = i.next();
  89
+            if (buildData.hasBeenBuilt(r.getSha1())) {
  90
+                i.remove();
  91
+            }
  92
+        }
  93
+
  94
+        // If we're in a build (not an SCM poll) and nothing new was found, run the last build again
  95
+        if (!isPollCall && branchRevs.isEmpty() && buildData.getLastBuiltRevision() != null) {
  96
+            listener.getLogger().println(Messages.BuildChooser_BuildingLastRevision());
  97
+            return Collections.singletonList(buildData.getLastBuiltRevision());
  98
+        }
  99
+
  100
+        // Sort revisions by the date of commit, old to new, to ensure fairness in scheduling
  101
+        Collections.sort(branchRevs, new CommitTimeComparator(utils.git.getRepository()));
  102
+        return branchRevs;
  103
+    }
  104
+
  105
+    @Extension
  106
+    public static final class DescriptorImpl extends BuildChooserDescriptor {
  107
+        @Override
  108
+        public String getDisplayName() {
  109
+            return Messages.BuildChooser_Inverse();
  110
+        }
  111
+    }
  112
+
  113
+    private static final long serialVersionUID = 1L;
  114
+
  115
+}
4  src/main/resources/hudson/plugins/git/Messages.properties
... ...
@@ -0,0 +1,4 @@
  1
+BuildChooser_Default=Default
  2
+BuildChooser_Inverse=Inverse
  3
+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.
  4
+BuildChooser_BuildingLastRevision=No new revisions were found; the most-recently built branch will be built again.
21  src/main/webapp/choosingStrategy.html
... ...
@@ -1,4 +1,21 @@
1 1
 <div>
2  
- Which strategy to use when selecting revision for building.
3  
- Default will search HEADs for different branches.
  2
+  Specifies the strategy Jenkins will use to decide which revision to build.
  3
+  <dl>
  4
+    <dt>Default</dt>
  5
+    <dd>
  6
+      For each branch which matches the "Branches to build" (or all branches, if none are listed),
  7
+      this strategy will select those whose most recent commit has not already been built.<br>
  8
+      If multiple branches match these criteria, the oldest will be selected.
  9
+    </dd>
  10
+    <dt>Inverse</dt>
  11
+    <dd>
  12
+      This does the opposite of the "Default" strategy &mdash; any branches listed under "Branches
  13
+      to build" will <strong>not</strong> be built. For example, entering the pattern
  14
+      <tt>*/master</tt> will cause Jenkins to build any changes found on any branch, so long as the
  15
+      branch name does <em>not</em> match this pattern.<br>
  16
+      If multiple branches match these criteria, the oldest will be selected.
  17
+    </dd>
  18
+  </dl>
  19
+  For details on any other strategies that may be listed in the drop-down box above, refer to the
  20
+  respective plugin's documentation.
4 21
 </div>
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.