Skip to content

Commit

Permalink
Merge pull request #51 from anenviousguest/master
Browse files Browse the repository at this point in the history
Fix #16: Added support for GitHub Enterprise instances
  • Loading branch information
jutzig committed Nov 18, 2023
2 parents f552c96 + b97ade0 commit ec6da48
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 44 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ Additional Parameters:
* `-Dgithub.commitish=release/1.0.0` allows to specify a commitsh

The plugin is available on Maven central

## Note on the GitHub API endpoints
The endpoint for GitHub API is inferred from `<scm>` connection string. When missing, it by default would use the public endpoint at https://api.github.com.
If you want to upload to a GitHub enterprise instance, then a respective `<scm>` connection string must be specified.
15 changes: 10 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
Expand Down Expand Up @@ -201,7 +201,7 @@
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>github-api</artifactId>
<version>1.95</version>
<version>1.317</version>
</dependency>

<dependency>
Expand All @@ -222,11 +222,16 @@
<version>3.5.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>3.2.1</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
46 changes: 41 additions & 5 deletions src/main/java/de/jutzig/github/release/plugin/UploadMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.model.FileSet;
import org.apache.maven.model.Scm;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
Expand All @@ -48,6 +51,7 @@
import org.kohsuke.github.GHReleaseBuilder;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;
import org.kohsuke.github.PagedIterable;

/**
Expand All @@ -56,6 +60,7 @@
@Mojo(name = "release", defaultPhase = LifecyclePhase.DEPLOY)
public class UploadMojo extends AbstractMojo implements Contextualizable{

private static final String PUBLIC_GITUHB_API_ENDPOINT = "https://api.github.com";
/**
* Server id for github access.
*/
Expand Down Expand Up @@ -149,6 +154,9 @@ public class UploadMojo extends AbstractMojo implements Contextualizable{
@Parameter(defaultValue = "false")
private Boolean failOnExistingRelease;

@Component
private MavenProject project;

public void execute() throws MojoExecutionException {
if(releaseName==null)
releaseName = tag;
Expand Down Expand Up @@ -270,7 +278,7 @@ private GHRelease findRelease(GHRepository repository, String releaseNameToFind)
*/
private static final Pattern REPOSITORY_PATTERN = Pattern.compile(
"^(scm:git[:|])?" + //Maven prefix for git SCM
"(https?://github\\.com/|git@github\\.com:)" + //GitHub prefix for HTTP/HTTPS/SSH/Subversion scheme
"(https?://[\\w\\d.-]+/|git@[\\w\\d.-]+:)" + //GitHub prefix for HTTP/HTTPS/SSH/Subversion scheme
"([^/]+/[^/\\.]+)" + //Repository ID
"(\\.git)?" + //Optional suffix ".git"
"(/.*)?$" //Optional child project path
Expand All @@ -285,13 +293,41 @@ public static String computeRepositoryId(String id) {
}
}

static String computeGithubApiEndpoint(Scm scm) {
if (scm == null || StringUtils.isEmpty(scm.getConnection())) {
return PUBLIC_GITUHB_API_ENDPOINT;
}
Matcher matcher = REPOSITORY_PATTERN.matcher(scm.getConnection());
if (!matcher.matches()) {
return PUBLIC_GITUHB_API_ENDPOINT;
}
String githubApiEndpoint = matcher.group(2);
if (githubApiEndpoint.contains("github.com")) {
return PUBLIC_GITUHB_API_ENDPOINT;
}

if (githubApiEndpoint.startsWith("git@")) {
// According to the regex pattern above, the matched group would be in a form of git@hostname:
githubApiEndpoint = githubApiEndpoint.substring(4, githubApiEndpoint.length() - 1);
}

githubApiEndpoint = StringUtils.removeEnd(githubApiEndpoint, "/");
if (!githubApiEndpoint.startsWith("http")) {
githubApiEndpoint = "https://" + githubApiEndpoint;
}
// See https://docs.github.com/en/enterprise-server@3.10/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#schema
return githubApiEndpoint + "/api/v3";
}

public GitHub createGithub(String serverId) throws MojoExecutionException, IOException {
String usernameProperty = System.getProperty("username");
String passwordProperty = System.getProperty("password");
String githubApiEndpoint = computeGithubApiEndpoint(project.getScm());
GitHubBuilder gitHubBuilder = new GitHubBuilder().withEndpoint(githubApiEndpoint);
if(usernameProperty!=null && passwordProperty!=null)
{
getLog().debug("Using server credentials from system properties 'username' and 'password'");
return GitHub.connectUsingPassword(usernameProperty, passwordProperty);
getLog().debug("Using server credentials from system properties 'username' and 'password'");
return gitHubBuilder.withPassword(usernameProperty, passwordProperty).build();
}

Server server = getServer(settings, serverId);
Expand All @@ -312,9 +348,9 @@ public GitHub createGithub(String serverId) throws MojoExecutionException, IOExc
String serverPassword = server.getPassword();
String serverAccessToken = server.getPrivateKey();
if (StringUtils.isNotEmpty(serverUsername) && StringUtils.isNotEmpty(serverPassword))
return GitHub.connectUsingPassword(serverUsername, serverPassword);
return gitHubBuilder.withPassword(serverUsername, serverPassword).build();
else if (StringUtils.isNotEmpty(serverAccessToken))
return GitHub.connectUsingOAuth(serverAccessToken);
return gitHubBuilder.withOAuthToken(serverAccessToken).build();
else
throw new MojoExecutionException("Configuration for server " + serverId + " has no login credentials");
}
Expand Down
140 changes: 106 additions & 34 deletions src/test/java/de/jutzig/github/release/plugin/UploadMojoTest.java
Original file line number Diff line number Diff line change
@@ -1,54 +1,71 @@
package de.jutzig.github.release.plugin;

import org.junit.Before;
import org.junit.Test;
import java.util.stream.Stream;

import java.util.HashMap;
import java.util.Map;
import org.apache.maven.model.Scm;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.*;

public class UploadMojoTest {
class UploadMojoTest {
@ParameterizedTest(name = "{0} should resolve to {1} repository id")
@CsvSource({
// Public
"scm:git:https://github.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"scm:git|https://github.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"https://github.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",

private Map<String, String> computeRepositoryIdData;
"scm:git:http://github.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"scm:git|http://github.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"http://github.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",

@Before
public void setUp() throws Exception {
computeRepositoryIdData = new HashMap<String, String>();
"scm:git:git@github.com:jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"scm:git|git@github.com:jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"git@github.com:jutzig/github-release-plugin.git, jutzig/github-release-plugin",

computeRepositoryIdData.put("scm:git:https://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
computeRepositoryIdData.put("scm:git|https://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
computeRepositoryIdData.put("https://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
"scm:git:https://github.com/jutzig/github-release-plugin, jutzig/github-release-plugin",
"scm:git|https://github.com/jutzig/github-release-plugin, jutzig/github-release-plugin",
"https://github.com/jutzig/github-release-plugin, jutzig/github-release-plugin",

computeRepositoryIdData.put("scm:git:http://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
computeRepositoryIdData.put("scm:git|http://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
computeRepositoryIdData.put("http://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
"scm:git:http://github.com/jutzig/github-release-plugin.git/child, jutzig/github-release-plugin",
"scm:git|http://github.com/jutzig/github-release-plugin.git/child, jutzig/github-release-plugin",
"http://github.com/jutzig/github-release-plugin.git/child, jutzig/github-release-plugin",

// Enterprise
"scm:git:https://github.acme.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"scm:git|https://github.acme.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"https://github.acme.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",

computeRepositoryIdData.put("scm:git:git@github.com:jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
computeRepositoryIdData.put("scm:git|git@github.com:jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
computeRepositoryIdData.put("git@github.com:jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
"scm:git:http://github.acme.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"scm:git|http://github.acme.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"http://github.acme.com/jutzig/github-release-plugin.git, jutzig/github-release-plugin",

computeRepositoryIdData.put("scm:git:https://github.com/jutzig/github-release-plugin", "jutzig/github-release-plugin");
computeRepositoryIdData.put("scm:git|https://github.com/jutzig/github-release-plugin", "jutzig/github-release-plugin");
computeRepositoryIdData.put("https://github.com/jutzig/github-release-plugin", "jutzig/github-release-plugin");
"scm:git:git@github.acme.com:jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"scm:git|git@github.acme.com:jutzig/github-release-plugin.git, jutzig/github-release-plugin",
"git@github.acme.com:jutzig/github-release-plugin.git, jutzig/github-release-plugin",

computeRepositoryIdData.put("scm:git:http://github.com/jutzig/github-release-plugin.git/child", "jutzig/github-release-plugin");
computeRepositoryIdData.put("scm:git|http://github.com/jutzig/github-release-plugin.git/child", "jutzig/github-release-plugin");
computeRepositoryIdData.put("http://github.com/jutzig/github-release-plugin.git/child", "jutzig/github-release-plugin");
"scm:git:https://github.acme.com/jutzig/github-release-plugin, jutzig/github-release-plugin",
"scm:git|https://github.acme.com/jutzig/github-release-plugin, jutzig/github-release-plugin",
"https://github.acme.com/jutzig/github-release-plugin, jutzig/github-release-plugin",

"scm:git:http://github.acme.com/jutzig/github-release-plugin.git/child, jutzig/github-release-plugin",
"scm:git|http://github.acme.com/jutzig/github-release-plugin.git/child, jutzig/github-release-plugin",
"http://github.acme.com/jutzig/github-release-plugin.git/child, jutzig/github-release-plugin"
})
void testComputeRepositoryId(String scmString, String expectedRepositoryId) {
assertEquals(expectedRepositoryId, UploadMojo.computeRepositoryId(scmString));
}

@Test
public void testComputeRepositoryId() throws Exception {
for (String source : computeRepositoryIdData.keySet()) {
String expected = computeRepositoryIdData.get(source);
assertEquals(source, expected, UploadMojo.computeRepositoryId(source));
}
@ParameterizedTest(name = "{0} should resolve to {1} endpoint")
@MethodSource("scmFixture")
void testGithubEndpoint(Scm scm, String expectedEndpoint) {
assertEquals(expectedEndpoint, UploadMojo.computeGithubApiEndpoint(scm));
}

@Test
public void testGuessPreRelease() {
void testGuessPreRelease() {
assertTrue(UploadMojo.guessPreRelease("1.0-SNAPSHOT"));
assertTrue(UploadMojo.guessPreRelease("1.0-alpha"));
assertTrue(UploadMojo.guessPreRelease("1.0-alpha-1"));
Expand All @@ -62,4 +79,59 @@ public void testGuessPreRelease() {
assertFalse(UploadMojo.guessPreRelease("1"));
assertFalse(UploadMojo.guessPreRelease("1.0"));
}

private static Stream<Arguments> scmFixture() {
return Stream.of(
// Public GitHub
Arguments.of(scmWithConnectionString("scm:git:https://github.com/jutzig/github-release-plugin.git"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("scm:git|https://github.com/jutzig/github-release-plugin.git"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("https://github.com/jutzig/github-release-plugin.git"), "https://api.github.com"),

Arguments.of(scmWithConnectionString("scm:git:http://github.com/jutzig/github-release-plugin.git"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("scm:git|http://github.com/jutzig/github-release-plugin.git"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("http://github.com/jutzig/github-release-plugin.git"), "https://api.github.com"),

Arguments.of(scmWithConnectionString("scm:git:git@github.com:jutzig/github-release-plugin.git"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("scm:git|git@github.com:jutzig/github-release-plugin.git"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("git@github.com:jutzig/github-release-plugin.git"), "https://api.github.com"),

Arguments.of(scmWithConnectionString("scm:git:https://github.com/jutzig/github-release-plugin"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("scm:git|https://github.com/jutzig/github-release-plugin"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("https://github.com/jutzig/github-release-plugin"), "https://api.github.com"),

Arguments.of(scmWithConnectionString("scm:git:http://github.com/jutzig/github-release-plugin.git/child"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("scm:git|http://github.com/jutzig/github-release-plugin.git/child"), "https://api.github.com"),
Arguments.of(scmWithConnectionString("http://github.com/jutzig/github-release-plugin.git/child"), "https://api.github.com"),

// GitHub Enterprise
Arguments.of(scmWithConnectionString("scm:git:https://github.acme.com/jutzig/github-release-plugin.git"), "https://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("scm:git|https://github.acme.com/jutzig/github-release-plugin.git"), "https://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("https://github.acme.com/jutzig/github-release-plugin.git"), "https://github.acme.com/api/v3"),

Arguments.of(scmWithConnectionString("scm:git:http://github.acme.com/jutzig/github-release-plugin.git"), "http://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("scm:git|http://github.acme.com/jutzig/github-release-plugin.git"), "http://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("http://github.acme.com/jutzig/github-release-plugin.git"), "http://github.acme.com/api/v3"),

Arguments.of(scmWithConnectionString("scm:git:git@github.acme.com:jutzig/github-release-plugin.git"), "https://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("scm:git|git@github.acme.com:jutzig/github-release-plugin.git"), "https://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("git@github.acme.com:jutzig/github-release-plugin.git"), "https://github.acme.com/api/v3"),

Arguments.of(scmWithConnectionString("scm:git:https://github.acme.com/jutzig/github-release-plugin"), "https://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("scm:git|https://github.acme.com/jutzig/github-release-plugin"), "https://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("https://github.acme.com/jutzig/github-release-plugin"), "https://github.acme.com/api/v3"),

Arguments.of(scmWithConnectionString("scm:git:http://github.acme.com/jutzig/github-release-plugin.git/child"), "http://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("scm:git|http://github.acme.com/jutzig/github-release-plugin.git/child"), "http://github.acme.com/api/v3"),
Arguments.of(scmWithConnectionString("http://github.acme.com/jutzig/github-release-plugin.git/child"), "http://github.acme.com/api/v3"),

// Fallback to public
Arguments.of(null, "https://api.github.com")
);
}

private static Scm scmWithConnectionString(String connection) {
Scm scm = new Scm();
scm.setConnection(connection);
return scm;
}
}

0 comments on commit ec6da48

Please sign in to comment.