DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Vincent A. Cicirello
Vincent A. Cicirello

Posted on

Deploying Java Artifacts to Multiple Maven Repositories with GitHub Actions

I use GitHub Actions to manage the CI/CD workflows for all of my projects. In this post, I explain my deployment workflow for Java projects. It is launched upon a GitHub release. It deploys the artifacts to both the Maven Central repository as well as to GitHub Packages. It triggers an ahead-of-time build on JitPack. And it uses the GitHub CLI to also upload the artifacts to the GitHub release.

This post assumes knowledge of configuring your Maven pom.xml as necessary for deployment to multiple Maven repositories, using Maven profiles for each of the repositories, as covered in my prior post:

This post also uses the trick explained in another prior post for triggering an ahead-of-time build on JitPack when using a reverse domain Maven groupId, rather than JitPack's default of on-demand builds:

My workflow that I explain in this post relies upon GitHub Releases. Creating a GitHub Release is the event that starts the workflow, and the workflow attaches the jar files to the release as one of its steps. For more information on GitHub Releases, and other things you can do with them, see @mishmanners great post from earlier today:

Table of Contents:

Workflow Step by Step

I'm going to walk through the full workflow from beginning to end.

On release

GitHub workflows begin with the events that trigger it to run. In this case, the workflow runs when a release is created.

name: Maven Package

on:
  release:
    types: [created]
Enter fullscreen mode Exit fullscreen mode

Set up the job

My deployment workflow has a single job and runs on Ubuntu. I define an environment variable artifact_name with the Maven artifactId that is used in a few of the steps of the workflow. This isn't strictly necessary, but I use the same workflow in several repositories, so it makes it simple to set things up for another repository. I haven't gotten around to consolidating into a reusable workflow since those were recently introduced by GitHub.

jobs:
  publish:

    runs-on: ubuntu-latest

    env:
      artifact_name: chips-n-salsa
Enter fullscreen mode Exit fullscreen mode

Checkout the repository

This step is self-explanatory, and is found at the start of most GitHub workflows.

    steps:
    - uses: actions/checkout@v3
Enter fullscreen mode Exit fullscreen mode

Process the release tag

I use the common release tag convention that begins with a v followed by the Semantic Version. For example, v1.2.3. However, Maven versions don't include a v. So this step gets the release tag, removes the v from it, and sets an output from the step named VERSION that can be used by later steps of the workflow.

    - name: Get the release version
      id: get_version
      run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT
Enter fullscreen mode Exit fullscreen mode

For more information on setting outputs from workflow steps, see my prior DEV post from last week:

Inject the release version into the pom.xml

We need the <version></version> inside the pom.xml to correspond to the version we are deploying. Remembering to edit the pom.xml manually is error prone, and easily forgotten. Instead, I inject the release version during the deployment workflow by using mvn versions:set. This command actually changes the pom.xml. If I wanted the pom.xml within the repository to be up to date with the latest release, I could add a step to commit the change. However, I leave it as a snapshot version in the repository (e.g., <version>6-SNAPSHOT</version>) to avoid confusing anyone who may build from the source into believing they've built a release version.

The step to inject the release version into the pom.xml uses the output from the previous step above and the mvn versions:set command as follows:

    - name: Update package version
      run: mvn versions:set -DnewVersion=${{ steps.get_version.outputs.VERSION }}
Enter fullscreen mode Exit fullscreen mode

Set up Java and deploy to Maven Central

This pair of steps first uses the setup-java GitHub Action to set up what we need to deploy to the Maven Central repository. It is mostly as described in the GitHub Actions documentation.

The second of this pair of steps runs mvn deploy -PossrhDeploy. The command line option -PossrhDeploy activates a Maven profile with an id of ossrhDeploy that I have defined in the pom.xml where I've configured everything needed for deployment to Maven Central. See my prior post on using Maven profiles for a tutorial on how Maven profiles can be used to selectively activate configuration, along with a specific example of configuring Maven to deploy to multiple repositories.

    - name: Set up JDK 17 for deploy to OSSRH
      uses: actions/setup-java@v3
      with:
        distribution: 'adopt'
        java-version: '17'
        server-id: ossrh 
        server-username: MAVEN_USERNAME
        server-password: MAVEN_CENTRAL_TOKEN
        gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
        gpg-passphrase: MAVEN_GPG_PASSPHRASE

    - name: Publish to Apache Maven Central
      run: mvn deploy -PossrhDeploy
      env:
        MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
        MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
        MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
Enter fullscreen mode Exit fullscreen mode

Set up Java and deploy to GitHub Packages

This pair of steps is a counterpart to the above pair of steps, but this time for deployment to GitHub Packages. The first step below uses setup-java, but this time for deployment to GitHub Packages. It is simpler than what is needed to deploy to Maven Central. The second step below does the actual deployment using: mvn deploy -PgithubDeploy. Just like above, I've configured the <distributionManagement><repository> in a Maven profile with an id of githubDeploy, which is activated by the command line option -PgithubDeploy.

    - name: Set up JDK 17 for deploy to github packages
      uses: actions/setup-java@v3
      with:
        distribution: 'adopt'
        java-version: '17'
        server-id: github 

    - name: Publish to GitHub Packages Apache Maven
      run: mvn deploy -PgithubDeploy
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 
Enter fullscreen mode Exit fullscreen mode

Use the GitHub CLI to upload the jar files as release assets

I also upload the various jar files produced during the build as release assets. I use the GitHub CLI to do this, which is always available to use during GitHub workflow runs. Specifically, I use the gh release upload command, which requires the tag of the release, as well as the file you are uploading. The project this is based on produces 4 jar files, including the usual 3 (jar of the library, jar of the sources, and jar of the javadocs), and the fourth jar is the library including all dependencies. Since I'm using Maven, the artifacts are created in a target directory during the build, and I use the environment variable I set at the very beginning with the Maven artifactId and the output from an earlier step with the Maven <version> (release tag without the v) to form the filenames.

    - name: Upload jar files to release as release assets
      run: |
        TAG=${GITHUB_REF/refs\/tags\//}
        gh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}.jar
        gh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}-sources.jar
        gh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}-javadoc.jar
        gh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}-jar-with-dependencies.jar 
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

Ahead-of-time JitPack release build

In a post some time back, I explained a trick to get JitPack to build ahead-of-time using your reverse domain groupId. JitPack normally build on-demand the first time an artifact is requested. It does so from the GitHub repository of the artifact. Thus, the first time someone imports a version of your library from JitPack, their build will be delayed while waiting for a build of your library. We can eliminate that delay with a simple curl as seen in the step below. The timeout is necessary because you don't actually need to download anything now. You just want JitPack to think you want to download the artifacts. By using a timeout, you avoid wasting cycles on the runner of your workflow.

IMPORTANT: If you use this trick in your own workflow, make sure you change the org/cicirello to whatever reverse domain you have configured with JitPack as your groupId.

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

Complete Workflow

Here's my full Java deployment workflow.

name: Maven Package

on:
  release:
    types: [created]

jobs:
  publish:

    runs-on: ubuntu-latest

    env:
      artifact_name: chips-n-salsa

    steps:
    - uses: actions/checkout@v3

    - name: Get the release version
      id: get_version
      run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT

    - name: Update package version
      run: mvn versions:set -DnewVersion=${{ steps.get_version.outputs.VERSION }}

    - name: Set up JDK 17 for deploy to OSSRH
      uses: actions/setup-java@v3
      with:
        distribution: 'adopt'
        java-version: '17'
        server-id: ossrh 
        server-username: MAVEN_USERNAME
        server-password: MAVEN_CENTRAL_TOKEN
        gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
        gpg-passphrase: MAVEN_GPG_PASSPHRASE

    - name: Publish to Apache Maven Central
      run: mvn deploy -PossrhDeploy
      env:
        MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
        MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
        MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}

    - name: Set up JDK 17 for deploy to github packages
      uses: actions/setup-java@v3
      with:
        distribution: 'adopt'
        java-version: '17'
        server-id: github 

    - name: Publish to GitHub Packages Apache Maven
      run: mvn deploy -PgithubDeploy
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 

    - name: Upload jar files to release as release assets
      run: |
        TAG=${GITHUB_REF/refs\/tags\//}
        gh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}.jar
        gh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}-sources.jar
        gh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}-javadoc.jar
        gh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}-jar-with-dependencies.jar 
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

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

Live Example

To see a live example, consult the maven-publish.yml workflow of one of my projects. Here is the GitHub 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 Mentioned in Awesome Machine Learning

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 annealing. It includes several classes for representing solutions to a variety of optimization problems. For…

Where You Can Find Me

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.




Or visit my website:

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

Top comments (1)

Collapse
 
user1111333 profile image
Sacred (void*)

Awesome, finally supporting "Maven Central". Now I can finally die in peace.

Timeless DEV post...

Arguably the single most important piece of documentation for any open source project is the README. A good README not only informs people what the project does and who it is for but also how they use and contribute to it.

How to write a kickass README