Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Validate GitHub scm source #34

Merged
merged 12 commits into from
Aug 25, 2020
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package io.jenkins.plugins.checks.github;

import java.util.Optional;

import org.apache.commons.lang3.StringUtils;

import edu.hm.hafner.util.FilteredLog;
import edu.umd.cs.findbugs.annotations.CheckForNull;

import org.jenkinsci.plugins.github_branch_source.GitHubAppCredentials;
import hudson.model.Job;
import org.apache.commons.lang3.StringUtils;
import org.jenkinsci.plugins.github_branch_source.GitHubAppCredentials;

import io.jenkins.plugins.util.PluginLogger;
import java.util.Optional;

/**
* Base class for a context that publishes GitHub checks.
Expand All @@ -25,19 +22,10 @@ abstract class GitHubChecksContext {
this.scmFacade = scmFacade;
}

/**
* Returns the Jenkins job.
*
* @return job for which the checks will be based on
*/
public Job<?, ?> getJob() {
return job;
}

/**
* Returns the commit sha of the run.
*
* @return the commit sha of the run or null
* @return the commit sha of the run
*/
public abstract String getHeadSha();

Expand All @@ -50,22 +38,26 @@ abstract class GitHubChecksContext {
public abstract String getRepository();

/**
* Returns the credentials to access the remote GitHub repository.
* Returns whether the context is valid (with all properties functional) to use.
*
* @return the credentials or null
* @param logger
* the filtered logger
* @return whether the context is valid to use
*/
public GitHubAppCredentials getCredentials() {
String credentialsId = getCredentialsId();
if (credentialsId == null) {
throw new IllegalStateException("No credentials available for job: " + getJob().getName());
}

return getGitHubAppCredentials(credentialsId);
}
public abstract boolean isValid(FilteredLog logger);

@CheckForNull
protected abstract String getCredentialsId();

/**
* Returns the credentials to access the remote GitHub repository.
*
* @return the credentials
*/
public GitHubAppCredentials getCredentials() {
return getGitHubAppCredentials(StringUtils.defaultIfEmpty(getCredentialsId(), ""));
}

/**
* Returns the URL of the run's summary page, e.g. https://ci.jenkins.io/job/Core/job/jenkins/job/master/2000/.
*
Expand All @@ -75,25 +67,19 @@ public String getURL() {
return url;
}

SCMFacade getScmFacade() {
return scmFacade;
protected Job<?, ?> getJob() {
return job;
}

protected GitHubAppCredentials getGitHubAppCredentials(final String credentialsId) {
Optional<GitHubAppCredentials> foundCredentials = findGitHubAppCredentials(credentialsId);
if (!foundCredentials.isPresent()) {
throw new IllegalStateException("No GitHub APP credentials available for job: " + getJob().getName());
}

return foundCredentials.get();
protected SCMFacade getScmFacade() {
return scmFacade;
}

Optional<GitHubAppCredentials> findGitHubAppCredentials(final String credentialsId) {
return getScmFacade().findGitHubAppCredentials(getJob(), credentialsId);
protected GitHubAppCredentials getGitHubAppCredentials(final String credentialsId) {
return findGitHubAppCredentials(credentialsId).orElseThrow(() ->
new IllegalStateException("No GitHub APP credentials available for job: " + getJob().getName()));
}

abstract boolean isValid(PluginLogger listener);

protected boolean hasGitHubAppCredentials() {
return findGitHubAppCredentials(StringUtils.defaultIfEmpty(getCredentialsId(), "")).isPresent();
}
Expand All @@ -102,20 +88,24 @@ protected boolean hasCredentialsId() {
return StringUtils.isNoneBlank(getCredentialsId());
}

protected boolean hasValidCredentials(final PluginLogger logger) {
protected boolean hasValidCredentials(final FilteredLog logger) {
if (!hasCredentialsId()) {
logger.log("No credentials found");
logger.logError("No credentials found");

return false;
}

if (!hasGitHubAppCredentials()) {
logger.log("No GitHub app credentials found: '%s'", getCredentialsId());
logger.log("See: https://github.com/jenkinsci/github-branch-source-plugin/blob/master/docs/github-app.adoc");
logger.logError("No GitHub app credentials found: '%s'", getCredentialsId());
logger.logError("See: https://github.com/jenkinsci/github-branch-source-plugin/blob/master/docs/github-app.adoc");

return false;
}

return true;
}

private Optional<GitHubAppCredentials> findGitHubAppCredentials(final String credentialsId) {
return getScmFacade().findGitHubAppCredentials(getJob(), credentialsId);
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
package io.jenkins.plugins.checks.github;

import edu.hm.hafner.util.VisibleForTesting;
import io.jenkins.plugins.checks.api.ChecksDetails;
import io.jenkins.plugins.checks.api.ChecksPublisher;
import io.jenkins.plugins.util.PluginLogger;
import org.apache.commons.lang3.StringUtils;
import org.jenkinsci.plugins.github_branch_source.Connector;
import org.jenkinsci.plugins.github_branch_source.GitHubAppCredentials;
import org.kohsuke.github.GHCheckRunBuilder;
import org.kohsuke.github.GitHub;

import java.io.IOException;
import java.time.Instant;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang3.StringUtils;

import edu.hm.hafner.util.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.CheckForNull;

import org.kohsuke.github.GHCheckRunBuilder;
import org.kohsuke.github.GitHub;
import org.jenkinsci.plugins.github_branch_source.Connector;
import org.jenkinsci.plugins.github_branch_source.GitHubAppCredentials;
import hudson.model.TaskListener;

import io.jenkins.plugins.checks.api.ChecksDetails;
import io.jenkins.plugins.checks.api.ChecksPublisher;

/**
* A publisher which publishes GitHub check runs.
*/
Expand All @@ -28,8 +24,7 @@ public class GitHubChecksPublisher extends ChecksPublisher {
private static final Logger LOGGER = Logger.getLogger(GitHubChecksPublisher.class.getName());

private final GitHubChecksContext context;
@CheckForNull
private final TaskListener listener;
private final PluginLogger logger;
private final String gitHubUrl;

/**
Expand All @@ -38,16 +33,15 @@ public class GitHubChecksPublisher extends ChecksPublisher {
* @param context
* a context which contains SCM properties
*/
public GitHubChecksPublisher(final GitHubChecksContext context, @CheckForNull final TaskListener listener) {
this(context, listener, GITHUB_URL);
public GitHubChecksPublisher(final GitHubChecksContext context, final PluginLogger logger) {
this(context, logger, GITHUB_URL);
}

GitHubChecksPublisher(final GitHubChecksContext context, @CheckForNull final TaskListener listener,
final String gitHubUrl) {
GitHubChecksPublisher(final GitHubChecksContext context, final PluginLogger logger, final String gitHubUrl) {
super();

this.context = context;
this.listener = listener;
this.logger = logger;
this.gitHubUrl = gitHubUrl;
}

Expand All @@ -66,17 +60,13 @@ public void publish(final ChecksDetails details) {

GitHubChecksDetails gitHubDetails = new GitHubChecksDetails(details);
createBuilder(gitHub, gitHubDetails).create();
if (listener != null) {
listener.getLogger().printf("GitHub check (name: %s, status: %s) has been published.%n",
gitHubDetails.getName(), gitHubDetails.getStatus());
}
logger.log("GitHub check (name: %s, status: %s) has been published.", gitHubDetails.getName(),
gitHubDetails.getStatus());
}
catch (IllegalStateException | IOException e) {
catch (IOException e) {
String message = "Failed Publishing GitHub checks: ";
LOGGER.log(Level.WARNING, (message + details).replaceAll("[\r\n]", ""), e);
uhafner marked this conversation as resolved.
Show resolved Hide resolved
if (listener != null) {
listener.getLogger().println(message + e);
}
logger.log(message + e);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,87 +1,64 @@
package io.jenkins.plugins.checks.github;

import java.util.Optional;

import org.jenkinsci.plugins.displayurlapi.DisplayURLProvider;

import edu.hm.hafner.util.FilteredLog;
import edu.hm.hafner.util.VisibleForTesting;
import io.jenkins.plugins.checks.api.ChecksPublisher;
import io.jenkins.plugins.checks.api.ChecksPublisherFactory;
import io.jenkins.plugins.util.PluginLogger;

import hudson.Extension;
import hudson.model.Job;
import hudson.model.Run;
import hudson.model.TaskListener;
import io.jenkins.plugins.checks.api.ChecksPublisher;
import io.jenkins.plugins.checks.api.ChecksPublisherFactory;
import io.jenkins.plugins.util.PluginLogger;
import org.jenkinsci.plugins.displayurlapi.DisplayURLProvider;

import java.util.Optional;

/**
* An factory which produces {@link GitHubChecksPublisher}.
*/
@Extension
public class GitHubChecksPublisherFactory extends ChecksPublisherFactory {
private final SCMFacade scmFacade;
private final DisplayURLProvider urlProvider;

/**
* Creates a new instance of {@link GitHubChecksPublisherFactory}.
*/
public GitHubChecksPublisherFactory() {
this(new SCMFacade());
this(new SCMFacade(), DisplayURLProvider.get());
}

@VisibleForTesting
GitHubChecksPublisherFactory(final SCMFacade scmFacade) {
GitHubChecksPublisherFactory(final SCMFacade scmFacade, final DisplayURLProvider urlProvider) {
super();

this.scmFacade = scmFacade;
this.urlProvider = urlProvider;
}

@Override
protected Optional<ChecksPublisher> createPublisher(final Run<?, ?> run, final TaskListener listener) {
return createPublisher(run, DisplayURLProvider.get().getRunURL(run), listener);
}

@VisibleForTesting
Optional<ChecksPublisher> createPublisher(final Run<?, ?> run, final String runURL, final TaskListener listener) {
PluginLogger logger = createLogger(getListener(listener));

GitSCMChecksContext gitSCMContext = new GitSCMChecksContext(run, runURL);
if (gitSCMContext.isValid(logger)) {
return Optional.of(new GitHubChecksPublisher(gitSCMContext, getListener(listener)));
}

return createPublisher(listener, logger, new GitHubSCMSourceChecksContext(run, runURL, scmFacade));
final String runURL = urlProvider.getRunURL(run);
return createPublisher(listener, new GitSCMChecksContext(run, runURL, scmFacade),
new GitHubSCMSourceChecksContext(run, runURL, scmFacade));
}

@Override
protected Optional<ChecksPublisher> createPublisher(final Job<?, ?> job, final TaskListener listener) {
return createPublisher(job, DisplayURLProvider.get().getJobURL(job), listener);
}

@VisibleForTesting
Optional<ChecksPublisher> createPublisher(final Job<?, ?> job, final String jobURL, final TaskListener listener) {
PluginLogger logger = createLogger(getListener(listener));

return createPublisher(listener, logger, new GitHubSCMSourceChecksContext(job, jobURL, scmFacade));
return createPublisher(listener, new GitHubSCMSourceChecksContext(job, urlProvider.getJobURL(job), scmFacade));
}

private Optional<ChecksPublisher> createPublisher(final TaskListener listener, final PluginLogger logger,
final GitHubChecksContext gitHubSCMSourceContext) {
if (gitHubSCMSourceContext.isValid(logger)) {
return Optional.of(new GitHubChecksPublisher(gitHubSCMSourceContext, getListener(listener)));
}
return Optional.empty();
}
private Optional<ChecksPublisher> createPublisher(final TaskListener listener, final GitHubChecksContext... contexts) {
FilteredLog causeLogger = new FilteredLog("Causes for no suitable publisher found: ");
PluginLogger consoleLogger = new PluginLogger(listener.getLogger(), "GitHub Checks");


private TaskListener getListener(final TaskListener taskListener) {
// FIXME: checks-API should use a Null listener
if (taskListener == null) {
return TaskListener.NULL;
for (GitHubChecksContext ctx : contexts) {
if (ctx.isValid(causeLogger)) {
return Optional.of(new GitHubChecksPublisher(ctx, consoleLogger));
}
}
return taskListener;
}

private PluginLogger createLogger(final TaskListener listener) {
return new PluginLogger(listener.getLogger(), "GitHub Checks");
consoleLogger.logEachLine(causeLogger.getErrorMessages());
return Optional.empty();
}
}
Loading