Skip to content
Permalink
Browse files
Merge pull request #6 from recena/CJP-2436
[JENKINS-31445] Include support for GitHub Enterprise
  • Loading branch information
recena committed Nov 9, 2015
2 parents 281dcdd + d9a1e38 commit 7c728a771ce349eaa35490393564977f01b6fc78
Showing 7 changed files with 75 additions and 35 deletions.
@@ -29,7 +29,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>0.3-beta-1</version>
<version>0.3-beta-2</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
@@ -40,8 +40,12 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.net.ssl.SSLHandshakeException;

import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadObserver;
@@ -273,6 +277,8 @@ protected SCMRevision retrieve(SCMHead head, TaskListener listener) throws IOExc

public static abstract class AbstractGitHubSCMSourceDescriptor extends SCMSourceDescriptor {

private static final Logger LOGGER = Logger.getLogger(AbstractGitHubSCMSourceDescriptor.class.getName());

public static final String defaultIncludes = "*";
public static final String defaultExcludes = "";
public static final String ANONYMOUS = "ANONYMOUS";
@@ -321,12 +327,14 @@ public ListBoxModel doFillRepositoryItems(@AncestorInPath SCMSourceOwner context
}
try {
GitHub github = Connector.connect(apiUri, Connector.lookupScanCredentials(context, apiUri, scanCredentialsId));

GHMyself myself = null;
try {
myself = github.getMyself();
} catch (IllegalStateException e) {
// may be anonymous... ok to ignore
LOGGER.log(Level.WARNING, e.getMessage());
} catch (IOException e) {
LOGGER.log(Level.WARNING, e.getMessage());
// may be anonymous or bad credentials (unauthorized)
// TODO: Manage this exception.
}
@@ -336,27 +344,40 @@ public ListBoxModel doFillRepositoryItems(@AncestorInPath SCMSourceOwner context
}
return result;
}

GHOrganization org = null;
try {
org = github.getOrganization(repoOwner);
} catch (FileNotFoundException fnf) {
LOGGER.log(Level.FINE, "There is not any GH Organization named " + repoOwner);
} catch (IOException e) {
LOGGER.log(Level.WARNING, e.getMessage());
}
if (org != null && repoOwner.equals(org.getLogin())) {
for (GHRepository repo : org.listRepositories()) {
result.add(repo.getName());
}
return result;
}

GHUser user = null;
try {
user = github.getUser(repoOwner);
} catch (FileNotFoundException fnf) {
LOGGER.log(Level.FINE, "There is not any GH User named " + repoOwner);
} catch (IOException e) {
// may be organization... ok to ignore
LOGGER.log(Level.WARNING, e.getMessage());
}
if (user != null && repoOwner.equals(user.getLogin())) {
for (String name : user.getRepositories().keySet()) {
result.add(name);
for (GHRepository repo : user.listRepositories()) {
result.add(repo.getName());
}
return result;
}
GHOrganization org = github.getOrganization(repoOwner);
if (org != null && repoOwner.equals(org.getLogin())) {
// TODO this is not what you want; use listRepositories(int) instead
for (String name : org.getRepositories().keySet()) {
result.add(name);
}
}
} catch (SSLHandshakeException he) {
LOGGER.log(Level.SEVERE, he.getMessage());
} catch (IOException e) {
// ignore
LOGGER.log(Level.SEVERE, e.getMessage());
}
return result;
}
@@ -58,12 +58,14 @@ public class GitHubSCMNavigator extends SCMNavigator {
private final String repoOwner;
private final String scanCredentialsId;
private final String checkoutCredentialsId;
private final String apiUri;
private String pattern = ".*";

@DataBoundConstructor public GitHubSCMNavigator(String repoOwner, String scanCredentialsId, String checkoutCredentialsId) {
@DataBoundConstructor public GitHubSCMNavigator(String apiUri, String repoOwner, String scanCredentialsId, String checkoutCredentialsId) {
this.repoOwner = repoOwner;
this.scanCredentialsId = Util.fixEmpty(scanCredentialsId);
this.checkoutCredentialsId = checkoutCredentialsId;
this.apiUri = apiUri;
}

public String getRepoOwner() {
@@ -84,6 +86,11 @@ public String getPattern() {
return pattern;
}

@CheckForNull
public String getApiUri() {
return apiUri;
}

@DataBoundSetter public void setPattern(String pattern) {
Pattern.compile(pattern);
this.pattern = pattern;
@@ -95,14 +102,13 @@ public String getPattern() {
listener.getLogger().format("Must specify user or organization%n");
return;
}
String apiUrl = null; // TODO GHE
StandardCredentials credentials = Connector.lookupScanCredentials(observer.getContext(), apiUrl, scanCredentialsId);
StandardCredentials credentials = Connector.lookupScanCredentials(observer.getContext(), null /* TODO apiUri */, scanCredentialsId);
if (credentials == null) {
listener.getLogger().println("No scan credentials, skipping");
listener.getLogger().format("No scan credentials, skipping%n");
return;
}
listener.getLogger().format("Connecting to GitHub using %s%n", CredentialsNameProvider.name(credentials));
GitHub github = Connector.connect(apiUrl, credentials);
GitHub github = Connector.connect(apiUri, credentials);
GHMyself myself = null;
try {
myself = github.getMyself();
@@ -155,15 +161,15 @@ private void add(TaskListener listener, SCMSourceObserver observer, GHRepository
}
SCMSourceObserver.ProjectObserver projectObserver = observer.observe(name);
for (GitHubSCMSourceAddition addition : ExtensionList.lookup(GitHubSCMSourceAddition.class)) {
for (SCMSource source : addition.sourcesFor(checkoutCredentialsId, scanCredentialsId, repoOwner, name)) {
for (SCMSource source : addition.sourcesFor(apiUri, checkoutCredentialsId, scanCredentialsId, repoOwner, name)) {
projectObserver.addSource(source);
}
}
projectObserver.complete();
}

public interface GitHubSCMSourceAddition extends ExtensionPoint {
List<? extends SCMSource> sourcesFor(String checkoutCredentialsId, String scanCredentialsId, String repoOwner, String repository);
List<? extends SCMSource> sourcesFor(String apiUri, String checkoutCredentialsId, String scanCredentialsId, String repoOwner, String repository);
}

@Extension public static class DescriptorImpl extends SCMNavigatorDescriptor {
@@ -173,7 +179,7 @@ public interface GitHubSCMSourceAddition extends ExtensionPoint {
}

@Override public SCMNavigator newInstance(String name) {
return new GitHubSCMNavigator(name, "", AbstractGitHubSCMSource.AbstractGitHubSCMSourceDescriptor.SAME);
return new GitHubSCMNavigator("", name, "", AbstractGitHubSCMSource.AbstractGitHubSCMSourceDescriptor.SAME);
}

public FormValidation doCheckScanCredentialsId(@QueryParameter String value) {
@@ -199,6 +205,14 @@ public ListBoxModel doFillCheckoutCredentialsIdItems(@AncestorInPath SCMSourceOw
return result;
}

public ListBoxModel doFillApiUriItems() {
ListBoxModel result = new ListBoxModel();
result.add("Github", "");
for (Endpoint e : GitHubConfiguration.get().getEndpoints()) {
result.add(e.getName() == null ? e.getApiUri() : e.getName(), e.getApiUri());
}
return result;
}
}

}
@@ -31,7 +31,10 @@ public class HttpsRepositoryUriResolver extends RepositoryUriResolver {

@Override
public String getRepositoryUri(String apiUri, String owner, String repository) {
return "https://" + hostnameFromApiUri(apiUri) + "/" + owner + "/" + repository + ".git";
if (apiUri.startsWith("https://")) {
return "https://" + hostnameFromApiUri(apiUri) + "/" + owner + "/" + repository + ".git";
} else {
return "http://" + hostnameFromApiUri(apiUri) + "/" + owner + "/" + repository + ".git";
}
}

}
@@ -103,8 +103,8 @@ protected List<RefSpec> getRefSpecs() {
}

@Extension public static class OriginGitHubSCMSourceAddition implements GitHubSCMNavigator.GitHubSCMSourceAddition {
@Override public List<? extends SCMSource> sourcesFor(String checkoutCredentialsId, String scanCredentialsId, String repoOwner, String repository) {
return Collections.singletonList(new OriginGitHubSCMSource(null, null, checkoutCredentialsId, scanCredentialsId, repoOwner, repository));
@Override public List<? extends SCMSource> sourcesFor(String apiUri, String checkoutCredentialsId, String scanCredentialsId, String repoOwner, String repository) {
return Collections.singletonList(new OriginGitHubSCMSource(null, apiUri, checkoutCredentialsId, scanCredentialsId, repoOwner, repository));
}
}

@@ -26,6 +26,8 @@

import hudson.Util;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@@ -38,17 +40,14 @@ public abstract class RepositoryUriResolver {

public static String hostnameFromApiUri(String apiUri) {
apiUri = Util.fixEmptyAndTrim(apiUri);
if (apiUri == null) {
return "github.com";
try {
URL endpoint = new URL(apiUri);
if (endpoint.getPath().startsWith("/api/v3")) {
return endpoint.getHost();
}
} catch (MalformedURLException e) {
// ignore
}
Pattern enterprise = Pattern.compile("^https?://(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*"
+ "([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])/api/v3/?.*$");
Matcher matcher = enterprise.matcher(apiUri);
if (matcher.matches()) {
return matcher.group(1);
}
// fall back to github
return "github.com";
}

}
@@ -11,6 +11,9 @@
<f:textbox default=".*"/>
</f:entry>
<f:advanced>
<f:entry title="${%API endpoint}" field="apiUri">
<f:select/>
</f:entry>
<f:entry title="${%Checkout credentials}" field="checkoutCredentialsId">
<f:select default="${descriptor.SAME}"/>
</f:entry>

0 comments on commit 7c728a7

Please sign in to comment.