DEV Community

Vincent A. Cicirello
Vincent A. Cicirello

Posted on

Ahead-of-time JitPack Builds with Custom GroupId via GitHub Actions

This post is a continuation of a series of tips and tricks for JitPack. The first post of this series concerned the unique Maven repository called JitPack that builds artifacts of dependencies on-demand from a git repository, and explained how to configure your repository for successful builds with recent JDK versions, essentially covering what I had to do to get JitPack builds to work for one of my libraries that requires JDK 17. Disclaimer: I am not affiliated with JitPack, and this is not official documentation.

In this installment, we'll see how to use your preferred Maven groupId with JitPack, rather than JitPack's default of com.github.USER (or similar for other git hosts). More importantly, we'll see how to do so effectively with ahead-of-time builds. JitPack is designed for on-demand builds, but does include a webhook that you can configure in your GitHub or Bitbucket repository to enable ahead-of-time builds. However, there isn't a built-in way of doing the same for a customized groupId, such as the conventional reverse domain name. The trick explained in this post may also be useful to those using JitPack with a git host not currently supported by JitPack's webhooks.

Additionally, the trick explained in this post utilizes GitHub Actions workflows, but you should be able to adapt the technique to CI/CD frameworks of other git hosts, or even from a shell script.

Table of Contents: The rest of this post is organized as follows:

How to use JitPack with a customized groupId

Maven Central, and other Maven repositories, typically use the reverse of a domain or subdomain controlled by a library's publisher as the Maven groupId. This is the conventional naming scheme for groupId. For example, for my libraries, I use org.cicirello as the groupId on Maven Central for all of my libraries. I can also, if I wanted to, use any org.cicirello.SUBDOMAIN on Maven Central, since I've verified ownership of the domain itself.

On JitPack, the groupId is instead based on a combination of git host (e.g., GitHub) and userid on that git host. This is because JitPack builds artifacts on-demand from the source repository the first time anyone imports a specific version via git tag, branch, commit hash, etc. The prior post of this series has a more detailed explanation of how JitPack works.

For consistency with artifacts published on Maven Central, or elsewhere, it would be nice to use the reverse domain approach on JitPack as well. And it turns out, JitPack does support this. You can find the details in the JitPack documentation. To accomplish this, you configure a TXT record with your DNS provider, mapping git.yourdomain.extension to https://github.com/USERNAME. When you, or someone else, references your library via a groupId equal to your reverse domain, JitPack checks for such a TXT record with your DNS provider, discovering the corresponding GitHub (or other git host) repository. It essentially creates an alias on JitPack.

The result is that via JitPack, your library can be imported either via JitPack's default git host / repository name approach, or with your reverse domain. An example for one of my libraries is as follows.

The default way of importing from JitPack involves adding the following to the pom.xml to use this as a dependency:

<dependency>
  <groupId>com.github.cicirello</groupId>
  <artifactId>chips-n-salsa</artifactId>
  <version>5.2.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

But once the domain is configured, the following works as well.

<dependency>
  <groupId>org.cicirello</groupId>
  <artifactId>chips-n-salsa</artifactId>
  <version>5.2.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

The latter is preferred since it matches the dependency specification required if importing from Maven Central. Conveniently for me, the repository name for this library is identical to the artifactId that I was already using on Maven Central, because JitPack doesn't provide a mechanism for using an artifactId that differs from repository name.

Note however that JitPack has an odd behavior that each of the above cases is treated as if they were distinct. The first time the artifact is imported using com.github.cicirello will require a build, and the first time that anyone imports using org.cicirello will also require a build. Thus, it is not exactly creating an alias. Both will build the library, producing jar files with identical contents, but with different names to the jar files, as if they were distinct artifacts.

How to build ahead-of-time with a customized groupId

Although JitPack is designed to build artifacts on-demand, once built, future requests by you or others for the same version no longer require a build. Thus, although the first import suffers the delay of waiting for a build, subsequent imports are as fast as most other Maven repositories (note: I have not actually timed this, but there is no human-discernible difference that I can see once built).

If you are simply using the git host / username combination for the groupId, then JitPack provides a webhook that you can configure on GitHub or Bitbucket. The details of JitPacks webhooks can be found in JitPack's documentation. Once configured, each push to your default branch (e.g., main) will utilize the webhook causing JitPack to build the current commit. In this way, the first actual import won't suffer from a delay awaiting a build. However, as we saw in the previous section, JitPack treats the custom reverse domain as a separate groupId from the git host based version. So those webhooks won't execute the ahead-of-time build against the custom reverse domain groupId. It also appears to only support GitHub and Bitbucket, so if your project is hosted elsewhere those webhooks may not be an option.

Trick 1:

After each push to the default branch, use curl to trick JitPack into thinking we're importing the latest repository snapshot, but using your reverse domain. JitPack will start a build. You don't actually want the artifacts. You just want to start a build. So use curl with a timeout to avoid waiting for the build.

Here's an example, assuming that your reverse domain is com.example, with a repository name (and thus artifactId) of REPOSITORY, and a default branch of main:

curl -s -m 30 https://jitpack.io/com/example/REPOSITORY/main-SNAPSHOT/
Enter fullscreen mode Exit fullscreen mode

The -m 30 above is a 30 second timeout. I've used a bit of trial-and-error on the timeout length. I started with 1 second, which often didn't start the build. Next, I tried 10 seconds, which did often start the build, but occasionally failed to get the build started on JitPack. 30 seconds seems to work consistently to get the build started.

Be aware that the above curl will actually "fail" with a non-zero error code once it times out (unless your project builds in under 30 seconds). Don't worry about it. You don't want the artifacts now anyway. And even if you did, the above curl won't give them to you. It will just serve a page where they are listed. You just want the artifacts to be available later. Assuming you use the above in a script, you might actually want something like what follows to avoid failing your entire job when curl times out.

curl -s -m 30 https://jitpack.io/com/example/REPOSITORY/main-SNAPSHOT/ || true
Enter fullscreen mode Exit fullscreen mode

Trick 2:

On each release of your library, use curl to trick JitPack into thinking we're importing the latest release via its corresponding version number. If the release tag is v1.2.3, then execute the following curl dropping the "v" (for consistency with other Maven repositories). This still assumes that your reverse domain is com.example, with a repository name of REPOSITORY, so just modify this for your real reverse domain and repository name.

curl -s -m 30 https://jitpack.io/com/example/REPOSITORY/1.2.3/ || true
Enter fullscreen mode Exit fullscreen mode

The URL in this example is where your release's artifacts will live once they are built. Requesting the URL to that directory gets the build started.

GitHub Actions workflows for ahead-of-time JitPack builds

If your project is on GitHub, you can put all of the above into one or more GitHub Actions workflows to automate this. Here's how I'm doing it.

On pushes to default branch:

On each push to the default branch (assumed main in this example), I'm using the following workflow (just fill in the details of your reverse domain, and default branch name, where relevant). This sample workflow makes the same assumptions as the previous section (e.g., that your reversed domain is com.example, etc). See jitpack-build.yml in one of my repositories for a live example.

name: jitpack-build

on:
  push:
    branches: [ main ]

jobs:
  jitpack:

    runs-on: ubuntu-latest

    steps:

    - name: Request main-SNAPSHOT from JitPack
      run: |
        # timeout in 30 seconds to avoid waiting for build
        curl -s -m 30 https://jitpack.io/com/example/REPOSITORY/main-SNAPSHOT/ || true
Enter fullscreen mode Exit fullscreen mode

Most GitHub workflows usually need an actions/checkout step, but we don't need that here because we're not actually doing anything with the code from our repository here.

On releases:

I use GitHub release events to start a workflow that builds and deploys artifacts to Maven Central as well as to GitHub Packages. At the end of that workflow, I added a step to handle the ahead-of-time builds on JitPack. Below is an abridged version of that workflow that only shows what is necessary for ahead-of-time builds on JitPack. It runs when you create a release. The first step removes the v from the release tag. After which, we use the curl trick.

name: Release

on:
  release:
    types: [created]

jobs:
  publish:

    runs-on: ubuntu-latest

    steps:
    - name: Get the release version, removing the v from the tag
      id: get_version
      run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}

    - name: Request release from JitPack to trigger build
      run: |
        JITPACK_URL="https://jitpack.io/com/example/REPOSITORY/${{ steps.get_version.outputs.VERSION }}/"
        # timeout in 30 seconds to avoid waiting for build
        curl -s -m 30 ${JITPACK_URL} || true
Enter fullscreen mode Exit fullscreen mode

The complete live workflow from my project is found here, if you want to see how I've incorporated this into a larger workflow that builds and deploys to Maven Central and GitHub Packages. That workflow is found within the following repository:

GitHub logo cicirello / Chips-n-Salsa

A Java library of Customizable, Hybridizable, Iterative, Parallel, Stochastic, and Self-Adaptive Local Search Algorithms

Chips-n-Salsa - A Java library of customizable, hybridizable, iterative, parallel, stochastic, and self-adaptive local search algorithms

Chips-n-Salsa - A Java library of customizable, hybridizable, iterative, parallel, stochastic, and self-adaptive local search algorithms

Copyright (C) 2002-2022 Vincent A. Cicirello.

Website: https://chips-n-salsa.cicirello.org/

API documentation: https://chips-n-salsa.cicirello.org/api/

Publications About the Library DOI
Packages and Releases Maven Central GitHub release (latest by date) JitPack
Build Status build docs CodeQL
JaCoCo Test Coverage coverage branches coverage
Security Snyk security score Snyk Known Vulnerabilities
DOI DOI
License GitHub
Support GitHub Sponsors Liberapay Ko-Fi

How to Cite

If you use this library in your research, please cite the following paper:

Cicirello, V. A., (2020). Chips-n-Salsa: A Java Library of Customizable, Hybridizable, Iterative, Parallel, Stochastic, and Self-Adaptive Local Search Algorithms. Journal of Open Source Software, 5(52), 2448, https://doi.org/10.21105/joss.02448 .

Overview

Chips-n-Salsa is a Java library of customizable, hybridizable, iterative, parallel, stochastic, and self-adaptive local search algorithms. The library includes implementations of several stochastic local search algorithms, including simulated annealing, hill climbers, as well as constructive search algorithms such as stochastic sampling. Chips-n-Salsa now also includes genetic algorithms as well as evolutionary algorithms more generally. The library very extensively supports simulated…

Where you can find me

On the Web:

Vincent A. Cicirello - Professor of Computer Science

Vincent A. Cicirello - Professor of Computer Science at Stockton University - is a researcher in artificial intelligence, evolutionary computation, swarm intelligence, and computational intelligence, with a Ph.D. in Robotics from Carnegie Mellon University. He is an ACM Senior Member, IEEE Senior Member, AAAI Life Member, EAI Distinguished Member, and SIAM Member.

favicon cicirello.org

Follow me here on DEV:

Follow me on GitHub:

GitHub logo cicirello / cicirello

My GitHub Profile

Vincent A Cicirello

Vincent A. Cicirello

Sites where you can find me or my work
Web and social media Personal Website LinkedIn DEV Profile
Software development Github Maven Central PyPI Docker Hub
Publications Google Scholar ORCID DBLP ACM Digital Library IEEE Xplore ResearchGate arXiv

My bibliometrics

My GitHub Activity

If you want to generate the equivalent to the above for your own GitHub profile, check out the cicirello/user-statistician GitHub Action.




Liberapay Ko-Fi
GitHub Sponsors

Top comments (1)

Collapse
 
cicirello profile image
Vincent A. Cicirello

The 30 second timeout used with curl in the examples isn't foolproof, but it is enough to get the build started most of the time. If you want to be absolutely sure it started, I believe you'd need to remove the timeout and wait for build to complete, probably also checking status code of the curl. That is overkill in my opinion wasting unnecessary resources when a simple 30 second timeout works almost every time. On the unusual case when it does not, only the first build of a dependent that requests the dependency will have a delay while it builds.