How to Configure JitPack for Recent JDK Versions
Tags: java, tutorial, kotlin, maven
Published: 2022-08-04 15:27:40

I intend for this to be the first post of a series with various tips and tricks related to effective use of Maven repositories for dependency management, artifact storage, etc, for Java and other JDK languages such as Kotlin. My preferred Maven repository is of course the Maven Central Repository for a variety of reasons, and I will likely include some posts on Maven Central later in the series. 

For now, however, I am beginning with a tip related to [JitPack](https://jitpack.io), specifically how to configure JitPack builds if your project requires recent JDK versions (e.g., JDK version > 8), or if you want to build with a specific JDK distribution. I am not affiliated with JitPack, and this post is based on my recent experience configuring JitPack builds in a couple of my projects where the JitPack documentation didn't entirely cover my requirements.

__Table of Contents:__ The rest of this post is organized as follows:
* [About JitPack](#about-jitpack): I need to start with an explanation of how JitPack works because it is a bit different than other Maven repositories.
* [Why JitPack?](#why-jitpack): Brief explanation of why I've begun making my libraries available via JitPack in addition to other repositories.
* [How to Specify a JitPack Dependency](#how-to-specify-a-jitpack-dependency): Although not the purpose of this post, it will be useful to explain how to specify a dependency on an artifact served from JitPack.
* [How to Configure JitPack Builds](#how-to-configure-jitpack-builds): Explanation with examples of configuring JitPack builds, including an example in a live project on GitHub.
* [Where you can find me](#where-you-can-find-me)

## About JitPack

[JitPack](https://jitpack.io) works a bit differently than most other Maven artifact repositories. For most Maven repositories, such as Maven Central or GitHub Packages, you first build the artifacts either locally on your own system, or perhaps as part of a CI/CD workflow (e.g., via GitHub Actions); and then after building your artifacts (e.g., various jar files of compiled class files, source code, documentation, etc) you deploy them to your chosen Maven artifact repository.

JitPack doesn't work like this. Instead, JitPack is designed to build artifacts on-demand direct from a source code repository hosted on GitHub or any other git host, such as GitLab, Bitbucket, etc. The on-demand build occurs the first time a version of an artifact is requested, and then simply served thereafter.

One unique aspect of JitPack that directly derives from its approach is that developers can include dependencies on any public git repository on GitHub, Bitbucket, and other git hosts, even if the maintainers of those repositories don't explicitly publish artifacts to any Maven repository. 

## Why JitPack?

I am already publishing artifacts of all of the Java libraries I maintain to Maven Central, arguably the definitive Maven repository, so why do I also publish to JitPack? JitPack enables specifying any git tag, branch, pull request, or commit hash as the version for a dependency. You wouldn't want to rely on one of these in production. However, during development at times it might be useful to build against a very specific unreleased version of a dependency. With JitPack, you can do this by specifying the hash of the commit as the version, or even specifying a branch. Isn't this just like using a SNAPSHOT build? Yes, but in this case there is no reason to create SNAPSHOT builds that you may or may not need. Or perhaps your SNAPSHOTs are built nightly, but you find a need to build against a specific point in the git history that occurred between the nightly builds. Or perhaps your nightly SNAPSHOTs are built from the default branch, and you want to build a dependent against a feature branch that is a work-in-progress in the dependency that you are also working on. 

This is essentially why I've started publishing artifacts to JitPack in addition to Maven Central. Essentially, as a source of SNAPSHOT builds for any and all commits, and for any and all branches including short-lived pull-request branches, without any intervention on my part.

But wait, did you just say "started publishing artifacts to JitPack"? I thought you said JitPack handles all of this regardless of whether the maintainer of a repository publishes artifacts anywhere? I did say that, and it is true, provided that JitPack can find a build file (such as a Maven pom.xml or the Gradle equivalent), and provided that your project can build with the JitPack defaults. [We'll get to this in a bit....](#how-to-configure-jitpack-builds)

## How to Specify a JitPack Dependency

The details of how to specify dependencies depends upon your build tool. I use Maven, so my examples will use Maven. By default, Maven searches for artifacts of dependencies in the Maven Central Repository. If you want to import dependencies from anywhere else, you need to add the details to the `<repositories></repositories>` section of your pom.xml. To import from JitPack, insert the following into that section of your pom.xml (you might need to create such a section if you are currently importing only from Maven Central):

```XML
<repository>
  <id>jitpack.io</id>
  <url>https://jitpack.io</url>
</repository>
```

To import a dependency from a Maven repository, you need to specify its coordinates, which consists in its `groupId`, its `artifactId`, and its `version`. The `groupId` is generally a reverse domain, or subdomain, controlled by the one publishing the artifacts. For example, on Maven Central and GitHub Packages, I use `org.cicirello` as the `groupId` for all of my Java libraries. However, although JitPack does support such custom groupIds, the `groupId` for JitPack is usually formed from the domain of the git host along with the username or organization name who owns the repository that you want to import. The `artifactId` is the name of the artifact, which is sometimes the name of a package or module within it, but it is not required to be. For JitPack, `artifactId` is the name of the repository. And `version` is, well, the version that you want.

My examples here will assume you are specifying a GitHub repository as a dependency, but you'll need something very similar for other hosts like Bitbucket.

To import using a tag, such as a release tag like `v1.2.3` insert the following into the `<dependencies></dependencies>` section of your pom.xml:

```XML
<dependency>
  <groupId>com.github.USERNAME</groupId>
  <artifactId>REPOSITORY_NAME</artifactId>
  <version>v1.2.3</version>
</dependency>
```

JitPack also automatically supports dropping the `v`, so if you want to be consistent with other Maven repositories, you can also use:

```XML
<dependency>
  <groupId>com.github.USERNAME</groupId>
  <artifactId>REPOSITORY_NAME</artifactId>
  <version>1.2.3</version>
</dependency>
```

Or if you want to build against the current state of a specific branch, perhaps "main", you can use:

```XML
<dependency>
  <groupId>com.github.USERNAME</groupId>
  <artifactId>REPOSITORY_NAME</artifactId>
  <version>main-SNAPSHOT</version>
</dependency>
```

Or even a specific commit hash with:

```XML
<dependency>
  <groupId>com.github.USERNAME</groupId>
  <artifactId>REPOSITORY_NAME</artifactId>
  <version>COMMIT_HASH_GOES_HERE</version>
</dependency>
```

Whichever of the above you use, be aware that if the version you are importing has not previously been built, there will be a delay in your build while Maven (or Gradle or whatever build tool you are using) waits for a response from JitPack since JitPack must first build the dependency before delivering artifacts. Any subsequent imports, whether by you or someone else, will be without such delay. 

Here are the equivalent examples as above, but using one of my repositories, for a Java library I maintain of stochastic local search and evolutionary algorithms, [Chips-n-Salsa](https://github.com/cicirello/Chips-n-Salsa).

__With tag:__

```XML
<dependency>
  <groupId>com.github.cicirello</groupId>
  <artifactId>chips-n-salsa</artifactId>
  <version>v5.2.0</version>
</dependency>
```

__Dropping the v from the tag:__

```XML
<dependency>
  <groupId>com.github.cicirello</groupId>
  <artifactId>chips-n-salsa</artifactId>
  <version>5.2.0</version>
</dependency>
```

__Latest commit in default branch:__


```XML
<dependency>
  <groupId>com.github.cicirello</groupId>
  <artifactId>chips-n-salsa</artifactId>
  <version>master-SNAPSHOT</version>
</dependency>
```

__From a commit hash:__

```XML
<dependency>
  <groupId>com.github.cicirello</groupId>
  <artifactId>chips-n-salsa</artifactId>
  <version>278803985ceb0e3d1515eefb2a69d0601e0176cf</version>
</dependency>
```

__From a pull-request:__

```XML
<dependency>
  <groupId>com.github.cicirello</groupId>
  <artifactId>chips-n-salsa</artifactId>
  <version>PR451-SNAPSHOT</version>
</dependency>
```

__JitPack also supports configuring reverse domain:__

I have also configured my reverse domain on JitPack, so all of the above will likewise work with `<groupId>org.cicirello</groupId>` in addition to `<groupId>com.github.cicirello</groupId>`, which is nice for consistency with Maven Central. But only the released versions are available via Maven Central, such as through a dependency like:

```XML
<dependency>
  <groupId>org.cicirello</groupId>
  <artifactId>chips-n-salsa</artifactId>
  <version>5.2.0</version>
</dependency>
```


## How to Configure JitPack Builds

You may or may not need to configure JitPack at all. If you have a Maven pom.xml in the root of your repository (or whatever the equivalent of this is for Gradle), AND if your project can be built with Java 8, then you likely won't need any configuration. You, or anyone else for that matter, can already utilize JitPack to import your repository as a Maven dependency. Upon finding a pom.xml at the root of your repository, JitPack will attempt to build using Java 8 with the following:

```Shell
mvn install -DskipTests
```

I imagine that it skips running your tests to decrease the length of the delay the first time an artifact is requested. This seems reasonable.

In my case, most of my libraries require Java 17, so without configuration JitPack builds fail. To configure for a more recent JDK, here are the steps.

__Step 1:__ Create a file named `jitpack.yml` at the root of your repository. This is where all configuration takes place. Feel free to consult the [jitpack.yml](https://github.com/cicirello/Chips-n-Salsa/blob/master/jitpack.yml) for the project this post is based upon, [Chips-n-Salsa](https://github.com/cicirello/Chips-n-Salsa), for full details.

__Step 2:__ Specify the JDK version within `jitpack.yml` such as with:

```YAML
jdk:
  - openjdk17
```

Note that if you were to guess (like I did) that the above is sufficient, you'd be incorrect. You now need to install the JDK distribution that you want to use, as well as explicitly specify that you want to use it. I'm using the Temurin distribution of OpenJDK in the project that this post is based upon. You need both of the statements I've added below. Without the `sdk use`, JitPack will continue to use Java 8. You can find the [full list of available JDKs on the sdkman site](https://sdkman.io/jdks).

```YAML
jdk:
  - openjdk17
before_install:
  - sdk install java 17.0.3-tem
  - sdk use java 17.0.3-tem
```

__Step 3:__ Update Maven. The above might be sufficient in some cases. For me, it was not. JitPack currently (as of the time of the writing of this post) has Maven 3.6.1 installed. Maven 3.6.1 was released over 3 years ago in April 2019. The `<release>` property for configuring Java version didn't exist then. If you are using that (as I am), your JitPack build will fail unless you update Maven. You can do that by revising the above to what follows (which as of the writing of this post will update Maven to 3.8.6):

```YAML
jdk:
  - openjdk17
before_install:
  - sdk install java 17.0.3-tem
  - sdk use java 17.0.3-tem
  - sdk install maven
  - mvn -v
```

You don't really need the `mvn -v` above. I included that to show the version in the build logs.

__Step 4:__ Customize the install step. You might be done at this point, provided that JitPack's default build command of `mvn install -DskipTests` is relevant to your project. In my case, I wanted to also disable one of JitPack's features. One of JitPack's features, which many probably like, is that if the build produces a jar of the javadocs, JitPack will automatically extract the javadocs and serve them, including maintaining the javadocs for each release. I would prefer it if they simply served the jar of the javadocs, as Maven Central does, rather than automatically hosting them. For the libraries that I maintain, I also host the javadocs on the project's site, and would prefer if the definitive version of the javadocs is unambiguously the version hosted on my domain, `cicirello.org`. Thus, for JitPack builds, I simply disable javadoc generation, leading to the complete `jitpack.yml` below: 

```YAML
jdk:
  - openjdk17
before_install:
  - sdk install java 17.0.3-tem
  - sdk use java 17.0.3-tem
  - sdk install maven
  - mvn -v
install:
  - mvn install -Dmaven.javadoc.skip=true -DskipTests
```

__Important Note:__ Because of the way JitPack essentially builds a snapshot of your repository, even for release builds, the `jitpack.yml` must be present within the specific snapshot of your repository in order to apply. For example, for the library that this post is based upon, [Chips-n-Salsa](https://github.com/cicirello/Chips-n-Salsa), the first release where the repository contains the configuration file is 5.0.1, so JitPack will fail to build any prior version, although earlier versions are available via Maven Central and GitHub Packages. Likewise, if you specify any commit hash prior to that of the commit where that configuration file was introduced, the JitPack build will also fail.

__Live Configuration Example:__ See this [jitpack.yml](https://github.com/cicirello/Chips-n-Salsa/blob/master/jitpack.yml) for a complete and live example, which is found within the following repository:

{% github cicirello/Chips-n-Salsa %}


## Where you can find me

On the Web:

{% embed https://www.cicirello.org/ %}

Follow me here on DEV:

{% user cicirello %}

Follow me on GitHub:

{% github cicirello/cicirello %}

| [![Liberapay](https://actions.cicirello.org/images/Liberapay.svg)](https://liberapay.com/cicirello) | [![Ko-Fi](https://actions.cicirello.org/images/ko-fi.svg)](https://ko-fi.com/cicirello) |
| :---: | :---: |
| [![GitHub Sponsors](https://actions.cicirello.org/images/github-sponsors.svg)](https://github.com/sponsors/cicirello) | |