Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JENKINS-43507] Migrate Bitbucket Plugin to Traits #53

Merged
merged 44 commits into from
Jul 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
43b5668
[JENKINS-43433] Adapt to ChangeRequestSCMHead2
stephenc Apr 11, 2017
301f8d0
[JENKINS-43433] Pick up scm api snapshot
stephenc Apr 11, 2017
74b8531
Merge branch 'jenkins-44413' into jenkins-43507
stephenc May 22, 2017
b8f9be3
[JENKINS-43507] Add endpoints to global configuration
stephenc May 29, 2017
3530fca
[JENKINS-43507] Add endpoints tests
stephenc May 29, 2017
dfd47ff
[JENKINS-43507] Add endpoints to global configuration
stephenc May 30, 2017
ee67e57
[JENKINS-43507] Tidy up implementation debt in BitbucketApi implement…
stephenc Jun 2, 2017
fd9e115
[JENKINS-43507] Align with endpoints expected serverUrls
stephenc Jun 2, 2017
824dfd2
[JENKINS-43507] Add backfill until JENKINS-42443 is available in base…
stephenc Jun 2, 2017
0e1e96a
[JENKINS-43507] Widen types of input parameter
stephenc Jun 7, 2017
88c4be3
[JENKINS-43507] Add source context and request
stephenc Jun 7, 2017
933a75a
[JENKINS-43507] Add navigator context and request
stephenc Jun 7, 2017
8f636d6
[JENKINS-40475] Use a more resilient API call to determine the commit…
stephenc Jun 8, 2017
95aac94
[JENKINS-43507] checkPathExists can take a branch OR a hash
stephenc Jun 8, 2017
50166af
[JENKINS-43507] Add API to derive web links to endpoint configuration
stephenc Jun 9, 2017
a4c6bf3
[JENKINS-43507] Switch to trait based configuration
stephenc Jun 12, 2017
71b6cae
[JENKINS-43507] Curses upon you IntelliJ with your "Reformat code" ch…
stephenc Jun 12, 2017
0d083de
[JENKINS-43507] Pick up latest timestamp snapshots
stephenc Jun 12, 2017
e0e18b2
[JENKINS-43507] Some code simplification
stephenc Jun 13, 2017
b0c72b8
[JENKINS-43507] Address @amuniz code review comments
stephenc Jun 15, 2017
933aae0
[JENKINS-43507] Pick up changes for JENKINS-44891
stephenc Jun 15, 2017
255396d
[JENKINS-43507] Pick up latest -SNAPSHOTs
stephenc Jun 16, 2017
4c80d63
[JENKINS-43507] Minor formatting tweaks
stephenc Jun 19, 2017
e0875fa
[JENKINS-43507] Documentation and formatting
stephenc Jun 19, 2017
9b71dd1
[JENKINS-43507] Now is good
stephenc Jun 19, 2017
b5f8765
[JENKINS-43507] i18n
stephenc Jun 19, 2017
0874e73
[JENKINS-43507] re-use Throwable.getMessage()
stephenc Jun 19, 2017
23b61e5
[JENKINS-43507] Remove redundant check (only ever called when value i…
stephenc Jun 19, 2017
c0cfdd7
[JENKINS-43507] De-duplicate and simplify
stephenc Jun 19, 2017
e1b6547
[JENKINS-43507] Remove potential user error
stephenc Jun 19, 2017
103082f
[JENKINS-43507] Fix formatting
stephenc Jun 19, 2017
107f747
[JENKINS-43507] Pick up alpha-1
stephenc Jun 20, 2017
ec0fc5b
Merge branch 'master' into jenkins-43507
stephenc Jun 20, 2017
6821f23
[maven-release-plugin] prepare release cloudbees-bitbucket-branch-sou…
stephenc Jun 20, 2017
8b3b9eb
[maven-release-plugin] prepare for next development iteration
stephenc Jun 20, 2017
17541eb
[JENKINS-43507] US people want to spell things incorrectly
stephenc Jun 20, 2017
3fa5f43
[JENKINS-43507] Fix trait drop-down list population
stephenc Jun 24, 2017
b9a3016
[JENKINS-43507] Prepare for -alpha-4
stephenc Jun 26, 2017
3c9bb0f
[maven-release-plugin] prepare release cloudbees-bitbucket-branch-sou…
stephenc Jun 26, 2017
6f9a49f
[maven-release-plugin] prepare for next development iteration
stephenc Jun 26, 2017
7ecc02d
[JENKINS-43507] Add missing continue
stephenc Jul 5, 2017
b307aaf
Merge branch 'master' into jenkins-43507
stephenc Jul 5, 2017
425d7bd
Merge branch 'master' into jenkins-43507
stephenc Jul 5, 2017
ee66530
[JENKINS-43507] Remove (now) useless dependency
stephenc Jul 5, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 30 additions & 15 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@
</parent>

<artifactId>cloudbees-bitbucket-branch-source</artifactId>
<version>2.1.3-SNAPSHOT</version>
<version>2.2.0-SNAPSHOT</version>
<packaging>hpi</packaging>

<name>Bitbucket Branch Source Plugin</name>
<url>https://wiki.jenkins-ci.org/display/JENKINS/Bitbucket+Branch+Source+Plugin</url>
<description>Discover and build Bitbucket Cloud and Bitbucket Server pull requests and branches and send status notifications with the build result.</description>
<description>Discover and build Bitbucket Cloud and Bitbucket Server pull requests and branches and send status
notifications with the build result.
</description>
<licenses>
<license>
<name>MIT License</name>
Expand All @@ -48,7 +50,8 @@

<properties>
<jenkins.version>1.642.3</jenkins.version>
<scm-api.version>2.0.4</scm-api.version>
<scm-api.version>2.2.0-alpha-1</scm-api.version>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be 2.2.0-SNAPSHOT to match what is in master?

<git.version>3.4.0-alpha-4</git.version>
</properties>

<scm>
Expand All @@ -58,16 +61,29 @@
<tag>HEAD</tag>
</scm>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>${scm-api.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>structs</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>git</artifactId>
<version>2.6.5</version>
<version>${git.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
Expand All @@ -78,7 +94,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>mercurial</artifactId>
<version>1.58</version>
<version>2.0-alpha-4</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
Expand All @@ -93,7 +109,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>branch-api</artifactId>
<version>2.0.6</version>
<version>2.0.11-alpha-1</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -118,7 +134,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>git</artifactId>
<version>2.6.5</version>
<version>${git.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
Expand All @@ -128,14 +144,6 @@
<version>2.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-aggregator</artifactId>
<version>1.15</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>

</dependencies>

<repositories>
Expand All @@ -156,6 +164,13 @@
<plugin>
<groupId>org.jenkins-ci.tools</groupId>
<artifactId>maven-hpi-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate-taglib-interface</goal>
</goals>
</execution>
</executions>
<configuration>
<compatibleSinceVersion>2.0.0</compatibleSinceVersion>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,158 +25,124 @@

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBuildStatus;
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.FilePath;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Job;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.listeners.RunListener;
import hudson.model.listeners.SCMListener;
import hudson.plugins.git.Revision;
import hudson.plugins.git.util.BuildData;
import hudson.plugins.mercurial.MercurialTagAction;
import hudson.plugins.mercurial.MercurialSCMSource;
import hudson.scm.SCM;
import hudson.scm.SCMRevisionState;
import java.io.File;
import java.io.IOException;
import jenkins.scm.api.SCMHead;
import javax.annotation.CheckForNull;
import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.scm.api.SCMHeadObserver;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMRevisionAction;
import jenkins.scm.api.SCMSource;
import jenkins.scm.api.SCMSourceOwner;
import org.jenkinsci.plugins.displayurlapi.DisplayURLProvider;

/**
* This class encapsulates all Bitbucket notifications logic.
* {@link JobCompletedListener} sends a notification to Bitbucket after a build finishes.
* Only builds derived from a job that was created as part of a multi branch project will be processed by this listener.
*
* The way the notification is sent is defined by the implementation of {@link BitbucketNotifier} returned by {@link #getNotifier(BitbucketApi)}.
*
*/
public class BitbucketBuildStatusNotifications {

private static void createBuildCommitStatus(@NonNull Run<?,?> build, @NonNull TaskListener listener, @NonNull BitbucketApi bitbucket)
private static void createStatus(@NonNull Run<?, ?> build, @NonNull TaskListener listener,
@NonNull BitbucketApi bitbucket, @NonNull String hash)
throws IOException, InterruptedException {
String revision = extractRevision(build);
if (revision != null) {
Result result = build.getResult();
String url;
try {
url = DisplayURLProvider.get().getRunURL(build);
} catch (IllegalStateException e) {
listener.getLogger().println("Can not determine Jenkins root URL. Commit status notifications are disabled until a root URL is configured in Jenkins global configuration.");
listener.getLogger().println(
"Can not determine Jenkins root URL. Commit status notifications are disabled until a root URL is"
+ " configured in Jenkins global configuration.");
return;
}
BitbucketBuildStatus status = null;
String key = build.getParent().getFullName(); // use the job full name as the key for the status
String name = build.getDisplayName(); // use the build number as the display name of the status
BitbucketBuildStatus status;
Result result = build.getResult();
if (Result.SUCCESS.equals(result)) {
status = new BitbucketBuildStatus(revision, "This commit looks good", "SUCCESSFUL", url, build.getParent().getName(), build.getDisplayName());
status = new BitbucketBuildStatus(hash, "This commit looks good", "SUCCESSFUL", url, key, name);
} else if (Result.UNSTABLE.equals(result)) {
status = new BitbucketBuildStatus(revision, "This commit has test failures", "FAILED", url, build.getParent().getName(), build.getDisplayName());
status = new BitbucketBuildStatus(hash, "This commit has test failures", "FAILED", url, key, name);
} else if (Result.FAILURE.equals(result)) {
status = new BitbucketBuildStatus(revision, "There was a failure building this commit", "FAILED", url, build.getParent().getName(), build.getDisplayName());
status = new BitbucketBuildStatus(hash, "There was a failure building this commit", "FAILED", url, key,
name);
} else if (result != null) { // ABORTED etc.
status = new BitbucketBuildStatus(revision, "Something is wrong with the build of this commit", "FAILED", url, build.getParent().getName(), build.getDisplayName());
status = new BitbucketBuildStatus(hash, "Something is wrong with the build of this commit", "FAILED", url,
key, name);
} else {
status = new BitbucketBuildStatus(revision, "The tests have started...", "INPROGRESS", url, build.getParent().getName(), build.getDisplayName());
}
if (status != null) {
getNotifier(bitbucket).buildStatus(status);
status = new BitbucketBuildStatus(hash, "The tests have started...", "INPROGRESS", url, key, name);
}
new BitbucketChangesetCommentNotifier(bitbucket).buildStatus(status);
if (result != null) {
listener.getLogger().println("[Bitbucket] Build result notified");
}
}
}

@CheckForNull
private static String extractRevision(Run<?, ?> build) {
String revision = null;
BuildData gitBuildData = build.getAction(BuildData.class);
if (gitBuildData != null) {
Revision lastBuiltRevision = gitBuildData.getLastBuiltRevision();
if (lastBuiltRevision != null) {
revision = lastBuiltRevision.getSha1String();
}
} else {
MercurialTagAction action = build.getAction(MercurialTagAction.class);
if (action != null) {
revision = action.getId();
}
}
return revision;
}

private static void createPullRequestCommitStatus(Run<?,?> build, TaskListener listener, BitbucketApi bitbucket)
private static void sendNotifications(Run<?, ?> build, TaskListener listener)
throws IOException, InterruptedException {
createBuildCommitStatus(build, listener, bitbucket);
final SCMSource s = SCMSource.SourceByItem.findSource(build.getParent());
if (!(s instanceof BitbucketSCMSource)) {
return;
}

private static BitbucketNotifier getNotifier(BitbucketApi bitbucket) {
return new BitbucketChangesetCommentNotifier(bitbucket);
BitbucketSCMSource source = (BitbucketSCMSource) s;
if (new BitbucketSCMSourceContext(null, SCMHeadObserver.none())
.withTraits(source.getTraits())
.notificationsDisabled()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I'll find a notification trait later in the review...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. I do not see the value in there being a notification disabling trait in the core plugin. But if somebody wants to write such a trait in an extension plugin they need this to disable the notifications... given that there are a number of people seeking just that I thought it worth the minor YAGNI to add this

return;
}

@CheckForNull
private static BitbucketSCMSource lookUpSCMSource(Run<?, ?> build) {
ItemGroup<?> multiBranchProject = build.getParent().getParent();
if (multiBranchProject instanceof SCMSourceOwner) {
SCMSourceOwner scmSourceOwner = (SCMSourceOwner) multiBranchProject;
BitbucketSCMSource source = lookUpBitbucketSCMSource(scmSourceOwner);
if (source != null) {
return source;
SCMRevision r = SCMRevisionAction.getRevision(build); // TODO JENKINS-44648 getRevision(s, build)
String hash = getHash(r);
if (hash == null) {
return;
}
if (r instanceof PullRequestSCMRevision) {
listener.getLogger().println("[Bitbucket] Notifying pull request build result");
createStatus(build, listener, source.buildBitbucketClient((PullRequestSCMHead) r.getHead()), hash);

} else {
listener.getLogger().println("[Bitbucket] Notifying commit build result");
createStatus(build, listener, source.buildBitbucketClient(), hash);
}
return null;
}



/**
* It is possible having more than one SCMSource in our MultiBranch project.
* TODO: Does it make sense having more than one of the same type?
*
* @param scmSourceOwner An {@link Item} that owns {@link SCMSource} instances.
* @return A source or null
*/
@CheckForNull
private static BitbucketSCMSource lookUpBitbucketSCMSource(final SCMSourceOwner scmSourceOwner) {
for (SCMSource scmSource : scmSourceOwner.getSCMSources()) {
if (scmSource instanceof BitbucketSCMSource) {
return (BitbucketSCMSource) scmSource;
private static String getHash(@CheckForNull SCMRevision revision) {
if (revision instanceof PullRequestSCMRevision) {
// unwrap
revision = ((PullRequestSCMRevision) revision).getPull();
}
if (revision instanceof MercurialSCMSource.MercurialRevision) {
return ((MercurialSCMSource.MercurialRevision) revision).getHash();
} else if (revision instanceof AbstractGitSCMSource.SCMRevisionImpl) {
return ((AbstractGitSCMSource.SCMRevisionImpl) revision).getHash();
}
return null;
}

private static void sendNotifications(Run<?, ?> build, TaskListener listener)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Diff on this class in horrible, difficult to follow changes, but I think it's ok.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of the issue here is that it is actually also fixing bugs that I found during the refactoring... and then after I found them I noticed that others had raise JIRA issues for these bugs... and in some cases also PRs... e.g. #52 was a parallel fix to the fix I had developed here

throws IOException, InterruptedException {
BitbucketSCMSource source = lookUpSCMSource(build);
if (source != null && extractRevision(build) != null) {
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] Notifying commit build result");
createBuildCommitStatus(build, listener, source.buildBitbucketClient());
}
}
}

/**
* Sends notifications to Bitbucket on Checkout (for the "In Progress" Status).
*/
@Extension
public static class JobCheckOutListener extends SCMListener {

@Override
public void onCheckout(Run<?, ?> build, SCM scm, FilePath workspace, TaskListener listener, File changelogFile, SCMRevisionState pollingBaseline) throws Exception {
public void onCheckout(Run<?, ?> build, SCM scm, FilePath workspace, TaskListener listener, File changelogFile,
SCMRevisionState pollingBaseline) throws Exception {
try {
sendNotifications(build, listener);
} catch (IOException | InterruptedException e) {
e.printStackTrace(listener.error("Could not send notifications"));
}
}
}

Expand Down
Loading