diff --git a/README.adoc b/README.adoc
new file mode 100644
index 000000000..f26b16f4e
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,6 @@
+== BitBucket Branch Source Plugin
+
+=== Notes
+
+* Unlike GitHub, in BitBucket, https://bitbucket.org/site/master/issues/4828/team-admins-dont-have-read-access-to-forks[team admins do not have access to forks].
+This means that when you have a private repository, or a private fork of a public repository, the team admin will not be able to see the PRs within the fork.
diff --git a/pom.xml b/pom.xml
index 644f93f38..41ddc52e2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
cloudbees-bitbucket-branch-source
- 1.10-SNAPSHOT
+ 2.0.0-beta-1-SNAPSHOThpiBitbucket Branch Source Plugin
@@ -47,6 +47,7 @@
1.642.3
+ 2.0.1-beta-1
@@ -60,17 +61,12 @@
org.jenkins-ci.pluginsscm-api
- 1.2
-
-
- org.jenkins-ci.plugins
- branch-api
- 1.10.2
+ ${scm-api.version}org.jenkins-ci.pluginsgit
- 2.3.5
+ 2.6.2-beta-1org.apache.httpcomponents
@@ -81,7 +77,7 @@
org.jenkins-ci.pluginsmercurial
- 1.54
+ 1.58-beta-1-SNAPSHOTorg.codehaus.jackson
@@ -93,6 +89,19 @@
display-url-api0.2
+
+ org.jenkins-ci.plugins
+ branch-api
+ 2.0.0-beta-1
+ test
+
+
+ org.jenkins-ci.plugins
+ scm-api
+ ${scm-api.version}
+ tests
+ test
+ org.jenkins-ci.plugins.workflowworkflow-multibranch
diff --git a/src/images/bitbucket-branch.svg b/src/images/bitbucket-branch.svg
new file mode 100644
index 000000000..438b2a7e0
--- /dev/null
+++ b/src/images/bitbucket-branch.svg
@@ -0,0 +1,2570 @@
+
+
+
+
diff --git a/src/images/bitbucket-logo.svg b/src/images/bitbucket-logo.svg
new file mode 100644
index 000000000..2f23d6bef
--- /dev/null
+++ b/src/images/bitbucket-logo.svg
@@ -0,0 +1,2419 @@
+
+
+
+
diff --git a/src/images/bitbucket-repository-git.svg b/src/images/bitbucket-repository-git.svg
new file mode 100644
index 000000000..6b6122d70
--- /dev/null
+++ b/src/images/bitbucket-repository-git.svg
@@ -0,0 +1,2267 @@
+
+
+
+
diff --git a/src/images/bitbucket-repository-hg.svg b/src/images/bitbucket-repository-hg.svg
new file mode 100644
index 000000000..f18bf08c1
--- /dev/null
+++ b/src/images/bitbucket-repository-hg.svg
@@ -0,0 +1,2363 @@
+
+
+
+
diff --git a/src/images/bitbucket-repository.svg b/src/images/bitbucket-repository.svg
new file mode 100644
index 000000000..483c2da9d
--- /dev/null
+++ b/src/images/bitbucket-repository.svg
@@ -0,0 +1,2245 @@
+
+
+
+
diff --git a/src/images/bitbucket-scmnavigator.svg b/src/images/bitbucket-scmnavigator.svg
new file mode 100644
index 000000000..cc5461978
--- /dev/null
+++ b/src/images/bitbucket-scmnavigator.svg
@@ -0,0 +1,2674 @@
+
+
+
+
diff --git a/src/images/make-inkscape.sh b/src/images/make-inkscape.sh
new file mode 100755
index 000000000..4ee9868c2
--- /dev/null
+++ b/src/images/make-inkscape.sh
@@ -0,0 +1,19 @@
+#!/bin/sh -e
+
+dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+for src in "$dir"/*.svg
+do
+ echo "Processing $(basename "$src")..."
+ file=$(basename "$src" | sed -e s/.svg/.png/ )
+ for sz in 16 24 32 48
+ do
+ mkdir -p "${dir}/../../src/main/webapp/images/${sz}x${sz}"
+ dst="${dir}/../../src/main/webapp/images/${sz}x${sz}/${file}"
+ if [ ! -e "$dst" -o "$src" -nt "$dst" ]
+ then
+ echo -n " generating ${sz}x${sz}..."
+ mkdir "${dir}/../../src/main/webapp/images/${sz}x${sz}" > /dev/null 2>&1 || true
+ inkscape -z -C -w ${sz} -h ${sz} -e "$dst" "$src" 2>&1 | grep "Bitmap saved as"
+ fi
+ done
+done
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/AbstractBranchJobFilter.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/AbstractBranchJobFilter.java
deleted file mode 100644
index dc8bb2f0c..000000000
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/AbstractBranchJobFilter.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.cloudbees.jenkins.plugins.bitbucket;
-
-import java.util.List;
-
-import com.cloudbees.jenkins.plugins.bitbucket.CustomComputedFolderItemListener.Sniffer;
-import com.cloudbees.jenkins.plugins.bitbucket.CustomComputedFolderItemListener.Sniffer.BranchMatch;
-
-import hudson.model.TopLevelItem;
-import hudson.model.View;
-import hudson.views.ViewJobFilter;
-import jenkins.scm.api.SCMHead;
-
-abstract class AbstractBranchJobFilter extends ViewJobFilter {
- public AbstractBranchJobFilter() {}
-
- @Override
- public List filter(List added, List all, View filteringView) {
- for (TopLevelItem i : all) {
- if (added.contains(i)) continue; // already in there
-
- BranchMatch b = Sniffer.matchBranch(i);
- if (b!=null) {
- SCMHead head = b.getScmBranch().getHead();
- if (shouldShow(head)) {
- added.add(i);
- }
- }
- }
- return added;
- }
-
- protected abstract boolean shouldShow(SCMHead head);
-}
-
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketApiConnector.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketApiConnector.java
index fb06b15a7..03c7a05a4 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketApiConnector.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketApiConnector.java
@@ -23,6 +23,8 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket;
+import hudson.model.Queue;
+import hudson.model.queue.Tasks;
import java.util.List;
import javax.annotation.CheckForNull;
@@ -44,6 +46,7 @@
import hudson.security.ACL;
import hudson.util.ListBoxModel;
import jenkins.scm.api.SCMSourceOwner;
+import org.apache.commons.lang.StringUtils;
public class BitbucketApiConnector {
@@ -74,30 +77,48 @@ public BitbucketApi create(String owner, StandardUsernamePasswordCredentials cre
@CheckForNull
public T lookupCredentials(@CheckForNull SCMSourceOwner context, @CheckForNull String id, Class type) {
- if (Util.fixEmpty(id) == null) {
- return null;
- } else {
- if (id != null) {
- return CredentialsMatchers.firstOrNull(
- CredentialsProvider.lookupCredentials(type, context, ACL.SYSTEM,
- bitbucketDomainRequirements()),
- CredentialsMatchers.allOf(
- CredentialsMatchers.withId(id),
- CredentialsMatchers.anyOf(CredentialsMatchers.instanceOf(type))));
- }
- return null;
+ if (StringUtils.isNotBlank(id)) {
+ return CredentialsMatchers.firstOrNull(
+ CredentialsProvider.lookupCredentials(
+ type,
+ context,
+ context instanceof Queue.Task
+ ? Tasks.getDefaultAuthenticationOf((Queue.Task) context)
+ : ACL.SYSTEM,
+ bitbucketDomainRequirements()
+ ),
+ CredentialsMatchers.allOf(
+ CredentialsMatchers.withId(id),
+ CredentialsMatchers.anyOf(CredentialsMatchers.instanceOf(type))
+ )
+ );
}
+ return null;
}
- public ListBoxModel fillCheckoutCredentials(StandardListBoxModel result, SCMSourceOwner context) {
- result.withMatching(bitbucketCheckoutCredentialsMatcher(), CredentialsProvider.lookupCredentials(
- StandardCredentials.class, context, ACL.SYSTEM, bitbucketDomainRequirements()));
+ public StandardListBoxModel fillCheckoutCredentials(StandardListBoxModel result, SCMSourceOwner context) {
+ result.includeMatchingAs(
+ context instanceof Queue.Task
+ ? Tasks.getDefaultAuthenticationOf((Queue.Task) context)
+ : ACL.SYSTEM,
+ context,
+ StandardCredentials.class,
+ bitbucketDomainRequirements(),
+ bitbucketCheckoutCredentialsMatcher()
+ );
return result;
}
- public ListBoxModel fillCredentials(StandardListBoxModel result, SCMSourceOwner context) {
- result.withMatching(bitbucketCredentialsMatcher(), CredentialsProvider.lookupCredentials(
- StandardUsernameCredentials.class, context, ACL.SYSTEM, bitbucketDomainRequirements()));
+ public StandardListBoxModel fillCredentials(StandardListBoxModel result, SCMSourceOwner context) {
+ result.includeMatchingAs(
+ context instanceof Queue.Task
+ ? Tasks.getDefaultAuthenticationOf((Queue.Task) context)
+ : ACL.SYSTEM,
+ context,
+ StandardUsernameCredentials.class,
+ bitbucketDomainRequirements(),
+ bitbucketCredentialsMatcher()
+ );
return result;
}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketBuildStatusNotifications.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketBuildStatusNotifications.java
index 9344974ba..320e41687 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketBuildStatusNotifications.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketBuildStatusNotifications.java
@@ -118,18 +118,6 @@ private static BitbucketNotifier getNotifier(BitbucketApi bitbucket) {
return new BitbucketChangesetCommentNotifier(bitbucket);
}
- @CheckForNull
- private static BitbucketApi buildBitbucketClientForBuild(Run,?> build, BitbucketSCMSource source) {
- Job, ?> job = build.getParent();
- StandardUsernamePasswordCredentials creds = source.getScanCredentials();
- SCMHead _head = SCMHead.HeadByItem.findHead(job);
- if (_head instanceof SCMHeadWithOwnerAndRepo) {
- SCMHeadWithOwnerAndRepo head = (SCMHeadWithOwnerAndRepo) _head;
- return new BitbucketApiConnector(source.getBitbucketServerUrl()).create(head.getRepoOwner(), head.getRepoName(), creds);
- }
- return null;
- }
-
@CheckForNull
private static BitbucketSCMSource lookUpSCMSource(Run, ?> build) {
ItemGroup> multiBranchProject = build.getParent().getParent();
@@ -165,18 +153,13 @@ private static BitbucketSCMSource lookUpBitbucketSCMSource(final SCMSourceOwner
private static void sendNotifications(Run, ?> build, TaskListener listener) {
BitbucketSCMSource source = lookUpSCMSource(build);
if (source != null && extractRevision(build) != null) {
- BitbucketApi bitbucket = buildBitbucketClientForBuild(build, source);
- if (bitbucket != null) {
- if (source.getRepoOwner().equals(bitbucket.getOwner()) &&
- source.getRepository().equals(bitbucket.getRepositoryName())) {
- listener.getLogger().println("[Bitbucket] Notifying commit build result");
- createBuildCommitStatus(build, listener, bitbucket);
- } else {
- listener.getLogger().println("[Bitbucket] Notifying pull request build result");
- createPullRequestCommitStatus(build, listener, bitbucket);
- }
+ SCMHead head = SCMHead.HeadByItem.findHead(build.getParent());
+ if (head instanceof PullRequestSCMHead) {
+ listener.getLogger().println("[Bitbucket] Notifying pull request build result");
+ createPullRequestCommitStatus(build, listener, source.buildBitbucketClient((PullRequestSCMHead) head));
} else {
- listener.getLogger().println("[Bitbucket] Can not get connection information from the source. Skipping notification...");
+ listener.getLogger().println("[Bitbucket] Notifying commit build result");
+ createBuildCommitStatus(build, listener, source.buildBitbucketClient());
}
}
}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketLink.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketLink.java
new file mode 100644
index 000000000..da123bdd9
--- /dev/null
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketLink.java
@@ -0,0 +1,100 @@
+package com.cloudbees.jenkins.plugins.bitbucket;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.model.Action;
+import java.net.URL;
+import jenkins.model.Jenkins;
+import org.apache.commons.jelly.JellyContext;
+import org.jenkins.ui.icon.Icon;
+import org.jenkins.ui.icon.IconSet;
+import org.jenkins.ui.icon.IconSpec;
+import org.kohsuke.stapler.Stapler;
+
+/**
+ * @author Stephen Connolly
+ */
+public class BitbucketLink implements Action, IconSpec {
+ /**
+ * The icon class name to use.
+ */
+ @NonNull
+ private final String iconClassName;
+
+ /**
+ * Target of the hyperlink to take the user to.
+ */
+ @NonNull
+ private final String url;
+
+ public BitbucketLink(@NonNull String iconClassName, @NonNull String url) {
+ this.iconClassName = iconClassName;
+ this.url = url;
+ }
+
+ @NonNull
+ public String getUrl() {
+ return url;
+ }
+
+ @Override
+ public String getIconClassName() {
+ return iconClassName;
+ }
+
+ @Override
+ public String getIconFileName() {
+ String iconClassName = getIconClassName();
+ if (iconClassName != null) {
+ Icon icon = IconSet.icons.getIconByClassSpec(iconClassName + " icon-md");
+ if (icon != null) {
+ JellyContext ctx = new JellyContext();
+ ctx.setVariable("resURL", Stapler.getCurrentRequest().getContextPath() + Jenkins.RESOURCE_PATH);
+ return icon.getQualifiedUrl(ctx);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return Messages.BitbucketLink_DisplayName();
+ }
+
+ @Override
+ public String getUrlName() {
+ return url;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ BitbucketLink that = (BitbucketLink) o;
+
+ if (!iconClassName.equals(that.iconClassName)) {
+ return false;
+ }
+ return url.equals(that.url);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = iconClassName.hashCode();
+ result = 31 * result + url.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "BitbucketLink{" +
+ "iconClassName='" + iconClassName + '\'' +
+ ", url='" + url + '\'' +
+ '}';
+ }
+
+}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketRepoMetadataAction.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketRepoMetadataAction.java
new file mode 100644
index 000000000..fda60eb19
--- /dev/null
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketRepoMetadataAction.java
@@ -0,0 +1,109 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2016 CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.cloudbees.jenkins.plugins.bitbucket;
+
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import jenkins.scm.api.metadata.AvatarMetadataAction;
+
+/**
+ * Invisible property that retains information about Bitbucket repository.
+ */
+public class BitbucketRepoMetadataAction extends AvatarMetadataAction{
+
+ private final String scm;
+
+ public BitbucketRepoMetadataAction(@NonNull BitbucketRepository repo) {
+ this(repo.getScm());
+ }
+
+ public BitbucketRepoMetadataAction(String scm) {
+ this.scm = scm;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getAvatarIconClassName() {
+ if ("git".equals(scm)) {
+ return "icon-bitbucket-repo-git";
+ }
+ if ("hg".equals(scm)) {
+ return "icon-bitbucket-repo-hg";
+ }
+ return "icon-bitbucket-repo";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getAvatarDescription() {
+ if ("git".equals(scm)) {
+ return Messages.BitbucketRepoMetadataAction_IconDescription_Git();
+ }
+ if ("hg".equals(scm)) {
+ return Messages.BitbucketRepoMetadataAction_IconDescription_Hg();
+ }
+ return Messages.BitbucketRepoMetadataAction_IconDescription();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ BitbucketRepoMetadataAction that = (BitbucketRepoMetadataAction) o;
+
+ return scm != null ? scm.equals(that.scm) : that.scm == null;
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return scm != null ? scm.hashCode() : 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "BitbucketRepoMetadataAction{" +
+ "scm='" + scm + '\'' +
+ '}';
+ }
+}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java
index 20a560b4c..d759702bd 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java
@@ -23,13 +23,25 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
+import hudson.console.HyperlinkNote;
+import hudson.model.Action;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
+import jenkins.scm.api.SCMNavigatorEvent;
+import jenkins.scm.api.SCMNavigatorOwner;
+import jenkins.scm.api.SCMSourceCategory;
+import jenkins.scm.api.metadata.ObjectMetadataAction;
+import jenkins.scm.impl.UncategorizedSCMSourceCategory;
import org.apache.commons.lang.StringUtils;
+import org.jenkins.ui.icon.Icon;
+import org.jenkins.ui.icon.IconSet;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
@@ -120,11 +132,15 @@ public void setSshPort(int sshPort) {
@DataBoundSetter
public void setBitbucketServerUrl(String url) {
+ if (StringUtils.equals(this.bitbucketServerUrl, url)) {
+ return;
+ }
this.bitbucketServerUrl = Util.fixEmpty(url);
if (this.bitbucketServerUrl != null) {
// Remove a possible trailing slash
this.bitbucketServerUrl = this.bitbucketServerUrl.replaceAll("/$", "");
}
+ resetId();
}
@CheckForNull
@@ -143,6 +159,12 @@ public void setBitbucketConnector(@NonNull BitbucketApiConnector bitbucketConnec
return bitbucketConnector;
}
+ @NonNull
+ @Override
+ protected String id() {
+ return bitbucketUrl() + "::" + repoOwner;
+ }
+
@Override
public void visitSources(SCMSourceObserver observer) throws IOException, InterruptedException {
TaskListener listener = observer.getListener();
@@ -155,9 +177,9 @@ public void visitSources(SCMSourceObserver observer) throws IOException, Interru
credentialsId, StandardUsernamePasswordCredentials.class);
if (credentials == null) {
- listener.getLogger().format("Connecting to %s with no credentials, anonymous access%n", bitbucketServerUrl == null ? "https://bitbucket.org" : bitbucketServerUrl);
+ listener.getLogger().format("Connecting to %s with no credentials, anonymous access%n", bitbucketUrl());
} else {
- listener.getLogger().format("Connecting to %s using %s%n", bitbucketServerUrl == null ? "https://bitbucket.org" : bitbucketServerUrl, CredentialsNameProvider.name(credentials));
+ listener.getLogger().format("Connecting to %s using %s%n", bitbucketUrl(), CredentialsNameProvider.name(credentials));
}
List extends BitbucketRepository> repositories;
BitbucketApi bitbucket = getBitbucketConnector().create(repoOwner, credentials);
@@ -172,6 +194,7 @@ public void visitSources(SCMSourceObserver observer) throws IOException, Interru
repositories = bitbucket.getRepositories(UserRoleInRepository.OWNER);
}
for (BitbucketRepository repo : repositories) {
+ checkInterrupt();
add(listener, observer, repo);
}
}
@@ -183,11 +206,13 @@ private void add(TaskListener listener, SCMSourceObserver observer, BitbucketRep
return;
}
listener.getLogger().format("Proposing %s%n", name);
- if (Thread.interrupted()) {
- throw new InterruptedException();
- }
+ checkInterrupt();
SCMSourceObserver.ProjectObserver projectObserver = observer.observe(name);
- BitbucketSCMSource scmSource = new BitbucketSCMSource(null, repoOwner, name);
+ BitbucketSCMSource scmSource = new BitbucketSCMSource(
+ getId() + "::" + name,
+ repoOwner,
+ name
+ );
scmSource.setBitbucketConnector(getBitbucketConnector());
scmSource.setCredentialsId(credentialsId);
scmSource.setCheckoutCredentialsId(checkoutCredentialsId);
@@ -198,7 +223,68 @@ private void add(TaskListener listener, SCMSourceObserver observer, BitbucketRep
projectObserver.complete();
}
- @Extension
+ private String bitbucketUrl() {
+ return StringUtils.defaultIfBlank(bitbucketServerUrl, "https://bitbucket.org");
+ }
+
+ @NonNull
+ @Override
+ public List retrieveActions(@NonNull SCMNavigatorOwner owner,
+ @CheckForNull SCMNavigatorEvent event,
+ @NonNull TaskListener listener)
+ throws IOException, InterruptedException {
+ // TODO when we have support for trusted events, use the details from event if event was from trusted source
+ listener.getLogger().printf("Looking up team details of %s...%n", getRepoOwner());
+ List result = new ArrayList<>();
+ StandardUsernamePasswordCredentials credentials =
+ getBitbucketConnector().lookupCredentials(owner,
+ credentialsId, StandardUsernamePasswordCredentials.class);
+
+ String serverUrl = StringUtils.removeEnd(bitbucketUrl(), "/");
+ if (credentials == null) {
+ listener.getLogger().format("Connecting to %s with no credentials, anonymous access%n",
+ serverUrl);
+ } else {
+ listener.getLogger().format("Connecting to %s using %s%n",
+ serverUrl,
+ CredentialsNameProvider.name(credentials));
+ }
+ BitbucketApi bitbucket = getBitbucketConnector().create(repoOwner, credentials);
+ BitbucketTeam team = bitbucket.getTeam();
+ if (team != null) {
+ String teamUrl =
+ StringUtils.defaultIfBlank(getLink(team.getLinks(), "html"), serverUrl + "/" + team.getName());
+ String teamDisplayName = StringUtils.defaultIfBlank(team.getDisplayName(), team.getName());
+ result.add(new ObjectMetadataAction(
+ teamDisplayName,
+ null,
+ teamUrl
+ ));
+ result.add(new BitbucketTeamMetadataAction(getLink(team.getLinks(), "avatar")));
+ result.add(new BitbucketLink("icon-bitbucket-logo", teamUrl));
+ listener.getLogger().printf("Team: %s%n", HyperlinkNote.encodeTo(teamUrl, teamDisplayName));
+ } else {
+ String teamUrl = serverUrl + "/" + repoOwner;
+ result.add(new ObjectMetadataAction(
+ repoOwner,
+ null,
+ teamUrl
+ ));
+ result.add(new BitbucketLink("icon-bitbucket-logo", teamUrl));
+ listener.getLogger().println("Could not resolve team details");
+ }
+ return result;
+ }
+
+ private static String getLink(Map links, String name) {
+ if (links == null) {
+ return null;
+ }
+ BitbucketHref href = links.get(name);
+ return href == null ? null : href.getHref();
+ }
+
+ @Extension
public static class DescriptorImpl extends SCMNavigatorDescriptor {
public static final String ANONYMOUS = BitbucketSCMSource.DescriptorImpl.ANONYMOUS;
@@ -219,6 +305,11 @@ public String getIconFilePathPattern() {
return "plugin/cloudbees-bitbucket-branch-source/images/:size/bitbucket-scmnavigator.png";
}
+ @Override
+ public String getIconClassName() {
+ return "icon-bitbucket-scmnavigator";
+ }
+
@Override
public SCMNavigator newInstance(String name) {
return new BitbucketSCMNavigator(name, "", BitbucketSCMSource.DescriptorImpl.SAME);
@@ -251,5 +342,116 @@ public ListBoxModel doFillCheckoutCredentialsIdItems(@AncestorInPath SCMSourceOw
return result;
}
+ @NonNull
+ @Override
+ protected SCMSourceCategory[] createCategories() {
+ return new SCMSourceCategory[]{
+ new UncategorizedSCMSourceCategory(Messages._BitbucketSCMNavigator_UncategorizedSCMSourceCategory_DisplayName())
+ };
+ }
+
+ static {
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-scm-navigator icon-sm",
+ "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-scmnavigator.png",
+ Icon.ICON_SMALL_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-scm-navigator icon-md",
+ "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-scmnavigator.png",
+ Icon.ICON_MEDIUM_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-scm-navigator icon-lg",
+ "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-scmnavigator.png",
+ Icon.ICON_LARGE_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-scm-navigator icon-xlg",
+ "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-scmnavigator.png",
+ Icon.ICON_XLARGE_STYLE));
+
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-logo icon-sm",
+ "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-logo.png",
+ Icon.ICON_SMALL_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-logo icon-md",
+ "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-logo.png",
+ Icon.ICON_MEDIUM_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-logo icon-lg",
+ "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-logo.png",
+ Icon.ICON_LARGE_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-logo icon-xlg",
+ "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-logo.png",
+ Icon.ICON_XLARGE_STYLE));
+
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo icon-sm",
+ "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-repository.png",
+ Icon.ICON_SMALL_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo icon-md",
+ "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-repository.png",
+ Icon.ICON_MEDIUM_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo icon-lg",
+ "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-repository.png",
+ Icon.ICON_LARGE_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo icon-xlg",
+ "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-repository.png",
+ Icon.ICON_XLARGE_STYLE));
+
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo-git icon-sm",
+ "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-repository-git.png",
+ Icon.ICON_SMALL_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo-git icon-md",
+ "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-repository-git.png",
+ Icon.ICON_MEDIUM_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo-git icon-lg",
+ "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-repository-git.png",
+ Icon.ICON_LARGE_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo-git icon-xlg",
+ "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-repository-git.png",
+ Icon.ICON_XLARGE_STYLE));
+
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo-hg icon-sm",
+ "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-repository-hg.png",
+ Icon.ICON_SMALL_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo-hg icon-md",
+ "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-repository-hg.png",
+ Icon.ICON_MEDIUM_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo-hg icon-lg",
+ "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-repository-hg.png",
+ Icon.ICON_LARGE_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-repo-hg icon-xlg",
+ "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-repository-hg.png",
+ Icon.ICON_XLARGE_STYLE));
+
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-branch icon-sm",
+ "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-branch.png",
+ Icon.ICON_SMALL_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-branch icon-md",
+ "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-branch.png",
+ Icon.ICON_MEDIUM_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-branch icon-lg",
+ "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-branch.png",
+ Icon.ICON_LARGE_STYLE));
+ IconSet.icons.addIcon(
+ new Icon("icon-bitbucket-branch icon-xlg",
+ "plugin/cloudbees-bitbucket-branch-sourcee/images/48x48/bitbucket-branch.png",
+ Icon.ICON_XLARGE_STYLE));
+ }
}
}
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 73754acba..60d782ac4 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java
@@ -23,37 +23,22 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-
-import com.cloudbees.plugins.credentials.CredentialsNameProvider;
-import org.apache.commons.lang.StringUtils;
-import org.kohsuke.stapler.AncestorInPath;
-import org.kohsuke.stapler.DataBoundConstructor;
-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.bitbucket.api.BitbucketRequestException;
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
+import com.cloudbees.plugins.credentials.CredentialsNameProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
-
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.Util;
+import hudson.model.Action;
import hudson.model.TaskListener;
import hudson.plugins.git.BranchSpec;
import hudson.plugins.git.GitSCM;
@@ -68,15 +53,37 @@
import hudson.scm.SCM;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.plugins.git.AbstractGitSCMSource.SpecificRevisionBuildChooser;
import jenkins.scm.api.SCMHead;
+import jenkins.scm.api.SCMHeadCategory;
+import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMHeadObserver;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMSource;
import jenkins.scm.api.SCMSourceCriteria;
import jenkins.scm.api.SCMSourceDescriptor;
+import jenkins.scm.api.SCMSourceEvent;
import jenkins.scm.api.SCMSourceOwner;
+import jenkins.scm.api.metadata.ObjectMetadataAction;
+import jenkins.scm.impl.ChangeRequestSCMHeadCategory;
+import jenkins.scm.impl.UncategorizedSCMHeadCategory;
+import org.apache.commons.lang.StringUtils;
+import org.eclipse.jgit.lib.Constants;
+import org.kohsuke.stapler.AncestorInPath;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.DataBoundSetter;
+import org.kohsuke.stapler.QueryParameter;
/**
* SCM source implementation for Bitbucket.
@@ -234,6 +241,10 @@ public String getBitbucketServerUrl() {
return bitbucketServerUrl;
}
+ private String bitbucketUrl() {
+ return StringUtils.defaultIfBlank(bitbucketServerUrl, "https://bitbucket.org");
+ }
+
public void setBitbucketConnector(@NonNull BitbucketApiConnector bitbucketConnector) {
this.bitbucketConnector = bitbucketConnector;
}
@@ -264,24 +275,29 @@ public BitbucketApi buildBitbucketClient() {
return getBitbucketConnector().create(repoOwner, repository, getScanCredentials());
}
- @Override
- protected void retrieve(SCMHeadObserver observer, final TaskListener listener) throws IOException,
- InterruptedException {
+ public BitbucketApi buildBitbucketClient(PullRequestSCMHead head) {
+ return getBitbucketConnector().create(head.getRepoOwner(), head.getRepository(), getScanCredentials());
+ }
+ @Override
+ protected void retrieve(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHeadObserver observer,
+ @CheckForNull SCMHeadEvent> event, @NonNull TaskListener listener)
+ throws IOException, InterruptedException {
StandardUsernamePasswordCredentials scanCredentials = getScanCredentials();
if (scanCredentials == null) {
- listener.getLogger().format("Connecting to %s with no credentials, anonymous access%n", bitbucketServerUrl == null ? "https://bitbucket.org" : bitbucketServerUrl);
+ listener.getLogger().format("Connecting to %s with no credentials, anonymous access%n", bitbucketUrl());
} else {
- listener.getLogger().format("Connecting to %s using %s%n", bitbucketServerUrl == null ? "https://bitbucket.org" : bitbucketServerUrl, CredentialsNameProvider.name(scanCredentials));
+ listener.getLogger().format("Connecting to %s using %s%n", bitbucketUrl(), CredentialsNameProvider.name(scanCredentials));
}
// Search branches
- retrieveBranches(observer, listener);
+ retrieveBranches(criteria, observer, listener);
// Search pull requests
- retrievePullRequests(observer, listener);
+ retrievePullRequests(criteria, observer, listener);
}
- private void retrievePullRequests(SCMHeadObserver observer, final TaskListener listener) throws IOException {
+ private void retrievePullRequests(SCMSourceCriteria criteria, SCMHeadObserver observer, final TaskListener listener)
+ throws IOException, InterruptedException {
String fullName = repoOwner + "/" + repository;
listener.getLogger().println("Looking up " + fullName + " for pull requests");
@@ -289,14 +305,29 @@ private void retrievePullRequests(SCMHeadObserver observer, final TaskListener l
if (bitbucket.isPrivate()) {
List extends BitbucketPullRequest> pulls = bitbucket.getPullRequests();
for (final BitbucketPullRequest pull : pulls) {
+ checkInterrupt();
listener.getLogger().println(
"Checking PR from " + pull.getSource().getRepository().getFullName() + " and branch "
+ pull.getSource().getBranch().getName());
// Resolve full hash. See https://bitbucket.org/site/master/issues/11415/pull-request-api-should-return-full-commit
- String hash = bitbucket.resolveSourceFullHash(pull);
+
+ String hash = null;
+ try {
+ hash = bitbucket.resolveSourceFullHash(pull);
+ } catch (BitbucketRequestException e) {
+ if (e.getHttpCode() == 403) {
+ listener.getLogger().println(
+ "Do not have permission to view PR from " + pull.getSource().getRepository().getFullName() + " and branch "
+ + pull.getSource().getBranch().getName());
+ } else {
+ e.printStackTrace(
+ listener.error("Cannot resolve hash: [%s]%n", pull.getSource().getCommit().getHash()));
+ }
+ continue;
+ }
if (hash != null) {
- observe(observer, listener,
+ observe(criteria, observer, listener,
pull.getSource().getRepository().getOwnerName(),
pull.getSource().getRepository().getRepositoryName(),
pull.getSource().getBranch().getName(),
@@ -314,7 +345,8 @@ private void retrievePullRequests(SCMHeadObserver observer, final TaskListener l
}
}
- private void retrieveBranches(@NonNull final SCMHeadObserver observer, @NonNull TaskListener listener)
+ private void retrieveBranches(SCMSourceCriteria criteria, @NonNull final SCMHeadObserver observer,
+ @NonNull TaskListener listener)
throws IOException, InterruptedException {
String fullName = repoOwner + "/" + repository;
listener.getLogger().println("Looking up " + fullName + " for branches");
@@ -322,20 +354,21 @@ private void retrieveBranches(@NonNull final SCMHeadObserver observer, @NonNull
final BitbucketApi bitbucket = getBitbucketConnector().create(repoOwner, repository, getScanCredentials());
List extends BitbucketBranch> branches = bitbucket.getBranches();
for (BitbucketBranch branch : branches) {
+ checkInterrupt();
listener.getLogger().println("Checking branch " + branch.getName() + " from " + fullName);
- observe(observer, listener, repoOwner, repository, branch.getName(),
+ observe(criteria, observer, listener, repoOwner, repository, branch.getName(),
branch.getRawNode(), null);
}
}
- private void observe(SCMHeadObserver observer, final TaskListener listener,
- final String owner, final String repositoryName,
- final String branchName, final String hash, BitbucketPullRequest pr) throws IOException {
+ private void observe(SCMSourceCriteria criteria, SCMHeadObserver observer, final TaskListener listener,
+ final String owner, final String repositoryName,
+ final String branchName, final String hash, BitbucketPullRequest pr) throws IOException {
if (isExcluded(branchName)) {
return;
}
final BitbucketApi bitbucket = getBitbucketConnector().create(owner, repositoryName, getScanCredentials());
- SCMSourceCriteria branchCriteria = getCriteria();
+ SCMSourceCriteria branchCriteria = criteria;
if (branchCriteria != null) {
SCMSourceCriteria.Probe probe = new SCMSourceCriteria.Probe() {
@@ -368,7 +401,9 @@ public boolean exists(@NonNull String path) throws IOException {
}
}
SCMRevision revision;
- SCMHeadWithOwnerAndRepo head = new SCMHeadWithOwnerAndRepo(owner, repositoryName, branchName, pr);
+ SCMHead head = pr != null
+ ? new PullRequestSCMHead(owner, repositoryName, branchName, pr)
+ : new BranchSCMHead(branchName);
if (getRepositoryType() == RepositoryType.MERCURIAL) {
revision = new MercurialRevision(head, hash);
} else {
@@ -377,13 +412,21 @@ public boolean exists(@NonNull String path) throws IOException {
observer.observe(head, revision);
}
+
+
@Override
protected SCMRevision retrieve(SCMHead head, TaskListener listener) throws IOException, InterruptedException {
- SCMHeadWithOwnerAndRepo bbHead = (SCMHeadWithOwnerAndRepo) head;
- BitbucketApi bitbucket = getBitbucketConnector().create(bbHead.getRepoOwner(), bbHead.getRepoName(), getScanCredentials());
+ BitbucketApi bitbucket = head instanceof PullRequestSCMHead
+ ? getBitbucketConnector().create(
+ ((PullRequestSCMHead) head).getRepoOwner(),
+ ((PullRequestSCMHead) head).getRepository(),
+ getScanCredentials()
+ )
+ : getBitbucketConnector().create(repoOwner, repository, getScanCredentials());
+ String branchName = head instanceof PullRequestSCMHead ? ((PullRequestSCMHead) head).getBranchName() : head.getName();
List extends BitbucketBranch> branches = bitbucket.getBranches();
for (BitbucketBranch b : branches) {
- if (b.getName().equals(bbHead.getBranchName())) {
+ if (branchName.equals(b.getName())) {
if (b.getRawNode() == null) {
if (getBitbucketServerUrl() == null) {
listener.getLogger().format("Cannot resolve the hash of the revision in branch %s", b.getName());
@@ -399,38 +442,75 @@ protected SCMRevision retrieve(SCMHead head, TaskListener listener) throws IOExc
}
}
}
- LOGGER.warning("No branch found in " + bbHead.getRepoOwner() + "/" + bbHead.getRepoName() + " with name [" + bbHead.getBranchName() + "]");
+ LOGGER.log(Level.WARNING, "No branch found in {0}/{1} with name [{2}]", head instanceof PullRequestSCMHead
+ ? new Object[]{
+ ((PullRequestSCMHead) head).getRepoOwner(),
+ ((PullRequestSCMHead) head).getRepository(),
+ ((PullRequestSCMHead) head).getBranchName()}
+ : new Object[]{repoOwner, repository, head.getName()});
return null;
}
@Override
public SCM build(SCMHead head, SCMRevision revision) {
- if (head instanceof SCMHeadWithOwnerAndRepo) { // Defensive, it must be always true
- SCMHeadWithOwnerAndRepo h = (SCMHeadWithOwnerAndRepo) head;
+ if (head instanceof PullRequestSCMHead) {
+ PullRequestSCMHead h = (PullRequestSCMHead) head;
if (getRepositoryType() == RepositoryType.MERCURIAL) {
- MercurialSCM scm = new MercurialSCM(getRemote(h.getRepoOwner(), h.getRepoName()));
+ MercurialSCM scm = new MercurialSCM(getRemote(h.getRepoOwner(), h.getRepository()));
// If no revision specified the branch name will be used as revision
- scm.setRevision(revision instanceof MercurialRevision ? ((MercurialRevision) revision).getHash() : h.getBranchName());
+ scm.setRevision(revision instanceof MercurialRevision
+ ? ((MercurialRevision) revision).getHash()
+ : h.getBranchName()
+ );
scm.setRevisionType(RevisionType.BRANCH);
scm.setCredentialsId(getCheckoutEffectiveCredentials());
return scm;
} else {
// Defaults to Git
- BuildChooser buildChooser = revision instanceof AbstractGitSCMSource.SCMRevisionImpl ? new SpecificRevisionBuildChooser(
- (AbstractGitSCMSource.SCMRevisionImpl) revision) : new DefaultBuildChooser();
- return new GitSCM(
- getGitRemoteConfigs(h),
+ BuildChooser buildChooser = revision instanceof AbstractGitSCMSource.SCMRevisionImpl
+ ? new SpecificRevisionBuildChooser((AbstractGitSCMSource.SCMRevisionImpl) revision)
+ : new DefaultBuildChooser();
+ return new GitSCM(getGitRemoteConfigs(h),
Collections.singletonList(new BranchSpec(h.getBranchName())),
false, Collections.emptyList(),
null, null, Collections.singletonList(new BuildChooserSetting(buildChooser)));
}
}
- throw new IllegalArgumentException("An SCMHeadWithOwnerAndRepo required as parameter");
+ if (head instanceof BranchSCMHead) {
+ if (getRepositoryType() == RepositoryType.MERCURIAL) {
+ MercurialSCM scm = new MercurialSCM(getRemote(repoOwner, repository));
+ // If no revision specified the branch name will be used as revision
+ scm.setRevision(revision instanceof MercurialRevision
+ ? ((MercurialRevision) revision).getHash()
+ : head.getName()
+ );
+ scm.setRevisionType(RevisionType.BRANCH);
+ scm.setCredentialsId(getCheckoutEffectiveCredentials());
+ return scm;
+ } else {
+ // Defaults to Git
+ BuildChooser buildChooser = revision instanceof AbstractGitSCMSource.SCMRevisionImpl
+ ? new SpecificRevisionBuildChooser((AbstractGitSCMSource.SCMRevisionImpl) revision)
+ : new DefaultBuildChooser();
+ return new GitSCM(getGitRemoteConfigs((BranchSCMHead)head),
+ Collections.singletonList(new BranchSpec(head.getName())),
+ false, Collections.emptyList(),
+ null, null, Collections.singletonList(new BuildChooserSetting(buildChooser)));
+ }
+ }
+ throw new IllegalArgumentException("Either PullRequestSCMHead or BranchSCMHead required as parameter");
+ }
+
+ protected List getGitRemoteConfigs(BranchSCMHead head) {
+ List result = new ArrayList();
+ String remote = getRemote(repoOwner, repository);
+ result.add(new UserRemoteConfig(remote, getRemoteName(), "+refs/heads/" + head.getName(), getCheckoutEffectiveCredentials()));
+ return result;
}
- protected List getGitRemoteConfigs(SCMHeadWithOwnerAndRepo head) {
+ protected List getGitRemoteConfigs(PullRequestSCMHead head) {
List result = new ArrayList();
- String remote = getRemote(head.getRepoOwner(), head.getRepoName());
+ String remote = getRemote(head.getRepoOwner(), head.getRepository());
result.add(new UserRemoteConfig(remote, getRemoteName(), "+refs/heads/" + head.getBranchName(), getCheckoutEffectiveCredentials()));
return result;
}
@@ -521,6 +601,66 @@ private String getCheckoutEffectiveCredentials() {
}
}
+ @NonNull
+ @Override
+ protected List retrieveActions(@CheckForNull SCMSourceEvent event,
+ @NonNull TaskListener listener)
+ throws IOException, InterruptedException {
+ // TODO when we have support for trusted events, use the details from event if event was from trusted source
+ List result = new ArrayList<>();
+ final BitbucketApi bitbucket = getBitbucketConnector().create(repoOwner, repository, getScanCredentials());
+ BitbucketRepository r = bitbucket.getRepository();
+ if (r != null) {
+ result.add(new BitbucketRepoMetadataAction(r));
+ }
+ String serverUrl = StringUtils.removeEnd(bitbucketUrl(), "/");
+ if (StringUtils.isNotEmpty(bitbucketServerUrl)) {
+ result.add(new BitbucketLink("icon-bitbucket-repo",
+ serverUrl + "/projects/" + repoOwner + "/repos/" + repository));
+ result.add(new ObjectMetadataAction(r == null ? null : r.getFullName(), null,
+ serverUrl + "/projects/" + repoOwner + "/repos/" + repository));
+ } else {
+ result.add(new BitbucketLink("icon-bitbucket-repo", serverUrl + "/" + repoOwner + "/" + repository));
+ result.add(new ObjectMetadataAction(r == null ? null : r.getFullName(), null,
+ serverUrl + "/" + repoOwner + "/" + repository));
+ }
+ return result;
+ }
+
+ @NonNull
+ @Override
+ protected List retrieveActions(@NonNull SCMHead head,
+ @CheckForNull SCMHeadEvent event,
+ @NonNull TaskListener listener)
+ throws IOException, InterruptedException {
+ // TODO when we have support for trusted events, use the details from event if event was from trusted source
+ List result = new ArrayList<>();
+ String serverUrl = StringUtils.removeEnd(bitbucketUrl(), "/");
+ if (StringUtils.isNotEmpty(bitbucketServerUrl)) {
+ String branchUrl;
+ if (head instanceof PullRequestSCMHead) {
+ PullRequestSCMHead pr = (PullRequestSCMHead) head;
+ branchUrl = "projects/" + repoOwner + "/repos/" + repository + "/pull-requests/"+pr.getId()+"/overview";
+ } else {
+ branchUrl = "projects/" + repoOwner + "/repos/" + repository + "/compare/commits?sourceBranch=" +
+ URLEncoder.encode(Constants.R_HEADS + head.getName(), "UTF-8");
+ }
+ result.add(new BitbucketLink("icon-bitbucket-branch", serverUrl + "/" + branchUrl));
+ result.add(new ObjectMetadataAction(null, null, serverUrl+"/"+branchUrl));
+ } else {
+ String branchUrl;
+ if (head instanceof PullRequestSCMHead) {
+ PullRequestSCMHead pr = (PullRequestSCMHead) head;
+ branchUrl = repoOwner + "/" + repository + "/pull-requests/" + pr.getId();
+ } else {
+ branchUrl = repoOwner + "/" + repository + "/branch/" + head.getName();
+ }
+ result.add(new BitbucketLink("icon-bitbucket-branch", serverUrl + "/" + branchUrl));
+ result.add(new ObjectMetadataAction(null, null, serverUrl + "/" + branchUrl));
+ }
+ return result;
+ }
+
@Extension
public static class DescriptorImpl extends SCMSourceDescriptor {
@@ -532,7 +672,8 @@ public String getDisplayName() {
return "Bitbucket";
}
- public FormValidation doCheckCredentialsId(@QueryParameter String value) {
+ public FormValidation doCheckCredentialsId(@QueryParameter String value,
+ @QueryParameter String bitbucketServerUrl) {
if (!value.isEmpty()) {
return FormValidation.ok();
} else {
@@ -555,7 +696,7 @@ public static FormValidation doCheckBitbucketServerUrl(@QueryParameter String bi
public ListBoxModel doFillCredentialsIdItems(@AncestorInPath SCMSourceOwner context, @QueryParameter String bitbucketServerUrl) {
StandardListBoxModel result = new StandardListBoxModel();
- result.withEmptySelection();
+ result.includeEmptyValue();
new BitbucketApiConnector(bitbucketServerUrl).fillCredentials(result, context);
return result;
}
@@ -568,6 +709,15 @@ public ListBoxModel doFillCheckoutCredentialsIdItems(@AncestorInPath SCMSourceOw
return result;
}
+ @NonNull
+ @Override
+ protected SCMHeadCategory[] createCategories() {
+ return new SCMHeadCategory[]{
+ new UncategorizedSCMHeadCategory(Messages._BitbucketSCMSource_UncategorizedSCMHeadCategory_DisplayName()),
+ new ChangeRequestSCMHeadCategory(Messages._BitbucketSCMSource_ChangeRequestSCMHeadCategory_DisplayName())
+ // TODO add support for tags and maybe feature branch identification
+ };
+ }
}
public static class MercurialRevision extends SCMRevision {
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketTeamMetadataAction.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketTeamMetadataAction.java
new file mode 100644
index 000000000..722862a68
--- /dev/null
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketTeamMetadataAction.java
@@ -0,0 +1,119 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2016 CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.cloudbees.jenkins.plugins.bitbucket;
+
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import jenkins.scm.api.metadata.AvatarMetadataAction;
+
+/**
+ * Invisible property that retains information about Bitbucket team.
+ */
+public class BitbucketTeamMetadataAction extends AvatarMetadataAction {
+ @CheckForNull
+ private final String avatarUrl;
+
+ public BitbucketTeamMetadataAction(@CheckForNull String avatarUrl) {
+ this.avatarUrl = avatarUrl;
+ }
+
+// TODO when bitbucket supports serving avatars with a size request - currently only works if using gravatar or server
+// /**
+// * {@inheritDoc}
+// */
+// @Override
+// public String getAvatarImageOf(String size) {
+// if (avatarUrl == null) {
+// // fall back to the generic github org icon
+// String image = avatarIconClassNameImageOf(getAvatarIconClassName(), size);
+// return image != null
+// ? image
+// : (Stapler.getCurrentRequest().getContextPath() + Jenkins.RESOURCE_PATH
+// + "/plugin/cloudbees-bitbucket-branch-source/images/" + size + "/bitbucket-logo.png");
+// } else {
+// String[] xy = size.split("x");
+// if (xy.length == 0) return avatarUrl;
+// if (avatarUrl.contains("?")) return avatarUrl + "&s=" + xy[0];
+// else return avatarUrl + "?s=" + xy[0];
+// }
+// }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getAvatarIconClassName() {
+ // TODO when bitbucket supports serving avatars with a size request
+ // return avatarUrl == null ? "icon-bitbucket-logo" : null;
+ return "icon-bitbucket-logo";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getAvatarDescription() {
+ return Messages.BitbucketTeamMetadataAction_IconDescription();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ BitbucketTeamMetadataAction that = (BitbucketTeamMetadataAction) o;
+
+ if (avatarUrl != null ? !avatarUrl.equals(that.avatarUrl) : that.avatarUrl != null) {
+ return false;
+ }
+ return true;
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return (avatarUrl != null ? avatarUrl.hashCode() : 0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "BitbucketTeamMetadataAction{" +
+ ", avatarUrl='" + avatarUrl + '\'' +
+ '}';
+ }
+}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BranchJobFilter.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BranchJobFilter.java
deleted file mode 100644
index 9a447423e..000000000
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BranchJobFilter.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.cloudbees.jenkins.plugins.bitbucket;
-
-import org.kohsuke.stapler.DataBoundConstructor;
-
-import hudson.Extension;
-import hudson.model.Descriptor;
-import hudson.views.ViewJobFilter;
-import jenkins.scm.api.SCMHead;
-
-public class BranchJobFilter extends AbstractBranchJobFilter {
- @DataBoundConstructor
- public BranchJobFilter() {}
-
- @Override
- protected boolean shouldShow(SCMHead head) {
- return head instanceof SCMHeadWithOwnerAndRepo && ((SCMHeadWithOwnerAndRepo) head).getPullRequestId() == null;
- }
-
- @Extension
- public static class DescriptorImpl extends Descriptor {
- @Override
- public String getDisplayName() {
- return "Bitbucket Branch Jobs Only";
- }
- }
-
-}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BranchSCMHead.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BranchSCMHead.java
new file mode 100644
index 000000000..a3214e8ac
--- /dev/null
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BranchSCMHead.java
@@ -0,0 +1,41 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2016, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.cloudbees.jenkins.plugins.bitbucket;
+
+import jenkins.scm.api.SCMHead;
+
+/**
+ * {@link SCMHead} for a BitBucket branch.
+ * @since FIXME
+ */
+public class BranchSCMHead extends SCMHead {
+
+ private static final long serialVersionUID = 1L;
+
+
+ public BranchSCMHead(String branchName) {
+ super(branchName);
+ }
+
+}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/CustomComputedFolderItemListener.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/CustomComputedFolderItemListener.java
deleted file mode 100644
index 181b9d1b5..000000000
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/CustomComputedFolderItemListener.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright (c) 2016, CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package com.cloudbees.jenkins.plugins.bitbucket;
-
-import static java.util.Arrays.asList;
-
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-
-import com.cloudbees.jenkins.plugins.bitbucket.CustomComputedFolderItemListener.Sniffer.OrgMatch;
-import com.cloudbees.jenkins.plugins.bitbucket.CustomComputedFolderItemListener.Sniffer.RepoMatch;
-import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketTeam;
-import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
-import com.google.inject.Inject;
-
-import hudson.BulkChange;
-import hudson.Extension;
-import hudson.model.AbstractItem;
-import hudson.model.AllView;
-import hudson.model.Item;
-import hudson.model.Job;
-import hudson.model.ListView;
-import hudson.model.listeners.ItemListener;
-import hudson.views.StatusColumn;
-import hudson.views.WeatherColumn;
-import jenkins.branch.Branch;
-import jenkins.branch.MultiBranchProject;
-import jenkins.branch.OrganizationFolder;
-import jenkins.scm.api.SCMNavigator;
-
-// TODO: this code should be mostly extracted to scm-api, and then the dependency to branch-api can be removed.
-@Extension
-public class CustomComputedFolderItemListener extends ItemListener {
-
- @Inject
- private Applier applier;
-
- @Override
- public void onUpdated(Item item) {
- maybeApply(item);
- }
-
- @Override
- public void onCreated(Item item) {
- maybeApply(item);
- }
-
- private void maybeApply(Item item) {
- OrgMatch f = Sniffer.matchOrg(item);
- if (f != null && f.folder.getDisplayNameOrNull() == null) {
- applier.applyOrg(f);
- }
- RepoMatch r = Sniffer.matchRepo(item);
- if (r != null) {
- applier.applyRepo(r);
- }
- }
-
- @Extension
- @Restricted(NoExternalUse.class)
- public static class Applier {
-
- public void applyOrg(OrgMatch f) {
- if (UPDATING.get().add(f.folder)) {
- BulkChange bc = new BulkChange(f.folder);
- try {
- StandardUsernamePasswordCredentials credentials = f.scm.getBitbucketConnector().lookupCredentials(f.folder,
- f.scm.getCredentialsId(), StandardUsernamePasswordCredentials.class);
- BitbucketTeam team = f.scm.getBitbucketConnector().create(f.scm.getRepoOwner(), credentials).getTeam();
- if (team != null) {
- try {
- f.folder.setDisplayName(team.getDisplayName());
- if (f.folder.getView("Repositories") == null && f.folder.getView("All") instanceof AllView) {
- // need to set the default view
- ListView lv = new ListView("Repositories");
- lv.getColumns().replaceBy(asList(
- new StatusColumn(),
- new WeatherColumn(),
- new CustomNameJobColumn(Messages.class, Messages._ListViewColumn_Repository())
- ));
- lv.setIncludeRegex(".*"); // show all
- f.folder.addView(lv);
- f.folder.deleteView(f.folder.getView("All"));
- f.folder.setPrimaryView(lv);
- }
- bc.commit();
- } catch (IOException e) {
- LOGGER.log(Level.INFO, "Can not set the Team/Project display name automatically. Skipping.");
- LOGGER.log(Level.FINE, "StackTrace:", e);
- }
- }
- } finally {
- bc.abort();
- UPDATING.get().remove(f.folder);
- }
- }
- }
-
- public void applyRepo(RepoMatch r) {
- if (UPDATING.get().add(r.repo)) {
- BulkChange bc = new BulkChange(r.repo);
- try {
- if (r.repo.getView("Branches") == null && r.repo.getView("All") instanceof AllView) {
- // create initial views
- ListView bv = new ListView("Branches");
- bv.getJobFilters().add(new BranchJobFilter());
-
- ListView pv = new ListView("Pull Requests");
- pv.getJobFilters().add(new PullRequestJobFilter());
-
- try {
- r.repo.addView(bv);
- r.repo.addView(pv);
- r.repo.deleteView(r.repo.getView("All"));
- r.repo.setPrimaryView(bv);
- bc.commit();
- } catch (IOException e) {
- LOGGER.log(Level.INFO, "Can not set the repo/PR views. Skipping.");
- LOGGER.log(Level.FINE, "StackTrace:", e);
- }
- }
- } finally {
- bc.abort();
- UPDATING.get().remove(r.repo);
- }
- }
- }
-
- /**
- * Keeps track of what we are updating to avoid recursion, because {@link AbstractItem#save()}
- * triggers {@link ItemListener}.
- */
- private final ThreadLocal> UPDATING = new ThreadLocal>() {
- @Override
- protected Set initialValue() {
- return new HashSet<>();
- }
- };
- }
-
- public static class Sniffer {
-
- static class OrgMatch {
- final OrganizationFolder folder;
- final BitbucketSCMNavigator scm;
-
- public OrgMatch(OrganizationFolder folder, BitbucketSCMNavigator scm) {
- this.folder = folder;
- this.scm = scm;
- }
- }
-
- public static OrgMatch matchOrg(Object item) {
- if (item instanceof OrganizationFolder) {
- OrganizationFolder of = (OrganizationFolder) item;
- List navigators = of.getNavigators();
- if (/* could be called from constructor */navigators != null && navigators.size() > 0) {
- SCMNavigator n = navigators.get(0);
- if (n instanceof BitbucketSCMNavigator) {
- return new OrgMatch(of, (BitbucketSCMNavigator) n);
- }
- }
- }
- return null;
- }
-
- static class RepoMatch extends OrgMatch {
- final MultiBranchProject repo;
-
- public RepoMatch(OrgMatch x, MultiBranchProject repo) {
- super(x.folder, x.scm);
- this.repo = repo;
- }
- }
-
- public static RepoMatch matchRepo(Object item) {
- if (item instanceof MultiBranchProject) {
- MultiBranchProject repo = (MultiBranchProject) item;
- OrgMatch org = matchOrg(repo.getParent());
- if (org != null)
- return new RepoMatch(org, repo);
- }
- return null;
- }
-
- static class BranchMatch extends RepoMatch {
- final Job branch;
-
- public BranchMatch(RepoMatch x, Job branch) {
- super(x,x.repo);
- this.branch = branch;
- }
-
- public Branch getScmBranch() {
- return repo.getProjectFactory().getBranch(branch);
- }
- }
-
- public static BranchMatch matchBranch(Item item) {
- if (item instanceof Job) {
- Job branch = (Job) item;
- RepoMatch x = matchRepo(item.getParent());
- if (x!=null) {
- return new BranchMatch(x, branch);
- }
- }
- return null;
- }
-
- }
-
- private static final Logger LOGGER = Logger.getLogger(CustomComputedFolderItemListener.class.getName());
-
-}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/CustomNameJobColumn.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/CustomNameJobColumn.java
deleted file mode 100644
index ccb5af6d1..000000000
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/CustomNameJobColumn.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.cloudbees.jenkins.plugins.bitbucket;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.jvnet.localizer.Localizable;
-import org.jvnet.localizer.ResourceBundleHolder;
-import org.kohsuke.stapler.DataBoundConstructor;
-
-import hudson.Extension;
-import hudson.views.JobColumn;
-import hudson.views.ListViewColumnDescriptor;
-import jenkins.model.Jenkins;
-import jenkins.util.NonLocalizable;
-
-public class CustomNameJobColumn extends JobColumn {
-
- /**
- * Resource bundle name.
- */
- private final String bundle;
- private final String key;
-
- private transient Localizable loc;
-
- @DataBoundConstructor
- public CustomNameJobColumn(String bundle, String key) {
- this.bundle = bundle;
- this.key = key;
- readResolve();
- }
-
- public CustomNameJobColumn(Class bundle, Localizable loc) {
- this.bundle = bundle.getName();
- this.key = loc.getKey();
- this.loc = loc;
- }
-
- public String getBundle() {
- return bundle;
- }
-
- public String getKey() {
- return loc.getKey();
- }
-
- public String getMessage() {
- return loc.toString();
- }
-
- private Object readResolve() {
- try {
- loc = new Localizable(
- ResourceBundleHolder.get(Jenkins.getActiveInstance().pluginManager.uberClassLoader.loadClass(bundle)),
- key);
- } catch (ClassNotFoundException e) {
- LOGGER.log(Level.WARNING, "No such bundle: " + bundle);
- loc = new NonLocalizable(bundle + ':' + key);
- }
- return this;
- }
-
- @Extension
- public static class DescriptorImpl extends ListViewColumnDescriptor {
- @Override
- public String getDisplayName() {
- return "Repositories";
- }
-
- @Override
- public boolean shownByDefault() {
- return false;
- }
- }
-
- private static final Logger LOGGER = Logger.getLogger(CustomNameJobColumn.class.getName());
-}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestAction.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestAction.java
index 1249d1ad7..1bf550e27 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestAction.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestAction.java
@@ -1,14 +1,42 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2016, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
package com.cloudbees.jenkins.plugins.bitbucket;
-import java.net.MalformedURLException;
-import java.net.URL;
-
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
-
import edu.umd.cs.findbugs.annotations.NonNull;
-import jenkins.scm.api.actions.ChangeRequestAction;
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.DoNotUse;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
-public class PullRequestAction extends ChangeRequestAction {
+/**
+ * Retained to help migrate legacy SCMHead instances
+ */
+@Deprecated
+@Restricted(NoExternalUse.class)
+class PullRequestAction {
private final String number;
private URL url;
private final String title;
@@ -25,23 +53,19 @@ public PullRequestAction(BitbucketPullRequest pr) {
userLogin = pr.getAuthorLogin();
}
- @Override
@NonNull
public String getId() {
return number;
}
- @Override
public URL getURL() {
return url;
}
- @Override
public String getTitle() {
return title;
}
- @Override
public String getAuthor() {
return userLogin;
}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestJobFilter.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestJobFilter.java
deleted file mode 100644
index 9a2527df7..000000000
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestJobFilter.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.cloudbees.jenkins.plugins.bitbucket;
-
-import org.kohsuke.stapler.DataBoundConstructor;
-
-import hudson.Extension;
-import hudson.model.Descriptor;
-import hudson.views.ViewJobFilter;
-import jenkins.scm.api.SCMHead;
-
-public class PullRequestJobFilter extends AbstractBranchJobFilter {
- @DataBoundConstructor
- public PullRequestJobFilter() {}
-
- @Override
- protected boolean shouldShow(SCMHead head) {
- return head instanceof SCMHeadWithOwnerAndRepo && ((SCMHeadWithOwnerAndRepo) head).getPullRequestId() != null;
- }
-
- @Extension
- public static class DescriptorImpl extends Descriptor {
- @Override
- public String getDisplayName() {
- return "Bitbucket Pull Request Jobs Only";
- }
- }
-
-}
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..90f058f58
--- /dev/null
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/PullRequestSCMHead.java
@@ -0,0 +1,94 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2016, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.cloudbees.jenkins.plugins.bitbucket;
+
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import jenkins.scm.api.mixin.ChangeRequestSCMHead;
+import jenkins.scm.api.SCMHead;
+
+/**
+ * {@link SCMHead} for a BitBucket Pull request
+ * @since FIXME
+ */
+public class PullRequestSCMHead extends SCMHead implements ChangeRequestSCMHead {
+
+ private static final String PR_BRANCH_PREFIX = "PR-";
+
+ private static final long serialVersionUID = 1L;
+
+ private final String repoOwner;
+
+ private final String repository;
+
+ private final String branchName;
+
+ private final String number;
+
+ private final BranchSCMHead target;
+
+ public PullRequestSCMHead(String repoOwner, String repository, String branchName,
+ String number, BranchSCMHead target) {
+ super(PR_BRANCH_PREFIX + number);
+ this.repoOwner = repoOwner;
+ this.repository = repository;
+ this.branchName = branchName;
+ this.number = number;
+ this.target = target;
+ }
+
+ public PullRequestSCMHead(String repoOwner, String repository, String branchName, BitbucketPullRequest pr) {
+ super(PR_BRANCH_PREFIX + pr.getId());
+ this.repoOwner = repoOwner;
+ this.repository = repository;
+ this.branchName = branchName;
+ this.number = pr.getId();
+ this.target = new BranchSCMHead(pr.getDestination().getBranch().getName());
+ }
+
+ public String getRepoOwner() {
+ return repoOwner;
+ }
+
+ public String getRepository() {
+ return repository;
+ }
+
+ public String getBranchName() {
+ return branchName;
+ }
+
+ @NonNull
+ @Override
+ public String getId() {
+ return number;
+ }
+
+ @NonNull
+ @Override
+ public SCMHead getTarget() {
+ return target;
+ }
+
+}
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 ddad12ac6..f1b731ae9 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/SCMHeadWithOwnerAndRepo.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/SCMHeadWithOwnerAndRepo.java
@@ -23,26 +23,27 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket;
-import java.util.LinkedList;
-import java.util.List;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestDestination;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestSource;
+import com.cloudbees.jenkins.plugins.bitbucket.server.client.pullrequest.BitbucketServerPullRequest;
+import com.cloudbees.jenkins.plugins.bitbucket.server.client.pullrequest.BitbucketServerPullRequestDestination;
+import com.cloudbees.jenkins.plugins.bitbucket.server.client.pullrequest.BitbucketServerPullRequestSource;
+import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import java.io.ObjectStreamException;
+import java.net.MalformedURLException;
+import java.net.URL;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import hudson.model.Action;
import jenkins.scm.api.SCMHead;
+import jenkins.scm.api.actions.ChangeRequestAction;
/**
- * {@link SCMHead} extended with additional information:
- *
- *
{@link #repoOwner}: the repository owner
- *
{@link #repoName}: the repository name
- *
{@link #metadata}: metadata related to Pull Requests - null if this object is not representing a PR
- *
- * 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.
+ * Legacy class retained to allow for graceful migration of serialized data.
+ * @deprecated use {@link PullRequestSCMHead} or {@link BranchSCMHead}
*/
+@Deprecated
public class SCMHeadWithOwnerAndRepo extends SCMHead {
private static final long serialVersionUID = 1L;
@@ -51,62 +52,21 @@ public class SCMHeadWithOwnerAndRepo extends SCMHead {
private final String repoName;
- private PullRequestAction metadata = null;
+ private transient PullRequestAction metadata;
- private static final String PR_BRANCH_PREFIX = "PR-";
-
- public SCMHeadWithOwnerAndRepo(String repoOwner, String repoName, String branchName, BitbucketPullRequest pr) {
+ public SCMHeadWithOwnerAndRepo(String repoOwner, String repoName, String branchName) {
super(branchName);
this.repoOwner = repoOwner;
this.repoName = repoName;
- if (pr != null) {
- this.metadata = new PullRequestAction(pr);
- }
}
- public SCMHeadWithOwnerAndRepo(String repoOwner, String repoName, String branchName) {
- this(repoOwner, repoName, branchName, null);
- }
-
- public String getRepoOwner() {
- return repoOwner;
- }
-
- public String getRepoName() {
- return repoName;
- }
-
- /**
- * @return the original branch name without the "PR-owner-" part.
- */
- 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 metadata != null ? PR_BRANCH_PREFIX + metadata.getId() : getBranchName();
- }
-
- @CheckForNull
- public Integer getPullRequestId() {
+ private Object readResolve() throws ObjectStreamException {
if (metadata != null) {
- return Integer.parseInt(metadata.getId());
- } else {
- return null;
+ // we just want to flag this as a PR, the legacy data did not contain the required information so
+ // we will end up triggering a rebuild on next index / event via take-over
+ return new PullRequestSCMHead(repoOwner, repoName, getName(), metadata.getId(), new BranchSCMHead("\u0000"));
}
+ return new BranchSCMHead(getName());
}
- @Override
- public List extends Action> getAllActions() {
- List actions = new LinkedList(super.getAllActions());
- if (metadata != null) {
- actions.add(metadata);
- }
- return actions;
- }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketHref.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketHref.java
new file mode 100644
index 000000000..787600e33
--- /dev/null
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketHref.java
@@ -0,0 +1,31 @@
+package com.cloudbees.jenkins.plugins.bitbucket.api;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.DoNotUse;
+
+/**
+ * A Href for something on bitbucket.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BitbucketHref {
+ private String href;
+
+
+ // Used for marshalling/unmarshalling
+ @Restricted(DoNotUse.class)
+ public BitbucketHref() {
+ }
+
+ public BitbucketHref(String href) {
+ this.href = href;
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+}
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 c2b943b74..aafb91fbf 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
@@ -36,6 +36,11 @@ public interface BitbucketPullRequest {
*/
BitbucketPullRequestSource getSource();
+ /**
+ * @return the target repository of this pull request
+ */
+ BitbucketPullRequestDestination getDestination();
+
/**
* @return pull request ID as provided by Bitbucket. It can be used for notifications.
*/
@@ -48,4 +53,4 @@ public interface BitbucketPullRequest {
String getAuthorLogin();
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketPullRequestDestination.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketPullRequestDestination.java
new file mode 100644
index 000000000..c6c7a167f
--- /dev/null
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketPullRequestDestination.java
@@ -0,0 +1,42 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2016 CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+package com.cloudbees.jenkins.plugins.bitbucket.api;
+
+/**
+ * Represents a pull request destination, which is a repository and a branch in that repository.
+ */
+public interface BitbucketPullRequestDestination {
+
+ /**
+ * @return source repository
+ */
+ BitbucketRepository getRepository();
+
+ /**
+ * @return source branch to be merged in the pull request
+ */
+ BitbucketBranch getBranch();
+
+}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketTeam.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketTeam.java
index 4e3c96b83..595c1396c 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketTeam.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketTeam.java
@@ -23,6 +23,8 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.api;
+import java.util.Map;
+
/**
* Represents a Bitbucket team (or a Project when working with Bitbucket Server).
*/
@@ -38,4 +40,10 @@ public interface BitbucketTeam {
*/
String getDisplayName();
-}
\ No newline at end of file
+ /**
+ * Gets the links of the project.
+ *
+ * @return the links of the project.
+ */
+ Map getLinks();
+}
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 de9fcb985..7c4127690 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
@@ -23,6 +23,7 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.client.pullrequest;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestDestination;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
@@ -30,6 +31,7 @@
@JsonIgnoreProperties(ignoreUnknown = true)
public class BitbucketPullRequestValue implements BitbucketPullRequest {
+ private BitbucketPullRequestValueDestination destination;
private BitbucketPullRequestValueRepository source;
private String id;
private String title;
@@ -46,6 +48,15 @@ public void setSource(BitbucketPullRequestValueRepository source) {
this.source = source;
}
+ @Override
+ public BitbucketPullRequestValueDestination getDestination() {
+ return destination;
+ }
+
+ public void setDestination(BitbucketPullRequestValueDestination destination) {
+ this.destination = destination;
+ }
+
public String getId() {
return id;
}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/pullrequest/BitbucketPullRequestValueDestination.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/pullrequest/BitbucketPullRequestValueDestination.java
new file mode 100644
index 000000000..30cbb300c
--- /dev/null
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/pullrequest/BitbucketPullRequestValueDestination.java
@@ -0,0 +1,66 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2016 CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+package com.cloudbees.jenkins.plugins.bitbucket.client.pullrequest;
+
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBranch;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketCommit;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestDestination;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestSource;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
+import com.cloudbees.jenkins.plugins.bitbucket.client.branch.BitbucketCloudBranch;
+import com.cloudbees.jenkins.plugins.bitbucket.client.branch.BitbucketCloudCommit;
+import com.cloudbees.jenkins.plugins.bitbucket.client.repository.BitbucketCloudRepository;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BitbucketPullRequestValueDestination implements BitbucketPullRequestDestination {
+ private BitbucketCloudRepository repository;
+ private BitbucketCloudBranch branch;
+
+ @Override
+ @JsonProperty("repository")
+ public BitbucketRepository getRepository() {
+ return repository;
+ }
+
+ @JsonProperty("repository")
+ public void setRepository(BitbucketCloudRepository repository) {
+ this.repository = repository;
+ }
+
+ @Override
+ @JsonProperty("branch")
+ public BitbucketBranch getBranch() {
+ return branch;
+ }
+
+ @JsonProperty("branch")
+ public void setBranch(BitbucketCloudBranch branch) {
+ this.branch = branch;
+ }
+
+}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/repository/BitbucketCloudTeam.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/repository/BitbucketCloudTeam.java
index 1989f4656..de0edbf35 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/repository/BitbucketCloudTeam.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/repository/BitbucketCloudTeam.java
@@ -23,6 +23,8 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.client.repository;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
+import java.util.Map;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
@@ -37,6 +39,9 @@ public class BitbucketCloudTeam implements BitbucketTeam {
@JsonProperty("display_name")
private String displayName;
+ @JsonProperty("links")
+ private Map links;
+
@Override
public String getName() {
return name;
@@ -55,4 +60,12 @@ public void setDisplayName(String displayName) {
this.displayName = displayName;
}
+ @Override
+ public Map getLinks() {
+ return links;
+ }
+
+ public void setLinks(Map links) {
+ this.links = links;
+ }
}
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 5e24939e0..b9b0dd12f 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
@@ -37,6 +37,9 @@ public class BitbucketServerPullRequest implements BitbucketPullRequest {
@JsonProperty("fromRef")
private BitbucketServerPullRequestSource source;
+ @JsonProperty("toRef")
+ private BitbucketServerPullRequestDestination destination;
+
private String title;
private String link;
@@ -52,6 +55,15 @@ public void setSource(BitbucketServerPullRequestSource source) {
this.source = source;
}
+ @Override
+ public BitbucketServerPullRequestDestination getDestination() {
+ return destination;
+ }
+
+ public void setDestination(BitbucketServerPullRequestDestination destination) {
+ this.destination = destination;
+ }
+
@Override
public String getId() {
return id;
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/pullrequest/BitbucketServerPullRequestDestination.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/pullrequest/BitbucketServerPullRequestDestination.java
new file mode 100644
index 000000000..5d5fe03d0
--- /dev/null
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/pullrequest/BitbucketServerPullRequestDestination.java
@@ -0,0 +1,65 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2016 CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+package com.cloudbees.jenkins.plugins.bitbucket.server.client.pullrequest;
+
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBranch;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketCommit;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestDestination;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestSource;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
+import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBranch;
+import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerCommit;
+import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BitbucketServerPullRequestDestination implements BitbucketPullRequestDestination {
+
+ @JsonProperty("displayId")
+ private String branchName;
+
+ private BitbucketServerRepository repository;
+
+ @Override
+ public BitbucketRepository getRepository() {
+ return repository;
+ }
+
+ @Override
+ public BitbucketBranch getBranch() {
+ return new BitbucketServerBranch(branchName, null);
+ }
+
+ public void setBranchName(String branchName) {
+ this.branchName = branchName;
+ }
+
+ public void setRepository(BitbucketServerRepository repository) {
+ this.repository = repository;
+ }
+
+}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/repository/BitbucketServerProject.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/repository/BitbucketServerProject.java
index 104f2e158..248651c47 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/repository/BitbucketServerProject.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/repository/BitbucketServerProject.java
@@ -23,10 +23,16 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.server.client.repository;
+import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketTeam;
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
@JsonIgnoreProperties(ignoreUnknown = true)
public class BitbucketServerProject implements BitbucketTeam {
@@ -37,6 +43,10 @@ public class BitbucketServerProject implements BitbucketTeam {
@JsonProperty("name")
private String displayName;
+ @JsonProperty("links")
+ @JsonDeserialize(keyAs = String.class, contentAs = BitbucketHref[].class)
+ private Map links;
+
@Override
public String getName() {
return name;
@@ -55,4 +65,25 @@ public void setDisplayName(String displayName) {
this.displayName = displayName;
}
+ @Override
+ public Map getLinks() {
+ Map result = new LinkedHashMap<>(links.size());
+ for (Map.Entry entry: links.entrySet()) {
+ if (entry.getValue().length == 0) {
+ continue;
+ }
+ result.put(entry.getKey(), entry.getValue()[0]);
+ }
+ return result;
+ }
+
+ // Do not call this setLinks or Jackson will have issues
+ public void links(Map links) {
+ Map result = new LinkedHashMap<>();
+ for (Map.Entry entry: links.entrySet()) {
+ BitbucketHref[] value = entry.getValue() == null ? new BitbucketHref[0] : new BitbucketHref[]{entry.getValue()};
+ result.put(entry.getKey(), value);
+ }
+ this.links = result;
+ }
}
diff --git a/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/AbstractBranchJobFilter/config.jelly b/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/AbstractBranchJobFilter/config.jelly
deleted file mode 100644
index 3637140da..000000000
--- a/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/AbstractBranchJobFilter/config.jelly
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/CustomNameJobColumn/columnHeader.jelly b/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/CustomNameJobColumn/columnHeader.jelly
deleted file mode 100644
index c6e4a9b5e..000000000
--- a/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/CustomNameJobColumn/columnHeader.jelly
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
${it.message}
-
\ No newline at end of file
diff --git a/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/CustomNameJobColumn/config.jelly b/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/CustomNameJobColumn/config.jelly
deleted file mode 100644
index 0de73c50a..000000000
--- a/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/CustomNameJobColumn/config.jelly
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/Messages.properties b/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/Messages.properties
index d2d325f33..9e34b7aae 100644
--- a/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/Messages.properties
+++ b/src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/Messages.properties
@@ -1,5 +1,13 @@
+BitbucketLink.DisplayName=Bitbucket
+BitbucketSCMNavigator.UncategorizedSCMSourceCategory.DisplayName=Repositories
+BitbucketSCMSource.UncategorizedSCMHeadCategory.DisplayName=Branches
+BitbucketSCMSource.ChangeRequestSCMHeadCategory.DisplayName=Pull requests
BitbucketSCMNavigator.DisplayName=Bitbucket Team/Project
BitbucketSCMNavigator.Description=Scans a Bitbucket Cloud Team (or Bitbucket Server Project) for all repositories matching some defined markers.
+BitbucketRepoMetadataAction.IconDescription=Bitbucket Repository
+BitbucketRepoMetadataAction.IconDescription.Git=Bitbucket Git Repository
+BitbucketRepoMetadataAction.IconDescription.Hg=Bitbucket Mercurial Repository
+BitbucketTeamMetadataAction.IconDescription=Bitbucket Team/Project
ListViewColumn.Repository=Repository
ListViewColumn.Branch=Branch
-ListViewColumn.PullRequest=Pull Request
\ No newline at end of file
+ListViewColumn.PullRequest=Pull Request
diff --git a/src/main/webapp/images/16x16/bitbucket-branch.png b/src/main/webapp/images/16x16/bitbucket-branch.png
new file mode 100644
index 000000000..b2ce6e4ab
Binary files /dev/null and b/src/main/webapp/images/16x16/bitbucket-branch.png differ
diff --git a/src/main/webapp/images/16x16/bitbucket-logo.png b/src/main/webapp/images/16x16/bitbucket-logo.png
new file mode 100644
index 000000000..c038daa48
Binary files /dev/null and b/src/main/webapp/images/16x16/bitbucket-logo.png differ
diff --git a/src/main/webapp/images/16x16/bitbucket-repository-git.png b/src/main/webapp/images/16x16/bitbucket-repository-git.png
new file mode 100644
index 000000000..cd958f2ba
Binary files /dev/null and b/src/main/webapp/images/16x16/bitbucket-repository-git.png differ
diff --git a/src/main/webapp/images/16x16/bitbucket-repository-hg.png b/src/main/webapp/images/16x16/bitbucket-repository-hg.png
new file mode 100644
index 000000000..2bccb50cb
Binary files /dev/null and b/src/main/webapp/images/16x16/bitbucket-repository-hg.png differ
diff --git a/src/main/webapp/images/16x16/bitbucket-repository.png b/src/main/webapp/images/16x16/bitbucket-repository.png
new file mode 100644
index 000000000..455ade178
Binary files /dev/null and b/src/main/webapp/images/16x16/bitbucket-repository.png differ
diff --git a/src/main/webapp/images/16x16/bitbucket-scmnavigator.png b/src/main/webapp/images/16x16/bitbucket-scmnavigator.png
new file mode 100644
index 000000000..29ac51d54
Binary files /dev/null and b/src/main/webapp/images/16x16/bitbucket-scmnavigator.png differ
diff --git a/src/main/webapp/images/24x24/bitbucket-branch.png b/src/main/webapp/images/24x24/bitbucket-branch.png
new file mode 100644
index 000000000..7b116fca7
Binary files /dev/null and b/src/main/webapp/images/24x24/bitbucket-branch.png differ
diff --git a/src/main/webapp/images/24x24/bitbucket-logo.png b/src/main/webapp/images/24x24/bitbucket-logo.png
new file mode 100644
index 000000000..7b80f400c
Binary files /dev/null and b/src/main/webapp/images/24x24/bitbucket-logo.png differ
diff --git a/src/main/webapp/images/24x24/bitbucket-repository-git.png b/src/main/webapp/images/24x24/bitbucket-repository-git.png
new file mode 100644
index 000000000..e0fab5b5e
Binary files /dev/null and b/src/main/webapp/images/24x24/bitbucket-repository-git.png differ
diff --git a/src/main/webapp/images/24x24/bitbucket-repository-hg.png b/src/main/webapp/images/24x24/bitbucket-repository-hg.png
new file mode 100644
index 000000000..fe5381dca
Binary files /dev/null and b/src/main/webapp/images/24x24/bitbucket-repository-hg.png differ
diff --git a/src/main/webapp/images/24x24/bitbucket-repository.png b/src/main/webapp/images/24x24/bitbucket-repository.png
new file mode 100644
index 000000000..743a839e5
Binary files /dev/null and b/src/main/webapp/images/24x24/bitbucket-repository.png differ
diff --git a/src/main/webapp/images/24x24/bitbucket-scmnavigator.png b/src/main/webapp/images/24x24/bitbucket-scmnavigator.png
new file mode 100644
index 000000000..5342551b5
Binary files /dev/null and b/src/main/webapp/images/24x24/bitbucket-scmnavigator.png differ
diff --git a/src/main/webapp/images/32x32/bitbucket-branch.png b/src/main/webapp/images/32x32/bitbucket-branch.png
new file mode 100644
index 000000000..00dc6d325
Binary files /dev/null and b/src/main/webapp/images/32x32/bitbucket-branch.png differ
diff --git a/src/main/webapp/images/32x32/bitbucket-logo.png b/src/main/webapp/images/32x32/bitbucket-logo.png
new file mode 100644
index 000000000..8cf8d459f
Binary files /dev/null and b/src/main/webapp/images/32x32/bitbucket-logo.png differ
diff --git a/src/main/webapp/images/32x32/bitbucket-repository-git.png b/src/main/webapp/images/32x32/bitbucket-repository-git.png
new file mode 100644
index 000000000..4284cc882
Binary files /dev/null and b/src/main/webapp/images/32x32/bitbucket-repository-git.png differ
diff --git a/src/main/webapp/images/32x32/bitbucket-repository-hg.png b/src/main/webapp/images/32x32/bitbucket-repository-hg.png
new file mode 100644
index 000000000..24ae39804
Binary files /dev/null and b/src/main/webapp/images/32x32/bitbucket-repository-hg.png differ
diff --git a/src/main/webapp/images/32x32/bitbucket-repository.png b/src/main/webapp/images/32x32/bitbucket-repository.png
new file mode 100644
index 000000000..c0396917e
Binary files /dev/null and b/src/main/webapp/images/32x32/bitbucket-repository.png differ
diff --git a/src/main/webapp/images/32x32/bitbucket-scmnavigator.png b/src/main/webapp/images/32x32/bitbucket-scmnavigator.png
new file mode 100644
index 000000000..461b33fef
Binary files /dev/null and b/src/main/webapp/images/32x32/bitbucket-scmnavigator.png differ
diff --git a/src/main/webapp/images/48x48/bitbucket-branch.png b/src/main/webapp/images/48x48/bitbucket-branch.png
new file mode 100644
index 000000000..31628eb0d
Binary files /dev/null and b/src/main/webapp/images/48x48/bitbucket-branch.png differ
diff --git a/src/main/webapp/images/48x48/bitbucket-logo.png b/src/main/webapp/images/48x48/bitbucket-logo.png
new file mode 100644
index 000000000..790eb9005
Binary files /dev/null and b/src/main/webapp/images/48x48/bitbucket-logo.png differ
diff --git a/src/main/webapp/images/48x48/bitbucket-repository-git.png b/src/main/webapp/images/48x48/bitbucket-repository-git.png
new file mode 100644
index 000000000..defab78d6
Binary files /dev/null and b/src/main/webapp/images/48x48/bitbucket-repository-git.png differ
diff --git a/src/main/webapp/images/48x48/bitbucket-repository-hg.png b/src/main/webapp/images/48x48/bitbucket-repository-hg.png
new file mode 100644
index 000000000..4170107d8
Binary files /dev/null and b/src/main/webapp/images/48x48/bitbucket-repository-hg.png differ
diff --git a/src/main/webapp/images/48x48/bitbucket-repository.png b/src/main/webapp/images/48x48/bitbucket-repository.png
new file mode 100644
index 000000000..41296e61f
Binary files /dev/null and b/src/main/webapp/images/48x48/bitbucket-repository.png differ
diff --git a/src/main/webapp/images/48x48/bitbucket-scmnavigator.png b/src/main/webapp/images/48x48/bitbucket-scmnavigator.png
index 03fe00455..1b8a208ce 100644
Binary files a/src/main/webapp/images/48x48/bitbucket-scmnavigator.png and b/src/main/webapp/images/48x48/bitbucket-scmnavigator.png differ
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 311a19aae..d46b85c7a 100644
--- a/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketClientMockUtils.java
+++ b/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketClientMockUtils.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import com.cloudbees.jenkins.plugins.bitbucket.client.pullrequest.BitbucketPullRequestValueDestination;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -159,6 +160,15 @@ private static BitbucketPullRequestValue getPullRequest() {
pr.setSource(source);
+ BitbucketPullRequestValueDestination destination = new BitbucketPullRequestValueDestination();
+ branch = new BitbucketCloudBranch();
+ branch.setName("branch1");
+ destination.setBranch(branch);
+ repository = new BitbucketCloudRepository();
+ repository.setFullName("amuniz/test-repos");
+ destination.setRepository(repository);
+ pr.setDestination(destination);
+
pr.setId("23");
pr.setAuthor(new BitbucketPullRequestValue.Author("amuniz"));
pr.setLinks(new BitbucketPullRequestValue.Links("https://bitbucket.org/amuniz/test-repos/pull-requests/23"));
diff --git a/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BranchScanningIntegrationTest.java b/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BranchScanningIntegrationTest.java
index a1df09864..01c73ce17 100644
--- a/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BranchScanningIntegrationTest.java
+++ b/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BranchScanningIntegrationTest.java
@@ -113,6 +113,16 @@ public static class MultiBranchProjectImpl extends MultiBranchProject remoteConfigs = source.getGitRemoteConfigs(new SCMHeadWithOwnerAndRepo("amuniz", "test-repos", "branch1"));
+ List remoteConfigs = source.getGitRemoteConfigs(new BranchSCMHead("branch1"));
assertEquals(1, remoteConfigs.size());
assertEquals("+refs/heads/branch1", remoteConfigs.get(0).getRefspec());
}
@@ -84,7 +84,7 @@ public void remoteConfigsTest() {
public void retrieveTest() throws IOException, InterruptedException {
BitbucketSCMSource source = getBitbucketSCMSourceMock(RepositoryType.GIT);
- SCMHeadWithOwnerAndRepo head = new SCMHeadWithOwnerAndRepo(repoOwner, repoName, branchName);
+ BranchSCMHead head = new BranchSCMHead(branchName);
SCMRevision rev = source.retrieve(head, BitbucketClientMockUtils.getTaskListenerMock());
// Last revision on branch1 must be returned
@@ -96,7 +96,7 @@ public void retrieveTest() throws IOException, InterruptedException {
public void scanTest() throws IOException, InterruptedException {
BitbucketSCMSource source = getBitbucketSCMSourceMock(RepositoryType.GIT);
SCMHeadObserverImpl observer = new SCMHeadObserverImpl();
- source.retrieve(observer, BitbucketClientMockUtils.getTaskListenerMock());
+ source.fetch(observer, BitbucketClientMockUtils.getTaskListenerMock());
// Only branch1 must be observed
assertEquals(1, observer.getBranches().size());
@@ -107,7 +107,7 @@ public void scanTest() throws IOException, InterruptedException {
public void scanTestPullRequests() throws IOException, InterruptedException {
BitbucketSCMSource source = getBitbucketSCMSourceMock(RepositoryType.GIT, true);
SCMHeadObserverImpl observer = new SCMHeadObserverImpl();
- source.retrieve(observer, BitbucketClientMockUtils.getTaskListenerMock());
+ source.fetch(observer, BitbucketClientMockUtils.getTaskListenerMock());
// Only branch1 and my-feature-branch PR must be observed
assertEquals(2, observer.getBranches().size());
@@ -129,7 +129,7 @@ public void mercurialSCMTest() {
private SCM scmBuild(RepositoryType type) {
BitbucketSCMSource source = getBitbucketSCMSourceMock(type);
- return source.build(new SCMHeadWithOwnerAndRepo("amuniz", "test-repos", "branch1"));
+ return source.build(new BranchSCMHead("branch1"));
}
private BitbucketSCMSource getBitbucketSCMSourceMock(RepositoryType type, boolean includePullRequests) {
@@ -155,6 +155,15 @@ public boolean isHead(Probe probe, TaskListener listener) throws IOException {
return probe.exists("markerfile.txt");
}
+ @Override
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return getClass().isInstance(obj);
+ }
});
return mocked;
}
diff --git a/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/SCMNavigatorTest.java b/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/SCMNavigatorTest.java
index 24f744d94..80ba8b823 100644
--- a/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/SCMNavigatorTest.java
+++ b/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/SCMNavigatorTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -43,6 +44,7 @@
import jenkins.scm.api.SCMSourceObserver;
import jenkins.scm.api.SCMSourceOwner;
import jenkins.scm.api.SCMSourceObserver.ProjectObserver;
+import org.mockito.Mockito;
public class SCMNavigatorTest {
@@ -51,7 +53,8 @@ public void teamRepositoriesDiscovering() throws IOException, InterruptedExcepti
BitbucketSCMNavigator navigator = new BitbucketSCMNavigator("myteam", null, null);
navigator.setPattern("repo(.*)");
navigator.setBitbucketConnector(getConnectorMock(RepositoryType.GIT, true));
- SCMSourceObserverImpl observer = new SCMSourceObserverImpl(BitbucketClientMockUtils.getTaskListenerMock());
+ SCMSourceObserverImpl observer = new SCMSourceObserverImpl(BitbucketClientMockUtils.getTaskListenerMock(),
+ Mockito.mock(SCMSourceOwner.class));
navigator.visitSources(observer);
assertEquals("myteam", navigator.getRepoOwner());
@@ -80,23 +83,28 @@ private class SCMSourceObserverImpl extends SCMSourceObserver {
List observed = new ArrayList();
List projectObservers = new ArrayList();
TaskListener listener;
+ SCMSourceOwner owner;
- public SCMSourceObserverImpl(TaskListener listener) {
+ public SCMSourceObserverImpl(TaskListener listener, SCMSourceOwner owner) {
this.listener = listener;
+ this.owner = owner;
}
+ @NonNull
@Override
public SCMSourceOwner getContext() {
- return null;
+ return owner;
}
+ @NonNull
@Override
public TaskListener getListener() {
return listener;
}
+ @NonNull
@Override
- public ProjectObserver observe(String projectName) throws IllegalArgumentException {
+ public ProjectObserver observe(@NonNull String projectName) throws IllegalArgumentException {
observed.add(projectName);
ProjectObserverImpl obs = new ProjectObserverImpl();
projectObservers.add(obs);
@@ -104,7 +112,7 @@ public ProjectObserver observe(String projectName) throws IllegalArgumentExcepti
}
@Override
- public void addAttribute(String key, Object value) throws IllegalArgumentException, ClassCastException {
+ public void addAttribute(@NonNull String key, Object value) throws IllegalArgumentException, ClassCastException {
}
public List getObserved() {
@@ -120,12 +128,12 @@ public class ProjectObserverImpl extends ProjectObserver {
private List sources = new ArrayList();
@Override
- public void addSource(SCMSource source) {
+ public void addSource(@NonNull SCMSource source) {
sources.add(source);
}
@Override
- public void addAttribute(String key, Object value) throws IllegalArgumentException, ClassCastException {
+ public void addAttribute(@NonNull String key, Object value) throws IllegalArgumentException, ClassCastException {
}
@Override