diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java index c709cdb13..d4609cbe7 100644 --- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java @@ -33,6 +33,13 @@ import java.util.logging.Logger; import java.util.regex.Pattern; +import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi; +import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBranch; +import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketCommit; +import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest; +import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestSource; +import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository; +import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey; import com.cloudbees.plugins.credentials.CredentialsNameProvider; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.AncestorInPath; @@ -40,11 +47,6 @@ import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; -import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi; -import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBranch; -import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketCommit; -import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest; -import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository; import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey; import com.cloudbees.plugins.credentials.common.StandardCredentials; import com.cloudbees.plugins.credentials.common.StandardListBoxModel; @@ -80,7 +82,7 @@ /** * SCM source implementation for Bitbucket. - * + *

* It provides a way to discover/retrieve branches and pull requests through the Bitbuclet REST API * which is much faster than the plain Git SCM source implementation. */ @@ -289,21 +291,24 @@ private void retrievePullRequests(SCMHeadObserver observer, final TaskListener l if (bitbucket.isPrivate()) { List pulls = bitbucket.getPullRequests(); for (final BitbucketPullRequest pull : pulls) { + BitbucketPullRequestSource source = pull.getSource(); + BitbucketRepository repository = source.getRepository(); + BitbucketBranch branch = source.getBranch(); listener.getLogger().println( - "Checking PR from " + pull.getSource().getRepository().getFullName() + " and branch " - + pull.getSource().getBranch().getName()); + "Checking PR from " + repository.getFullName() + " and branch " + + branch.getName()); // Resolve full hash. See https://bitbucket.org/site/master/issues/11415/pull-request-api-should-return-full-commit String hash = bitbucket.resolveSourceFullHash(pull); if (hash != null) { - observe(observer, listener, - pull.getSource().getRepository().getOwnerName(), - pull.getSource().getRepository().getRepositoryName(), - pull.getSource().getBranch().getName(), - hash, - Integer.parseInt(pull.getId())); + if (shouldObserve(listener, repository.getOwnerName(), + repository.getRepositoryName(), + branch.getName(), hash)) { + PullrequestSCMHead prHead = new PullrequestSCMHead(pull); + observe(observer, prHead, hash); + } } else { - listener.getLogger().format("Can not resolve hash: [%s]%n", pull.getSource().getCommit().getHash()); + listener.getLogger().format("Can not resolve hash: [%s]%n", source.getCommit().getHash()); } if (!observer.isObserving()) { return; @@ -323,16 +328,18 @@ private void retrieveBranches(@NonNull final SCMHeadObserver observer, @NonNull List branches = bitbucket.getBranches(); for (BitbucketBranch branch : branches) { listener.getLogger().println("Checking branch " + branch.getName() + " from " + fullName); - observe(observer, listener, repoOwner, repository, branch.getName(), - branch.getRawNode(), null); + if (shouldObserve(listener, repoOwner, repository, branch.getName(), branch.getRawNode())) { + SCMHeadWithOwnerAndRepo head = new SCMHeadWithOwnerAndRepo(repoOwner, repository, branch.getName()); + observe(observer, head, branch.getRawNode()); + } } } - private void observe(SCMHeadObserver observer, final TaskListener listener, - final String owner, final String repositoryName, - final String branchName, final String hash, Integer prId) throws IOException { + private boolean shouldObserve(final TaskListener listener, + final String owner, final String repositoryName, + final String branchName, final String hash) throws IOException { if (isExcluded(branchName)) { - return; + return false; } final BitbucketApi bitbucket = getBitbucketConnector().create(owner, repositoryName, getScanCredentials()); SCMSourceCriteria branchCriteria = getCriteria(); @@ -362,13 +369,18 @@ public boolean exists(@NonNull String path) throws IOException { }; if (branchCriteria.isHead(probe, listener)) { listener.getLogger().println("Met criteria"); + return true; } else { listener.getLogger().println("Does not meet criteria"); - return; + return false; } } + return true; + } + + private synchronized void observe(SCMHeadObserver observer, + SCMHead head, final String hash) throws IOException { SCMRevision revision; - SCMHeadWithOwnerAndRepo head = new SCMHeadWithOwnerAndRepo(owner, repositoryName, branchName, prId); if (getRepositoryType() == RepositoryType.MERCURIAL) { revision = new MercurialRevision(head, hash); } else { @@ -450,12 +462,12 @@ private StandardCredentials getCheckoutCredentials() { } public String getRemoteName() { - return "origin"; + return "origin"; } /** * Returns true if the branchName isn't matched by includes or is matched by excludes. - * + * * @param branchName * @return true if branchName is excluded or is not included */ @@ -465,10 +477,10 @@ private boolean isExcluded(String branchName) { } /** - * Returns the pattern corresponding to the branches containing wildcards. - * - * @param branches space separated list of expressions. - * For example "*" which would match all branches and branch* would match branch1, branch2, etc. + * Returns the pattern corresponding to the branches containing wildcards. + * + * @param branches space separated list of expressions. + * For example "*" which would match all branches and branch* would match branch1, branch2, etc. * @return pattern corresponding to the branches containing wildcards (ready to be used by {@link Pattern}) */ private String getPattern(String branches) { @@ -548,7 +560,7 @@ public static FormValidation doCheckBitbucketServerUrl(@QueryParameter String bi try { new URL(bitbucketServerUrl); } catch (MalformedURLException e) { - return FormValidation.error("Invalid URL: " + e.getMessage()); + return FormValidation.error("Invalid URL: " + e.getMessage()); } return FormValidation.ok(); } diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestAction.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestAction.java new file mode 100644 index 000000000..585bd0e10 --- /dev/null +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestAction.java @@ -0,0 +1,27 @@ +package com.cloudbees.jenkins.plugins.bitbucket; + +import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest; +import jenkins.scm.api.actions.ChangeRequestAction; + +final class PullRequestAction extends ChangeRequestAction { + + private static final long serialVersionUID = 1L; + + private final String number; + private final String title; + + PullRequestAction(BitbucketPullRequest pr) { + number = pr.getId(); + title = pr.getTitle(); + } + + @Override + public String getId() { + return number; + } + + @Override + public String getTitle() { + return title; + } +} \ No newline at end of file diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullrequestSCMHead.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullrequestSCMHead.java new file mode 100644 index 000000000..bed3041b7 --- /dev/null +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullrequestSCMHead.java @@ -0,0 +1,37 @@ +package com.cloudbees.jenkins.plugins.bitbucket; + +import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.model.Action; + +import java.util.ArrayList; +import java.util.List; + +public class PullrequestSCMHead extends SCMHeadWithOwnerAndRepo { + + private static final long serialVersionUID = 1L; + private static final String PR_BRANCH_PREFIX = "PR-"; + + private final PullRequestAction metatdata; + + public PullrequestSCMHead(BitbucketPullRequest pullRequest) { + super(pullRequest.getSource().getRepository().getOwnerName(), + pullRequest.getSource().getRepository().getRepositoryName(), + pullRequest.getSource().getBranch().getName()); + this.metatdata = new PullRequestAction(pullRequest); + } + + @NonNull + @Override + public String getName() { + return PR_BRANCH_PREFIX + metatdata.getId(); + } + + @NonNull + @Override + public List getAllActions() { + List actions = new ArrayList<>(super.getAllActions()); + actions.add(metatdata); + return actions; + } +} diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/SCMHeadWithOwnerAndRepo.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/SCMHeadWithOwnerAndRepo.java index 24952d051..caeffccfb 100644 --- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/SCMHeadWithOwnerAndRepo.java +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/SCMHeadWithOwnerAndRepo.java @@ -23,7 +23,6 @@ */ package com.cloudbees.jenkins.plugins.bitbucket; -import edu.umd.cs.findbugs.annotations.CheckForNull; import jenkins.scm.api.SCMHead; /** @@ -31,11 +30,7 @@ *

- * This information is required in this plugin since {@link BitbucketSCMSource} is processing pull requests - * and they are managed as separate repositories in Bitbucket without any reference to them in the destination - * repository. */ public class SCMHeadWithOwnerAndRepo extends SCMHead { @@ -45,19 +40,10 @@ public class SCMHeadWithOwnerAndRepo extends SCMHead { private final String repoName; - private final Integer pullRequestId; - - private static final String PR_BRANCH_PREFIX = "PR-"; - - public SCMHeadWithOwnerAndRepo(String repoOwner, String repoName, String branchName, Integer pullRequestId) { + public SCMHeadWithOwnerAndRepo(String repoOwner, String repoName, String branchName) { super(branchName); this.repoOwner = repoOwner; this.repoName = repoName; - this.pullRequestId = pullRequestId; - } - - public SCMHeadWithOwnerAndRepo(String repoOwner, String repoName, String branchName) { - this(repoOwner, repoName, branchName, null); } public String getRepoOwner() { @@ -69,24 +55,9 @@ public String getRepoName() { } /** - * @return the original branch name without the "PR-owner-" part. + * @return the original branch name. */ public String getBranchName() { return super.getName(); } - - /** - * Returns the prettified branch name by adding "PR-[ID]" if the branch is coming from a PR. - * Use {@link #getBranchName()} to get the real branch name. - */ - @Override - public String getName() { - return pullRequestId != null ? PR_BRANCH_PREFIX + pullRequestId : getBranchName(); - } - - @CheckForNull - public Integer getPullRequestId() { - return pullRequestId; - } - } diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketPullRequest.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketPullRequest.java index 17ef3ccae..31fde2efa 100644 --- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketPullRequest.java +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketPullRequest.java @@ -39,4 +39,8 @@ public interface BitbucketPullRequest { */ String getId(); + /** + * @return pull request Title as provided by Bitbucket. It will be used for the job name. + */ + String getTitle(); } \ No newline at end of file diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/pullrequest/BitbucketPullRequestValue.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/pullrequest/BitbucketPullRequestValue.java index 671eb918e..cac82fbcd 100644 --- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/pullrequest/BitbucketPullRequestValue.java +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/pullrequest/BitbucketPullRequestValue.java @@ -32,6 +32,7 @@ public class BitbucketPullRequestValue implements BitbucketPullRequest { private BitbucketPullRequestValueRepository source; private String id; + private String title; public BitbucketPullRequestSource getSource() { return source; @@ -49,4 +50,11 @@ public void setId(String id) { this.id = id; } + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } } diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/pullrequest/BitbucketServerPullRequest.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/pullrequest/BitbucketServerPullRequest.java index 89caa6144..0738c5fbd 100644 --- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/pullrequest/BitbucketServerPullRequest.java +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/pullrequest/BitbucketServerPullRequest.java @@ -34,6 +34,8 @@ public class BitbucketServerPullRequest implements BitbucketPullRequest { private String id; + private String title; + @JsonProperty("fromRef") private BitbucketServerPullRequestSource source; @@ -55,4 +57,12 @@ public void setId(String id) { this.id = id; } + @Override + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } } diff --git a/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketClientMockUtils.java b/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketClientMockUtils.java index 78c202282..7677bd67c 100644 --- a/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketClientMockUtils.java +++ b/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketClientMockUtils.java @@ -159,6 +159,7 @@ private static BitbucketPullRequestValue getPullRequest() { pr.setSource(source); pr.setId("23"); + pr.setTitle("Title"); return pr; }