diff --git a/README.md b/README.md
index dc7c254..ac60640 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ To use the plugin you need to configure it in your `pom.xml` like so
Description of your release
1.0 Final
${project.version}
-
+
@@ -48,4 +48,27 @@ These credentials can be overridden by setting `username` and `password` as syst
Thanks to a contribution from rowanseymour you can also use your API token by adding it as `` to your server definition in the `settings.xml`.
+## GitHub Enterprise users
+
+It is also possible to upload to GitHub Enterprise. For that, two additional options are available
+in the configuration: `githubHostname`, `githubApiUrlPrefix` and `githubApiUploadUrlPrefix`. They can be used like this:
+
+```
+
+ de.jutzig
+ github-release-plugin
+ 1.1.1
+
+ Description of your release
+ 1.0 Final
+ ${project.version}
+
+ github-enterprise.domain
+ http://github-enterprise.domain/api/v3
+ https://github-enterprise.domain/api/v3
+ ...
+
+
+```
+
The plugin is available on Maven central
diff --git a/pom.xml b/pom.xml
index 819898f..cfae67b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,6 +66,14 @@
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ 3.3
+
+ github-release
+
+
@@ -154,20 +162,32 @@
org.kohsuke
github-api
- 1.49
+ 1.75-SNAPSHOT
org.apache.maven
maven-plugin-api
- 3.1.0
+ 3.3.9
provided
org.apache.maven
maven-core
- 3.1.0
+ 3.3.9
provided
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ 3.3
+ provided
+
+
+ org.apache.maven
+ maven-compat
+ 3.3.9
+ test
+
junit
@@ -175,5 +195,19 @@
4.11
test
+
+
+ org.apache.maven.plugin-testing
+ maven-plugin-testing-harness
+ 3.3.0
+ test
+
+
+
+ commons-io
+ commons-io
+ 2.4
+ test
+
diff --git a/src/main/java/de/jutzig/github/release/plugin/UploadMojo.java b/src/main/java/de/jutzig/github/release/plugin/UploadMojo.java
index 2c2652d..55f59d1 100644
--- a/src/main/java/de/jutzig/github/release/plugin/UploadMojo.java
+++ b/src/main/java/de/jutzig/github/release/plugin/UploadMojo.java
@@ -29,6 +29,10 @@
import org.apache.maven.model.FileSet;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+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;
@@ -50,100 +54,133 @@
/**
* Goal which attaches a file to a GitHub release
- *
- * @goal release
- *
- * @phase deploy
*/
+@Mojo( name="release", defaultPhase = LifecyclePhase.DEPLOY)
public class UploadMojo extends AbstractMojo implements Contextualizable{
+ private final static String DEFAULT_GITHUBHOSTNAME = "github.com";
+ private final static String DEFAULT_GITAPIURLPREFIX = "https://api.github.com";
+
/**
+ *
+ * //@parameter default-value="github" expression="github"
* Server id for github access.
- *
- * @parameter default-value="github" expression="github"
*/
- private String serverId;
+ @Parameter( defaultValue = "github")
+ private String serverId;
/**
* The tag name this release is based on.
- *
- * @parameter expression="${project.version}"
+ *
+ * //@parameter expression="${project.version}"
*/
+ @Parameter( property = "project.version" )
private String tag;
/**
* The name of the release
- *
- * @parameter expression="${release.name}"
+ *
+ * //@parameter expression="${release.name}"
*/
- private String releaseName;
+ @Parameter( property = "release.name" )
+ private String releaseName;
/**
* The release description
- *
- * @parameter expression="${project.description}"
+ *
+ * //@parameter expression="${project.description}"
*/
+ @Parameter( property = "project.description" )
private String description;
/**
* The github id of the project. By default initialized from the project scm connection
- *
- * @parameter default-value="${project.scm.connection}" expression="${release.repositoryId}"
+ *
+ * //@parameter default-value="${project.scm.connection}" expression="${release.repositoryId}"
* @required
*/
+ @Parameter( defaultValue = "${project.scm.connection}", property = "release.repositoryId")
private String repositoryId;
/**
* The Maven settings
*
- * @parameter expression="${settings}
+ * //@parameter expression="${settings}
*/
+ @Parameter( defaultValue = "${settings}", readonly = true)
private Settings settings;
/**
* The Maven session
*
- * @parameter expression="${session}"
+ * //@parameter expression="${session}"
*/
+ @Parameter( defaultValue = "${session}", readonly = true)
private MavenSession session;
/**
* The file to upload to the release. Default is ${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging} (the main artifact)
*
- * @parameter default-value="${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging}" expression="${release.artifact}"
+ * //@parameter default-value="${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging}" expression="${release.artifact}"
*/
+ @Parameter( defaultValue = "${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging}",
+ property = "release.artifact")
private String artifact;
/**
* A specific fileSet rule to select files and directories for upload to the release.
*
- * @parameter
+ * //@parameter
*/
private FileSet fileSet;
/**
* A list of fileSet rules to select files and directories for upload to the release.
*
- * @parameter
+ * //@parameter
*/
private List fileSets;
/**
* Flag to indicate to overwrite the asset in the release if it already exists. Default is false
*
- * @parameter default-value=false
+ * //@parameter default-value=false
*/
+ @Parameter( defaultValue = "false" )
private Boolean overwriteArtifact;
+ /**
+ * Github (Enterprise) hostname. Default is 'github.com'
+ *
+ * ////@parameter default-value="github.com"
+ */
+ @Parameter( defaultValue = DEFAULT_GITHUBHOSTNAME )
+ private String githubHostname;
+
+ /**
+ * Github (Enterprise) API URL (prefix). Default is 'https://api.github.com'
+ */
+ @Parameter( defaultValue = DEFAULT_GITAPIURLPREFIX )
+ private String githubApiUrlPrefix;
+
+ /**
+ * URL prefix to upload the release to Github. Default is 'https://uploads.github.com/'
+ *
+ * ////@parameter default-value="https://uploads.github.com/"
+ */
+ @Parameter( defaultValue = "https://uploads.github.com/")
+ private String githubApiUploadUrlPrefix;
+
@Requirement
private PlexusContainer container;
/**
* If this is a prerelease. By default it will use true if the tag ends in -SNAPSHOT
*
- * @parameter
- *
+ * //@parameter
+ *
*/
+ @Parameter
private Boolean prerelease;
public void execute() throws MojoExecutionException {
@@ -188,7 +225,7 @@ public void execute() throws MojoExecutionException {
uploadAssets(release, set);
} catch (IOException e) {
-
+
getLog().error(e);
throw new MojoExecutionException("Failed to upload assets", e);
}
@@ -197,14 +234,12 @@ public void execute() throws MojoExecutionException {
private void uploadAsset(GHRelease release, File asset) throws IOException {
getLog().info("Processing asset "+asset.getPath());
- URL url = new URL(MessageFormat.format("https://uploads.github.com/repos/{0}/releases/{1}/assets?name={2}",repositoryId,Long.toString(release.getId()),asset.getName()));
-
List existingAssets = release.getAssets();
for ( GHAsset a : existingAssets ){
if (a.getName().equals( asset.getName() )){
if(overwriteArtifact) {
getLog().info(" Deleting existing asset");
- a.delete();
+ a.delete();
}
else
{
@@ -216,7 +251,7 @@ private void uploadAsset(GHRelease release, File asset) throws IOException {
getLog().info(" Upload asset");
// for some reason this doesn't work currently
- release.uploadAsset(asset, "application/zip");
+ release.uploadAsset(asset, "application/zip", ensureNoTrailingSlash(githubApiUploadUrlPrefix));
}
private void uploadAssets(GHRelease release, FileSet fileset) throws IOException {
@@ -239,17 +274,18 @@ private GHRelease findRelease(GHRepository repository, String releaseName2) thro
return null;
}
- /**
- * @see SCM URL Format
- */
- private static final Pattern REPOSITORY_PATTERN = Pattern.compile(
+ public String computeRepositoryId(String id) {
+
+ String githubHostnameForRegexp = githubHostname.replace(".", "\\.");
+
+ 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?://" + githubHostnameForRegexp + "/|git@" + githubHostnameForRegexp + ":)" + //GitHub prefix for HTTP/HTTPS/SSH/Subversion scheme
"([^/]+/[^/]*?)" + //Repository ID
"(\\.git)?$" //Optional suffix ".git"
- , Pattern.CASE_INSENSITIVE);
+ , Pattern.CASE_INSENSITIVE);
+ getLog().debug("using following repository pattern: " + REPOSITORY_PATTERN.toString());
- public static String computeRepositoryId(String id) {
Matcher matcher = REPOSITORY_PATTERN.matcher(id);
if (matcher.matches()) {
return matcher.group(3);
@@ -263,10 +299,10 @@ public GitHub createGithub(String serverId) throws MojoExecutionException, IOExc
String passwordProperty = System.getProperty("password");
if(usernameProperty!=null && passwordProperty!=null)
{
- getLog().debug("Using server credentials from system properties 'username' and 'password'");
+ getLog().debug("Using server credentials from system properties 'username' and 'password'");
return GitHub.connectUsingPassword(usernameProperty, passwordProperty);
}
-
+
Server server = getServer(settings, serverId);
if (server == null)
throw new MojoExecutionException(MessageFormat.format("Server ''{0}'' not found in settings", serverId));
@@ -285,16 +321,18 @@ 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 DEFAULT_GITHUBHOSTNAME.equals(githubHostname) ? GitHub.connectUsingPassword(serverUsername, serverPassword)
+ : GitHub.connectToEnterprise(githubApiUrlPrefix, serverUsername, serverPassword);
else if (StringUtils.isNotEmpty(serverAccessToken))
- return GitHub.connectUsingOAuth(serverAccessToken);
+ return DEFAULT_GITHUBHOSTNAME.equals(githubHostname) ? GitHub.connectUsingOAuth(serverAccessToken)
+ : GitHub.connectToEnterprise(githubApiUrlPrefix, serverAccessToken);
else
throw new MojoExecutionException("Configuration for server " + serverId + " has no login credentials");
}
/**
* Get server with given id
- *
+ *
* @param settings
* @param serverId
* must be non-null and non-empty
@@ -316,4 +354,17 @@ protected Server getServer(final Settings settings, final String serverId) {
public void contextualize(Context context) throws ContextException {
container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
}
+
+ /**
+ * @param str
+ * @return trimmed str minus the trailing '/', if any
+ */
+ String ensureNoTrailingSlash(String str) {
+ if(str == null || str.isEmpty()) {
+ return str;
+ }
+ String strTrimmed = str.trim();
+ return strTrimmed.endsWith("/") ? strTrimmed.substring(0, strTrimmed.length() - 1) : strTrimmed;
+ }
+
}
diff --git a/src/main/resources/META-INF/plexus/components.xml b/src/main/resources/META-INF/plexus/components.xml
new file mode 100644
index 0000000..8bad9f0
--- /dev/null
+++ b/src/main/resources/META-INF/plexus/components.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ org.apache.maven.artifact.handler.ArtifactHandler
+ github-release
+ org.apache.maven.artifact.handler.DefaultArtifactHandler
+
+ jar
+ jar
+ jar
+ java
+
+
+
+
+ org.apache.maven.lifecycle.mapping.LifecycleMapping
+ github-release
+ org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping
+
+
+ de.jutzig:github-release-plugin:initialize
+ de.jutzig:github-release-plugin:validate-project
+ org.apache.maven.plugins:maven-resources-plugin:resources
+ de.jutzig:github-release-plugin:validate-application
+ de.jutzig:github-release-plugin:package
+ org.apache.maven.plugins:maven-install-plugin:install
+ org.apache.maven.plugins:maven-deploy-plugin:deploy
+
+
+
+
+
diff --git a/src/test/java/de/jutzig/github/release/plugin/UploadMojoTest.java b/src/test/java/de/jutzig/github/release/plugin/UploadMojoTest.java
index e2c2e5b..f5da71f 100644
--- a/src/test/java/de/jutzig/github/release/plugin/UploadMojoTest.java
+++ b/src/test/java/de/jutzig/github/release/plugin/UploadMojoTest.java
@@ -2,42 +2,131 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.Rule;
+import static org.junit.Assert.*;
import java.util.HashMap;
import java.util.Map;
+import java.io.File;
-import static org.junit.Assert.assertEquals;
+import org.apache.maven.plugin.testing.MojoRule;
+import org.apache.maven.plugin.testing.WithoutMojo;
+import org.apache.maven.plugin.testing.resources.TestResources;
public class UploadMojoTest {
- private Map computeRepositoryIdData;
+ @Rule
+ public MojoRule rule = new MojoRule() {
+ @Override
+ protected void before() throws Throwable { }
- @Before
+ @Override
+ protected void after() { }
+ };
+
+ @Rule
+ public TestResources resources = new TestResources();
+
+ private Map computeRepositoryIdData_github;
+ private Map computeRepositoryIdData_github_enterprise;
+ private Map ensureNoTrailingSlash;
+
+ @Before
public void setUp() throws Exception {
- computeRepositoryIdData = new HashMap();
+ computeRepositoryIdData_github = new HashMap();
+
+ computeRepositoryIdData_github.put("scm:git:https://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github.put("scm:git|https://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github.put("https://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+
+ computeRepositoryIdData_github.put("scm:git:http://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github.put("scm:git|http://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github.put("http://github.com/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+
+ computeRepositoryIdData_github.put("scm:git:git@github.com:jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github.put("scm:git|git@github.com:jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github.put("git@github.com:jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+
+ computeRepositoryIdData_github.put("scm:git:https://github.com/jutzig/github-release-plugin", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github.put("scm:git|https://github.com/jutzig/github-release-plugin", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github.put("https://github.com/jutzig/github-release-plugin", "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");
+ computeRepositoryIdData_github_enterprise = new HashMap();
- 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");
+ computeRepositoryIdData_github_enterprise.put("scm:git:https://some-github-enterprise-server.domain/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github_enterprise.put("scm:git|https://some-github-enterprise-server.domain/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github_enterprise.put("https://some-github-enterprise-server.domain/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");
+ computeRepositoryIdData_github_enterprise.put("scm:git:http://some-github-enterprise-server.domain/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github_enterprise.put("scm:git|http://some-github-enterprise-server.domain/jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github_enterprise.put("http://some-github-enterprise-server.domain/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");
+ computeRepositoryIdData_github_enterprise.put("scm:git:git@some-github-enterprise-server.domain:jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github_enterprise.put("scm:git|git@some-github-enterprise-server.domain:jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github_enterprise.put("git@some-github-enterprise-server.domain:jutzig/github-release-plugin.git", "jutzig/github-release-plugin");
+
+ computeRepositoryIdData_github_enterprise.put("scm:git:https://some-github-enterprise-server.domain/jutzig/github-release-plugin", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github_enterprise.put("scm:git|https://some-github-enterprise-server.domain/jutzig/github-release-plugin", "jutzig/github-release-plugin");
+ computeRepositoryIdData_github_enterprise.put("https://some-github-enterprise-server.domain/jutzig/github-release-plugin", "jutzig/github-release-plugin");
+
+ ensureNoTrailingSlash = new HashMap();
+
+ ensureNoTrailingSlash.put(null, null);
+ ensureNoTrailingSlash.put("", "");
+ ensureNoTrailingSlash.put("no trailing slash", "no trailing slash");
+ ensureNoTrailingSlash.put("trailing slash/", "trailing slash");
+ ensureNoTrailingSlash.put("no trailing slash trimmed ", "no trailing slash trimmed");
+ ensureNoTrailingSlash.put("trimmed trailing slash/ ", "trimmed trailing slash");
}
@Test
- public void testComputeRepositoryId() throws Exception {
- for (String source : computeRepositoryIdData.keySet()) {
- String expected = computeRepositoryIdData.get(source);
- assertEquals(source, expected, UploadMojo.computeRepositoryId(source));
+ public void testComputeRepositoryId_github_com() throws Exception {
+
+ File testProjectDir = this.resources.getBasedir( "github.com" );
+ assertNotNull( testProjectDir );
+ assertTrue( testProjectDir.exists() );
+
+ UploadMojo mojo = (UploadMojo) rule.lookupConfiguredMojo( testProjectDir , "release");
+ assertNotNull(mojo);
+
+ for (String source : computeRepositoryIdData_github.keySet()) {
+ String expected = computeRepositoryIdData_github.get(source);
+ assertEquals(source, expected, mojo.computeRepositoryId(source));
}
}
+
+ @Test
+ public void testComputeRepositoryId_github_enterprise() throws Exception {
+
+ File testProjectDir = this.resources.getBasedir( "github.enterprise" );
+ File pom = new File(testProjectDir, "pom.xml");
+ assertNotNull( pom );
+ assertTrue( pom.exists() );
+
+ UploadMojo mojo = (UploadMojo) rule.lookupMojo("release", pom);
+ assertNotNull(mojo);
+
+ for (String source : computeRepositoryIdData_github_enterprise.keySet()) {
+ String expected = computeRepositoryIdData_github_enterprise.get(source);
+ assertEquals(source, expected, mojo.computeRepositoryId(source));
+ }
+ }
+
+ @Test
+ public void testEnsureNoTrailingSlash() throws Exception {
+
+ File testProjectDir = this.resources.getBasedir( "github.com" );
+ assertNotNull( testProjectDir );
+ assertTrue( testProjectDir.exists() );
+
+ UploadMojo mojo = (UploadMojo) rule.lookupConfiguredMojo( testProjectDir , "release");
+ assertNotNull(mojo);
+
+ for (String str : ensureNoTrailingSlash.keySet()) {
+ String expected = ensureNoTrailingSlash.get(str);
+ assertEquals(str, expected, mojo.ensureNoTrailingSlash(str));
+ }
+
+ }
+
}
diff --git a/src/test/projects/github.com/pom.xml b/src/test/projects/github.com/pom.xml
new file mode 100644
index 0000000..ba304cf
--- /dev/null
+++ b/src/test/projects/github.com/pom.xml
@@ -0,0 +1,41 @@
+
+ 4.0.0
+ github-release-plugin.unit
+ github-release-plugin
+ jar
+ 1.0-SNAPSHOT
+ test-project
+ testing POM for github.com tests
+
+
+ https://github.com/jutzig/github-release-plugin
+ scm:git:https://github.com/jutzig/github-release-plugin.git
+ scm:git:https://github.com/jutzig/github-release-plugin.git
+ HEAD
+
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+
+
+
+ github-release-plugin
+
+ github-ncia
+ Description of your release
+ 1.0 Final
+ ${project.version}
+
+
+
+
+
+
+
diff --git a/src/test/projects/github.enterprise/pom.xml b/src/test/projects/github.enterprise/pom.xml
new file mode 100644
index 0000000..c488603
--- /dev/null
+++ b/src/test/projects/github.enterprise/pom.xml
@@ -0,0 +1,41 @@
+
+ 4.0.0
+ github-release-plugin.unit
+ github-release-plugin
+ jar
+ 1.0-SNAPSHOT
+ test-project
+ testing POM for github enterprise tests
+
+
+ https://github.com/jutzig/github-release-plugin
+ scm:git:https://github.com/jutzig/github-release-plugin.git
+ scm:git:https://github.com/jutzig/github-release-plugin.git
+ HEAD
+
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+
+
+
+ github-release-plugin
+
+ github-ncia
+ Description of your release
+ v${project.version}
+ ${project.version}
+ some-github-enterprise-server.domain
+
+
+
+
+
+