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:
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 Packageon:release:types:[created]
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.
This step is self-explanatory, and is found at the start of most GitHub workflows.
steps:-uses:actions/checkout@v3
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 versionid:get_versionrun:echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT
For more information on setting outputs from workflow steps, see my prior DEV post from last week:
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:
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 OSSRHuses:actions/setup-java@v3with:distribution:'adopt'java-version:'17'server-id:ossrhserver-username:MAVEN_USERNAMEserver-password:MAVEN_CENTRAL_TOKENgpg-private-key:${{ secrets.MAVEN_GPG_PRIVATE_KEY }}gpg-passphrase:MAVEN_GPG_PASSPHRASE-name:Publish to Apache Maven Centralrun:mvn deploy -PossrhDeployenv:MAVEN_USERNAME:${{ secrets.MAVEN_CENTRAL_USERNAME }}MAVEN_CENTRAL_TOKEN:${{ secrets.MAVEN_CENTRAL_TOKEN }}MAVEN_GPG_PASSPHRASE:${{ secrets.MAVEN_GPG_PASSPHRASE }}
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 packagesuses:actions/setup-java@v3with:distribution:'adopt'java-version:'17'server-id:github-name:Publish to GitHub Packages Apache Mavenrun:mvn deploy -PgithubDeployenv:GITHUB_TOKEN:${{ secrets.GITHUB_TOKEN }}
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.
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 buildrun:|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 buildcurl -s -m 30 ${JITPACK_URL} || true
Complete Workflow
Here's my full Java deployment workflow.
name:Maven Packageon:release:types:[created]jobs:publish:runs-on:ubuntu-latestenv:artifact_name:chips-n-salsasteps:-uses:actions/checkout@v3-name:Get the release versionid:get_versionrun:echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT-name:Update package versionrun:mvn versions:set -DnewVersion=${{ steps.get_version.outputs.VERSION }}-name:Set up JDK 17 for deploy to OSSRHuses:actions/setup-java@v3with:distribution:'adopt'java-version:'17'server-id:ossrhserver-username:MAVEN_USERNAMEserver-password:MAVEN_CENTRAL_TOKENgpg-private-key:${{ secrets.MAVEN_GPG_PRIVATE_KEY }}gpg-passphrase:MAVEN_GPG_PASSPHRASE-name:Publish to Apache Maven Centralrun:mvn deploy -PossrhDeployenv: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 packagesuses:actions/setup-java@v3with:distribution:'adopt'java-version:'17'server-id:github-name:Publish to GitHub Packages Apache Mavenrun:mvn deploy -PgithubDeployenv:GITHUB_TOKEN:${{ secrets.GITHUB_TOKEN }}-name:Upload jar files to release as release assetsrun:|TAG=${GITHUB_REF/refs\/tags\//}gh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}.jargh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}-sources.jargh release upload ${TAG} target/${{ env.artifact_name }}-${{ steps.get_version.outputs.VERSION }}-javadoc.jargh 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 buildrun:|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 buildcurl -s -m 30 ${JITPACK_URL} || true
Live Example
To see a live example, consult the maven-publish.yml workflow of one of my projects. Here is the GitHub repository:
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…
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.