From 523e5384fc123a554d9a0babc1ae3493d50499bb Mon Sep 17 00:00:00 2001 From: Maxim Epishchev Date: Wed, 13 Jan 2016 15:46:45 +0300 Subject: Add branches to build filter (similar issue:61) --- .../bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly | 5 ++++- .../BitbucketBuildTrigger/help-branchesFilter.html | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html (limited to 'src/main/resources/bitbucketpullrequestbuilder') diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly index 82ab08c..845926d 100644 --- a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly +++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly @@ -14,6 +14,9 @@ + + + @@ -29,4 +32,4 @@ - \ No newline at end of file + 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..8b801db --- /dev/null +++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html @@ -0,0 +1,5 @@ +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" \ No newline at end of file -- cgit v1.2.3 From 31b3fbaa1926c751b651d58044a5216b64566029 Mon Sep 17 00:00:00 2001 From: Maxim Epishchev Date: Fri, 15 Jan 2016 19:34:49 +0300 Subject: Fix UTF-8 build statuses and fix issue:1; also minor fixes --- pom.xml | 2 +- .../BitbucketBuildFilter.java | 7 +++-- .../BitbucketPullRequestsBuilder.java | 12 ++++++++ .../BitbucketRepository.java | 33 ++++++++++++++-------- .../bitbucket/ApiClient.java | 20 +++++++++---- .../BitbucketBuildTrigger/help-branchesFilter.html | 2 +- 6 files changed, 54 insertions(+), 22 deletions(-) (limited to 'src/main/resources/bitbucketpullrequestbuilder') diff --git a/pom.xml b/pom.xml index 2bb3a93..6c9ed74 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ bitbucket-pullrequest-builder Bitbucket Pullrequest Builder Plugin - 1.4.10 + 1.4.11 This Jenkins plugin builds pull requests from Bitbucket.org and will report the test results. hpi https://wiki.jenkins-ci.org/display/JENKINS/Bitbucket+pullrequest+builder+plugin diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java index eb80d86..230432d 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java @@ -85,22 +85,23 @@ public class BitbucketBuildFilter { } public BitbucketBuildFilter(String f) { - this.filter = f; + this.filter = f != null ? f : ""; 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; } - } - logger.warning("No available filters to use ..."); + } } public boolean approved(BitbucketCause cause) { + logger.log(Level.INFO, "Approve cause: {0}", cause.toString()); return this.currFilter.apply(this.filter, cause); } } 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 e0a4b19..2686ffd 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java @@ -9,6 +9,7 @@ import java.util.logging.Logger; import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.ApiClient; import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BuildState; import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest; +import java.util.logging.Level; import jenkins.model.Jenkins; /** @@ -87,7 +88,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) { @@ -110,9 +111,20 @@ public class BitbucketRepository { String owner = destination.getRepository().getOwnerName(); String repositoryName = destination.getRepository().getRepositoryName(); + Pullrequest.Repository sourceRepository = source.getRepository(); + + boolean commitAlreadyBeenProcessed = this.client.hasBuildStatus( + sourceRepository.getOwnerName(), sourceRepository.getRepositoryName(), sourceCommit, this.builder.getProjectId() + ); + if (commitAlreadyBeenProcessed) logger.log(Level.INFO, + "Commit {0}#{1} has already been processed", + new Object[]{ sourceCommit, this.builder.getProjectId() } + ); + String id = pullRequest.getId(); List comments = client.getPullRequestComments(owner, repositoryName, id); + boolean rebuildCommentAvailable = false; if (comments != null) { Collections.sort(comments); Collections.reverse(comments); @@ -123,19 +135,18 @@ public class BitbucketRepository { } if (content.contains(BUILD_REQUEST_MARKER.toLowerCase())) { - return true; + rebuildCommentAvailable = true; + logger.log(Level.INFO, + "Rebuild comment available for commit {0} and comment #{1}", + new Object[]{ sourceCommit, comment.getId() } + ); + this.client.deleteComment(id, Integer.toString(comment.getId())); } } - } - - Pullrequest.Repository sourceRepository = source.getRepository(); - - if (this.client.hasBuildStatus(sourceRepository.getOwnerName(), sourceRepository.getRepositoryName(), sourceCommit)) { - logger.info("Commit " + sourceCommit + " has already been processed"); - return false; - } + } - return true; + logger.log(Level.INFO, "Build target? {0}", rebuildCommentAvailable || !commitAlreadyBeenProcessed); + return rebuildCommentAvailable || !commitAlreadyBeenProcessed; } return false; diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java index c8f1818..18ca86f 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java @@ -24,6 +24,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; @@ -56,27 +57,33 @@ public class ApiClient { return Collections.EMPTY_LIST; } - 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/" + String.format(COMPUTED_KEY_FORMAT, this.key, 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 = String.format(COMPUTED_KEY_FORMAT, this.key, 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 deleteComment(String pullRequestId, String commentId) { + delete(v1("/pullrequests/" + pullRequestId + "/comments/" + commentId + "/")); + } public Pullrequest.Participant postPullRequestApproval(String pullRequestId) { try { @@ -128,6 +135,7 @@ 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); } diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html index 8b801db..f49da40 100644 --- a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html +++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html @@ -2,4 +2,4 @@ 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" \ No newline at end of file +* filtered by source and destination branches: "s:source-branch d:dest-branch" -- cgit v1.2.3 From 91a85604177f7df2eb204c9e82564142dbe328f8 Mon Sep 17 00:00:00 2001 From: Maxim Epishchev Date: Thu, 21 Jan 2016 19:52:23 +0300 Subject: Refactoring filters code and add tests Now we can using more complex filters with source/destination filtration. Also add experimental integration with Git SCM plugin. --- pom.xml | 8 +- .../BitbucketBuildFilter.java | 94 ++++++++++-- .../BitbucketBuildTrigger.java | 7 + .../BitbucketRepository.java | 15 +- .../BitbucketBuildTrigger/config.jelly | 3 + .../BitbucketBuildTrigger/help-branchesFilter.html | 7 + .../help-branchesFilterBySCMIncludes.html | 3 + src/test/java/BitbucketBuildFilterTest.java | 170 +++++++++++++++++++++ 8 files changed, 295 insertions(+), 12 deletions(-) create mode 100644 src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilterBySCMIncludes.html create mode 100644 src/test/java/BitbucketBuildFilterTest.java (limited to 'src/main/resources/bitbucketpullrequestbuilder') diff --git a/pom.xml b/pom.xml index 6c9ed74..d161718 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ bitbucket-pullrequest-builder Bitbucket Pullrequest Builder Plugin - 1.4.11 + 1.4.12 This Jenkins plugin builds pull requests from Bitbucket.org and will report the test results. hpi https://wiki.jenkins-ci.org/display/JENKINS/Bitbucket+pullrequest+builder+plugin @@ -66,6 +66,12 @@ guava 14.0-rc3 + + org.easymock + easymock + 3.4 + test + diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java index 230432d..1072337 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java @@ -1,11 +1,14 @@ package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; +import hudson.ExtensionList; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.ArrayList; -import java.util.Collections; 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 @@ -24,8 +27,15 @@ abstract class Filter { 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 { @@ -42,27 +52,57 @@ class AnyFlag extends Filter { 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) { - return Pattern.compile( - filter.startsWith(RX_FILTER_FLAG_SINGLE) ? filter.substring(RX_FILTER_FLAG_SINGLE.length()) : Pattern.quote(filter), - Pattern.CASE_INSENSITIVE).matcher(cause.getTargetBranch()).find(); + 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 !(Pattern.matches("(s:)|(d:)", filter)); + return !HasSourceOrDestPartsPredicate(filter); } } -class SourceDestFlag extends 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 false; + 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 Pattern.matches("(s:)|(d:)", filter); + return HasSourceOrDestPartsPredicate(filter); } } @@ -80,12 +120,13 @@ public class BitbucketBuildFilter { ArrayList filters = new ArrayList(); filters.add(new AnyFlag()); filters.add(new OnlyDestFlag()); - filters.add(new EmptyFilter()); + filters.add(new SourceDestFlag()); + filters.add(new EmptyFilter()); AvailableFilters = filters; } public BitbucketBuildFilter(String f) { - this.filter = f != null ? f : ""; + this.filter = (f != null ? f : "").trim(); this.buildFilter(this.filter); } @@ -104,4 +145,37 @@ public class BitbucketBuildFilter { 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(ExtensionList scmSources, String defaultFilter) { + logger.log(Level.FINE, "Filter instance by using SCM"); + AbstractGitSCMSource gitscm = null; + for(SCMSource scm : scmSources) { + gitscm = (AbstractGitSCMSource)scm; + if (gitscm != null) 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 254eec3..cd9345c 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java @@ -29,6 +29,7 @@ public class BitbucketBuildTrigger extends Trigger> { 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; @@ -49,6 +50,7 @@ public class BitbucketBuildTrigger extends Trigger> { String repositoryOwner, String repositoryName, String branchesFilter, + boolean branchesFilterBySCMIncludes, String ciKey, String ciName, String ciSkipPhrases, @@ -63,6 +65,7 @@ public class BitbucketBuildTrigger extends Trigger> { this.repositoryOwner = repositoryOwner; this.repositoryName = repositoryName; this.branchesFilter = branchesFilter; + this.branchesFilterBySCMIncludes = branchesFilterBySCMIncludes; this.ciKey = ciKey; this.ciName = ciName; this.ciSkipPhrases = ciSkipPhrases; @@ -97,6 +100,10 @@ public class BitbucketBuildTrigger extends Trigger> { public String getBranchesFilter() { return branchesFilter; } + + public boolean getBranchesFilterBySCMIncludes() { + return branchesFilterBySCMIncludes; + } public String getCiKey() { return ciKey; diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java index 2686ffd..c71cc99 100644 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java +++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java @@ -10,7 +10,10 @@ import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.ApiClie import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BuildState; import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest; import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import jenkins.model.Jenkins; +import jenkins.scm.api.SCMSource; /** * Created by nishio @@ -19,6 +22,7 @@ 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_MARKER_COUNTER_RX = "\\[(\\d+)]\\"; private String projectPath; private BitbucketPullRequestsBuilder builder; @@ -99,6 +103,11 @@ public class BitbucketRepository { this.client.postPullRequestApproval(pullRequestId); } + private Integer extractRebuildTimesFromComment(String content) { + Matcher matcher = Pattern.compile(BUILD_REQUEST_MARKER_COUNTER_RX).matcher(content); + return matcher.groupCount() >= 1 ? Integer.parseInt(matcher.group(1)) : 0; + } + private boolean isBuildTarget(Pullrequest pullRequest) { if (pullRequest.getState() != null && pullRequest.getState().equals("OPEN")) { if (isSkipBuild(pullRequest.getTitle()) || !isFilteredBuild(pullRequest)) { @@ -178,7 +187,11 @@ public class BitbucketRepository { pullRequest.getSource().getCommit().getHash(), pullRequest.getDestination().getCommit().getHash() ); - BitbucketBuildFilter filter = new BitbucketBuildFilter(this.trigger.getBranchesFilter()); + + BitbucketBuildFilter filter = !this.trigger.getBranchesFilterBySCMIncludes() ? + BitbucketBuildFilter.InstanceByString(this.trigger.getBranchesFilter()) : + BitbucketBuildFilter.InstanceBySCM(Jenkins.getInstance().getExtensionList(SCMSource.class), this.trigger.getBranchesFilter()); + return filter.approved(cause); } } diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly index 845926d..25acac0 100644 --- a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly +++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly @@ -17,6 +17,9 @@ + + + diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html index f49da40..88af799 100644 --- a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html +++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/help-branchesFilter.html @@ -3,3 +3,10 @@ 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..0765a4a --- /dev/null +++ b/src/test/java/BitbucketBuildFilterTest.java @@ -0,0 +1,170 @@ +/* + * 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 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, "")); + } +} -- cgit v1.2.3