package org.jenkinsci.plugins.ghprb;
import antlr.ANTLRException;
import com.coravy.hudson.plugins.github.GithubProjectProperty;
import com.google.common.annotations.VisibleForTesting;
import hudson.Extension;
import hudson.Util;
import hudson.matrix.MatrixProject;
import hudson.model.*;
import hudson.model.queue.QueueTaskFuture;
import hudson.plugins.git.util.BuildData;
import hudson.triggers.TriggerDescriptor;
import hudson.util.DescribableList;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.ListBoxModel.Option;
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.jenkinsci.plugins.ghprb.extensions.*;
import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildLog;
import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage;
import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildStatus;
import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbPublishJenkinsUrl;
import org.jenkinsci.plugins.ghprb.extensions.status.GhprbSimpleStatus;
import org.kohsuke.github.GHCommitState;
import org.kohsuke.github.GHEventPayload.IssueComment;
import org.kohsuke.github.GHEventPayload.PullRequest;
import org.kohsuke.github.GitHub;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Honza Brázdil jbrazdil@redhat.com
*/
public class GhprbTrigger extends GhprbTriggerBackwardsCompatible {
@Extension
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
private static final Logger logger = Logger.getLogger(GhprbTrigger.class.getName());
private final String adminlist;
private final Boolean allowMembersOfWhitelistedOrgsAsAdmin;
private final String orgslist;
private final String cron;
private final String buildDescTemplate;
private final Boolean onlyTriggerPhrase;
private final Boolean useGitHubHooks;
private final Boolean permitAll;
private String whitelist;
private Boolean autoCloseFailedPullRequests;
private Boolean displayBuildErrorsOnDownstreamBuilds;
private List<GhprbBranch> whiteListTargetBranches;
private List<GhprbBranch> blackListTargetBranches;
private String gitHubAuthId;
private String triggerPhrase;
private String skipBuildPhrase;
private String blackListLabels;
private String whiteListLabels;
private transient Ghprb helper;
private transient GhprbRepository repository;
private transient GhprbBuilds builds;
private transient GhprbGitHub ghprbGitHub;
private DescribableList<GhprbExtension, GhprbExtensionDescriptor> extensions = new DescribableList<GhprbExtension, GhprbExtensionDescriptor>(Saveable.NOOP);
public DescribableList<GhprbExtension, GhprbExtensionDescriptor> getExtensions() {
if (extensions == null) {
extensions = new DescribableList<GhprbExtension, GhprbExtensionDescriptor>(Saveable.NOOP,Util.fixNull(extensions));
extensions.add(new GhprbSimpleStatus());
}
return extensions;
}
private void setExtensions(List<GhprbExtension> extensions) {
DescribableList<GhprbExtension, GhprbExtensionDescriptor> rawList = new DescribableList<GhprbExtension, GhprbExtensionDescriptor>(
Saveable.NOOP,Util.fixNull(extensions));
// Filter out items that we only want one of, like the status updater.
this.extensions = Ghprb.onlyOneEntry(rawList,
GhprbCommitStatus.class
);
// Make sure we have at least one of the types we need one of.
for (GhprbExtension ext : getDescriptor().getExtensions()) {
if (ext instanceof GhprbGlobalDefault) {
Ghprb.addIfMissing(this.extensions, Ghprb.getGlobal(ext.getClass()), ext.getClass());
}
}
}
@DataBoundConstructor
public GhprbTrigger(String adminlist,
String whitelist,
String orgslist,
String cron,
String triggerPhrase,
Boolean onlyTriggerPhrase,
Boolean useGitHubHooks,
Boolean permitAll,
Boolean autoCloseFailedPullRequests,
Boolean displayBuildErrorsOnDownstreamBuilds,
String commentFilePath,
String skipBuildPhrase,
List<GhprbBranch> whiteListTargetBranches,
List<GhprbBranch> blackListTargetBranches,
Boolean allowMembersOfWhitelistedOrgsAsAdmin,
String msgSuccess,
String msgFailure,
String commitStatusContext,
String gitHubAuthId,
String buildDescTemplate,
String blackListLabels,
String whiteListLabels,
List<GhprbExtension> extensions
) throws ANTLRException {
super(cron);
this.adminlist = adminlist;
this.whitelist = whitelist;
this.orgslist = orgslist;
this.cron = cron;
this.triggerPhrase = triggerPhrase;
this.onlyTriggerPhrase = onlyTriggerPhrase;
this.useGitHubHooks = useGitHubHooks;
this.permitAll = permitAll;
this.autoCloseFailedPullRequests = autoCloseFailedPullRequests;
this.displayBuildErrorsOnDownstreamBuilds = displayBuildErrorsOnDownstreamBuilds;
this.skipBuildPhrase = skipBuildPhrase;
this.whiteListTargetBranches = whiteListTargetBranches;
this.blackListTargetBranches = blackListTargetBranches;
this.gitHubAuthId = gitHubAuthId;
this.allowMembersOfWhitelistedOrgsAsAdmin = allowMembersOfWhitelistedOrgsAsAdmin;
this.buildDescTemplate = buildDescTemplate;
this.blackListLabels = blackListLabels;
this.whiteListLabels = whiteListLabels;
setExtensions(extensions);
configVersion = latestVersion;
}
@Override
public Object readResolve() {
convertPropertiesToExtensions();
checkGitHubApiAuth();
return this;
}
@SuppressWarnings("deprecation")
private void checkGitHubApiAuth() {
if (gitHubApiAuth != null) {
gitHubAuthId = gitHubApiAuth.getId();
gitHubApiAuth = null;
}
}
public static DescriptorImpl getDscp() {
return DESCRIPTOR;
}
@SuppressWarnings("deprecation")
private void initState() throws IOException {
final GithubProjectProperty ghpp = super.job.getProperty(GithubProjectProperty.class);
if (ghpp == null || ghpp.getProjectUrl() == null) {
throw new IllegalStateException("A GitHub project url is required.");
}
String baseUrl = ghpp.getProjectUrl().baseUrl();
Matcher m = Ghprb.githubUserRepoPattern.matcher(baseUrl);
if (!m.matches()) {
throw new IllegalStateException(String.format("Invalid GitHub project url: %s", baseUrl));
}
final String reponame = m.group(2);
this.repository = new GhprbRepository(reponame, this);
this.repository.load();
Map<Integer, GhprbPullRequest> pulls = this.pullRequests;
this.pullRequests = null;
try {
Map<Integer, GhprbPullRequest> prs = getDescriptor().getPullRequests(super.job.getFullName());
if (prs != null) {
prs = new ConcurrentHashMap<Integer, GhprbPullRequest>(prs);
if (pulls == null) {
pulls = prs;
} else {
pulls.putAll(prs);
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Unable to transfer map of pull requests", e);
}
if (pulls != null) {
this.repository.addPullRequests(pulls);
this.repository.save();
}
this.builds = new GhprbBuilds(this, repository);
this.repository.init();
this.ghprbGitHub = new GhprbGitHub(this);
}
@Override
public void start(Job<?, ?> project, boolean newInstance) {
// We should always start the trigger, and handle cases where we don't run in the run function.
super.start(project, newInstance);
String name = project.getFullName();
if (!project.isBuildable()) {
logger.log(Level.FINE, "Project is disabled, not starting trigger for job " + name);
return;
}
if (project.getProperty(GithubProjectProperty.class) == null) {
logger.log(Level.INFO, "GitHub project property is missing the URL, cannot start ghprb trigger for job " + name);
return;
}
try {
initState();
} catch (Exception ex) {
logger.log(Level.SEVERE, "Can't start ghprb trigger", ex);
return;
}
logger.log(Level.INFO, "Starting the ghprb trigger for the {0} job; newInstance is {1}",
new String[] { name, String.valueOf(newInstance) });
helper = new Ghprb(this);
if (getUseGitHubHooks()) {
if (GhprbTrigger.getDscp().getManageWebhooks()) {
this.repository.createHook();
}
DESCRIPTOR.addRepoTrigger(getRepository().getName(), super.job);
}
}
@Override
public void stop() {
String name = super.job != null ? super.job.getFullName(): "NOT STARTED";
logger.log(Level.INFO, "Stopping the ghprb trigger for project {0}", name);
if (this.repository != null) {
String repo = this.repository.getName();
if (!StringUtils.isEmpty(repo)) {
DESCRIPTOR.removeRepoTrigger(repo, super.job);
}
}
super.stop();
}
@Override
public void run() {
// triggers are always triggered on the cron, but we just no-op if we are using GitHub hooks.
if (getUseGitHubHooks()) {
logger.log(Level.FINE, "Use webHooks is set, so not running trigger");
return;
}
if (!isActive()) {
return;
}
logger.log(Level.FINE, "Running trigger for {0}", super.job.getFullName());
this.repository.check();
}
public QueueTaskFuture<?> scheduleBuild(GhprbCause cause, GhprbRepository repo) {
try {
for (GhprbExtension ext : Ghprb.getJobExtensions(this, GhprbBuildStep.class)) {
if (ext instanceof GhprbBuildStep) {
((GhprbBuildStep) ext).onScheduleBuild(super.job, cause);
}
}
} catch(Exception e) {
logger.log(Level.SEVERE, "Unable to execute extentions for scheduleBuild", e);
}
ArrayList<ParameterValue> values = getDefaultParameters();
final String commitSha = cause.isMerged() ? "origin/pr/" + cause.getPullID() + "/merge" : cause.getCommit();
values.add(new StringParameterValue("sha1", commitSha));
values.add(new StringParameterValue("ghprbActualCommit", cause.getCommit()));
String triggerAuthor = "";
String triggerAuthorEmail = "";
String triggerAuthorLogin = "";
GhprbPullRequest pr = getRepository().getPullRequest(cause.getPullID());
String lastBuildId = pr.getLastBuildId();
BuildData buildData = null;
if (!(job instanceof MatrixProject) && !StringUtils.isEmpty(lastBuildId)) {
Run<?, ?> lastBuild = job.getBuild(lastBuildId);
if (lastBuild != null) {
buildData = lastBuild.getAction(BuildData.class);
}
}
try {
triggerAuthor = getString(cause.getTriggerSender().getName(), "");
} catch (Exception e) {}
try {
triggerAuthorEmail = getString(cause.getTriggerSender().getEmail(), "");
} catch (Exception e) {}
try {
triggerAuthorLogin = getString(cause.getTriggerSender().getLogin(), "");
} catch (Exception e) {}
setCommitAuthor(cause, values);
values.add(new StringParameterValue("ghprbAuthorRepoGitUrl", getString(cause.getAuthorRepoGitUrl(), "")));
values.add(new StringParameterValue("ghprbTriggerAuthor", triggerAuthor));
values.add(new StringParameterValue("ghprbTriggerAuthorEmail", triggerAuthorEmail));
values.add(new StringParameterValue("ghprbTriggerAuthorLogin", triggerAuthorLogin));
values.add(new StringParameterValue("ghprbTriggerAuthorLoginMention", !triggerAuthorLogin.isEmpty() ? "@"
+ triggerAuthorLogin : ""));
final StringParameterValue pullIdPv = new StringParameterValue("ghprbPullId", String.valueOf(cause.getPullID()));
values.add(pullIdPv);
values.add(new StringParameterValue("ghprbTargetBranch", String.valueOf(cause.getTargetBranch())));
values.add(new StringParameterValue("ghprbSourceBranch", String.valueOf(cause.getSourceBranch())));
values.add(new StringParameterValue("GIT_BRANCH", String.valueOf(cause.getSourceBranch())));
// it's possible the GHUser doesn't have an associated email address
values.add(new StringParameterValue("ghprbPullAuthorEmail", getString(cause.getAuthorEmail(), "")));
values.add(new StringParameterValue("ghprbPullAuthorLogin", String.valueOf(cause.getPullRequestAuthor().getLogin())));
values.add(new StringParameterValue("ghprbPullAuthorLoginMention", "@" + cause.getPullRequestAuthor().getLogin()));
values.add(new StringParameterValue("ghprbPullDescription", escapeText(String.valueOf(cause.getShortDescription()))));
values.add(new StringParameterValue("ghprbPullTitle", escapeText(String.valueOf(cause.getTitle()))));
values.add(new StringParameterValue("ghprbPullLink", String.valueOf(cause.getUrl())));
values.add(new StringParameterValue("ghprbPullLongDescription", escapeText(String.valueOf(cause.getDescription()))));
values.add(new StringParameterValue("ghprbCommentBody", escapeText(String.valueOf(cause.getCommentBody()))));
values.add(new StringParameterValue("ghprbGhRepository", getString(cause.getRepositoryName(), "")));
values.add(new StringParameterValue("ghprbCredentialsId", getString(cause.getCredentialsId(), "")));
ParameterizedJobMixIn scheduledJob = new ParameterizedJobMixIn() {
@Override
protected Job asJob() {
return job;
}
};
// add the previous pr BuildData as an action so that the correct change log is generated by the GitSCM plugin
// note that this will be removed from the Actions list after the job is completed so that the old (and incorrect)
// one isn't there
return scheduledJob.scheduleBuild2(Jenkins.getInstance().getQuietPeriod(),new CauseAction(cause),new GhprbParametersAction(values), buildData);
}
private void setCommitAuthor(GhprbCause cause, ArrayList<ParameterValue> values) {
String authorName = "";
String authorEmail = "";
if (cause.getCommitAuthor() != null) {
authorName = getString(cause.getCommitAuthor().getName(), "");
authorEmail = getString(cause.getCommitAuthor().getEmail(), "");
}
values.add(new StringParameterValue("ghprbActualCommitAuthor", authorName));
values.add(new StringParameterValue("ghprbActualCommitAuthorEmail", authorEmail));
}
private String escapeText(String text) {
return text.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\\\"");
}
private ArrayList<ParameterValue> getDefaultParameters() {
ArrayList<ParameterValue> values = new ArrayList<ParameterValue>();
ParametersDefinitionProperty pdp = this.job.getProperty(ParametersDefinitionProperty.class);
if (pdp != null) {
for (ParameterDefinition pd : pdp.getParameterDefinitions()) {
values.add(pd.getDefaultParameterValue());
}
}
return values;
}
private String getString(String actual, String d) {
return actual == null ? d : actual;
}
public String getGitHubAuthId() {
return gitHubAuthId == null ? "" : gitHubAuthId;
}
public GhprbGitHubAuth getGitHubApiAuth() {
if (gitHubAuthId == null) {
for (GhprbGitHubAuth auth: getDescriptor().getGithubAuth()){
gitHubAuthId = auth.getId();
getDescriptor().save();
return auth;
}
}
return getDescriptor().getGitHubAuth(gitHubAuthId);
}
public GitHub getGitHub() throws IOException {
GhprbGitHubAuth auth = getGitHubApiAuth();
return auth.getConnection(getActualProject());
}
public Job<?, ?> getActualProject() {
return super.job;
}
public void addWhitelist(String author) {
whitelist = whitelist + " " + author;
try {
this.job.save();
} catch (IOException ex) {
logger.log(Level.SEVERE, "Failed to save new whitelist", ex);
}
}
public String getBuildDescTemplate() {
return buildDescTemplate == null ? "" : buildDescTemplate;
}
public String getAdminlist() {
if (adminlist == null) {
return "";
}
return adminlist;
}
public Boolean getAllowMembersOfWhitelistedOrgsAsAdmin() {
return allowMembersOfWhitelistedOrgsAsAdmin != null && allowMembersOfWhitelistedOrgsAsAdmin;
}
public String getWhitelist() {
if (whitelist == null) {
return "";
}
return whitelist;
}
public String getOrgslist() {
if (orgslist == null) {
return "";
}
return orgslist;
}
public String getCron() {
return cron;
}
public String getTriggerPhrase() {
if (triggerPhrase == null) {
return "";
}
return triggerPhrase;
}
public String getSkipBuildPhrase() {
if (StringUtils.isEmpty(skipBuildPhrase)) {
// if it's empty grab the global value
return getDescriptor().getSkipBuildPhrase();
}
return skipBuildPhrase;
}
public String getBlackListLabels() {
if (blackListLabels == null) {
return "";
}
return blackListLabels;
}
public String getWhiteListLabels() {
if (whiteListLabels == null) {
return "";
}
return whiteListLabels;
}
public Boolean getOnlyTriggerPhrase() {
return onlyTriggerPhrase != null && onlyTriggerPhrase;
}
public Boolean getUseGitHubHooks() {
return useGitHubHooks != null && useGitHubHooks;
}
public Ghprb getHelper() {
if (helper == null) {
helper = new Ghprb(this);
}
return helper;
}
public Boolean getPermitAll() {
return permitAll != null && permitAll;
}
public Boolean getAutoCloseFailedPullRequests() {
if (autoCloseFailedPullRequests == null) {
Boolean autoClose = getDescriptor().getAutoCloseFailedPullRequests();
return (autoClose != null && autoClose);
}
return autoCloseFailedPullRequests;
}
public Boolean getDisplayBuildErrorsOnDownstreamBuilds() {
if (displayBuildErrorsOnDownstreamBuilds == null) {
Boolean displayErrors = getDescriptor().getDisplayBuildErrorsOnDownstreamBuilds();
return (displayErrors != null && displayErrors);
}
return displayBuildErrorsOnDownstreamBuilds;
}
private List<GhprbBranch> normalizeTargetBranches(List<GhprbBranch> branches) {
if (branches == null ||
(branches.size() == 1 && branches.get(0).getBranch().equals(""))) {
return new ArrayList<GhprbBranch>();
} else {
return branches;
}
}
public List<GhprbBranch> getWhiteListTargetBranches() {
return normalizeTargetBranches(whiteListTargetBranches);
}
public List<GhprbBranch> getBlackListTargetBranches() {
return normalizeTargetBranches(blackListTargetBranches);
}
@Override
public DescriptorImpl getDescriptor() {
return DESCRIPTOR;
}
@VisibleForTesting
void setHelper(Ghprb helper) {
this.helper = helper;
}
public GhprbBuilds getBuilds() {
if (this.builds == null && this.isActive()) {
this.builds = new GhprbBuilds(this, getRepository());
}
return this.builds;
}
public GhprbGitHub getGhprbGitHub() {
if (this.ghprbGitHub == null && this.isActive()) {
this.ghprbGitHub = new GhprbGitHub(this);
}
return this.ghprbGitHub;
}
public boolean isActive() {
String name = super.job != null ? super.job.getFullName() : "NOT STARTED";
boolean isActive = true;
if (super.job == null) {
logger.log(Level.FINE, "Project was never set, start was never run");
isActive = false;
} else if (!super.job.isBuildable()) {
logger.log(Level.FINE, "Project is disabled, ignoring trigger run call for job {0}", name);
isActive = false;
} else if (getRepository() == null) {
logger.log(Level.SEVERE, "The ghprb trigger for {0} wasn''t properly started - repository is null", name);
isActive = false;
}
return isActive;
}
public GhprbRepository getRepository() {
if (this.repository == null && super.job != null && super.job.isBuildable()) {
try {
this.initState();
} catch (IOException e) {
logger.log(Level.SEVERE, "Unable to init trigger state!", e);
}
}
return this.repository;
}
public String getProjectName() {
String projectName = super.job == null ? "NOT_STARTED" : super.job.getFullName();
return projectName;
}
public boolean matchSignature(String body, String signature) {
if (!isActive()) {
return false;
}
GhprbGitHubAuth auth = getGitHubApiAuth();
return auth == null ? false : auth.checkSignature(body, signature);
}
public void handleComment(IssueComment issueComment) throws IOException {
GhprbRepository repo = getRepository();
logger.log(Level.INFO, "Checking comment on PR #{0} for job {1}", new Object[] {issueComment.getIssue().getNumber(), getProjectName()});
repo.onIssueCommentHook(issueComment);
}
public void handlePR(PullRequest pr) throws IOException {
GhprbRepository repo = getRepository();
logger.log(Level.INFO, "Checking PR #{0} for job {1}", new Object[] {pr.getNumber(), getProjectName()});
repo.onPullRequestHook(pr);
}
public static final class DescriptorImpl extends TriggerDescriptor {
// GitHub username may only contain alphanumeric characters or dashes and cannot begin with a dash
private static final Pattern adminlistPattern = Pattern.compile("((\\p{Alnum}[\\p{Alnum}-]*)|\\s)*");
private Integer configVersion;
/**
* These settings only really affect testing. When Jenkins calls configure() then the formdata will
* be used to replace all of these fields. Leaving them here is useful for
* testing, but must not be confused with a default. They also should not be used as the default
* value in the global.jelly file as this value is dynamic and will not be
* retained once configure() is called.
*/
private String whitelistPhrase = ".*add\\W+to\\W+whitelist.*";
private String okToTestPhrase = ".*ok\\W+to\\W+test.*";
private String retestPhrase = ".*test\\W+this\\W+please.*";
private String skipBuildPhrase = ".*\\[skip\\W+ci\\].*";
private String cron = "H/5 * * * *";
private Boolean useComments = false;
private Boolean useDetailedComments = false;
private Boolean manageWebhooks = true;
private GHCommitState unstableAs = GHCommitState.FAILURE;
private List<GhprbBranch> whiteListTargetBranches;
private List<GhprbBranch> blackListTargetBranches;
private Boolean autoCloseFailedPullRequests = false;
private Boolean displayBuildErrorsOnDownstreamBuilds = false;
private String blackListLabels;
private String whiteListLabels;
private List<GhprbGitHubAuth> githubAuth;
public GhprbGitHubAuth getGitHubAuth(String gitHubAuthId) {
if (gitHubAuthId == null) {
return getGithubAuth().get(0);
}
GhprbGitHubAuth firstAuth = null;
for (GhprbGitHubAuth auth : getGithubAuth()) {
if (firstAuth == null) {
firstAuth = auth;
}
if (auth.getId().equals(gitHubAuthId)) {
return auth;
}
}
return firstAuth;
}
public List<GhprbGitHubAuth> getGithubAuth() {
if (githubAuth == null || githubAuth.size() == 0) {
githubAuth = new ArrayList<GhprbGitHubAuth>(1);
githubAuth.add(new GhprbGitHubAuth(null, null, null, "Anonymous connection", null, null));
}
return githubAuth;
}
public List<GhprbGitHubAuth> getDefaultAuth(List<GhprbGitHubAuth> githubAuth) {
if (githubAuth != null && githubAuth.size() > 0) {
return githubAuth;
}
return getGithubAuth();
}
private String adminlist;
private String requestForTestingPhrase;
// map of jobs (by their fullName) and their map of pull requests
private transient Map<String, Map<Integer, GhprbPullRequest>> jobs;
/**
* map of jobs (by the repo name); No need to keep the projects from shutdown to startup.
* New triggers will register here, and ones that are stopping will remove themselves.
*/
private transient Map<String, Set<Job<?, ?>>> repoJobs;
public List<GhprbExtensionDescriptor> getExtensionDescriptors() {
return GhprbExtensionDescriptor.allProject();
}
public List<GhprbExtensionDescriptor> getGlobalExtensionDescriptors() {
return GhprbExtensionDescriptor.allGlobal();
}
private DescribableList<GhprbExtension, GhprbExtensionDescriptor> extensions;
public DescribableList<GhprbExtension, GhprbExtensionDescriptor> getExtensions() {
if (extensions == null) {
extensions = new DescribableList<GhprbExtension, GhprbExtensionDescriptor>(Saveable.NOOP);
}
return extensions;
}
public DescriptorImpl() {
load();
readBackFromLegacy();
if (repoJobs == null) {
repoJobs = new ConcurrentHashMap<String, Set<Job<?, ?>>>();
}
saveAfterPause();
}
private void saveAfterPause() {
new java.util.Timer().schedule(
new java.util.TimerTask() {
@Override
public void run() {
save();
}
},
5000
);
}
@Override
public boolean isApplicable(Item item) {
return item instanceof Job && item instanceof ParameterizedJobMixIn.ParameterizedJob;
}
@Override
public String getDisplayName() {
return "GitHub Pull Request Builder";
}
@Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
adminlist = formData.getString("adminlist");
requestForTestingPhrase = formData.getString("requestForTestingPhrase");
whitelistPhrase = formData.getString("whitelistPhrase");
okToTestPhrase = formData.getString("okToTestPhrase");
retestPhrase = formData.getString("retestPhrase");
skipBuildPhrase = formData.getString("skipBuildPhrase");
cron = formData.getString("cron");
useComments = formData.getBoolean("useComments");
useDetailedComments = formData.getBoolean("useDetailedComments");
manageWebhooks = formData.getBoolean("manageWebhooks");
unstableAs = GHCommitState.valueOf(formData.getString("unstableAs"));
autoCloseFailedPullRequests = formData.getBoolean("autoCloseFailedPullRequests");
displayBuildErrorsOnDownstreamBuilds = formData.getBoolean("displayBuildErrorsOnDownstreamBuilds");
blackListLabels = formData.getString("blackListLabels");
whiteListLabels = formData.getString("whiteListLabels");
githubAuth = req.bindJSONToList(GhprbGitHubAuth.class, formData.get("githubAuth"));
extensions = new DescribableList<GhprbExtension, GhprbExtensionDescriptor>(Saveable.NOOP);
try {
extensions.rebuildHetero(req, formData, getGlobalExtensionDescriptors(), "extensions");
// Now make sure we have at least one of the types we need one of.
Ghprb.addIfMissing(this.extensions, new GhprbSimpleStatus(), GhprbCommitStatus.class);
} catch (IOException e) {
e.printStackTrace();
}
readBackFromLegacy();
saveAfterPause();
return super.configure(req, formData);
}
public FormValidation doCheckAdminlist(@QueryParameter String value) throws ServletException {
if (!adminlistPattern.matcher(value).matches()) {
return FormValidation.error("GitHub username may only contain alphanumeric characters or dashes "
+ "and cannot begin with a dash. Separate them with whitespaces.");
}
return FormValidation.ok();
}
public ListBoxModel doFillUnstableAsItems() {
ListBoxModel items = new ListBoxModel();
GHCommitState[] results = new GHCommitState[] {GHCommitState.SUCCESS,GHCommitState.ERROR,GHCommitState.FAILURE};
for (GHCommitState nextResult : results) {
String text = StringUtils.capitalize(nextResult.toString().toLowerCase());
items.add(text, nextResult.toString());
if (unstableAs.toString().equals(nextResult.toString())) {
items.get(items.size()-1).selected = true;
}
}
return items;
}
public String getAdminlist() {
return adminlist;
}
public String getRequestForTestingPhrase() {
return requestForTestingPhrase;
}
public String getWhitelistPhrase() {
return whitelistPhrase;
}
public String getOkToTestPhrase() {
return okToTestPhrase;
}
public String getRetestPhrase() {
return retestPhrase;
}
public String getSkipBuildPhrase() {
return skipBuildPhrase;
}
public String getCron() {
return cron;
}
public Boolean getUseComments() {
return useComments;
}
public Boolean getUseDetailedComments() {
return useDetailedComments;
}
public String getBlackListLabels() {
return blackListLabels;
}
public String getWhiteListLabels() {
return whiteListLabels;
}
public Boolean getManageWebhooks() {
return manageWebhooks;
}
public Boolean getAutoCloseFailedPullRequests() {
return autoCloseFailedPullRequests;
}
public Boolean getDisplayBuildErrorsOnDownstreamBuilds() {
return displayBuildErrorsOnDownstreamBuilds;
}
public GHCommitState getUnstableAs() {
return unstableAs;
}
public Integer getConfigVersion() {
return configVersion;
}
public boolean isUseComments() {
return (useComments != null && useComments);
}
public boolean isUseDetailedComments() {
return (useDetailedComments != null && useDetailedComments);
}
public ListBoxModel doFillGitHubAuthIdItems(@QueryParameter("gitHubAuthId") String gitHubAuthId) {
ListBoxModel model = new ListBoxModel();
for (GhprbGitHubAuth auth : getGithubAuth()) {
String description = Util.fixNull(auth.getDescription());
int length = description.length();
length = length > 50 ? 50 : length;
Option next = new Option(auth.getServerAPIUrl() + " : " + description.substring(0, length), auth.getId());
if (!StringUtils.isEmpty(gitHubAuthId) && gitHubAuthId.equals(auth.getId())) {
next.selected = true;
}
model.add(next);
}
return model;
}
public Map<Integer, GhprbPullRequest> getPullRequests(String projectName) {
Map<Integer, GhprbPullRequest> ret = null;
if (jobs != null && jobs.containsKey(projectName)) {
ret = jobs.get(projectName);
jobs.remove(projectName);
}
return ret;
}
private void addRepoTrigger(String repo, Job<?, ?> project) {
if (project == null || StringUtils.isEmpty(repo)) {
return;
}
logger.log(Level.FINE, "Adding [{0}] to webhooks repo [{1}]", new Object[]{project.getFullName(), repo});
synchronized (this) {
Set<Job<?, ?>> projects = repoJobs.get(repo);
if (projects == null) {
logger.log(Level.FINE, "No other projects found, creating new repo set");
projects = Collections.newSetFromMap(new WeakHashMap<Job<?, ?>, Boolean>());
repoJobs.put(repo, projects);
} else {
logger.log(Level.FINE, "Adding project to current repo set, length: {0}", new Object[]{projects.size()});
}
projects.add(project);
}
}
private void removeRepoTrigger(String repo, Job<?, ?> project) {
Set<Job<?, ?>> projects = repoJobs.get(repo);
if (project != null && projects != null) {
logger.log(Level.FINE, "Removing [{0}] from webhooks repo [{1}]", new Object[]{repo, project.getFullName()});
projects.remove(project);
}
}
public Set<Job<?, ?>> getRepoTriggers(String repo) {
if (repoJobs == null) {
repoJobs = new ConcurrentHashMap<String, Set<Job<?, ?>>>(5);
}
logger.log(Level.FINE, "Retrieving triggers for repo [{0}]", new Object[]{repo});
Set<Job<?, ?>> projects = repoJobs.get(repo);
if (projects != null) {
for (Job<?, ?> project : projects) {
logger.log(Level.FINE, "Found project [{0}] for webhook repo [{1}]", new Object[]{project.getFullName(), repo});
}
} else {
projects = Collections.newSetFromMap(new WeakHashMap<Job<?, ?>, Boolean>(0));
}
return projects;
}
public List<GhprbBranch> getWhiteListTargetBranches() {
return whiteListTargetBranches;
}
public List<GhprbBranch> getBlackListTargetBranches() {
return blackListTargetBranches;
}
@Deprecated
private transient String publishedURL;
@Deprecated
private transient Integer logExcerptLines;
@Deprecated
private transient String msgSuccess;
@Deprecated
private transient String msgFailure;
@Deprecated
private transient String commitStatusContext;
@Deprecated
private transient String accessToken;
@Deprecated
private transient String username;
@Deprecated
private transient String password;
@Deprecated
private transient String serverAPIUrl;
public void readBackFromLegacy() {
if (configVersion == null) {
configVersion = 0;
}
if (logExcerptLines != null && logExcerptLines > 0) {
addIfMissing(new GhprbBuildLog(logExcerptLines));
logExcerptLines = null;
}
if (!StringUtils.isEmpty(publishedURL)) {
addIfMissing(new GhprbPublishJenkinsUrl(publishedURL));
publishedURL = null;
}
if (!StringUtils.isEmpty(msgFailure) || !StringUtils.isEmpty(msgSuccess)) {
List<GhprbBuildResultMessage> messages = new ArrayList<GhprbBuildResultMessage>(2);
if (!StringUtils.isEmpty(msgFailure)) {
messages.add(new GhprbBuildResultMessage(GHCommitState.FAILURE, msgFailure));
msgFailure = null;
}
if (!StringUtils.isEmpty(msgSuccess)) {
messages.add(new GhprbBuildResultMessage(GHCommitState.SUCCESS, msgSuccess));
msgSuccess = null;
}
addIfMissing(new GhprbBuildStatus(messages));
}
if (configVersion < 1) {
GhprbSimpleStatus status = new GhprbSimpleStatus(commitStatusContext);
addIfMissing(status);
commitStatusContext = null;
}
if (!StringUtils.isEmpty(accessToken)) {
try {
GhprbGitHubAuth auth = new GhprbGitHubAuth(serverAPIUrl, null, Ghprb.createCredentials(serverAPIUrl, accessToken), "Pre credentials Token", null, null);
if (githubAuth == null) {
githubAuth = new ArrayList<GhprbGitHubAuth>(1);
}
githubAuth.add(auth);
accessToken = null;
serverAPIUrl = null;
} catch (Exception e) {
e.printStackTrace();
}
}
if (!StringUtils.isEmpty(username) || !StringUtils.isEmpty(password)) {
try {
GhprbGitHubAuth auth = new GhprbGitHubAuth(serverAPIUrl, null, Ghprb.createCredentials(serverAPIUrl, username, password), "Pre credentials username and password", null, null);
if (githubAuth == null) {
githubAuth = new ArrayList<GhprbGitHubAuth>(1);
}
githubAuth.add(auth);
username = null;
password = null;
serverAPIUrl = null;
} catch (Exception e) {
e.printStackTrace();
}
}
configVersion = 1;
}
private void addIfMissing(GhprbExtension ext) {
if (getExtensions().get(ext.getClass()) == null) {
getExtensions().add(ext);
}
}
}
}