aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml11
-rw-r--r--pom.xml23
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java184
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java20
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java12
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java166
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java62
-rw-r--r--src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly8
-rw-r--r--src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html12
-rw-r--r--src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html3
-rw-r--r--src/test/java/BitbucketBuildFilterTest.java311
12 files changed, 771 insertions, 43 deletions
diff --git a/.gitignore b/.gitignore
index de7d788..4ed3c09 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
.idea
bitbucket-pullrequest-builder.iml
-target
+target/
work
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6a2566e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: java
+
+jdk:
+ - openjdk7
+ - oraclejdk7
+
+script:
+ mvn install -U
+
+after_failure:
+ - cat target/surefire-reports/*.txt
diff --git a/pom.xml b/pom.xml
index 8863269..4eda429 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,15 +3,15 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
- <version>1.509.4</version><!-- which version of Jenkins is this plugin built against? -->
+ <version>1.509.4</version><!-- which version of Jenkins is this plugin built against? -->
</parent>
<artifactId>bitbucket-pullrequest-builder</artifactId>
<name>Bitbucket Pullrequest Builder Plugin</name>
- <version>1.4.9-SNAPSHOT</version>
+ <version>1.4.13</version>
<description>This Jenkins plugin builds pull requests from Bitbucket.org and will report the test results.</description>
<packaging>hpi</packaging>
- <url>https://wiki.jenkins-ci.org/display/JENKINS/Bitbucket+pullrequest+builder+plugin</url>
+ <url>https://wiki.jenkins-ci.org/display/JENKINS/Bitbucket+pullrequest+builder+plugin</url>
<scm>
<connection>scm:git:ssh://git@github.com/jenkinsci/bitbucket-pullrequest-builder-plugin.git</connection>
@@ -25,10 +25,10 @@
<name>nishio_dens</name>
<email>nishio@densan-labs.net</email>
</developer>
- </developers>
-
+ </developers>
+
<!-- get every artifact through repo.jenkins-ci.org, which proxies all the artifacts that we need -->
- <repositories>
+ <repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
@@ -61,6 +61,17 @@
<artifactId>git</artifactId>
<version>2.2.4</version>
</dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>14.0-rc3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>3.4</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<pluginRepositories>
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java
new file mode 100644
index 0000000..c251930
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java
@@ -0,0 +1,184 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
+
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.regex.Matcher;
+import jenkins.plugins.git.AbstractGitSCMSource;
+import jenkins.scm.api.SCMSource;
+
+/**
+ * Mutable wrapper
+ */
+class Mutable<T> {
+ private T value;
+ public Mutable() { this.value = null; }
+ public Mutable(T value) { this.value = value; }
+ T get() { return this.value; }
+ void set(T value) { this.value = value; }
+}
+
+abstract class Filter {
+ protected static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName());
+
+ public static final String RX_FILTER_FLAG = "r";
+ public static final String RX_FILTER_FLAG_SINGLE = RX_FILTER_FLAG + ":";
+
+ 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 BRANCH_FILTER_RX_PART = "([^\\s$]*)";
+
+ abstract public boolean apply(String filter, BitbucketCause cause);
+ abstract public boolean check(String 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(); }
+}
+
+class EmptyFilter extends Filter {
+ @Override
+ public boolean apply(String filter, BitbucketCause cause) { return true; }
+ @Override
+ public boolean check(String filter) { return true; }
+}
+
+class AnyFlag extends Filter {
+ @Override
+ public boolean apply(String filter, BitbucketCause cause) { return true; }
+ @Override
+ public boolean check(String filter) { return filter.isEmpty() || filter.contains("*") || filter.toLowerCase().contains("any"); }
+}
+
+class OnlySourceFlag 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, "OnlySourceFlag using filter: {0}", selectedRx);
+ Matcher matcher = Pattern.compile(selectedRx, Pattern.CASE_INSENSITIVE).matcher(cause.getSourceBranch());
+ return filter.startsWith(RX_FILTER_FLAG_SINGLE) ? matcher.find() : matcher.matches();
+ }
+ @Override
+ public boolean check(String filter) {
+ return false;
+ }
+}
+
+class OnlyDestFlag 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, "OnlyDestFlag using filter: {0}", selectedRx);
+ Matcher matcher = Pattern.compile(selectedRx, Pattern.CASE_INSENSITIVE).matcher(cause.getTargetBranch());
+ return filter.startsWith(RX_FILTER_FLAG_SINGLE) ? matcher.find() : matcher.matches();
+ }
+ @Override
+ public boolean check(String filter) {
+ return !HasSourceOrDestPartsPredicate(filter);
+ }
+}
+
+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) &&
+ this.applyByRx(DST_MATCHER_RX, new OnlyDestFlag(), filter, cause);
+ }
+ @Override
+ public boolean check(String filter) {
+ return HasSourceOrDestPartsPredicate(filter);
+ }
+}
+
+/**
+ * Created by maxvodo
+ */
+public class BitbucketBuildFilter {
+ private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName());
+
+ private final String filter;
+ private Filter currFilter = null;
+ private static final List<Filter> AvailableFilters;
+
+ static {
+ ArrayList<Filter> filters = new ArrayList<Filter>();
+ filters.add(new AnyFlag());
+ filters.add(new OnlyDestFlag());
+ filters.add(new SourceDestFlag());
+ filters.add(new EmptyFilter());
+ AvailableFilters = filters;
+ }
+
+ public BitbucketBuildFilter(String f) {
+ this.filter = (f != null ? f : "").trim();
+ this.buildFilter(this.filter);
+ }
+
+ private void buildFilter(String filter) {
+ logger.log(Level.INFO, "Build filter by phrase: {0}", filter);
+ for(Filter f : AvailableFilters) {
+ if (f.check(filter)) {
+ this.currFilter = f;
+ logger.log(Level.INFO, "Using filter: {0}", f.getClass().getSimpleName());
+ break;
+ }
+ }
+ }
+
+ public boolean approved(BitbucketCause cause) {
+ logger.log(Level.INFO, "Approve cause: {0}", cause.toString());
+ return this.currFilter.apply(this.filter, cause);
+ }
+
+ public static BitbucketBuildFilter InstanceByString(String filter) {
+ logger.log(Level.INFO, "Filter instance by filter string");
+ return new BitbucketBuildFilter(filter);
+ }
+
+ static public String FilterFromGitSCMSource(AbstractGitSCMSource gitscm, String defaultFilter) {
+ if (gitscm == null) {
+ logger.log(Level.INFO, "Git SCMSource unavailable. Using default value: {0}", defaultFilter);
+ return defaultFilter;
+ }
+
+ String filter = defaultFilter;
+ final String includes = gitscm.getIncludes();
+ if (includes != null && !includes.isEmpty()) {
+ for(String part : includes.split("\\s+")) {
+ filter += String.format("%s ", part.replaceAll("\\*\\/", "d:"));
+ }
+ }
+
+ logger.log(Level.INFO, "Git includes transformation to filter result: {1} -> {0}; default: {2}", new Object[]{ filter, includes, defaultFilter });
+ return filter.trim();
+ }
+
+ public static BitbucketBuildFilter InstanceBySCM(Collection<SCMSource> scmSources, String defaultFilter) {
+ logger.log(Level.INFO, "Filter instance by using SCMSources list with {0} items", scmSources.size());
+ AbstractGitSCMSource gitscm = null;
+ for(SCMSource scm : scmSources) {
+ logger.log(Level.INFO, "Check {0} SCMSource ", scm.getClass());
+ if (scm instanceof AbstractGitSCMSource) {
+ gitscm = (AbstractGitSCMSource)scm;
+ break;
+ }
+ }
+ return new BitbucketBuildFilter(FilterFromGitSCMSource(gitscm, defaultFilter));
+ }
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
index 247bd67..cd9345c 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
@@ -28,16 +28,18 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
private final String password;
private final String repositoryOwner;
private final String repositoryName;
+ private final String branchesFilter;
+ private final boolean branchesFilterBySCMIncludes;
private final String ciKey;
private final String ciName;
private final String ciSkipPhrases;
private final boolean checkDestinationCommit;
private final boolean approveIfSuccess;
-
+
transient private BitbucketPullRequestsBuilder bitbucketPullRequestsBuilder;
@Extension
- public static final BitbucketBuildTriggerDescriptor descriptor = new BitbucketBuildTriggerDescriptor();
+ public static final BitbucketBuildTriggerDescriptor descriptor = new BitbucketBuildTriggerDescriptor();
@DataBoundConstructor
public BitbucketBuildTrigger(
@@ -47,6 +49,8 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
String password,
String repositoryOwner,
String repositoryName,
+ String branchesFilter,
+ boolean branchesFilterBySCMIncludes,
String ciKey,
String ciName,
String ciSkipPhrases,
@@ -60,6 +64,8 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
this.password = password;
this.repositoryOwner = repositoryOwner;
this.repositoryName = repositoryName;
+ this.branchesFilter = branchesFilter;
+ this.branchesFilterBySCMIncludes = branchesFilterBySCMIncludes;
this.ciKey = ciKey;
this.ciName = ciName;
this.ciSkipPhrases = ciSkipPhrases;
@@ -91,6 +97,14 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
return repositoryName;
}
+ public String getBranchesFilter() {
+ return branchesFilter;
+ }
+
+ public boolean getBranchesFilterBySCMIncludes() {
+ return branchesFilterBySCMIncludes;
+ }
+
public String getCiKey() {
return ciKey;
}
@@ -102,7 +116,7 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
public String getCiSkipPhrases() {
return ciSkipPhrases;
}
-
+
public boolean getCheckDestinationCommit() {
return checkDestinationCommit;
}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java
index 3671ea6..c5d4159 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java
@@ -2,9 +2,11 @@ package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest;
import hudson.model.AbstractProject;
+import java.security.MessageDigest;
import java.util.Collection;
import java.util.logging.Logger;
+import org.apache.commons.codec.binary.Hex;
/**
* Created by nishio
@@ -50,6 +52,16 @@ public class BitbucketPullRequestsBuilder {
public AbstractProject<?, ?> getProject() {
return this.project;
+ }
+
+ public String getProjectId() {
+ try {
+ final MessageDigest MD5 = MessageDigest.getInstance("MD5");
+ return new String(Hex.encodeHex(MD5.digest(this.project.getFullName().getBytes("UTF-8"))));
+ } catch (Exception exc) {
+ logger.severe(exc.toString());
+ }
+ return this.project.getFullName();
}
public BitbucketBuildTrigger getTrigger() {
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
index dd0f5e5..6736ee9 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
@@ -9,7 +9,15 @@ import java.util.logging.Logger;
import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.ApiClient;
import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BuildState;
import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest;
+import java.util.LinkedList;
+import java.util.logging.Level;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import jenkins.model.Jenkins;
+import jenkins.scm.api.SCMSource;
+import jenkins.scm.api.SCMSourceOwner;
+import jenkins.scm.api.SCMSourceOwners;
+import org.apache.commons.lang.StringUtils;
/**
* Created by nishio
@@ -18,6 +26,9 @@ public class BitbucketRepository {
private static final Logger logger = Logger.getLogger(BitbucketRepository.class.getName());
private static final String BUILD_DESCRIPTION = "%s: %s into %s";
private static final String BUILD_REQUEST_MARKER = "test this please";
+ private static final String BUILD_REQUEST_DONE_MARKER = "ttp build flag";
+ private static final String BUILD_REQUEST_MARKER_TAG_SINGLE_RX = "\\#[\\w\\-\\d]+";
+ private static final String BUILD_REQUEST_MARKER_TAGS_RX = "\\[bid\\:\\s?(.*)\\]";
private String projectPath;
private BitbucketPullRequestsBuilder builder;
@@ -30,15 +41,19 @@ public class BitbucketRepository {
}
public void init() {
- trigger = this.builder.getTrigger();
- client = new ApiClient(
+ this.init(null);
+ }
+
+ public void init(ApiClient client) {
+ this.trigger = this.builder.getTrigger();
+ this.client = (client == null) ? new ApiClient(
trigger.getUsername(),
trigger.getPassword(),
trigger.getRepositoryOwner(),
trigger.getRepositoryName(),
trigger.getCiKey(),
trigger.getCiName()
- );
+ ) : client;
}
public Collection<Pullrequest> getTargetPullRequests() {
@@ -87,7 +102,7 @@ public class BitbucketRepository {
comment = String.format(BUILD_DESCRIPTION, builder.getProject().getDisplayName(), sourceCommit, destinationBranch);
}
- this.client.setBuildStatus(owner, repository, sourceCommit, state, buildUrl, comment);
+ this.client.setBuildStatus(owner, repository, sourceCommit, state, buildUrl, comment, this.builder.getProjectId());
}
public void deletePullRequestApproval(String pullRequestId) {
@@ -97,10 +112,73 @@ public class BitbucketRepository {
public void postPullRequestApproval(String pullRequestId) {
this.client.postPullRequestApproval(pullRequestId);
}
-
+
+ public String getMyBuildTag(String buildKey) {
+ return "#" + this.client.buildStatusKey(buildKey);
+ }
+
+ final static Pattern BUILD_TAGS_RX = Pattern.compile(BUILD_REQUEST_MARKER_TAGS_RX, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ);
+ final static Pattern SINGLE_BUILD_TAG_RX = Pattern.compile(BUILD_REQUEST_MARKER_TAG_SINGLE_RX, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ);
+ final static String CONTENT_PART_TEMPLATE = "```[bid: %s]```";
+
+ private List<String> getAvailableBuildTagsFromTTPComment(String buildTags) {
+ logger.log(Level.INFO, "Parse {0}", new Object[]{ buildTags });
+ List<String> availableBuildTags = new LinkedList<String>();
+ Matcher subBuildTagMatcher = SINGLE_BUILD_TAG_RX.matcher(buildTags);
+ while(subBuildTagMatcher.find()) availableBuildTags.add(subBuildTagMatcher.group(0).trim());
+ return availableBuildTags;
+ }
+
+ public boolean hasMyBuildTagInTTPComment(String content, String buildKey) {
+ Matcher tagsMatcher = BUILD_TAGS_RX.matcher(content);
+ if (tagsMatcher.find()) {
+ logger.log(Level.INFO, "Content {0} g[1]:{1} mykey:{2}", new Object[] { content, tagsMatcher.group(1).trim(), this.getMyBuildTag(buildKey) });
+ return this.getAvailableBuildTagsFromTTPComment(tagsMatcher.group(1).trim()).contains(this.getMyBuildTag(buildKey));
+ }
+ else return false;
+ }
+
+ private void postBuildTagInTTPComment(String pullRequestId, String content, String buildKey) {
+ logger.log(Level.INFO, "Update build tag for {0} build key", buildKey);
+ List<String> builds = this.getAvailableBuildTagsFromTTPComment(content);
+ builds.add(this.getMyBuildTag(buildKey));
+ content += " " + String.format(CONTENT_PART_TEMPLATE, StringUtils.join(builds, " "));
+ 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());
+ }
+
+ private boolean isTTPCommentBuildTags(String content) {
+ return content.toLowerCase().contains(BUILD_REQUEST_DONE_MARKER.toLowerCase());
+ }
+
+ public List<Pullrequest.Comment> filterPullRequestComments(List<Pullrequest.Comment> comments) {
+ logger.info("Filter PullRequest Comments.");
+ Collections.sort(comments);
+ Collections.reverse(comments);
+ List<Pullrequest.Comment> filteredComments = new LinkedList<Pullrequest.Comment>();
+ for(Pullrequest.Comment comment : comments) {
+ String content = comment.getContent();
+ if (content == null || content.isEmpty()) continue;
+ boolean isTTP = this.isTTPComment(content);
+ boolean isTTPBuild = this.isTTPCommentBuildTags(content);
+ if (isTTP || isTTPBuild) filteredComments.add(comment);
+ if (isTTP) break;
+ }
+ return filteredComments;
+ }
+
private boolean isBuildTarget(Pullrequest pullRequest) {
if (pullRequest.getState() != null && pullRequest.getState().equals("OPEN")) {
- if (isSkipBuild(pullRequest.getTitle())) {
+ if (isSkipBuild(pullRequest.getTitle()) || !isFilteredBuild(pullRequest)) {
return false;
}
@@ -110,32 +188,41 @@ public class BitbucketRepository {
String owner = destination.getRepository().getOwnerName();
String repositoryName = destination.getRepository().getRepositoryName();
- String id = pullRequest.getId();
+ Pullrequest.Repository sourceRepository = source.getRepository();
+ String buildKeyPart = this.builder.getProjectId();
+
+ final boolean commitAlreadyBeenProcessed = this.client.hasBuildStatus(
+ sourceRepository.getOwnerName(), sourceRepository.getRepositoryName(), sourceCommit, buildKeyPart
+ );
+ if (commitAlreadyBeenProcessed) logger.log(Level.INFO,
+ "Commit {0}#{1} has already been processed",
+ new Object[]{ sourceCommit, buildKeyPart }
+ );
+
+ final String id = pullRequest.getId();
List<Pullrequest.Comment> comments = client.getPullRequestComments(owner, repositoryName, id);
+ boolean rebuildCommentAvailable = false;
if (comments != null) {
- Collections.sort(comments);
- Collections.reverse(comments);
- for (Pullrequest.Comment comment : comments) {
+ Collection<Pullrequest.Comment> filteredComments = this.filterPullRequestComments(comments);
+ for (Pullrequest.Comment comment : filteredComments) {
String content = comment.getContent();
- if (content == null || content.isEmpty()) {
- continue;
- }
-
- if (content.contains(BUILD_REQUEST_MARKER.toLowerCase())) {
- return true;
- }
+ if (this.isTTPComment(content)) {
+ rebuildCommentAvailable = true;
+ logger.log(Level.INFO,
+ "Rebuild comment available for commit {0} and comment #{1}",
+ new Object[]{ sourceCommit, comment.getId() }
+ );
+ }
+ rebuildCommentAvailable &= this.processTTPCommentBuildTags(content, buildKeyPart);
+ if (!rebuildCommentAvailable) break;
}
- }
-
- Pullrequest.Repository sourceRepository = source.getRepository();
-
- if (this.client.hasBuildStatus(sourceRepository.getOwnerName(), sourceRepository.getRepositoryName(), sourceCommit)) {
- logger.info("Commit " + sourceCommit + " has already been processed");
- return false;
- }
+ }
+ if (rebuildCommentAvailable) this.postBuildTagInTTPComment(id, "TTP build flag", buildKeyPart);
- return true;
+ final boolean canBuildTarget = rebuildCommentAvailable || !commitAlreadyBeenProcessed;
+ logger.log(Level.INFO, "Build target? {0} [rebuild:{1} processed:{2}]", new Object[]{ canBuildTarget, rebuildCommentAvailable, commitAlreadyBeenProcessed});
+ return canBuildTarget;
}
return false;
@@ -153,4 +240,31 @@ public class BitbucketRepository {
}
return false;
}
+
+ private boolean isFilteredBuild(Pullrequest pullRequest) {
+ BitbucketCause cause = new BitbucketCause(
+ pullRequest.getSource().getBranch().getName(),
+ pullRequest.getDestination().getBranch().getName(),
+ pullRequest.getSource().getRepository().getOwnerName(),
+ pullRequest.getSource().getRepository().getRepositoryName(),
+ pullRequest.getId(),
+ pullRequest.getDestination().getRepository().getOwnerName(),
+ pullRequest.getDestination().getRepository().getRepositoryName(),
+ pullRequest.getTitle(),
+ pullRequest.getSource().getCommit().getHash(),
+ pullRequest.getDestination().getCommit().getHash()
+ );
+
+ //@FIXME: Way to iterate over all available SCMSources
+ List<SCMSource> sources = new LinkedList<SCMSource>();
+ for(SCMSourceOwner owner : SCMSourceOwners.all())
+ for(SCMSource src : owner.getSCMSources())
+ sources.add(src);
+
+ BitbucketBuildFilter filter = !this.trigger.getBranchesFilterBySCMIncludes() ?
+ BitbucketBuildFilter.InstanceByString(this.trigger.getBranchesFilter()) :
+ BitbucketBuildFilter.InstanceBySCM(sources, this.trigger.getBranchesFilter());
+
+ return filter.approved(cause);
+ }
}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java
index c8f1818..e635f65 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java
@@ -16,6 +16,9 @@ import java.util.logging.Logger;
import jenkins.model.Jenkins;
import hudson.ProxyConfiguration;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.commons.httpclient.util.EncodingUtil;
/**
* Created by nishio
@@ -24,6 +27,7 @@ public class ApiClient {
private static final Logger logger = Logger.getLogger(ApiClient.class.getName());
private static final String V1_API_BASE_URL = "https://bitbucket.org/api/1.0/repositories/";
private static final String V2_API_BASE_URL = "https://bitbucket.org/api/2.0/repositories/";
+ private static final String COMPUTED_KEY_FORMAT = "%s-%s";
private String owner;
private String repositoryName;
private Credentials credentials;
@@ -55,28 +59,53 @@ public class ApiClient {
}
return Collections.EMPTY_LIST;
}
+
+ public String getName() {
+ return this.name;
+ }
+
+ private String computeAPIKey(String keyExPart) {
+ return String.format(COMPUTED_KEY_FORMAT, this.key, keyExPart);
+ }
+
+ public String buildStatusKey(String bsKey) {
+ return this.computeAPIKey(bsKey);
+ }
- public boolean hasBuildStatus(String owner, String repositoryName, String revision) {
- String url = v2(owner, repositoryName, "/commit/" + revision + "/statuses/build/" + this.key);
+ public boolean hasBuildStatus(String owner, String repositoryName, String revision, String keyEx) {
+ String url = v2(owner, repositoryName, "/commit/" + revision + "/statuses/build/" + this.computeAPIKey(keyEx));
return get(url).contains("\"state\"");
}
- public void setBuildStatus(String owner, String repositoryName, String revision, BuildState state, String buildUrl, String comment) {
+ public void setBuildStatus(String owner, String repositoryName, String revision, BuildState state, String buildUrl, String comment, String keyEx) {
String url = v2(owner, repositoryName, "/commit/" + revision + "/statuses/build");
+ String computedKey = this.computeAPIKey(keyEx);
NameValuePair[] data = new NameValuePair[]{
new NameValuePair("description", comment),
- new NameValuePair("key", this.key),
+ new NameValuePair("key", computedKey),
new NameValuePair("name", this.name),
new NameValuePair("state", state.toString()),
new NameValuePair("url", buildUrl),
};
- logger.info("POST state " + state + " to " + url);
- post(url, data);
+ logger.log(Level.INFO, "POST state {0} to {1} with key {2} with response {3}", new Object[]{
+ state, url, computedKey, post(url, data)}
+ );
}
public void deletePullRequestApproval(String pullRequestId) {
delete(v2("/pullrequests/" + pullRequestId + "/approve"));
}
+
+ public void deletePullRequestComment(String pullRequestId, String commentId) {
+ delete(v1("/pullrequests/" + pullRequestId + "/comments/" + commentId));
+ }
+
+ public void updatePullRequestComment(String pullRequestId, String content, String commentId) {
+ NameValuePair[] data = new NameValuePair[] {
+ new NameValuePair("content", content),
+ };
+ put(v1("/pullrequests/" + pullRequestId + "/comments/" + commentId), data);
+ }
public Pullrequest.Participant postPullRequestApproval(String pullRequestId) {
try {
@@ -87,6 +116,19 @@ public class ApiClient {
}
return null;
}
+
+ public Pullrequest.Comment postPullRequestComment(String pullRequestId, String content) {
+ NameValuePair[] data = new NameValuePair[] {
+ new NameValuePair("content", content),
+ };
+ try {
+ return parse(post(v1("/pullrequests/" + pullRequestId + "/comments"), data), new TypeReference<Pullrequest.Comment>() {});
+ } catch(Exception e) {
+ logger.log(Level.WARNING, "Invalid pull request comment response.", e);
+ e.printStackTrace();
+ }
+ return null;
+ }
private HttpClient getHttpClient() {
HttpClient client = new HttpClient();
@@ -128,12 +170,20 @@ public class ApiClient {
private String post(String path, NameValuePair[] data) {
PostMethod req = new PostMethod(path);
req.setRequestBody(data);
+ req.getParams().setContentCharset("utf-8");
return send(req);
}
private void delete(String path) {
send(new DeleteMethod(path));
}
+
+ private void put(String path, NameValuePair[] data) {
+ PutMethod req = new PutMethod(path);
+ req.setRequestBody(EncodingUtil.formUrlEncode(data, "utf-8"));
+ req.getParams().setContentCharset("utf-8");
+ send(req);
+ }
private String send(HttpMethodBase req) {
HttpClient client = getHttpClient();
diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly
index 82ab08c..25acac0 100644
--- a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly
+++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly
@@ -14,6 +14,12 @@
<f:entry title="RepositoryName" field="repositoryName">
<f:textbox />
</f:entry>
+ <f:entry title="BranchesFilter" field="branchesFilter">
+ <f:textbox />
+ </f:entry>
+ <f:entry title="Using Git SCM 'Branches to build' option to filter pull requests?" field="branchesFilterBySCMIncludes">
+ <f:checkbox />
+ </f:entry>
<f:entry title="CI Identifier" field="ciKey">
<f:textbox default="jenkins" />
</f:entry>
@@ -29,4 +35,4 @@
<f:entry title="Approve if build success?" field="approveIfSuccess">
<f:checkbox />
</f:entry>
-</j:jelly> \ No newline at end of file
+</j:jelly>
diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html
new file mode 100644
index 0000000..88af799
--- /dev/null
+++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html
@@ -0,0 +1,12 @@
+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:"
diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html
new file mode 100644
index 0000000..3f24419
--- /dev/null
+++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html
@@ -0,0 +1,3 @@
+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".
diff --git a/src/test/java/BitbucketBuildFilterTest.java b/src/test/java/BitbucketBuildFilterTest.java
new file mode 100644
index 0000000..0fabfe4
--- /dev/null
+++ b/src/test/java/BitbucketBuildFilterTest.java
@@ -0,0 +1,311 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.BitbucketBuildFilter;
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.BitbucketCause;
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.BitbucketPullRequestsBuilder;
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.BitbucketRepository;
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.ApiClient;
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+import jenkins.plugins.git.AbstractGitSCMSource;
+import org.easymock.*;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.Rule;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.WithoutJenkins;
+
+/**
+ *
+ * @author maxvodo
+ */
+public class BitbucketBuildFilterTest {
+
+ @Rule
+ public JenkinsRule jRule = new JenkinsRule();
+
+ @Test
+ @WithoutJenkins
+ public void mockTest() {
+ BitbucketCause cause = EasyMock.createMock(BitbucketCause.class);
+ EasyMock.expect(cause.getTargetBranch()).andReturn("mock").anyTimes();
+ EasyMock.replay(cause);
+ for(Integer i : new Integer[] {1, 2, 3, 4, 5}) assertEquals("mock", cause.getTargetBranch());
+ }
+
+ @Test
+ @WithoutJenkins
+ public void anyFilter() {
+ BitbucketCause cause = EasyMock.createMock(BitbucketCause.class);
+ EasyMock.expect(cause.getTargetBranch()).andReturn("master").anyTimes();
+ EasyMock.replay(cause);
+
+ for(String f : new String[] {"", "*", "any"}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertTrue(filter.approved(cause));
+ }
+
+ for(String f : new String[] {"foo", "bar", " baz "}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertFalse(filter.approved(cause));
+ }
+ }
+
+ @Test
+ @WithoutJenkins
+ public void onlyDestinationFilter() {
+ BitbucketCause cause = EasyMock.createMock(BitbucketCause.class);
+ EasyMock.expect(cause.getTargetBranch()).andReturn("master-branch").anyTimes();
+ EasyMock.replay(cause);
+
+ for(String f : new String[] {"master-branch", "r:^master", "r:branch$", " master-branch "}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertTrue(filter.approved(cause));
+ }
+
+ for(String f : new String[] {"develop", "feature-good-thing", "r:develop$"}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertFalse(filter.approved(cause));
+ }
+ }
+
+ @Test
+ @WithoutJenkins
+ public void rxSourceDestCheck() {
+ for(String f : new String[] {"", "master", "r:master", "*"})
+ assertFalse(Pattern.compile("(s:)|(d:)").matcher(f).find());
+
+ for(String f : new String[] {"s:master d:feature-master", "s:master d:r:^feature", "s:r:^master d:r:^feature"})
+ assertTrue(Pattern.compile("(s:)|(d:)").matcher(f).find());
+ }
+
+ @Test
+ @WithoutJenkins
+ public void sourceAndDestFilter() {
+ BitbucketCause cause = EasyMock.createMock(BitbucketCause.class);
+ EasyMock.expect(cause.getTargetBranch()).andReturn("master").anyTimes();
+ EasyMock.expect(cause.getSourceBranch()).andReturn("feature-for-master").anyTimes();
+ EasyMock.replay(cause);
+
+ for(String f : new String[] {"s:feature-for-master d:master", "s:r:^feature d:master", "s:feature-for-master d:r:^m", "s:r:^feature d:r:^master"}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertTrue(filter.approved(cause));
+ }
+
+ for(String f : new String[] {"s:feature-for-master d:foo", "s:bar d:master", "s:foo d:bar"}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertFalse(filter.approved(cause));
+ }
+ }
+
+ @Test
+ @WithoutJenkins
+ public void multipleSrcDestFilter() {
+ BitbucketCause cause = EasyMock.createMock(BitbucketCause.class);
+ EasyMock.expect(cause.getTargetBranch()).andReturn("master").anyTimes();
+ EasyMock.expect(cause.getSourceBranch()).andReturn("feature-master").anyTimes();
+ EasyMock.replay(cause);
+
+ for(String f : new String[] {"s: d:", "s:r:^feature s:good-branch d:r:.*", "s:good-branch s:feature-master d:r:.*", "s: d:r:.*", "d:master d:foo d:bar s:"}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertTrue(filter.approved(cause));
+ }
+
+ for(String f : new String[] {"d:ggg d:ooo d:333 s:feature-master", "s:111 s:222 s:333 d:master"}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertFalse(filter.approved(cause));
+ }
+ }
+
+ @Test
+ @WithoutJenkins
+ public void sourceAndDestPartiallyFilter() {
+ BitbucketCause cause = EasyMock.createMock(BitbucketCause.class);
+ EasyMock.expect(cause.getTargetBranch()).andReturn("master").anyTimes();
+ EasyMock.expect(cause.getSourceBranch()).andReturn("feature-master").anyTimes();
+ EasyMock.replay(cause);
+
+ for(String f : new String[] {"s:feature-master d:", "d:master s:"}) {
+ BitbucketBuildFilter filter = BitbucketBuildFilter.InstanceByString(f);
+ assertTrue(filter.approved(cause));
+ }
+
+ for(String f : new String[] {"s:feature-master", "d:master"}) {
+ 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();
+ EasyMock.replay(cause);
+
+ assertTrue(BitbucketBuildFilter.FilterFromGitSCMSource(null, "").isEmpty());
+ assertEquals("default", BitbucketBuildFilter.FilterFromGitSCMSource(null, "default"));
+
+ assertTrue(BitbucketBuildFilter.InstanceByString(
+ BitbucketBuildFilter.FilterFromGitSCMSource(null, "")).approved(cause)
+ );
+ }
+
+ @Test
+ @WithoutJenkins
+ public void fromGitSCMFilter() {
+ AbstractGitSCMSource git = EasyMock.createMock(AbstractGitSCMSource.class);
+ EasyMock.expect(git.getIncludes())
+ .andReturn("").times(1)
+ .andReturn("").times(1)
+ .andReturn("*/master */feature-branch").times(1)
+ .andReturn("*/master").anyTimes();
+ EasyMock.replay(git);
+
+ assertTrue(git.getIncludes().isEmpty());
+ assertEquals("", BitbucketBuildFilter.FilterFromGitSCMSource(git, ""));
+ assertEquals("d:master d:feature-branch", BitbucketBuildFilter.FilterFromGitSCMSource(git, ""));
+ assertEquals("d:master", BitbucketBuildFilter.FilterFromGitSCMSource(git, ""));
+ }
+
+ @Test
+ @WithoutJenkins
+ public void filterPRComments() {
+ BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class);
+ EasyMock.expect(builder.getTrigger()).andReturn(null).anyTimes();
+ EasyMock.replay(builder);
+
+ List<Pullrequest.Comment> comments = new LinkedList<Pullrequest.Comment>();
+ for(String commentContent : new String[] {
+ "check",
+ "",
+ "Hello from mock",
+ "Jenkins: test this please",
+ "TTP build flag [bid: #jenkins-902f259e962ff16100843123480a0970]",
+ "check",
+ "",
+ "Hello from mock",
+ "Jenkins: test this please",
+ "TTP build flag [bid: #jenkins-902f259e962ff16100843123480a0970]",
+ "TTP build flag [bid: #jenkins-902f259e962ff16100843123480a0970 #jenkins-foo]",
+ "TTP build flag [bid: #jenkins-902f259e962ff16100843123480a0970 #jenkins-foo #jenkins-bar]",
+ }) {
+ Pullrequest.Comment comment = EasyMock.createNiceMock(Pullrequest.Comment.class);
+ EasyMock.expect(comment.getContent()).andReturn(commentContent).anyTimes();
+ EasyMock.expect(comment.getId()).andReturn(new java.sql.Timestamp(Calendar.getInstance().getTime().getTime()).getNanos()).anyTimes();
+ EasyMock.replay(comment);
+ comments.add(comment);
+ }
+
+ // Check twice
+ assertEquals("check", comments.get(0).getContent());
+ assertEquals("check", comments.get(0).getContent());
+
+ assertEquals("Hello from mock", comments.get(2).getContent());
+
+ BitbucketRepository repo = new BitbucketRepository("", builder);
+ repo.init(EasyMock.createNiceMock(ApiClient.class));
+
+ List<Pullrequest.Comment> filteredComments = repo.filterPullRequestComments(comments);
+
+ assertTrue(filteredComments.size() == 4);
+ assertEquals("Jenkins: test this please", filteredComments.get(filteredComments.size() - 1).getContent());
+ }
+
+ @Test
+ @WithoutJenkins
+ public void checkHashMyBuildTagTrue() {
+ BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class);
+ EasyMock.expect(builder.getTrigger()).andReturn(null).anyTimes();
+ EasyMock.replay(builder);
+
+ IMockBuilder<BitbucketRepository> repoBuilder = EasyMock.partialMockBuilder(BitbucketRepository.class);
+ repoBuilder.addMockedMethod("getMyBuildTag");
+ BitbucketRepository repo = repoBuilder.createMock();
+ EasyMock.expect(repo.getMyBuildTag(EasyMock.anyString())).andReturn("#jenkins-902f259e962ff16100843123480a0970").anyTimes();
+ EasyMock.replay(repo);
+
+ List<Pullrequest.Comment> comments = new LinkedList<Pullrequest.Comment>();
+ for(String commentContent : new String[] {
+ "TTP build flag [bid: #jenkins-902f259e962ff16100843123480a0970]",
+ "TTP build flag [bid: #jenkins-902f259e962ff16100843123480a0970 #jenkins-foo]",
+ "TTP build flag [bid: #jenkins-902f259e962ff16100843123480a0970 #jenkins-foo #jenkins-bar]",
+ "TTP build flag ```[bid: #jenkins-902f259e962ff16100843123480a0970 #jenkins-foo #jenkins-bar]```",
+ }) {
+ Pullrequest.Comment comment = EasyMock.createNiceMock(Pullrequest.Comment.class);
+ EasyMock.expect(comment.getContent()).andReturn(commentContent).anyTimes();
+ EasyMock.expect(comment.getId()).andReturn(new java.sql.Timestamp(Calendar.getInstance().getTime().getTime()).getNanos()).anyTimes();
+ EasyMock.replay(comment);
+ comments.add(comment);
+ }
+
+ String myBuildKey = "902f259e962ff16100843123480a0970";
+ for(Pullrequest.Comment comment : comments)
+ assertTrue(repo.hasMyBuildTagInTTPComment(comment.getContent(), myBuildKey));
+ }
+
+ @Test
+ @WithoutJenkins
+ public void checkHashMyBuildTagFalse() {
+ BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class);
+ EasyMock.expect(builder.getTrigger()).andReturn(null).anyTimes();
+ EasyMock.replay(builder);
+
+ IMockBuilder<BitbucketRepository> repoBuilder = EasyMock.partialMockBuilder(BitbucketRepository.class);
+ repoBuilder.addMockedMethod("getMyBuildTag");
+ BitbucketRepository repo = repoBuilder.createMock();
+ EasyMock.expect(repo.getMyBuildTag(EasyMock.anyString())).andReturn("#jenkins-902f259e962ff16100843123480a0970").anyTimes();
+ EasyMock.replay(repo);
+
+ List<Pullrequest.Comment> comments = new LinkedList<Pullrequest.Comment>();
+ for(String commentContent : new String[] {
+ "check",
+ "",
+ "Hello from mock",
+ "Jenkins: test this please",
+ "TTP build flag [bid: #jenkins]",
+ "TTP build flag [bid: #jenkins-foo]",
+ "TTP build flag [bid: #jenkins-foo #jenkins-bar]",
+ "TTP build flag ```[bid: #jenkins-foo #jenkins-bar]```",
+ }) {
+ Pullrequest.Comment comment = EasyMock.createNiceMock(Pullrequest.Comment.class);
+ EasyMock.expect(comment.getContent()).andReturn(commentContent).anyTimes();
+ EasyMock.expect(comment.getId()).andReturn(new java.sql.Timestamp(Calendar.getInstance().getTime().getTime()).getNanos()).anyTimes();
+ EasyMock.replay(comment);
+ comments.add(comment);
+ }
+
+ String myBuildKey = "902f259e962ff16100843123480a0970";
+ for(Pullrequest.Comment comment : comments)
+ assertFalse(repo.hasMyBuildTagInTTPComment(comment.getContent(), myBuildKey));
+ }
+
+ //@Test
+ @WithoutJenkins
+ public void ttpCommentTest() {
+ ApiClient client = EasyMock.createNiceMock(ApiClient.class);
+ Collection<List<Pullrequest>> prs = new LinkedList<List<Pullrequest>>();
+
+ prs.add(Arrays.asList(new Pullrequest[] {
+ new Pullrequest()
+ }));
+
+ for(List<Pullrequest> pr : prs) EasyMock.expect(client.getPullRequests()).andReturn(pr).times(1);
+ BitbucketPullRequestsBuilder builder = EasyMock.createMock(BitbucketPullRequestsBuilder.class);
+ EasyMock.replay(client, builder);
+
+ BitbucketRepository repo = new BitbucketRepository("", builder);
+ repo.init(client);
+
+ Collection<Pullrequest> targetPRs = repo.getTargetPullRequests();
+ }
+}