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:
How to Use Maven Profiles to Selectively Activate Plugins and Other Configuration from the Command Line
Vincent A. Cicirello ・ Oct 19 '22
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:
Ahead-of-time JitPack Builds with Custom GroupId via GitHub Actions
Vincent A. Cicirello ・ Aug 11 '22
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:
How to automatically ship releases for your project. Why release?
Michelle Duke for GitHub ・ Nov 2 '22
Table of Contents:
- Workflow Step by Step walks you through my deployment workflow one step at a time.
- Complete Workflow.
- Live Example from one of my projects on GitHub.
- Where You Can Find Me.
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]
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
Checkout the repository
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 version
id: get_version
run: 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:
How to Patch the Deprecated set-output in GitHub Workflows and in Container Actions
Vincent A. Cicirello ・ Oct 26 '22
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 }}
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 }}
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 }}
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 }}
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
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
Live Example
To see a live example, consult the maven-publish.yml workflow of one of my projects. Here is the GitHub repository:
cicirello / Chips-n-Salsa
A Java library of Customizable, Hybridizable, Iterative, Parallel, Stochastic, and Self-Adaptive Local Search Algorithms
Copyright (C) 2002-2024 Vincent A. Cicirello.
Website: https://chips-n-salsa.cicirello.org/
API documentation: https://chips-n-salsa.cicirello.org/api/
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…
Where You Can Find Me
Follow me here on DEV:
Follow me on GitHub:
Vincent A Cicirello
View My Detailed 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:
Top comments (1)
Awesome, finally supporting "Maven Central". Now I can finally die in peace.