diff options
Diffstat (limited to 'src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder')
12 files changed, 0 insertions, 1958 deletions
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/.gitignore b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/.gitignore deleted file mode 100644 index 21e5893..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -target/ -work/ - -# -# Eclipse metadata. -# -.project -.classpath -.settings/ - -# -# Eclipse and Maven build results -# -bin/ - -# IntelliJ metadata. -*.iml -*.ipr -*.iws -.idea/ diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketAdditionalParameterEnvironmentContributor.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketAdditionalParameterEnvironmentContributor.java deleted file mode 100644 index c531ca1..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketAdditionalParameterEnvironmentContributor.java +++ /dev/null @@ -1,40 +0,0 @@ -package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; - -import hudson.EnvVars; -import hudson.Extension; -import hudson.model.*; - -import java.io.IOException; - -@Extension -public class BitbucketAdditionalParameterEnvironmentContributor extends EnvironmentContributor { - @Override - public void buildEnvironmentFor(Run run, EnvVars envVars, TaskListener taskListener) - throws IOException, InterruptedException { - - BitbucketCause cause = (BitbucketCause) run.getCause(BitbucketCause.class); - if (cause == null) { - return; - } - - putEnvVar(envVars, "sourceBranch", cause.getSourceBranch()); - putEnvVar(envVars, "targetBranch", cause.getTargetBranch()); - putEnvVar(envVars, "repositoryOwner", cause.getRepositoryOwner()); - putEnvVar(envVars, "repositoryName", cause.getRepositoryName()); - putEnvVar(envVars, "pullRequestId", cause.getPullRequestId()); - putEnvVar(envVars, "destinationRepositoryOwner", cause.getDestinationRepositoryOwner()); - putEnvVar(envVars, "destinationRepositoryName", cause.getDestinationRepositoryName()); - putEnvVar(envVars, "pullRequestTitle", cause.getPullRequestTitle()); - putEnvVar(envVars, "pullRequestAuthor", cause.getPullRequestAuthor()); - - } - - private static void putEnvVar(EnvVars envs, String name, String value) { - envs.put(name, getString(value, "")); - } - - private static String getString(String actual, String d) { - return actual == null ? d : actual; - } - -} diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java deleted file mode 100644 index 6aa7344..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildFilter.java +++ /dev/null @@ -1,241 +0,0 @@ -package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; - -import java.util.logging.Logger; -import java.util.regex.Pattern; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.logging.Level; -import java.util.regex.Matcher; -import jenkins.plugins.git.AbstractGitSCMSource; -import jenkins.scm.api.SCMSource; - -/** - * Mutable wrapper - */ -class Mutable<T> { - private T value; - public Mutable() { this.value = null; } - public Mutable(T value) { this.value = value; } - T get() { return this.value; } - void set(T value) { this.value = value; } -} - -abstract class Filter { - protected static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName()); - - public static final String RX_FILTER_FLAG = "r"; - public static final String RX_FILTER_FLAG_SINGLE = RX_FILTER_FLAG + ":"; - - public static final String SRC_RX = "s:(" + RX_FILTER_FLAG_SINGLE + ")?"; - public static final String DST_RX = "d:(" + RX_FILTER_FLAG_SINGLE + ")?"; - public static final String AUTHOR_RX = "a:(" + RX_FILTER_FLAG_SINGLE + ")?"; - public static final String BRANCH_FILTER_RX_PART = "([^\\s$]*)"; - - abstract public boolean apply(String filter, BitbucketCause cause); - abstract public boolean check(String filter); - - static final Pattern RX_SRC_DST_PARTS = Pattern.compile("(s:)|(d:)"); - public static boolean HasSourceOrDestPartsPredicate(String filter) { return RX_SRC_DST_PARTS.matcher(filter).find(); } - - static final Pattern RX_AUTHOR_PARTS = Pattern.compile("(a:)"); - public static boolean HasAuthorPartsPredicate(String filter) { return RX_AUTHOR_PARTS.matcher(filter).find(); } - - protected boolean applyByRx(Pattern rx, Filter usedFilter, String filter, BitbucketCause cause) { - Matcher srcMatch = rx.matcher(filter); - boolean apply = false; - while (srcMatch.find()) { - String computedFilter = ((srcMatch.group(1) == null ? "" : srcMatch.group(1)) + srcMatch.group(2)).trim(); - logger.log(Level.FINE, "Apply computed filter: {0}", computedFilter); - apply = apply || (computedFilter.isEmpty() ? true : usedFilter.apply(computedFilter, cause)); - } - return apply; - } -} - -class EmptyFilter extends Filter { - @Override - public boolean apply(String filter, BitbucketCause cause) { return true; } - @Override - public boolean check(String filter) { return true; } -} - -class AnyFlag extends Filter { - @Override - public boolean apply(String filter, BitbucketCause cause) { return true; } - @Override - public boolean check(String filter) { return filter.isEmpty() || filter.contains("*") || filter.toLowerCase().contains("any"); } -} - -class OnlySourceFlag extends Filter { - @Override - public boolean apply(String filter, BitbucketCause cause) { - String selectedRx = filter.startsWith(RX_FILTER_FLAG_SINGLE) ? filter.substring(RX_FILTER_FLAG_SINGLE.length()) : Pattern.quote(filter); - logger.log(Level.FINE, "OnlySourceFlag using filter: {0}", selectedRx); - Matcher matcher = Pattern.compile(selectedRx, Pattern.CASE_INSENSITIVE).matcher(cause.getSourceBranch()); - return filter.startsWith(RX_FILTER_FLAG_SINGLE) ? matcher.find() : matcher.matches(); - } - @Override - public boolean check(String filter) { - return false; - } -} - -class OnlyDestFlag extends Filter { - @Override - public boolean apply(String filter, BitbucketCause cause) { - String selectedRx = filter.startsWith(RX_FILTER_FLAG_SINGLE) ? filter.substring(RX_FILTER_FLAG_SINGLE.length()) : Pattern.quote(filter); - logger.log(Level.FINE, "OnlyDestFlag using filter: {0}", selectedRx); - Matcher matcher = Pattern.compile(selectedRx, Pattern.CASE_INSENSITIVE).matcher(cause.getTargetBranch()); - return filter.startsWith(RX_FILTER_FLAG_SINGLE) ? matcher.find() : matcher.matches(); - } - @Override - public boolean check(String filter) { - return !HasSourceOrDestPartsPredicate(filter); - } -} - -class SourceDestFlag extends Filter { - static final Pattern SRC_MATCHER_RX = Pattern.compile(SRC_RX + BRANCH_FILTER_RX_PART, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ); - static final Pattern DST_MATCHER_RX = Pattern.compile(DST_RX + BRANCH_FILTER_RX_PART, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ); - - @Override - public boolean apply(String filter, BitbucketCause cause) { - return this.applyByRx(SRC_MATCHER_RX, new OnlySourceFlag(), filter, cause) && - this.applyByRx(DST_MATCHER_RX, new OnlyDestFlag(), filter, cause); - } - @Override - public boolean check(String filter) { - return HasSourceOrDestPartsPredicate(filter); - } -} - -class AuthorFlag extends Filter { - static final Pattern AUTHOR_MATCHER_RX = Pattern.compile(AUTHOR_RX + BRANCH_FILTER_RX_PART, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ); - - static class AuthorFlagImpl extends Filter { - @Override - public boolean apply(String filter, BitbucketCause cause) { - String selectedRx = filter.startsWith(RX_FILTER_FLAG_SINGLE) ? filter.substring(RX_FILTER_FLAG_SINGLE.length()) : Pattern.quote(filter); - logger.log(Level.FINE, "AuthorFlagImpl using filter: {0}", selectedRx); - Matcher matcher = Pattern.compile(selectedRx, Pattern.CASE_INSENSITIVE).matcher(cause.getPullRequestAuthor()); - return filter.startsWith(RX_FILTER_FLAG_SINGLE) ? matcher.find() : matcher.matches(); - } - @Override - public boolean check(String filter) { return false; } - } - - @Override - public boolean apply(String filter, BitbucketCause cause) { - return this.applyByRx(AUTHOR_MATCHER_RX, new AuthorFlagImpl(), filter, cause); - } - @Override - public boolean check(String filter) { - return HasAuthorPartsPredicate(filter); - } -} - -class CombinedFlags extends Filter { - private final Filter[] _filters; - public CombinedFlags(Filter[] filters) { - _filters = filters; - } - - @Override - public boolean apply(String filter, BitbucketCause cause) { - boolean applied = true; - for(Filter f: _filters) - if (f.check(filter)) - applied = applied && f.apply(filter, cause); - return applied; - } - @Override - public boolean check(String filter) { - for(Filter f: _filters) - if (f.check(filter)) - return true; - return false; - } -} - -/** - * Created by maxvodo - */ -public class BitbucketBuildFilter { - private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName()); - - private final String filter; - private Filter currFilter = null; - private static final List<Filter> AvailableFilters; - - static { - ArrayList<Filter> filters = new ArrayList<Filter>(); - - filters.add(new AnyFlag()); - filters.add(new CombinedFlags(new Filter[] { - new SourceDestFlag(), - new AuthorFlag() - })); - filters.add(new OnlyDestFlag()); - filters.add(new EmptyFilter()); - - AvailableFilters = filters; - } - - public BitbucketBuildFilter(String f) { - this.filter = (f != null ? f : "").trim(); - this.buildFilter(this.filter); - } - - private void buildFilter(String filter) { - logger.log(Level.FINE, "Build filter by phrase: {0}", filter); - for(Filter f : AvailableFilters) { - if (f.check(filter)) { - this.currFilter = f; - logger.log(Level.FINE, "Using filter: {0}", f.getClass().getSimpleName()); - break; - } - } - } - - public boolean approved(BitbucketCause cause) { - logger.log(Level.FINE, "Approve cause: {0}", cause.toString()); - return this.currFilter.apply(this.filter, cause); - } - - public static BitbucketBuildFilter instanceByString(String filter) { - logger.log(Level.FINE, "Filter instance by filter string"); - return new BitbucketBuildFilter(filter); - } - - static public String filterFromGitSCMSource(AbstractGitSCMSource gitscm, String defaultFilter) { - if (gitscm == null) { - logger.log(Level.FINE, "Git SCMSource unavailable. Using default value: {0}", defaultFilter); - return defaultFilter; - } - - StringBuffer filter = new StringBuffer(defaultFilter); - final String includes = gitscm.getIncludes(); - if (includes != null && !includes.isEmpty()) { - for(String part : includes.split("\\s+")) { - filter.append(String.format("%s ", part.replaceAll("\\*\\/", "d:"))); - } - } - - logger.log(Level.FINE, "Git includes transformation to filter result: {1} -> {0}; default: {2}", new Object[]{ filter, includes, defaultFilter }); - return filter.toString().trim(); - } - - public static BitbucketBuildFilter instanceBySCM(Collection<SCMSource> scmSources, String defaultFilter) { - logger.log(Level.FINE, "Filter instance by using SCMSources list with {0} items", scmSources.size()); - AbstractGitSCMSource gitscm = null; - for(SCMSource scm : scmSources) { - logger.log(Level.FINE, "Check {0} SCMSource ", scm.getClass()); - if (scm instanceof AbstractGitSCMSource) { - gitscm = (AbstractGitSCMSource)scm; - break; - } - } - return new BitbucketBuildFilter(filterFromGitSCMSource(gitscm, defaultFilter)); - } -} diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java deleted file mode 100644 index 230253b..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildListener.java +++ /dev/null @@ -1,58 +0,0 @@ -package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; - -import hudson.Extension; -import hudson.model.AbstractBuild; -import hudson.model.Job; -import hudson.model.Run; -import hudson.model.TaskListener; -import hudson.model.listeners.RunListener; -import hudson.triggers.Trigger; -import jenkins.model.ParameterizedJobMixIn; - -import javax.annotation.Nonnull; -import java.util.logging.Logger; - -/** - * Created by nishio - */ -@Extension -public class BitbucketBuildListener extends RunListener<Run<?, ?>> { - private static final Logger logger = Logger.getLogger(BitbucketBuildListener.class.getName()); - - @Override - public void onStarted(Run r, TaskListener listener) { - logger.fine("BitbucketBuildListener onStarted called."); - BitbucketBuilds builds = builds(r); - if (builds != null) { - builds.onStarted((BitbucketCause) r.getCause(BitbucketCause.class), r); - } - } - - @Override - public void onCompleted(Run r, @Nonnull TaskListener listener) { - logger.fine("BitbucketBuildListener onCompleted called."); - BitbucketBuilds builds = builds(r); - if (builds != null) { - builds.onCompleted((BitbucketCause) r.getCause(BitbucketCause.class), r.getResult(), r.getUrl()); - } - } - - private BitbucketBuilds builds(Run<?, ?> r) { - BitbucketBuildTrigger trigger = null; - if (r instanceof AbstractBuild) { - trigger = BitbucketBuildTrigger.getTrigger(((AbstractBuild) r).getProject()); - } else { - Job job = r.getParent(); - if (job instanceof ParameterizedJobMixIn.ParameterizedJob) { - - for (Trigger<?> t : ((ParameterizedJobMixIn.ParameterizedJob) job).getTriggers().values()) { - if (t instanceof BitbucketBuildTrigger) { - trigger = (BitbucketBuildTrigger) t; - } - } - } - } - return trigger == null ? null : trigger.getBuilder().getBuilds(); - } - -} diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java deleted file mode 100644 index be36382..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java +++ /dev/null @@ -1,332 +0,0 @@ -package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; - -import antlr.ANTLRException; -import com.cloudbees.plugins.credentials.CredentialsProvider; -import com.cloudbees.plugins.credentials.common.StandardListBoxModel; -import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; -import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials; -import hudson.Extension; -import hudson.model.*; -import hudson.model.Queue; -import hudson.model.queue.QueueTaskFuture; -import hudson.plugins.git.RevisionParameterAction; -import hudson.triggers.Trigger; -import hudson.triggers.TriggerDescriptor; -import hudson.util.ListBoxModel; -import jenkins.model.Jenkins; -import jenkins.model.ParameterizedJobMixIn; -import net.sf.json.JSONObject; -import org.apache.commons.lang.StringUtils; -import org.jenkinsci.Symbol; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf; - -/** - * Created by nishio - */ -public class BitbucketBuildTrigger extends Trigger<Job<?, ?>> { - private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName()); - private final String projectPath; - private final String cron; - private final String credentialsId; - private final String username; - private final String password; - private final String repositoryOwner; - private final String repositoryName; - private final String branchesFilter; - private final boolean branchesFilterBySCMIncludes; - private final String ciKey; - private final String ciName; - private final String ciSkipPhrases; - private final boolean checkDestinationCommit; - private final boolean approveIfSuccess; - private final boolean cancelOutdatedJobs; - private final String commentTrigger; - - transient private BitbucketPullRequestsBuilder bitbucketPullRequestsBuilder; - - public static final BitbucketBuildTriggerDescriptor descriptor = new BitbucketBuildTriggerDescriptor(); - - @DataBoundConstructor - public BitbucketBuildTrigger( - String projectPath, - String cron, - String credentialsId, - String username, - String password, - String repositoryOwner, - String repositoryName, - String branchesFilter, - boolean branchesFilterBySCMIncludes, - String ciKey, - String ciName, - String ciSkipPhrases, - boolean checkDestinationCommit, - boolean approveIfSuccess, - boolean cancelOutdatedJobs, - String commentTrigger - ) throws ANTLRException { - super(cron); - this.projectPath = projectPath; - this.cron = cron; - this.credentialsId = credentialsId; - this.username = username; - this.password = password; - this.repositoryOwner = repositoryOwner; - this.repositoryName = repositoryName; - this.branchesFilter = branchesFilter; - this.branchesFilterBySCMIncludes = branchesFilterBySCMIncludes; - this.ciKey = ciKey; - this.ciName = ciName; - this.ciSkipPhrases = ciSkipPhrases; - this.checkDestinationCommit = checkDestinationCommit; - this.approveIfSuccess = approveIfSuccess; - this.cancelOutdatedJobs = cancelOutdatedJobs; - this.commentTrigger = commentTrigger; - } - - public String getProjectPath() { - return this.projectPath; - } - - public String getCron() { - return this.cron; - } - - public String getCredentialsId() { - return credentialsId; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - public String getRepositoryOwner() { - return repositoryOwner; - } - - public String getRepositoryName() { - return repositoryName; - } - - public String getBranchesFilter() { - return branchesFilter; - } - - public boolean getBranchesFilterBySCMIncludes() { - return branchesFilterBySCMIncludes; - } - - public String getCiKey() { - return ciKey; - } - - public String getCiName() { - return ciName; - } - - public String getCiSkipPhrases() { - return ciSkipPhrases; - } - - public boolean getCheckDestinationCommit() { - return checkDestinationCommit; - } - - public boolean getApproveIfSuccess() { - return approveIfSuccess; - } - - public boolean getCancelOutdatedJobs() { - return cancelOutdatedJobs; - } - /** - * @return a phrase that when entered in a comment will trigger a new build - */ - public String getCommentTrigger() { - return commentTrigger; - } - - @Override - public void start(Job<?, ?> 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; - } - - private ParameterizedJobMixIn retrieveScheduleJob(final Job<?, ?> job) { - // TODO 1.621+ use standard method - return new ParameterizedJobMixIn() { - @Override - protected Job asJob() { - return job; - } - }; - } - - public QueueTaskFuture<?> startJob(BitbucketCause cause) { - Map<String, ParameterValue> values = this.getDefaultParameters(); - - if (getCancelOutdatedJobs()) { - cancelPreviousJobsInQueueThatMatch(cause); - abortRunningJobsThatMatch(cause); - } - - return retrieveScheduleJob(this.job).scheduleBuild2(0, - new CauseAction(cause), - new ParametersAction(new ArrayList(values.values())), - new RevisionParameterAction(cause.getSourceCommitHash())); - } - - private void cancelPreviousJobsInQueueThatMatch(@Nonnull BitbucketCause bitbucketCause) { - logger.fine("Looking for queued jobs that match PR ID: " + bitbucketCause.getPullRequestId()); - Queue queue = getInstance().getQueue(); - - for (Queue.Item item : queue.getItems()) { - if (hasCauseFromTheSamePullRequest(item.getCauses(), bitbucketCause)) { - logger.fine("Canceling item in queue: " + item); - queue.cancel(item); - } - } - } - - private Jenkins getInstance() { - final Jenkins instance = Jenkins.getInstance(); - if (instance == null){ - throw new IllegalStateException("Jenkins instance is NULL!"); - } - return instance; - } - - private void abortRunningJobsThatMatch(@Nonnull BitbucketCause bitbucketCause) { - logger.fine("Looking for running jobs that match PR ID: " + bitbucketCause.getPullRequestId()); - for (Object o : job.getBuilds()) { - if (o instanceof Run) { - Run build = (Run) o; - if (build.isBuilding() && hasCauseFromTheSamePullRequest(build.getCauses(), bitbucketCause)) { - logger.fine("Aborting build: " + build + " since PR is outdated"); - setBuildDescription(build); - final Executor executor = build.getExecutor(); - if (executor == null){ - throw new IllegalStateException("Executor can't be NULL"); - } - executor.interrupt(Result.ABORTED); - } - } - } - } - - private void setBuildDescription(final Run build) { - try { - build.setDescription("Aborting build by `Bitbucket Pullrequest Builder Plugin`: " + build + " since PR is outdated"); - } catch (IOException e) { - logger.warning("Can't set up build description due to an IOException: " + e.getMessage()); - } - } - - private boolean hasCauseFromTheSamePullRequest(@Nullable List<Cause> causes, @Nullable BitbucketCause pullRequestCause) { - if (causes != null && pullRequestCause != null) { - for (Cause cause : causes) { - if (cause instanceof BitbucketCause) { - BitbucketCause sc = (BitbucketCause) cause; - if (StringUtils.equals(sc.getPullRequestId(), pullRequestCause.getPullRequestId()) && - StringUtils.equals(sc.getRepositoryName(), pullRequestCause.getRepositoryName())) { - return true; - } - } - } - } - return false; - } - - 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() { - Job<?,?> project = this.getBuilder().getProject(); - if (project instanceof AbstractProject && ((AbstractProject)project).isDisabled()) { - logger.fine("Build Skip."); - } else { - this.bitbucketPullRequestsBuilder.run(); - this.getDescriptor().save(); - } - } - - @Override - public void stop() { - super.stop(); - } - - @Extension - @Symbol("bitbucketpr") - public static final class BitbucketBuildTriggerDescriptor extends TriggerDescriptor { - public BitbucketBuildTriggerDescriptor() { - load(); - } - - @Override - public boolean isApplicable(Item item) { - return item instanceof Job && item instanceof ParameterizedJobMixIn.ParameterizedJob; - } - - @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); - } - - public ListBoxModel doFillCredentialsIdItems() { - return new StandardListBoxModel() - .withEmptySelection() - .withMatching(instanceOf(UsernamePasswordCredentials.class), - CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class)); - } - } -} diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java deleted file mode 100644 index 6ba3dab..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuilds.java +++ /dev/null @@ -1,54 +0,0 @@ -package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; - -import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BuildState; -import hudson.model.*; -import jenkins.model.JenkinsLocationConfiguration; - -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; - this.repository.init(); - } - - void onStarted(BitbucketCause cause, Run<?, ?> build) { - if (cause == null) { - return; - } - try { - build.setDescription(cause.getShortDescription()); - } catch (IOException e) { - logger.log(Level.SEVERE, "Can't update build description", e); - } - } - - void onCompleted(BitbucketCause cause, Result result, String buildUrl) { - if (cause == null) { - return; - } - JenkinsLocationConfiguration globalConfig = new JenkinsLocationConfiguration(); - String rootUrl = globalConfig.getUrl(); - if (rootUrl == null) { - logger.warning("PLEASE SET JENKINS ROOT URL IN GLOBAL CONFIGURATION FOR BUILD STATE REPORTING"); - } else { - buildUrl = rootUrl + buildUrl; - BuildState state = result == Result.SUCCESS ? BuildState.SUCCESSFUL : BuildState.FAILED; - repository.setBuildStatus(cause, state, buildUrl); - } - - if (this.trigger.getApproveIfSuccess() && result == Result.SUCCESS) { - this.repository.postPullRequestApproval(cause.getPullRequestId()); - } - } -} diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java deleted file mode 100644 index 3cb107b..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketCause.java +++ /dev/null @@ -1,93 +0,0 @@ -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; - private final String sourceCommitHash; - private final String destinationCommitHash; - private final String pullRequestAuthor; - 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, - String sourceCommitHash, - String destinationCommitHash, - String pullRequestAuthor) { - this.sourceBranch = sourceBranch; - this.targetBranch = targetBranch; - this.repositoryOwner = repositoryOwner; - this.repositoryName = repositoryName; - this.pullRequestId = pullRequestId; - this.destinationRepositoryOwner = destinationRepositoryOwner; - this.destinationRepositoryName = destinationRepositoryName; - this.pullRequestTitle = pullRequestTitle; - this.sourceCommitHash = sourceCommitHash; - this.destinationCommitHash = destinationCommitHash; - this.pullRequestAuthor = pullRequestAuthor; - } - - 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; - } - - public String getSourceCommitHash() { return sourceCommitHash; } - - public String getDestinationCommitHash() { return destinationCommitHash; } - - @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; - } - - public String getPullRequestAuthor() { - return this.pullRequestAuthor; - } -} diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java deleted file mode 100644 index 5b37fea..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; - -import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest; - -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import java.util.Collection; -import java.util.logging.Level; -import java.util.logging.Logger; - -import hudson.model.Job; -import org.apache.commons.codec.binary.Hex; - -/** - * Created by nishio - */ -public class BitbucketPullRequestsBuilder { - private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName()); - private Job<?, ?> 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.fine("Build Start."); - this.repository.init(); - Collection<Pullrequest> targetPullRequests = this.repository.getTargetPullRequests(); - 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.repository.init(); - this.builds = new BitbucketBuilds(this.trigger, this.repository); - return this; - } - - public void setProject(Job<?, ?> project) { - this.project = project; - } - - public void setTrigger(BitbucketBuildTrigger trigger) { - this.trigger = trigger; - } - - public Job<?, ?> getProject() { - return this.project; - } - - /** - * Return MD5 hashed full project name or full project name, if MD5 hash provider inaccessible - * @return unique project id - */ - public String getProjectId() { - try { - final MessageDigest MD5 = MessageDigest.getInstance("MD5"); - return new String(Hex.encodeHex(MD5.digest(this.project.getFullName().getBytes("UTF-8")))); - } catch (NoSuchAlgorithmException exc) { - logger.log(Level.WARNING, "Failed to produce hash", exc); - } catch (UnsupportedEncodingException exc) { - logger.log(Level.WARNING, "Failed to produce hash", exc); - } - return this.project.getFullName(); - - } - - 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 deleted file mode 100644 index 3b0a314..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java +++ /dev/null @@ -1,346 +0,0 @@ -package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.logging.Logger; - -import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.ApiClient; -import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BuildState; -import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest; - -import java.util.LinkedList; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.cloudbees.plugins.credentials.CredentialsMatchers; -import com.cloudbees.plugins.credentials.CredentialsProvider; -import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; -import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials; - -import jenkins.model.Jenkins; -import jenkins.scm.api.SCMSource; -import jenkins.scm.api.SCMSourceOwner; -import jenkins.scm.api.SCMSourceOwners; - -import org.apache.commons.lang.StringUtils; - -import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf; - -/** - * Created by nishio - */ -public class BitbucketRepository { - private static final Logger logger = Logger.getLogger(BitbucketRepository.class.getName()); - private static final String BUILD_DESCRIPTION = "%s: %s into %s"; - private static final String BUILD_REQUEST_DONE_MARKER = "ttp build flag"; - private static final String BUILD_REQUEST_MARKER_TAG_SINGLE_RX = "\\#[\\w\\-\\d]+"; - private static final String BUILD_REQUEST_MARKER_TAGS_RX = "\\[bid\\:\\s?(.*)\\]"; - /** - * Default value for comment trigger. - */ - public static final String DEFAULT_COMMENT_TRIGGER = "test this please"; - - private String projectPath; - private BitbucketPullRequestsBuilder builder; - private BitbucketBuildTrigger trigger; - private ApiClient client; - - public BitbucketRepository(String projectPath, BitbucketPullRequestsBuilder builder) { - this.projectPath = projectPath; - this.builder = builder; - } - - public void init() { - this.init(null, null); - } - - public <T extends ApiClient.HttpClientFactory> void init(T httpFactory) { - this.init(null, httpFactory); - } - - public void init(ApiClient client) { - this.init(client, null); - } - - public <T extends ApiClient.HttpClientFactory> void init(ApiClient client, T httpFactory) { - this.trigger = this.builder.getTrigger(); - - if (client == null) { - String username = trigger.getUsername(); - String password = trigger.getPassword(); - StandardUsernamePasswordCredentials credentials = getCredentials(trigger.getCredentialsId()); - if (credentials != null) { - username = credentials.getUsername(); - password = credentials.getPassword().getPlainText(); - } - this.client = new ApiClient( - username, - password, - trigger.getRepositoryOwner(), - trigger.getRepositoryName(), - trigger.getCiKey(), - trigger.getCiName(), - httpFactory - ); - - } else this.client = client; - } - - public Collection<Pullrequest> getTargetPullRequests() { - logger.fine("Fetch PullRequests."); - List<Pullrequest> pullRequests = client.getPullRequests(); - List<Pullrequest> targetPullRequests = new ArrayList<Pullrequest>(); - for(Pullrequest pullRequest : pullRequests) { - if (isBuildTarget(pullRequest)) { - targetPullRequests.add(pullRequest); - } - } - return targetPullRequests; - } - - public ApiClient getClient() { - return this.client; - } - - public void addFutureBuildTasks(Collection<Pullrequest> pullRequests) { - for(Pullrequest pullRequest : pullRequests) { - if ( this.trigger.getApproveIfSuccess() ) { - deletePullRequestApproval(pullRequest.getId()); - } - 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(), - pullRequest.getSource().getCommit().getHash(), - pullRequest.getDestination().getCommit().getHash(), - pullRequest.getAuthor().getCombinedUsername() - ); - setBuildStatus(cause, BuildState.INPROGRESS, getInstance().getRootUrl()); - this.builder.getTrigger().startJob(cause); - } - } - - private Jenkins getInstance() { - final Jenkins instance = Jenkins.getInstance(); - if (instance == null){ - throw new IllegalStateException("Jenkins instance is NULL!"); - } - return instance; - } - - - public void setBuildStatus(BitbucketCause cause, BuildState state, String buildUrl) { - String comment = null; - String sourceCommit = cause.getSourceCommitHash(); - String owner = cause.getRepositoryOwner(); - String repository = cause.getRepositoryName(); - String destinationBranch = cause.getTargetBranch(); - - logger.fine("setBuildStatus " + state + " for commit: " + sourceCommit + " with url " + buildUrl); - - if (state == BuildState.FAILED || state == BuildState.SUCCESSFUL) { - comment = String.format(BUILD_DESCRIPTION, builder.getProject().getDisplayName(), sourceCommit, destinationBranch); - } - - this.client.setBuildStatus(owner, repository, sourceCommit, state, buildUrl, comment, this.builder.getProjectId()); - } - - public void deletePullRequestApproval(String pullRequestId) { - this.client.deletePullRequestApproval(pullRequestId); - } - - public void postPullRequestApproval(String pullRequestId) { - this.client.postPullRequestApproval(pullRequestId); - } - - public String getMyBuildTag(String buildKey) { - return "#" + this.client.buildStatusKey(buildKey); - } - - final static Pattern BUILD_TAGS_RX = Pattern.compile(BUILD_REQUEST_MARKER_TAGS_RX, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ); - final static Pattern SINGLE_BUILD_TAG_RX = Pattern.compile(BUILD_REQUEST_MARKER_TAG_SINGLE_RX, Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ); - final static String CONTENT_PART_TEMPLATE = "```[bid: %s]```"; - - private List<String> getAvailableBuildTagsFromTTPComment(String buildTags) { - logger.log(Level.FINE, "Parse {0}", new Object[]{ buildTags }); - List<String> availableBuildTags = new LinkedList<String>(); - Matcher subBuildTagMatcher = SINGLE_BUILD_TAG_RX.matcher(buildTags); - while(subBuildTagMatcher.find()) availableBuildTags.add(subBuildTagMatcher.group(0).trim()); - return availableBuildTags; - } - - public boolean hasMyBuildTagInTTPComment(String content, String buildKey) { - Matcher tagsMatcher = BUILD_TAGS_RX.matcher(content); - if (tagsMatcher.find()) { - logger.log(Level.FINE, "Content {0} g[1]:{1} mykey:{2}", new Object[] { content, tagsMatcher.group(1).trim(), this.getMyBuildTag(buildKey) }); - return this.getAvailableBuildTagsFromTTPComment(tagsMatcher.group(1).trim()).contains(this.getMyBuildTag(buildKey)); - } - else return false; - } - - private void postBuildTagInTTPComment(String pullRequestId, String content, String buildKey) { - logger.log(Level.FINE, "Update build tag for {0} build key", buildKey); - List<String> builds = this.getAvailableBuildTagsFromTTPComment(content); - builds.add(this.getMyBuildTag(buildKey)); - content += " " + String.format(CONTENT_PART_TEMPLATE, StringUtils.join(builds, " ")); - logger.log(Level.FINE, "Post comment: {0} with original content {1}", new Object[]{ content, this.client.postPullRequestComment(pullRequestId, content).getId() }); - } - - private boolean isTTPComment(String content) { - // special case: in unit tests, trigger is null and can't be mocked - String commentTrigger = DEFAULT_COMMENT_TRIGGER; - if(trigger != null && StringUtils.isNotBlank(trigger.getCommentTrigger())) { - commentTrigger = trigger.getCommentTrigger(); - } - return content.toLowerCase().contains(commentTrigger); - } - - private boolean isTTPCommentBuildTags(String content) { - return content.toLowerCase().contains(BUILD_REQUEST_DONE_MARKER.toLowerCase()); - } - - public List<Pullrequest.Comment> filterPullRequestComments(List<Pullrequest.Comment> comments) { - logger.fine("Filter PullRequest Comments."); - Collections.sort(comments); - Collections.reverse(comments); - List<Pullrequest.Comment> filteredComments = new LinkedList<Pullrequest.Comment>(); - for(Pullrequest.Comment comment : comments) { - String content = comment.getContent(); - if (content == null || content.isEmpty()) continue; - boolean isTTP = this.isTTPComment(content); - boolean isTTPBuild = this.isTTPCommentBuildTags(content); - if (isTTP || isTTPBuild) filteredComments.add(comment); - if (isTTP) break; - } - return filteredComments; - } - - private boolean isBuildTarget(Pullrequest pullRequest) { - if (pullRequest.getState() != null && pullRequest.getState().equals("OPEN")) { - if (isSkipBuild(pullRequest.getTitle()) || !isFilteredBuild(pullRequest)) { - return false; - } - - Pullrequest.Revision source = pullRequest.getSource(); - String sourceCommit = source.getCommit().getHash(); - Pullrequest.Revision destination = pullRequest.getDestination(); - String owner = destination.getRepository().getOwnerName(); - String repositoryName = destination.getRepository().getRepositoryName(); - - Pullrequest.Repository sourceRepository = source.getRepository(); - String buildKeyPart = this.builder.getProjectId(); - - final boolean commitAlreadyBeenProcessed = this.client.hasBuildStatus( - sourceRepository.getOwnerName(), sourceRepository.getRepositoryName(), sourceCommit, buildKeyPart - ); - if (commitAlreadyBeenProcessed) logger.log(Level.FINE, - "Commit {0}#{1} has already been processed", - new Object[]{ sourceCommit, buildKeyPart } - ); - - final String id = pullRequest.getId(); - List<Pullrequest.Comment> comments = client.getPullRequestComments(owner, repositoryName, id); - - boolean rebuildCommentAvailable = false; - if (comments != null) { - Collection<Pullrequest.Comment> filteredComments = this.filterPullRequestComments(comments); - boolean hasMyBuildTag = false; - for (Pullrequest.Comment comment : filteredComments) { - String content = comment.getContent(); - if (this.isTTPComment(content)) { - rebuildCommentAvailable = true; - logger.log(Level.FINE, - "Rebuild comment available for commit {0} and comment #{1}", - new Object[]{ sourceCommit, comment.getId() } - ); - } - if (isTTPCommentBuildTags(content)) - hasMyBuildTag |= this.hasMyBuildTagInTTPComment(content, buildKeyPart); - } - rebuildCommentAvailable &= !hasMyBuildTag; - } - if (rebuildCommentAvailable) this.postBuildTagInTTPComment(id, "TTP build flag", buildKeyPart); - - final boolean canBuildTarget = rebuildCommentAvailable || !commitAlreadyBeenProcessed; - logger.log(Level.FINE, "Build target? {0} [rebuild:{1} processed:{2}]", new Object[]{ canBuildTarget, rebuildCommentAvailable, commitAlreadyBeenProcessed}); - return canBuildTarget; - } - - return false; - } - - private boolean isSkipBuild(String pullRequestTitle) { - String skipPhrases = this.trigger.getCiSkipPhrases(); - if (skipPhrases != null && !"".equals(skipPhrases)) { - String[] phrases = skipPhrases.split(","); - for(String phrase : phrases) { - if (pullRequestTitle.toLowerCase().contains(phrase.trim().toLowerCase())) { - return true; - } - } - } - return false; - } - - private boolean isFilteredBuild(Pullrequest pullRequest) { - - final String pullRequestId = pullRequest.getId(); - final String pullRequestTitle = pullRequest.getTitle(); - final String destinationRepoName = pullRequest.getDestination().getRepository().getRepositoryName(); - - // pullRequest.getDestination().getCommit() may return null for pull requests with merge conflicts - // * see: https://github.com/nishio-dens/bitbucket-pullrequest-builder-plugin/issues/119 - // * see: https://github.com/nishio-dens/bitbucket-pullrequest-builder-plugin/issues/98 - final String destinationCommitHash; - if (pullRequest.getDestination().getCommit() == null) { - logger.log(Level.INFO, "Pull request #{0} ''{1}'' in repo ''{2}'' has a null value for destination commit.", - new Object[]{pullRequestId, pullRequestTitle, destinationRepoName}); - destinationCommitHash = null; - } else { - destinationCommitHash = pullRequest.getDestination().getCommit().getHash(); - } - - BitbucketCause cause = new BitbucketCause( - pullRequest.getSource().getBranch().getName(), - pullRequest.getDestination().getBranch().getName(), - pullRequest.getSource().getRepository().getOwnerName(), - pullRequest.getSource().getRepository().getRepositoryName(), - pullRequestId, - pullRequest.getDestination().getRepository().getOwnerName(), - destinationRepoName, - pullRequestTitle, - pullRequest.getSource().getCommit().getHash(), - destinationCommitHash, - pullRequest.getAuthor().getCombinedUsername() - ); - - //@FIXME: Way to iterate over all available SCMSources - List<SCMSource> sources = new LinkedList<SCMSource>(); - for(SCMSourceOwner owner : SCMSourceOwners.all()) - for(SCMSource src : owner.getSCMSources()) - sources.add(src); - - BitbucketBuildFilter filter = !this.trigger.getBranchesFilterBySCMIncludes() ? - BitbucketBuildFilter.instanceByString(this.trigger.getBranchesFilter()) : - BitbucketBuildFilter.instanceBySCM(sources, this.trigger.getBranchesFilter()); - - return filter.approved(cause); - } - - private StandardUsernamePasswordCredentials getCredentials(String credentialsId) { - if (null == credentialsId) return null; - return CredentialsMatchers - .firstOrNull( - CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class), - CredentialsMatchers.allOf(CredentialsMatchers.withId(credentialsId), - instanceOf(UsernamePasswordCredentials.class))); - } -} diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java deleted file mode 100644 index 0c21806..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java +++ /dev/null @@ -1,285 +0,0 @@ -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.apache.commons.httpclient.methods.DeleteMethod; -import org.apache.commons.httpclient.params.HttpClientParams; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.type.TypeFactory; -import org.codehaus.jackson.type.JavaType; -import org.codehaus.jackson.type.TypeReference; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import jenkins.model.Jenkins; -import hudson.ProxyConfiguration; - -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.httpclient.methods.PutMethod; -import org.apache.commons.httpclient.util.EncodingUtil; - -/** - * Created by nishio - */ -public class ApiClient { - private static final Logger logger = Logger.getLogger(ApiClient.class.getName()); - private static final String V1_API_BASE_URL = "https://bitbucket.org/api/1.0/repositories/"; - private static final String V2_API_BASE_URL = "https://bitbucket.org/api/2.0/repositories/"; - private static final String COMPUTED_KEY_FORMAT = "%s-%s"; - private String owner; - private String repositoryName; - private Credentials credentials; - private String key; - private String name; - private HttpClientFactory factory; - - public static final byte MAX_KEY_SIZE_BB_API = 40; - - public static class HttpClientFactory { - public static final HttpClientFactory INSTANCE = new HttpClientFactory(); - private static final int DEFAULT_TIMEOUT = 60000; - - public HttpClient getInstanceHttpClient() { - HttpClient client = new HttpClient(); - - HttpClientParams params = client.getParams(); - params.setConnectionManagerTimeout(DEFAULT_TIMEOUT); - params.setSoTimeout(DEFAULT_TIMEOUT); - - if (Jenkins.getInstance() == null) return client; - - ProxyConfiguration proxy = getInstance().proxy; - if (proxy == null) return client; - - logger.log(Level.FINE, "Jenkins proxy: {0}:{1}", new Object[]{ proxy.name, proxy.port }); - client.getHostConfiguration().setProxy(proxy.name, proxy.port); - String username = proxy.getUserName(); - String password = proxy.getPassword(); - - // Consider it to be passed if username specified. Sufficient? - if (username != null && !"".equals(username.trim())) { - logger.log(Level.FINE, "Using proxy authentication (user={0})", username); - client.getState().setProxyCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(username, password)); - } - - return client; - } - - private Jenkins getInstance() { - final Jenkins instance = Jenkins.getInstance(); - if (instance == null){ - throw new IllegalStateException("Jenkins instance is NULL!"); - } - return instance; - } - } - - - - public <T extends HttpClientFactory> ApiClient( - String username, String password, - String owner, String repositoryName, - String key, String name, - T httpFactory - ) { - this.credentials = new UsernamePasswordCredentials(username, password); - this.owner = owner; - this.repositoryName = repositoryName; - this.key = key; - this.name = name; - this.factory = httpFactory != null ? httpFactory : HttpClientFactory.INSTANCE; - } - - public List<Pullrequest> getPullRequests() { - return getAllValues(v2("/pullrequests/"), 50, Pullrequest.class); - } - - public List<Pullrequest.Comment> getPullRequestComments(String commentOwnerName, String commentRepositoryName, String pullRequestId) { - return getAllValues(v2("/pullrequests/" + pullRequestId + "/comments"), 100, Pullrequest.Comment.class); - } - - public String getName() { - return this.name; - } - - private static MessageDigest SHA1 = null; - - /** - * Retrun - * @param keyExPart - * @return key parameter for call BitBucket API - */ - private String computeAPIKey(String keyExPart) { - String computedKey = String.format(COMPUTED_KEY_FORMAT, this.key, keyExPart); - - if (computedKey.length() > MAX_KEY_SIZE_BB_API) { - try { - if (SHA1 == null) SHA1 = MessageDigest.getInstance("SHA1"); - return new String(Hex.encodeHex(SHA1.digest(computedKey.getBytes("UTF-8")))); - } catch(NoSuchAlgorithmException e) { - logger.log(Level.WARNING, "Failed to create hash provider", e); - } catch (UnsupportedEncodingException e) { - logger.log(Level.WARNING, "Failed to create hash provider", e); - } - } - return (computedKey.length() <= MAX_KEY_SIZE_BB_API) ? computedKey : computedKey.substring(0, MAX_KEY_SIZE_BB_API); - } - - public String buildStatusKey(String bsKey) { - return this.computeAPIKey(bsKey); - } - - public boolean hasBuildStatus(String owner, String repositoryName, String revision, String keyEx) { - String url = v2(owner, repositoryName, "/commit/" + revision + "/statuses/build/" + this.computeAPIKey(keyEx)); - String reqBody = get(url); - return reqBody != null && reqBody.contains("\"state\""); - } - - public void setBuildStatus(String owner, String repositoryName, String revision, BuildState state, String buildUrl, String comment, String keyEx) { - String url = v2(owner, repositoryName, "/commit/" + revision + "/statuses/build"); - String computedKey = this.computeAPIKey(keyEx); - NameValuePair[] data = new NameValuePair[]{ - new NameValuePair("description", comment), - new NameValuePair("key", computedKey), - new NameValuePair("name", this.name), - new NameValuePair("state", state.toString()), - new NameValuePair("url", buildUrl), - }; - logger.log(Level.FINE, "POST state {0} to {1} with key {2} with response {3}", new Object[]{ - state, url, computedKey, post(url, data)} - ); - } - - public void deletePullRequestApproval(String pullRequestId) { - delete(v2("/pullrequests/" + pullRequestId + "/approve")); - } - - public void deletePullRequestComment(String pullRequestId, String commentId) { - delete(v1("/pullrequests/" + pullRequestId + "/comments/" + commentId)); - } - - public void updatePullRequestComment(String pullRequestId, String content, String commentId) { - NameValuePair[] data = new NameValuePair[] { - new NameValuePair("content", content), - }; - put(v1("/pullrequests/" + pullRequestId + "/comments/" + commentId), data); - } - - public Pullrequest.Participant postPullRequestApproval(String pullRequestId) { - try { - return parse(post(v2("/pullrequests/" + pullRequestId + "/approve"), - new NameValuePair[]{}), Pullrequest.Participant.class); - } catch (IOException e) { - logger.log(Level.WARNING, "Invalid pull request approval response.", e); - } - return null; - } - - public Pullrequest.Comment postPullRequestComment(String pullRequestId, String content) { - NameValuePair[] data = new NameValuePair[] { - new NameValuePair("content", content), - }; - try { - return parse(post(v1("/pullrequests/" + pullRequestId + "/comments"), data), new TypeReference<Pullrequest.Comment>() {}); - } catch(Exception e) { - logger.log(Level.WARNING, "Invalid pull request comment response.", e); - } - return null; - } - - private <T> List<T> getAllValues(String rootUrl, int pageLen, Class<T> cls) { - List<T> values = new ArrayList<T>(); - try { - String url = rootUrl + "?pagelen=" + pageLen; - do { - final JavaType type = TypeFactory.defaultInstance().constructParametricType(Pullrequest.Response.class, cls); - Pullrequest.Response<T> response = parse(get(url), type); - values.addAll(response.getValues()); - url = response.getNext(); - } while (url != null); - } catch (Exception e) { - logger.log(Level.WARNING, "invalid response.", e); - } - return values; - } - - private HttpClient getHttpClient() { - return this.factory.getInstanceHttpClient(); - } - - private String v1(String url) { - return V1_API_BASE_URL + this.owner + "/" + this.repositoryName + url; - } - - private String v2(String path) { - return v2(this.owner, this.repositoryName, path); - } - - private String v2(String owner, String repositoryName, String path) { - return V2_API_BASE_URL + owner + "/" + repositoryName + path; - } - - private String get(String path) { - return send(new GetMethod(path)); - } - - private String post(String path, NameValuePair[] data) { - PostMethod req = new PostMethod(path); - req.setRequestBody(data); - req.getParams().setContentCharset("utf-8"); - return send(req); - } - - private void delete(String path) { - send(new DeleteMethod(path)); - } - - private void put(String path, NameValuePair[] data) { - PutMethod req = new PutMethod(path); - req.setRequestBody(EncodingUtil.formUrlEncode(data, "utf-8")); - req.getParams().setContentCharset("utf-8"); - send(req); - } - - private String send(HttpMethodBase req) { - HttpClient client = getHttpClient(); - client.getState().setCredentials(AuthScope.ANY, credentials); - client.getParams().setAuthenticationPreemptive(true); - try { - int statusCode = client.executeMethod(req); - if (statusCode != HttpStatus.SC_OK) { - logger.log(Level.WARNING, "Response status: " + req.getStatusLine()+" URI: "+req.getURI()); - }else{ - return req.getResponseBodyAsString(); - } - } catch (HttpException e) { - logger.log(Level.WARNING, "Failed to send request.", e); - } catch (IOException e) { - logger.log(Level.WARNING, "Failed to send request.", e); - } finally { - req.releaseConnection(); - } - return null; - } - - private <R> R parse(String response, Class<R> cls) throws IOException { - return new ObjectMapper().readValue(response, cls); - } - private <R> R parse(String response, JavaType type) throws IOException { - return new ObjectMapper().readValue(response, type); - } - private <R> R parse(String response, TypeReference<R> ref) throws IOException { - return new ObjectMapper().readValue(response, ref); - } -} diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BuildState.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BuildState.java deleted file mode 100644 index a3ef1f1..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/BuildState.java +++ /dev/null @@ -1,10 +0,0 @@ -package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket; - -/** - * Valid build states for a pull request - * - * @see "https://confluence.atlassian.com/bitbucket/buildstatus-resource-779295267.html" - */ -public enum BuildState { - FAILED, INPROGRESS, SUCCESSFUL -} diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/Pullrequest.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/Pullrequest.java deleted file mode 100644 index b69805e..0000000 --- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/Pullrequest.java +++ /dev/null @@ -1,392 +0,0 @@ -package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket; - -import java.util.List; -import java.util.Comparator; -import java.util.Map; - -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonProperty; - -/** - * POJOs representing the pull-requests extracted from the - * JSON response of the Bitbucket API V2. - * - * @see "https://confluence.atlassian.com/bitbucket/pullrequests-resource-423626332.html#pullrequestsResource-GETaspecificpullrequest" - */ - -@JsonIgnoreProperties(ignoreUnknown = true) -public class Pullrequest { - - private String description; - private Boolean closeSourceBranch; - private String title; - private Revision destination; - private String reason; - private String closedBy; - private Revision source; - private String state; - private String createdOn; - private String updatedOn; - private String mergeCommit; - private String id; - private Author author; - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Response<T> { - private int pageLength; - private List<T> values; - private int page; - private int size; - private String next; - - @JsonProperty("pagelen") - public int getPageLength() { - return pageLength; - } - @JsonProperty("pagelen") - public void setPageLength(int pageLength) { - this.pageLength = pageLength; - } - public List<T> getValues() { - return values; - } - public void setValues(List<T> values) { - this.values = values; - } - public int getPage() { - return page; - } - public void setPage(int page) { - this.page = page; - } - public int getSize() { - return size; - } - public void setSize(int size) { - this.size = size; - } - public String getNext() { - return next; - } - public void setNext(String next) { - this.next = next; - } - } - - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Revision { - private Repository repository; - private Branch branch; - private Commit commit; - - public Repository getRepository() { - return repository; - } - public void setRepository(Repository repository) { - this.repository = repository; - } - public Branch getBranch() { - return branch; - } - public void setBranch(Branch branch) { - this.branch = branch; - } - public Commit getCommit() { - return commit; - } - public void setCommit(Commit commit) { - this.commit = commit; - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Repository { - private String fullName; - private String name; - private String ownerName; - private String repositoryName; - - @JsonProperty("full_name") - public String getFullName() { - return fullName; - } - @JsonProperty("full_name") - public void setFullName(String fullName) { - // Also extract owner- and reponame - if (fullName != null) { - this.ownerName = fullName.split("/")[0]; - this.repositoryName = fullName.split("/")[1]; - } - this.fullName = fullName; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public String getOwnerName() { - return ownerName; - } - public String getRepositoryName() { - return repositoryName; - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Branch { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Commit { - private String hash; - - public String getHash() { - return hash; - } - - public void setHash(String hash) { - this.hash = hash; - } - } - - // Was: Approval - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Participant { - private String role; - private Boolean approved; - - public String getRole() { - return role; - } - public void setRole(String role) { - this.role = role; - } - public Boolean getApproved() { - return approved; - } - public void setApproved(Boolean approved) { - this.approved = approved; - } - } - - // https://confluence.atlassian.com/bitbucket/pullrequests-resource-1-0-296095210.html#pullrequestsResource1.0-POSTanewcomment - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Comment implements Comparable<Comment> { - private Integer id; - private String filename; - private String content; - - @Override - public int compareTo(Comment target) { - if (target == null){ - return -1; - } else if (this.getId() > target.getId()) { - return 1; - } else if (this.getId().equals(target.getId())) { - return 0; - } else { - return -1; - } - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - final Comment comment = (Comment) o; - - return getId() != null ? getId().equals(comment.getId()) : comment.getId() == null; - } - - @Override - public int hashCode() { - return getId() != null ? getId().hashCode() : 0; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getFilename() { - return filename; - } - - public void setFilename(String filename) { - this.filename = filename; - } - - public String getContent() { - return content; - } - - public void setContent(Object content) { - if (content instanceof String) { - this.content = (String)content; - } else if (content instanceof Map){ - this.content = (String)((Map)content).get("raw"); - } - return; - } - - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Author { - private String username; - private String display_name; - public static final String COMBINED_NAME = "%s <@%s>"; - - public String getUsername() { - return username; - } - public void setUsername(String username) { - this.username = username; - } - - @JsonProperty("display_name") - public String getDisplayName() { - return display_name; - } - - @JsonProperty("display_name") - public void setDisplayName(String display_name) { - this.display_name = display_name; - } - public String getCombinedUsername() { - return String.format(COMBINED_NAME, this.getDisplayName(), this.getUsername()); - } - } - - //-------------------- only getters and setters follow ----------------- - - 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 Revision getDestination() { - return destination; - } - - public void setDestination(Revision 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 Revision getSource() { - return source; - } - - public void setSource(Revision 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; - } - - public Author getAuthor() { - return this.author; - } - - public void setAutohor(Author author) { - this.author = author; - } - -} |