Andy Berry

Musings of a Software Engineer

HomePostsAboutProjectsExperience
All posts
September 13th, 2015devops

Host your own Maven repository for free using GitHub Pages


If you're writing Java code the chances are you're probably using Maven. The popular Java build tools, Ant; Gradle; and Maven itself; use Maven repositories in order to resolve dependencies required by the project. Most dependencies you're going to need will be in the Maven Central Repository or another public Maven repository, but what if you have some dependencies that aren't?

There are several Maven repository servers available for hosting your own Artifacts and they make it reasonably straightforward to do. Nexus and Artifactory are two commonly used examples. But what if you're writing an Open Source project; a side project that you don't want to pour money into; or just don't want the hassle or expense of running servers? Well GitHub pages can help.

Maven repositories are just a series of Artifacts; their hashes and metadata files, each of which have a known format and have a specific URL format. There isn't anything particularly special about a basic Maven repository where restricting access etc. isn't a requirement. Since all we require here is a set of files to be hosted we can use GitHub pages to host them.

Maven and GitHub Pages

So now we just have the problem of creating the correct metadata files in the correct location. This is where we can use a build tool to do the work for us. So long as the chosen build tool can deploy to Maven repositories on the local disk rather than over HTTP it will work, in my case I've chosen Gradle since it doesn't rely on lots of crazy XML to get the job done.

Gradle Build Script

It's reasonably straightforward to deploy an artifact to a local Maven repository using Gradle. First we need to add the maven plugin, add our file as an artifact so Gradle knows what it's uploading and then configure the uploadArchives task to deploy to our local repository. So some basic Gradle config might look like:

apply plugin : 'maven'
artifacts {
archives file: file("/A/path/to/MyFile.zip"), name: MyArtifact
}
uploadArchives {
repositories {
mavenDeployer {
repository ( url: "file://localhost${project.projectDir}/repo" )
pom.version = "1.2.3"
pom.artifactId = "MyArtifact"
pom.groupId = "my.artifact.group"
}
}
}

Running gradle uploadArchives will then copy the artifact to the 'repo' directory and calculate the required metadata. This can then be committed to and pushed to the remote gh-pages branch and it'll all be hosted publicly. To use the artifact(s) add http://<github-user>.github.io/<repo-name>/ as the URL for your Maven repository in the project that's using the artifacts.

Now you have yourself a publicly hosted Artifact used by your project. Magic!

A more generic solution

So the solution above has a lot of hardcoded values, the path of the artifact and it's version for example. To make this a more generic script that can be used again and again we can accept the relevant information via arguments. The easiest way to do this in Gradle is using properties which can be set via the command line by using -PSOME.PROP=some.value.

So our script can be changed to:

apply plugin : 'maven'
ext.depVars = {}
def depProperties = ['depGroup', 'depVersion', 'depArtifact', 'depFile']
depProperties.each {
depVars[it] = (String) property(it)
}
def depFile = file( depVars['depFile'] )
artifacts {
archives file: depFile, name: depVars['depArtifact']
}
uploadArchives {
repositories {
mavenDeployer {
repository ( url: "file://localhost${projectDir}/repo" )
pom.version = depVars['depVersion']
pom.artifactId = depVars['depArtifact']
pom.groupId = depVars['depGroup']
}
}
}
defaultTasks 'uploadArchives'
The example above has been simplified for the purpose of this post so please check out the full build script.

To now deploy an artifact we can run the command gradle -PdepGroup=my.artifact.group -PdepVersion=1.2.3 -PdepArtifact=MyArtifact -PdepFile=/A/path/to/MyFile.zip. To deploy another artifact we use the same command but change the arguments.

Gradle Wrapper

The previous commands require Gradle to be installed. Gradle helpfully provides a Wrapper that can be checked in to source control which will download the required version of Gradle, so no need to have a pre-installed Gradle. It also has the added advantage that the version of Gradle used is tied to the build file version, so no more finding the build fails because you have the wrong version of the build tool installed.

Rather than reproduce the Gradle wrapper documentation here I'll let you read the Wrapper docs yourself. In a nutshell, you install Gradle on one machine so you can use Gradle to create the wrapper; create a build file with the wrapper task; run gradlew wrapper to add the wrapper script and then commit it to your repo. From then onwards you use the wrapper, via ./gradlew, to run Gradle rather than the gradle command.

Give me some code!

You can download the code required for a Gradle controlled maven repo from https://github.com/andy-berry-dev/gh-pages-maven-repo. Just fork the project and clone locally. Run the Gradle wrapper, for example ./gradlew -PdepGroup=my.artifact.group -PdepVersion=1.2.3 -PdepArtifact=MyArtifact -PdepFile=/A/path/to/MyFile.zip, to add your Artifact to the repo then commit and push to gh-pages.


Published September 13th, 2015
gitgithubmavencontinuous-integrationbuild

Thoughts and opinions inspired by life in the Software Engineering industry

© Andy Berry, All rights reserved..

HomeCVLicenseSitemap