aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketAdditionalParameterEnvironmentContributor.java40
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java86
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java8
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java4
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java9
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java21
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/Pullrequest.java36
-rw-r--r--src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html31
-rw-r--r--src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html10
-rw-r--r--src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-ciSkipPhrases.html5
-rw-r--r--src/test/java/BitbucketBuildFilterTest.java20
11 files changed, 220 insertions, 50 deletions
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketAdditionalParameterEnvironmentContributor.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketAdditionalParameterEnvironmentContributor.java
new file mode 100644
index 0000000..c531ca1
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketAdditionalParameterEnvironmentContributor.java
@@ -0,0 +1,40 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
+
+import hudson.EnvVars;
+import hudson.Extension;
+import hudson.model.*;
+
+import java.io.IOException;
+
+@Extension
+public class BitbucketAdditionalParameterEnvironmentContributor extends EnvironmentContributor {
+ @Override
+ public void buildEnvironmentFor(Run run, EnvVars envVars, TaskListener taskListener)
+ throws IOException, InterruptedException {
+
+ BitbucketCause cause = (BitbucketCause) run.getCause(BitbucketCause.class);
+ if (cause == null) {
+ return;
+ }
+
+ putEnvVar(envVars, "sourceBranch", cause.getSourceBranch());
+ putEnvVar(envVars, "targetBranch", cause.getTargetBranch());
+ putEnvVar(envVars, "repositoryOwner", cause.getRepositoryOwner());
+ putEnvVar(envVars, "repositoryName", cause.getRepositoryName());
+ putEnvVar(envVars, "pullRequestId", cause.getPullRequestId());
+ putEnvVar(envVars, "destinationRepositoryOwner", cause.getDestinationRepositoryOwner());
+ putEnvVar(envVars, "destinationRepositoryName", cause.getDestinationRepositoryName());
+ putEnvVar(envVars, "pullRequestTitle", cause.getPullRequestTitle());
+ putEnvVar(envVars, "pullRequestAuthor", cause.getPullRequestAuthor());
+
+ }
+
+ private static void putEnvVar(EnvVars envs, String name, String value) {
+ envs.put(name, getString(value, ""));
+ }
+
+ private static String getString(String actual, String d) {
+ return actual == null ? d : actual;
+ }
+
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java
index c251930..d4173a2 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java
@@ -1,5 +1,6 @@
package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
+import hudson.model.Action;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.ArrayList;
@@ -29,6 +30,7 @@ abstract class Filter {
public static final String SRC_RX = "s:(" + RX_FILTER_FLAG_SINGLE + ")?";
public static final String DST_RX = "d:(" + RX_FILTER_FLAG_SINGLE + ")?";
+ public static final String AUTHOR_RX = "a:(" + RX_FILTER_FLAG_SINGLE + ")?";
public static final String BRANCH_FILTER_RX_PART = "([^\\s$]*)";
abstract public boolean apply(String filter, BitbucketCause cause);
@@ -36,6 +38,20 @@ abstract class Filter {
static final Pattern RX_SRC_DST_PARTS = Pattern.compile("(s:)|(d:)");
public static boolean HasSourceOrDestPartsPredicate(String filter) { return RX_SRC_DST_PARTS.matcher(filter).find(); }
+
+ static final Pattern RX_AUTHOR_PARTS = Pattern.compile("(a:)");
+ public static boolean HasAuthorPartsPredicate(String filter) { return RX_AUTHOR_PARTS.matcher(filter).find(); }
+
+ protected boolean applyByRx(Pattern rx, Filter usedFilter, String filter, BitbucketCause cause) {
+ Matcher srcMatch = rx.matcher(filter);
+ boolean apply = false;
+ while (srcMatch.find()) {
+ String computedFilter = ((srcMatch.group(1) == null ? "" : srcMatch.group(1)) + srcMatch.group(2)).trim();
+ logger.log(Level.INFO, "Apply computed filter: {0}", computedFilter);
+ apply = apply || (computedFilter.isEmpty() ? true : usedFilter.apply(computedFilter, cause));
+ }
+ return apply;
+ }
}
class EmptyFilter extends Filter {
@@ -84,17 +100,6 @@ class SourceDestFlag extends Filter {
static final Pattern SRC_MATCHER_RX = Pattern.compile(SRC_RX + BRANCH_FILTER_RX_PART, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ);
static final Pattern DST_MATCHER_RX = Pattern.compile(DST_RX + BRANCH_FILTER_RX_PART, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ);
- boolean applyByRx(Pattern rx, Filter usedFilter, String filter, BitbucketCause cause) {
- Matcher srcMatch = rx.matcher(filter);
- boolean apply = rx.matcher(filter).matches();
- while (srcMatch.find()) {
- String computedFilter = ((srcMatch.group(1) == null ? "" : srcMatch.group(1)) + srcMatch.group(2)).trim();
- logger.log(Level.INFO, "Apply computed filter: {0}", computedFilter);
- apply = apply || (computedFilter.isEmpty() ? true : usedFilter.apply(computedFilter, cause));
- }
- return apply;
- }
-
@Override
public boolean apply(String filter, BitbucketCause cause) {
return this.applyByRx(SRC_MATCHER_RX, new OnlySourceFlag(), filter, cause) &&
@@ -106,6 +111,54 @@ class SourceDestFlag extends Filter {
}
}
+class AuthorFlag extends Filter {
+ static final Pattern AUTHOR_MATCHER_RX = Pattern.compile(AUTHOR_RX + BRANCH_FILTER_RX_PART, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ);
+
+ class AuthorFlagImpl extends Filter {
+ @Override
+ public boolean apply(String filter, BitbucketCause cause) {
+ String selectedRx = filter.startsWith(RX_FILTER_FLAG_SINGLE) ? filter.substring(RX_FILTER_FLAG_SINGLE.length()) : Pattern.quote(filter);
+ logger.log(Level.INFO, "AuthorFlagImpl using filter: {0}", selectedRx);
+ Matcher matcher = Pattern.compile(selectedRx, Pattern.CASE_INSENSITIVE).matcher(cause.getPullRequestAuthor());
+ return filter.startsWith(RX_FILTER_FLAG_SINGLE) ? matcher.find() : matcher.matches();
+ }
+ @Override
+ public boolean check(String filter) { return false; }
+ }
+
+ @Override
+ public boolean apply(String filter, BitbucketCause cause) {
+ return this.applyByRx(AUTHOR_MATCHER_RX, new AuthorFlagImpl(), filter, cause);
+ }
+ @Override
+ public boolean check(String filter) {
+ return HasAuthorPartsPredicate(filter);
+ }
+}
+
+class CombinedFlags extends Filter {
+ private final Filter[] _filters;
+ public CombinedFlags(Filter[] filters) {
+ _filters = filters;
+ }
+
+ @Override
+ public boolean apply(String filter, BitbucketCause cause) {
+ boolean applied = true;
+ for(Filter f: _filters)
+ if (f.check(filter))
+ applied = applied && f.apply(filter, cause);
+ return applied;
+ }
+ @Override
+ public boolean check(String filter) {
+ for(Filter f: _filters)
+ if (f.check(filter))
+ return true;
+ return false;
+ }
+}
+
/**
* Created by maxvodo
*/
@@ -118,10 +171,15 @@ public class BitbucketBuildFilter {
static {
ArrayList<Filter> filters = new ArrayList<Filter>();
- filters.add(new AnyFlag());
+
+ filters.add(new AnyFlag());
+ filters.add(new CombinedFlags(new Filter[] {
+ new SourceDestFlag(),
+ new AuthorFlag()
+ }));
filters.add(new OnlyDestFlag());
- filters.add(new SourceDestFlag());
- filters.add(new EmptyFilter());
+ filters.add(new EmptyFilter());
+
AvailableFilters = filters;
}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
index 2375ec7..aedfb91 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
@@ -164,14 +164,6 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
public QueueTaskFuture<?> startJob(BitbucketCause cause) {
Map<String, ParameterValue> values = this.getDefaultParameters();
- values.put("sourceBranch", new StringParameterValue("sourceBranch", cause.getSourceBranch()));
- values.put("targetBranch", new StringParameterValue("targetBranch", cause.getTargetBranch()));
- values.put("repositoryOwner", new StringParameterValue("repositoryOwner", cause.getRepositoryOwner()));
- values.put("repositoryName", new StringParameterValue("repositoryName", cause.getRepositoryName()));
- values.put("pullRequestId", new StringParameterValue("pullRequestId", cause.getPullRequestId()));
- values.put("destinationRepositoryOwner", new StringParameterValue("destinationRepositoryOwner", cause.getDestinationRepositoryOwner()));
- values.put("destinationRepositoryName", new StringParameterValue("destinationRepositoryName", cause.getDestinationRepositoryName()));
- values.put("pullRequestTitle", new StringParameterValue("pullRequestTitle", cause.getPullRequestTitle()));
return this.job.scheduleBuild2(0, cause, new ParametersAction(new ArrayList(values.values())), new RevisionParameterAction(cause.getSourceCommitHash()));
}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java
index 3e8e84b..ea8e892 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java
@@ -5,6 +5,7 @@ import hudson.model.AbstractBuild;
import hudson.model.Cause;
import hudson.model.Result;
import jenkins.model.Jenkins;
+import jenkins.model.JenkinsLocationConfiguration;
import java.io.IOException;
import java.util.logging.Level;
@@ -49,7 +50,8 @@ public class BitbucketBuilds {
return;
}
Result result = build.getResult();
- String rootUrl = Jenkins.getInstance().getRootUrl();
+ JenkinsLocationConfiguration globalConfig = new JenkinsLocationConfiguration();
+ String rootUrl = globalConfig.getUrl();
String buildUrl = "";
if (rootUrl == null) {
logger.warning("PLEASE SET JENKINS ROOT URL IN GLOBAL CONFIGURATION FOR BUILD STATE REPORTING");
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java
index e233371..3cb107b 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java
@@ -16,6 +16,7 @@ public class BitbucketCause extends Cause {
private final String pullRequestTitle;
private final String sourceCommitHash;
private final String destinationCommitHash;
+ private final String pullRequestAuthor;
public static final String BITBUCKET_URL = "https://bitbucket.org/";
public BitbucketCause(String sourceBranch,
@@ -27,7 +28,8 @@ public class BitbucketCause extends Cause {
String destinationRepositoryName,
String pullRequestTitle,
String sourceCommitHash,
- String destinationCommitHash) {
+ String destinationCommitHash,
+ String pullRequestAuthor) {
this.sourceBranch = sourceBranch;
this.targetBranch = targetBranch;
this.repositoryOwner = repositoryOwner;
@@ -38,6 +40,7 @@ public class BitbucketCause extends Cause {
this.pullRequestTitle = pullRequestTitle;
this.sourceCommitHash = sourceCommitHash;
this.destinationCommitHash = destinationCommitHash;
+ this.pullRequestAuthor = pullRequestAuthor;
}
public String getSourceBranch() {
@@ -83,4 +86,8 @@ public class BitbucketCause extends Cause {
description += "\">#" + this.getPullRequestId() + " " + this.getPullRequestTitle() + "</a>";
return description;
}
+
+ public String getPullRequestAuthor() {
+ return this.pullRequestAuthor;
+ }
}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
index 9b92775..660ec21 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
@@ -117,7 +117,9 @@ public class BitbucketRepository {
pullRequest.getDestination().getRepository().getRepositoryName(),
pullRequest.getTitle(),
pullRequest.getSource().getCommit().getHash(),
- pullRequest.getDestination().getCommit().getHash());
+ pullRequest.getDestination().getCommit().getHash(),
+ pullRequest.getAuthor().getCombinedUsername()
+ );
setBuildStatus(cause, BuildState.INPROGRESS, Jenkins.getInstance().getRootUrl());
this.builder.getTrigger().startJob(cause);
}
@@ -180,12 +182,6 @@ public class BitbucketRepository {
logger.log(Level.INFO, "Post comment: {0} with original content {1}", new Object[]{ content, this.client.postPullRequestComment(pullRequestId, content).getId() });
}
- private boolean processTTPCommentBuildTags(String content, String buildKey) {
- if (!this.isTTPCommentBuildTags(content)) return true;
- logger.log(Level.INFO, "Processing ttp with build comment: {0}", content);
- return !this.hasMyBuildTagInTTPComment(content, buildKey);
- }
-
private boolean isTTPComment(String content) {
return content.toLowerCase().contains(BUILD_REQUEST_MARKER.toLowerCase());
}
@@ -239,6 +235,7 @@ public class BitbucketRepository {
boolean rebuildCommentAvailable = false;
if (comments != null) {
Collection<Pullrequest.Comment> filteredComments = this.filterPullRequestComments(comments);
+ boolean hasMyBuildTag = false;
for (Pullrequest.Comment comment : filteredComments) {
String content = comment.getContent();
if (this.isTTPComment(content)) {
@@ -247,10 +244,11 @@ public class BitbucketRepository {
"Rebuild comment available for commit {0} and comment #{1}",
new Object[]{ sourceCommit, comment.getId() }
);
- }
- rebuildCommentAvailable &= this.processTTPCommentBuildTags(content, buildKeyPart);
- if (!rebuildCommentAvailable) break;
+ }
+ if (isTTPCommentBuildTags(content))
+ hasMyBuildTag |= this.hasMyBuildTagInTTPComment(content, buildKeyPart);
}
+ rebuildCommentAvailable &= !hasMyBuildTag;
}
if (rebuildCommentAvailable) this.postBuildTagInTTPComment(id, "TTP build flag", buildKeyPart);
@@ -286,7 +284,8 @@ public class BitbucketRepository {
pullRequest.getDestination().getRepository().getRepositoryName(),
pullRequest.getTitle(),
pullRequest.getSource().getCommit().getHash(),
- pullRequest.getDestination().getCommit().getHash()
+ pullRequest.getDestination().getCommit().getHash(),
+ pullRequest.getAuthor().getCombinedUsername()
);
//@FIXME: Way to iterate over all available SCMSources
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/Pullrequest.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/Pullrequest.java
index 1e8bf5c..140c213 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/Pullrequest.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/Pullrequest.java
@@ -28,6 +28,7 @@ public class Pullrequest {
private String updatedOn;
private String mergeCommit;
private String id;
+ private Author author;
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Response {
@@ -233,6 +234,33 @@ public class Pullrequest {
this.createdOn = createdOn;
}
}
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Author {
+ private String username;
+ private String display_name;
+ public static final String COMBINED_NAME = "%s <@%s>";
+
+ public String getUsername() {
+ return username;
+ }
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ @JsonProperty("display_name")
+ public String getDisplayName() {
+ return display_name;
+ }
+
+ @JsonProperty("display_name")
+ public void setDisplayName(String display_name) {
+ this.display_name = display_name;
+ }
+ public String getCombinedUsername() {
+ return String.format(COMBINED_NAME, this.getDisplayName(), this.getUsername());
+ }
+ }
//-------------------- only getters and setters follow -----------------
@@ -341,5 +369,13 @@ public class Pullrequest {
public void setId(String id) {
this.id = id;
}
+
+ public Author getAuthor() {
+ return this.author;
+ }
+
+ public void setAutohor(Author author) {
+ this.author = author;
+ }
}
diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html
index 88af799..30e5f5e 100644
--- a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html
+++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html
@@ -1,12 +1,19 @@
-Filter option in custom format. Default value is empty or "any".
-Available formats:
-* any pull requests applied for this project: "any", "*" or empty string.
-* filtered by destination branch: "my-branch" or more complex reg-ex filter "r:^master" (must be started with "r:" and case insensitive match).
-* filtered by source and destination branches: "s:source-branch d:dest-branch"
-* filtered by source and destination branches with regex: "s:r:^feature d:r:master$"
-* filtered by many destination/source branches: "s:one s:two s:three d:master d:r:master$"
-* filtered by many sources branches: "s:one s:two s:r:^three d:"
-When you using format with source branch filter "s" or destination filter "d", you must cpecify great than one source and destination filter, eg "s:1 s:2 s:... d:".
-Any sources and any destinations for pull request:
-* filter string: "*"
-* filter string: "s: d:"
+<div>
+ Filter option in custom format. Default value is empty or <tt>any</tt>.<br/>
+ Available formats:
+ <ul>
+ <li>any pull requests applied for this project: <tt>any</tt>, <tt>*</tt> or empty string</li>
+ <li>filtered by destination branch: <tt>my-branch</tt> or more complex reg-ex filter <tt>r:^master</tt> (must be started with <tt>r:</tt> and case insensitive match).</li>
+ <li>filtered by source and destination branches: <tt>s:source-branch d:dest-branch</tt></li>
+ <li>filtered by source and destination branches with regex: <tt>s:r:^feature d:r:master$</tt></li>
+ <li>filtered by many destination/source branches: <tt>s:one s:two s:three d:master d:r:master$</tt></li>
+ <li>filtered by many sources branches: <tt>s:one s:two s:r:^three d:</tt></li>
+ </ul>
+ <p/>
+ When you using format with source branch filter <tt>s</tt> or destination filter <tt>d</tt>, you must specify great than one source and destination filter, eg <tt>s:1 s:2 s:... d:</tt>.<br/>
+ Any sources and any destinations for pull request:
+ <ul>
+ <li>filter string: <tt>*</tt></li>
+ <li>filter string: <tt>s: d:</tt></li>
+ </ul>
+</div>
diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html
index 3f24419..cc9d47c 100644
--- a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html
+++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html
@@ -1,3 +1,7 @@
-It is sugar-option for synchronize option "BranchesFilter" to Git SCM option "Branches to build" without manual editing.
-Check this option suppose than your "BranchesFilter" field has logick equal value with Git SCM "Branches to build" option (original value from "BranchesFilter" field will e ignored).
-If "Branches to build" option has values "*/master */feature-master */build-with-jenkins", then "BranchesFilter" field will have value "d:master d:feature-master d:build-with-jenkins".
+Uses the Git SCM option "Branches to build" option as the value for
+"BranchesFilter". If the "BranchesFilter" field itself has any content,
+it will be ignored.
+<br>
+If the "Branches to build" option has values
+"*/master */feature-master */build-with-jenkins", then "BranchesFilter"
+field will have value "d:master d:feature-master d:build-with-jenkins".
diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-ciSkipPhrases.html b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-ciSkipPhrases.html
new file mode 100644
index 0000000..f30f306
--- /dev/null
+++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-ciSkipPhrases.html
@@ -0,0 +1,5 @@
+A comma-separated list of strings to search the pull request title for.
+<br>
+e.g. If set to "trivial,[skiptest]", any PRs containing either "trivial" or
+"[skiptest]" (case-insensitive) will not be built.
+
diff --git a/src/test/java/BitbucketBuildFilterTest.java b/src/test/java/BitbucketBuildFilterTest.java
index 3b35dbc..3a94c82 100644
--- a/src/test/java/BitbucketBuildFilterTest.java
+++ b/src/test/java/BitbucketBuildFilterTest.java
@@ -142,6 +142,26 @@ public class BitbucketBuildFilterTest {
@Test
@WithoutJenkins
+ public void authorFilter() {
+ BitbucketCause cause = EasyMock.createMock(BitbucketCause.class);
+ EasyMock.expect(cause.getTargetBranch()).andReturn("master").anyTimes();
+ EasyMock.expect(cause.getSourceBranch()).andReturn("feature-master").anyTimes();
+ EasyMock.expect(cause.getPullRequestAuthor()).andReturn("test").anyTimes();
+ EasyMock.replay(cause);
+
+ for(String f : new String[] {"a:test", "a:r:^test", "d: s: a:", "a:", "a:foo a:test"}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertTrue(filter.approved(cause));
+ }
+
+ for(String f : new String[] {"s:feature-master", "d:master", "s:feature-master d: a:foo", "a:bar"}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertFalse(filter.approved(cause));
+ }
+ }
+
+ @Test
+ @WithoutJenkins
public void emptyGitSCMFilter() {
BitbucketCause cause = EasyMock.createMock(BitbucketCause.class);
EasyMock.expect(cause.getTargetBranch()).andReturn("master").anyTimes();