aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore20
-rw-r--r--pom.xml57
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java36
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java160
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java65
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java76
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java63
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java119
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketApiClient.java117
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestComment.java118
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponse.java58
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValue.java130
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepository.java35
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepositoryBranch.java16
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepositoryRepository.java43
-rw-r--r--src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly17
-rw-r--r--src/main/resources/index.jelly6
17 files changed, 1136 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..21e5893
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,20 @@
+target/
+work/
+
+#
+# Eclipse metadata.
+#
+.project
+.classpath
+.settings/
+
+#
+# Eclipse and Maven build results
+#
+bin/
+
+# IntelliJ metadata.
+*.iml
+*.ipr
+*.iws
+.idea/
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..5131ef8
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,57 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.jenkins-ci.plugins</groupId>
+ <artifactId>plugin</artifactId>
+ <version>1.509.4</version><!-- which version of Jenkins is this plugin built against? -->
+ </parent>
+
+ <groupId>bitbucket-pullrequest-builder</groupId>
+ <artifactId>bitbucket-pullrequest-builder</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>hpi</packaging>
+
+ <!-- get every artifact through repo.jenkins-ci.org, which proxies all the artifacts that we need -->
+ <repositories>
+ <repository>
+ <id>repo.jenkins-ci.org</id>
+ <url>http://repo.jenkins-ci.org/public/</url>
+ </repository>
+ </repositories>
+
+ <dependencies>
+ <dependency>
+ <groupId>commons-httpclient</groupId>
+ <artifactId>commons-httpclient</artifactId>
+ <version>3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-httpclient</groupId>
+ <artifactId>commons-httpclient</artifactId>
+ <version>3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-http</artifactId>
+ <version>2.4</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-httpclient</groupId>
+ <artifactId>commons-httpclient</artifactId>
+ <version>3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-jaxrs</artifactId>
+ <version>1.9.13</version>
+ </dependency>
+ </dependencies>
+
+ <pluginRepositories>
+ <pluginRepository>
+ <id>repo.jenkins-ci.org</id>
+ <url>http://repo.jenkins-ci.org/public/</url>
+ </pluginRepository>
+ </pluginRepositories>
+
+</project>
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java
new file mode 100644
index 0000000..95931ad
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java
@@ -0,0 +1,36 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
+
+import hudson.Extension;
+import hudson.model.AbstractBuild;
+import hudson.model.TaskListener;
+import hudson.model.listeners.RunListener;
+
+import javax.annotation.Nonnull;
+import java.util.logging.Logger;
+
+/**
+ * Created by nishio
+ */
+@Extension
+public class BitbucketBuildListener extends RunListener<AbstractBuild> {
+ private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName());
+
+ @Override
+ public void onStarted(AbstractBuild abstractBuild, TaskListener listener) {
+ logger.info("BuildListener onStarted called.");
+ BitbucketBuildTrigger trigger = BitbucketBuildTrigger.getTrigger(abstractBuild.getProject());
+ if (trigger == null) {
+ return;
+ }
+ trigger.getBuilder().getBuilds().onStarted(abstractBuild);
+ }
+
+ @Override
+ public void onCompleted(AbstractBuild abstractBuild, @Nonnull TaskListener listener) {
+ BitbucketBuildTrigger trigger = BitbucketBuildTrigger.getTrigger(abstractBuild.getProject());
+ if (trigger == null) {
+ return;
+ }
+ trigger.getBuilder().getBuilds().onCompleted(abstractBuild);
+ }
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
new file mode 100644
index 0000000..47d94dc
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
@@ -0,0 +1,160 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
+
+import antlr.ANTLRException;
+import hudson.Extension;
+import hudson.model.*;
+import hudson.model.queue.QueueTaskFuture;
+import hudson.triggers.Trigger;
+import hudson.triggers.TriggerDescriptor;
+import net.sf.json.JSONObject;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.StaplerRequest;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by nishio
+ */
+public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
+ private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName());
+ private final String projectPath;
+ private final String cron;
+ private final String username;
+ private final String password;
+ private final String repositoryOwner;
+ private final String repositoryName;
+ transient private BitbucketPullRequestsBuilder bitbucketPullRequestsBuilder;
+
+ @Extension
+ public static final BitbucketBuildTriggerDescriptor descriptor = new BitbucketBuildTriggerDescriptor();
+
+ @DataBoundConstructor
+ public BitbucketBuildTrigger(
+ String projectPath,
+ String cron,
+ String username,
+ String password,
+ String repositoryOwner,
+ String repositoryName) throws ANTLRException {
+ super(cron);
+ this.projectPath = projectPath;
+ this.cron = cron;
+ this.username = username;
+ this.password = password;
+ this.repositoryOwner = repositoryOwner;
+ this.repositoryName = repositoryName;
+ }
+
+ public String getProjectPath() {
+ return this.projectPath;
+ }
+
+ public String getCron() {
+ return this.cron;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getRepositoryOwner() {
+ return repositoryOwner;
+ }
+
+ public String getRepositoryName() {
+ return repositoryName;
+ }
+
+ @Override
+ public void start(AbstractProject<?, ?> project, boolean newInstance) {
+ try {
+ this.bitbucketPullRequestsBuilder = BitbucketPullRequestsBuilder.getBuilder();
+ this.bitbucketPullRequestsBuilder.setProject(project);
+ this.bitbucketPullRequestsBuilder.setTrigger(this);
+ this.bitbucketPullRequestsBuilder.setupBuilder();
+ } catch(IllegalStateException e) {
+ logger.log(Level.SEVERE, "Can't start trigger", e);
+ return;
+ }
+ super.start(project, newInstance);
+ }
+
+ public static BitbucketBuildTrigger getTrigger(AbstractProject project) {
+ Trigger trigger = project.getTrigger(BitbucketBuildTrigger.class);
+ return (BitbucketBuildTrigger)trigger;
+ }
+
+ public BitbucketPullRequestsBuilder getBuilder() {
+ return this.bitbucketPullRequestsBuilder;
+ }
+
+ public QueueTaskFuture<?> startJob(BitbucketCause cause) {
+ Map<String, ParameterValue> values = new HashMap<String, ParameterValue>();
+ values.put("sourceBranch", new StringParameterValue("sourceBranch", cause.getSourceBranch()));
+ values.put("targetBranch", new StringParameterValue("targetBranch", cause.getTargetBranch()));
+ values.put("repositoryOwner", new StringParameterValue("repositoryOwner", cause.getRepositoryOwner()));
+ values.put("repositonyName", new StringParameterValue("repositoryName", cause.getRepositoryName()));
+ values.put("pullRequestId", new StringParameterValue("pullRequestId", cause.getPullRequestId()));
+ values.put("destinationRepositoryOwner", new StringParameterValue("destinationRepositoryOwner", cause.getDestinationRepositoryOwner()));
+ values.put("destinationRepositoryName", new StringParameterValue("destinationRepositoryName", cause.getDestinationRepositoryName()));
+ values.put("pullRequestTitle", new StringParameterValue("pullRequestTitle", cause.getPullRequestTitle()));
+ return this.job.scheduleBuild2(0, cause, new ParametersAction(new ArrayList(values.values())));
+ }
+
+ private Map<String, ParameterValue> getDefaultParameters() {
+ Map<String, ParameterValue> values = new HashMap<String, ParameterValue>();
+ ParametersDefinitionProperty definitionProperty = this.job.getProperty(ParametersDefinitionProperty.class);
+
+ if (definitionProperty != null) {
+ for (ParameterDefinition definition : definitionProperty.getParameterDefinitions()) {
+ values.put(definition.getName(), definition.getDefaultParameterValue());
+ }
+ }
+ return values;
+ }
+
+ @Override
+ public void run() {
+ if(this.getBuilder().getProject().isDisabled()) {
+ logger.info("Build Skip.");
+ } else {
+ this.bitbucketPullRequestsBuilder.run();
+ }
+ this.getDescriptor().save();
+ }
+
+ @Override
+ public void stop() {
+ super.stop();
+ }
+
+ public static final class BitbucketBuildTriggerDescriptor extends TriggerDescriptor {
+ public BitbucketBuildTriggerDescriptor() {
+ load();
+ }
+
+ @Override
+ public boolean isApplicable(Item item) {
+ return true;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Bitbucket Pull Requests Builder";
+ }
+
+ @Override
+ public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
+ save();
+ return super.configure(req, json);
+ }
+ }
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java
new file mode 100644
index 0000000..a940ca9
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java
@@ -0,0 +1,65 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
+
+import hudson.model.AbstractBuild;
+import hudson.model.Cause;
+import hudson.model.Result;
+import jenkins.model.Jenkins;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by nishio
+ */
+public class BitbucketBuilds {
+ private static final Logger logger = Logger.getLogger(BitbucketBuilds.class.getName());
+ private BitbucketBuildTrigger trigger;
+ private BitbucketRepository repository;
+
+ public BitbucketBuilds(BitbucketBuildTrigger trigger, BitbucketRepository repository) {
+ this.trigger = trigger;
+ this.repository = repository;
+ }
+
+ public BitbucketCause getCause(AbstractBuild build) {
+ Cause cause = build.getCause(BitbucketCause.class);
+ if (cause == null || !(cause instanceof BitbucketCause)) {
+ return null;
+ }
+ return (BitbucketCause) cause;
+ }
+
+ public void onStarted(AbstractBuild build) {
+ BitbucketCause cause = this.getCause(build);
+ if (cause == null) {
+ return;
+ }
+ try {
+ build.setDescription(cause.getShortDescription());
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, "Can't update build description", e);
+ }
+ }
+
+ public void onCompleted(AbstractBuild build) {
+ BitbucketCause cause = this.getCause(build);
+ if (cause == null) {
+ return;
+ }
+ Result result = build.getResult();
+ String rootUrl = Jenkins.getInstance().getRootUrl();
+ String buildUrl = "";
+ if (rootUrl == null) {
+ buildUrl = " PLEASE SET JENKINS ROOT URL FROM GLOBAL CONFIGURATION " + build.getUrl();
+ }
+ else {
+ buildUrl = rootUrl + build.getUrl();
+ }
+ if(result == Result.SUCCESS) {
+ repository.postFinishedComment(cause.getPullRequestId(), true, buildUrl);
+ } else if (result == Result.FAILURE || result == Result.UNSTABLE) {
+ repository.postFinishedComment(cause.getPullRequestId(), false, buildUrl);
+ }
+ }
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java
new file mode 100644
index 0000000..1cb5da4
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java
@@ -0,0 +1,76 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
+
+import hudson.model.Cause;
+
+/**
+ * Created by nishio
+ */
+public class BitbucketCause extends Cause {
+ private final String sourceBranch;
+ private final String targetBranch;
+ private final String repositoryOwner;
+ private final String repositoryName;
+ private final String pullRequestId;
+ private final String destinationRepositoryOwner;
+ private final String destinationRepositoryName;
+ private final String pullRequestTitle;
+ public static final String BITBUCKET_URL = "https://bitbucket.org/";
+
+ public BitbucketCause(String sourceBranch,
+ String targetBranch,
+ String repositoryOwner,
+ String repositoryName,
+ String pullRequestId,
+ String destinationRepositoryOwner,
+ String destinationRepositoryName,
+ String pullRequestTitle) {
+ this.sourceBranch = sourceBranch;
+ this.targetBranch = targetBranch;
+ this.repositoryOwner = repositoryOwner;
+ this.repositoryName = repositoryName;
+ this.pullRequestId = pullRequestId;
+ this.destinationRepositoryOwner = destinationRepositoryOwner;
+ this.destinationRepositoryName = destinationRepositoryName;
+ this.pullRequestTitle = pullRequestTitle;
+ }
+
+ public String getSourceBranch() {
+ return sourceBranch;
+ }
+ public String getTargetBranch() {
+ return targetBranch;
+ }
+
+ public String getRepositoryOwner() {
+ return repositoryOwner;
+ }
+
+ public String getRepositoryName() {
+ return repositoryName;
+ }
+
+ public String getPullRequestId() {
+ return pullRequestId;
+ }
+
+
+ public String getDestinationRepositoryOwner() {
+ return destinationRepositoryOwner;
+ }
+
+ public String getDestinationRepositoryName() {
+ return destinationRepositoryName;
+ }
+
+ public String getPullRequestTitle() {
+ return pullRequestTitle;
+ }
+
+ @Override
+ public String getShortDescription() {
+ String description = "<a href=" + BITBUCKET_URL + this.getDestinationRepositoryOwner() + "/";
+ description += this.getDestinationRepositoryName() + "/pull-request/" + this.getPullRequestId();
+ description += ">#" + this.getPullRequestId() + " " + this.getPullRequestTitle() + "</a>";
+ return description;
+ }
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java
new file mode 100644
index 0000000..2f0a018
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java
@@ -0,0 +1,63 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
+
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BitbucketPullRequestResponseValue;
+import hudson.model.AbstractProject;
+
+import java.util.Collection;
+import java.util.logging.Logger;
+
+/**
+ * Created by nishio
+ */
+public class BitbucketPullRequestsBuilder {
+ private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName());
+ private AbstractProject<?, ?> project;
+ private BitbucketBuildTrigger trigger;
+ private BitbucketRepository repository;
+ private BitbucketBuilds builds;
+
+ public static BitbucketPullRequestsBuilder getBuilder() {
+ return new BitbucketPullRequestsBuilder();
+ }
+
+ public void stop() {
+ // TODO?
+ }
+
+ public void run() {
+ logger.info("Build Start.");
+ this.repository.init();
+ Collection<BitbucketPullRequestResponseValue> targetPullRequests = this.repository.getTargetPullRequests();
+ this.repository.postBuildStartCommentTo(targetPullRequests);
+ this.repository.addFutureBuildTasks(targetPullRequests);
+ }
+
+ public BitbucketPullRequestsBuilder setupBuilder() {
+ if (this.project == null || this.trigger == null) {
+ throw new IllegalStateException();
+ }
+ this.repository = new BitbucketRepository(this.trigger.getProjectPath(), this);
+ this.builds = new BitbucketBuilds(this.trigger, this.repository);
+ return this;
+ }
+
+ public void setProject(AbstractProject<?, ?> project) {
+ this.project = project;
+ }
+
+ public void setTrigger(BitbucketBuildTrigger trigger) {
+ this.trigger = trigger;
+ }
+
+ public AbstractProject<?, ?> getProject() {
+ return this.project;
+ }
+
+ public BitbucketBuildTrigger getTrigger() {
+ return this.trigger;
+ }
+
+ public BitbucketBuilds getBuilds() {
+ return this.builds;
+ }
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
new file mode 100644
index 0000000..ec13f38
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
@@ -0,0 +1,119 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
+
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BitbucketApiClient;
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BitbucketPullRequestComment;
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BitbucketPullRequestResponseValue;
+import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BitbucketPullRequestResponseValueRepository;
+import jenkins.model.Jenkins;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * Created by nishio
+ */
+public class BitbucketRepository {
+ private static final Logger logger = Logger.getLogger(BitbucketRepository.class.getName());
+ public static final String BUILD_START_MARKER = "[BuildStart]";
+ public static final String BUILD_FINISH_MARKER = "[BuildFinished]";
+ public static final String BUILD_REQUEST_MARKER = "test this please";
+ private String projectPath;
+ private BitbucketPullRequestsBuilder builder;
+ private BitbucketBuildTrigger trigger;
+ private BitbucketApiClient client;
+
+ public BitbucketRepository(String projectPath, BitbucketPullRequestsBuilder builder) {
+ this.projectPath = projectPath;
+ this.builder = builder;
+ }
+
+ public void init() {
+ trigger = this.builder.getTrigger();
+ client = new BitbucketApiClient(
+ trigger.getUsername(),
+ trigger.getPassword(),
+ trigger.getRepositoryOwner(),
+ trigger.getRepositoryName());
+ }
+
+ public Collection<BitbucketPullRequestResponseValue> getTargetPullRequests() {
+ logger.info("Fetch PullRequests.");
+ List<BitbucketPullRequestResponseValue> pullRequests = client.getPullRequests();
+ List<BitbucketPullRequestResponseValue> targetPullRequests = new ArrayList<BitbucketPullRequestResponseValue>();
+ for(BitbucketPullRequestResponseValue pullRequest : pullRequests) {
+ if (isBuildTarget(pullRequest)) {
+ targetPullRequests.add(pullRequest);
+ }
+ }
+ return targetPullRequests;
+ }
+
+ public void postBuildStartCommentTo(Collection<BitbucketPullRequestResponseValue> pullRequests) {
+ for(BitbucketPullRequestResponseValue pullRequest : pullRequests) {
+ String comment = BUILD_START_MARKER;
+ comment += " Build Triggered. Waiting to hear about " + pullRequest.getSource().getRepository().getFullName();
+ this.client.postPullRequestComment(pullRequest.getId(), comment);
+ }
+ }
+
+ public void addFutureBuildTasks(Collection<BitbucketPullRequestResponseValue> pullRequests) {
+ for(BitbucketPullRequestResponseValue pullRequest : pullRequests) {
+ BitbucketCause cause = new BitbucketCause(
+ pullRequest.getSource().getBranch().getName(),
+ pullRequest.getDestination().getBranch().getName(),
+ pullRequest.getSource().getRepository().getOwnerName(),
+ pullRequest.getSource().getRepository().getRepositoryName(),
+ pullRequest.getId(),
+ pullRequest.getDestination().getRepository().getOwnerName(),
+ pullRequest.getDestination().getRepository().getRepositoryName(),
+ pullRequest.getTitle());
+ this.builder.getTrigger().startJob(cause);
+ }
+ }
+
+ public void postFinishedComment(String pullRequestId, boolean success, String buildUrl) {
+ String comment = BUILD_FINISH_MARKER;
+ if (success) {
+ comment += " Test PASSed. Refer to this link for build results.";
+ } else {
+ comment += " Test FAILed. Refer to this link for build results.";
+ }
+ comment += buildUrl;
+ this.client.postPullRequestComment(pullRequestId, comment);
+ }
+
+ private boolean isBuildTarget(BitbucketPullRequestResponseValue pullRequest) {
+ boolean shouldBuild = true;
+ if (pullRequest.getState() != null && pullRequest.getState().equals("OPEN")) {
+ BitbucketPullRequestResponseValueRepository destination = pullRequest.getDestination();
+ String owner = destination.getRepository().getOwnerName();
+ String repositoryName = destination.getRepository().getRepositoryName();
+ String id = pullRequest.getId();
+ List<BitbucketPullRequestComment> comments = client.getPullRequestComments(owner, repositoryName, id);
+ if (comments != null) {
+ Collections.sort(comments);
+ Collections.reverse(comments);
+ for(BitbucketPullRequestComment comment : comments) {
+ String content = comment.getContent();
+ if (content == null || content.isEmpty()) {
+ continue;
+ }
+ content = content.toLowerCase();
+ if (content.contains(BUILD_START_MARKER.toLowerCase())) {
+ shouldBuild = false;
+ break;
+ }
+ if (content.contains(BUILD_REQUEST_MARKER.toLowerCase())) {
+ shouldBuild = true;
+ break;
+ }
+ }
+ }
+ }
+ return shouldBuild;
+ }
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketApiClient.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketApiClient.java
new file mode 100644
index 0000000..200b81e
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketApiClient.java
@@ -0,0 +1,117 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket;
+
+import org.apache.commons.httpclient.*;
+import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by nishio
+ */
+public class BitbucketApiClient {
+ private static final Logger logger = Logger.getLogger(BitbucketApiClient.class.getName());
+ private static final String BITBUCKET_HOST = "bitbucket.org";
+ private static final String V1_API_BASE_URL = "https://bitbucket.org/api/1.0/repositories/";
+ private static final String V2_API_BASE_URL = "https://bitbucket.org/api/2.0/repositories/";
+ private String owner;
+ private String repositoryName;
+ private Credentials credentials;
+
+ public BitbucketApiClient(String username, String password, String owner, String repositoryName) {
+ this.credentials = new UsernamePasswordCredentials(username, password);
+ this.owner = owner;
+ this.repositoryName = repositoryName;
+ }
+
+ public List<BitbucketPullRequestResponseValue> getPullRequests() {
+ String response = getRequest(V2_API_BASE_URL + this.owner + "/" + this.repositoryName + "/pullrequests/");
+ try {
+ return parsePullRequestJson(response).getPrValues();
+ } catch(Exception e) {
+ logger.log(Level.WARNING, "invalid pull request response.", e);
+ }
+ return null;
+ }
+
+ public List<BitbucketPullRequestComment> getPullRequestComments(String commentOwnerName, String commentRepositoryName, String pullRequestId) {
+ String response = getRequest(
+ V1_API_BASE_URL + commentOwnerName + "/" + commentRepositoryName + "/pullrequests/" + pullRequestId + "/comments");
+ try {
+ return parseCommentJson(response);
+ } catch(Exception e) {
+ logger.log(Level.WARNING, "invalid pull request response.", e);
+ }
+ return null;
+ }
+
+ public void postPullRequestComment(String pullRequestId, String comment) {
+ String path = V1_API_BASE_URL + this.owner + "/" + this.repositoryName + "/pullrequests/" + pullRequestId + "/comments";
+ try {
+ NameValuePair content = new NameValuePair("content", comment);
+ postRequest(path, new NameValuePair[]{ content });
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private String getRequest(String path) {
+ HttpClient client = new HttpClient();
+ client.getState().setCredentials(AuthScope.ANY, credentials);
+ GetMethod httpget = new GetMethod(path);
+ client.getParams().setAuthenticationPreemptive(true);
+ String response = null;
+ try {
+ client.executeMethod(httpget);
+ response = httpget.getResponseBodyAsString();
+ } catch (HttpException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return response;
+ }
+
+ private void postRequest(String path, NameValuePair[] params) throws UnsupportedEncodingException {
+ HttpClient client = new HttpClient();
+ client.getState().setCredentials(AuthScope.ANY, credentials);
+ PostMethod httppost = new PostMethod(path);
+ httppost.setRequestBody(params);
+ client.getParams().setAuthenticationPreemptive(true);
+ try {
+ client.executeMethod(httppost);
+ String response = httppost.getResponseBodyAsString();
+ logger.info("API Request Response: " + response);
+ } catch (HttpException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private BitbucketPullRequestResponse parsePullRequestJson(String response) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ BitbucketPullRequestResponse parsedResponse;
+ parsedResponse = mapper.readValue(response, BitbucketPullRequestResponse.class);
+ return parsedResponse;
+ }
+
+ private List<BitbucketPullRequestComment> parseCommentJson(String response) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ List<BitbucketPullRequestComment> parsedResponse;
+ parsedResponse = mapper.readValue(
+ response,
+ new TypeReference<List<BitbucketPullRequestComment>>() {
+ });
+ return parsedResponse;
+ }
+
+}
+
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestComment.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestComment.java
new file mode 100644
index 0000000..2e1e18e
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestComment.java
@@ -0,0 +1,118 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+import java.util.Comparator;
+
+/**
+ * Created by nishio
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BitbucketPullRequestComment implements Comparable<BitbucketPullRequestComment> {
+ private Boolean isEntityAuthor;
+ private Integer pullRequestId;
+ private String contentRendered;
+ private Boolean deleted;
+ private String UtcLastUpdated;
+ private Integer commentId;
+ private String content;
+ private String UtcCreatedOn;
+ private Boolean isSpam;
+
+ @JsonProperty("is_entity_author")
+ public Boolean getIsEntityAuthor() {
+ return isEntityAuthor;
+ }
+
+ @JsonProperty("is_entity_author")
+ public void setIsEntityAuthor(Boolean isEntityAuthor) {
+ this.isEntityAuthor = isEntityAuthor;
+ }
+
+ @JsonProperty("pull_request_id")
+ public Integer getPullRequestId() {
+ return pullRequestId;
+ }
+
+ @JsonProperty("pull_request_id")
+ public void setPullRequestId(Integer pullRequestId) {
+ this.pullRequestId = pullRequestId;
+ }
+
+ @JsonProperty("content_rendered")
+ public String getContentRendered() {
+ return contentRendered;
+ }
+
+ @JsonProperty("content_rendered")
+ public void setContentRendered(String contentRendered) {
+ this.contentRendered = contentRendered;
+ }
+
+ public Boolean getDeleted() {
+ return deleted;
+ }
+
+ public void setDeleted(Boolean deleted) {
+ this.deleted = deleted;
+ }
+
+ @JsonProperty("utc_last_updated")
+ public String getUtcLastUpdated() {
+ return UtcLastUpdated;
+ }
+
+ @JsonProperty("utc_last_updated")
+ public void setUtcLastUpdated(String utcLastUpdated) {
+ UtcLastUpdated = utcLastUpdated;
+ }
+
+ @JsonProperty("comment_id")
+ public Integer getCommentId() {
+ return commentId;
+ }
+
+ @JsonProperty("comment_id")
+ public void setCommentId(Integer commentId) {
+ this.commentId = commentId;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ @JsonProperty("utc_created_on")
+ public String getUtcCreatedOn() {
+ return UtcCreatedOn;
+ }
+
+ @JsonProperty("utc_created_on")
+ public void setUtcCreatedOn(String utcCreatedOn) {
+ UtcCreatedOn = utcCreatedOn;
+ }
+
+ @JsonProperty("is_spam")
+ public Boolean getIsSpam() {
+ return isSpam;
+ }
+
+ @JsonProperty("is_spam")
+ public void setIsSpam(Boolean isSpam) {
+ this.isSpam = isSpam;
+ }
+
+ public int compareTo(BitbucketPullRequestComment target) {
+ if (this.getCommentId() > target.getCommentId()) {
+ return 1;
+ } else if (this.getCommentId().equals(target.getCommentId())) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponse.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponse.java
new file mode 100644
index 0000000..82e6009
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponse.java
@@ -0,0 +1,58 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+import java.util.List;
+
+/**
+ * Created by nishio
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BitbucketPullRequestResponse {
+ private int pageLength;
+ private List<BitbucketPullRequestResponseValue> prValues;
+ private int page;
+ private int size;
+
+ @JsonProperty("pagelen")
+ public int getPageLength() {
+ return pageLength;
+ }
+
+ @JsonProperty("pagelen")
+ public void setPageLength(int pageLength) {
+ this.pageLength = pageLength;
+ }
+
+ @JsonProperty("values")
+ public List<BitbucketPullRequestResponseValue> getPrValues() {
+ return prValues;
+ }
+
+ @JsonProperty("values")
+ public void setPrValues(List<BitbucketPullRequestResponseValue> prValues) {
+ this.prValues = prValues;
+ }
+
+ @JsonProperty("page")
+ public int getPage() {
+ return page;
+ }
+
+ @JsonProperty("page")
+ public void setPage(int page) {
+ this.page = page;
+ }
+
+ @JsonProperty("size")
+ public int getSize() {
+ return size;
+ }
+
+ @JsonProperty("size")
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValue.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValue.java
new file mode 100644
index 0000000..e8be708
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValue.java
@@ -0,0 +1,130 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * Created by nishio
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BitbucketPullRequestResponseValue {
+ private String description;
+ private Boolean closeSourceBranch;
+ private String title;
+ private BitbucketPullRequestResponseValueRepository destination;
+ private String reason;
+ private String closedBy;
+ private BitbucketPullRequestResponseValueRepository source;
+ private String state;
+ private String createdOn;
+ private String updatedOn;
+ private String mergeCommit;
+ private String id;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @JsonProperty("close_source_branch")
+ public Boolean getCloseSourceBranch() {
+ return closeSourceBranch;
+ }
+
+ @JsonProperty("close_source_branch")
+ public void setCloseSourceBranch(Boolean closeSourceBranch) {
+ this.closeSourceBranch = closeSourceBranch;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public BitbucketPullRequestResponseValueRepository getDestination() {
+ return destination;
+ }
+
+ public void setDestination(BitbucketPullRequestResponseValueRepository destination) {
+ this.destination = destination;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+
+ @JsonProperty("closed_by")
+ public String getClosedBy() {
+ return closedBy;
+ }
+
+ @JsonProperty("closed_by")
+ public void setClosedBy(String closedBy) {
+ this.closedBy = closedBy;
+ }
+
+ public BitbucketPullRequestResponseValueRepository getSource() {
+ return source;
+ }
+
+ public void setSource(BitbucketPullRequestResponseValueRepository source) {
+ this.source = source;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ @JsonProperty("created_on")
+ public String getCreatedOn() {
+ return createdOn;
+ }
+
+ @JsonProperty("created_on")
+ public void setCreatedOn(String createdOn) {
+ this.createdOn = createdOn;
+ }
+
+ @JsonProperty("updated_on")
+ public String getUpdatedOn() {
+ return updatedOn;
+ }
+
+ @JsonProperty("updated_on")
+ public void setUpdatedOn(String updatedOn) {
+ this.updatedOn = updatedOn;
+ }
+
+ @JsonProperty("merge_commit")
+ public String getMergeCommit() {
+ return mergeCommit;
+ }
+
+ @JsonProperty("merge_commit")
+ public void setMergeCommit(String mergeCommit) {
+ this.mergeCommit = mergeCommit;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepository.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepository.java
new file mode 100644
index 0000000..76eeaf2
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepository.java
@@ -0,0 +1,35 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * Created by nishio
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BitbucketPullRequestResponseValueRepository {
+ private BitbucketPullRequestResponseValueRepositoryRepository repository;
+ private BitbucketPullRequestResponseValueRepositoryBranch branch;
+
+ @JsonProperty("repository")
+ public BitbucketPullRequestResponseValueRepositoryRepository getRepository() {
+ return repository;
+ }
+
+ @JsonProperty("repository")
+ public void setRepository(BitbucketPullRequestResponseValueRepositoryRepository repository) {
+ this.repository = repository;
+ }
+
+ @JsonProperty("branch")
+ public BitbucketPullRequestResponseValueRepositoryBranch getBranch() {
+ return branch;
+ }
+
+ @JsonProperty("branch")
+ public void setBranch(BitbucketPullRequestResponseValueRepositoryBranch branch) {
+ this.branch = branch;
+ }
+}
+
+
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepositoryBranch.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepositoryBranch.java
new file mode 100644
index 0000000..2fa2eee
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepositoryBranch.java
@@ -0,0 +1,16 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BitbucketPullRequestResponseValueRepositoryBranch {
+ private String Name;
+
+ public String getName() {
+ return Name;
+ }
+
+ public void setName(String name) {
+ Name = name;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepositoryRepository.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepositoryRepository.java
new file mode 100644
index 0000000..d8ff67e
--- /dev/null
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BitbucketPullRequestResponseValueRepositoryRepository.java
@@ -0,0 +1,43 @@
+package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BitbucketPullRequestResponseValueRepositoryRepository {
+ private String fullName;
+ private String name;
+
+ @JsonProperty("full_name")
+ public String getFullName() {
+ return fullName;
+ }
+
+ @JsonProperty("full_name")
+ public void setFullName(String fullName) {
+ this.fullName = fullName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getOwnerName() {
+ if (this.fullName != null) {
+ return this.fullName.split("/")[0];
+ }
+ return null;
+ }
+
+ public String getRepositoryName() {
+ if (this.fullName != null) {
+ return this.fullName.split("/")[1];
+ }
+ return null;
+ }
+}
+
diff --git a/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly
new file mode 100644
index 0000000..964cc85
--- /dev/null
+++ b/src/main/resources/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger/config.jelly
@@ -0,0 +1,17 @@
+<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
+ <f:entry title="Cron" field="cron">
+ <f:textbox />
+ </f:entry>
+ <f:entry title="Bitbucket BasicAuth Username" field="username">
+ <f:textbox />
+ </f:entry>
+ <f:entry title="Bitbucket BasicAuth Password" field="password">
+ <f:password />
+ </f:entry>
+ <f:entry title="RepositoryOwner" field="repositoryOwner">
+ <f:textbox />
+ </f:entry>
+ <f:entry title="RespositoryName" field="repositoryName">
+ <f:textbox />
+ </f:entry>
+</j:jelly> \ No newline at end of file
diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly
new file mode 100644
index 0000000..538998f
--- /dev/null
+++ b/src/main/resources/index.jelly
@@ -0,0 +1,6 @@
+<!--
+ This view is used to render the installed plugins page.
+-->
+<div>
+ This plugin is a sample to explain how to write a Jenkins plugin.
+</div>