Streamlining Java Distribution: A Comprehensive Guide to JReleaser

Article

For Java developers, building the application is only half the battle. Distributing it to users across multiple platforms in a professional, automated manner has traditionally been complex and time-consuming. JReleaser solves this problem by providing a powerful, flexible tool that automates the entire release and distribution process for Java projects.


What is JReleaser?

JReleaser is a Java-based release automation tool that simplifies publishing artifacts to multiple distribution channels. It handles the entire release lifecycle—from building native images and containers to publishing on GitHub, GitLab, Maven Central, and various package managers.

Key Benefits for Java Teams:

  • Multi-Platform Distribution: Generate and publish artifacts for Windows, macOS, and Linux
  • Native Image Support: Seamless integration with GraalVM Native Image
  • Package Manager Integration: Publish to Homebrew, SDKMAN!, Chocolatey, Scoop, and more
  • Containerization: Build and publish Docker images to multiple registries
  • Release Automation: Fully automate your release process via CI/CD
  • Standardized Releases: Consistent release process across all your Java projects

Getting Started with JReleaser

Installation

Using SDKMAN!:

sdk install jreleaser

Using Homebrew:

brew install jreleaser

Using Docker:

docker run -it --rm -v "$(pwd):/project" jreleaser/jreleaser:latest

As a Maven Plugin: Add to your pom.xml:

<plugin> <groupId>org.jreleaser</groupId> <artifactId>jreleaser-maven-plugin</artifactId> <version>1.8.0</version> <!-- Check for latest version --> </plugin>

As a Gradle Plugin: Add to your build.gradle:

plugins { id 'org.jreleaser' version '1.8.0' }

Configuration Guide

JReleaser is configured via a jreleaser.yml file in your project root. Here's a comprehensive example:

project: name: "my-java-app" description: "A fantastic Java application" version: "1.0.0" authors: - "John Doe <[email protected]>" license: "Apache-2.0" java: version: "17" links: homepage: "https://my-java-app.example.com" release: github: owner: "myorganization" name: "my-java-app" overwrite: true tagName: "v{{projectVersion}}" releaseName: "{{projectName}} {{projectVersion}}" updateLatest: true distributions: app: type: JAVA_BINARY executable: "my-app" artifacts: - "target/my-java-app-{{projectVersion}}.jar" java: mainClass: "com.example.MyApp" mainModule: "com.example.myapp" # For modular applications brew: active: ALWAYS tap: owner: "myorganization" name: "homebrew-tap" multiPlatform: true sdkman: active: ALWAYS candidate: "myapp" releaseNotesUrl: "https://github.com/myorganization/my-java-app/releases/tag/v{{projectVersion}}" chocolatey: active: ALWAYS packageName: "my-java-app" scoop: active: ALWAYS bucket: owner: "myorganization" name: "scoop-bucket" assemble: jlink: jdks: - "17" imageName: "my-app" modulePath: "target/modules" modules: - "com.example.myapp" jpackage: types: - "app-image" - "dmg" - "pkg" - "msi" - "deb" jdks: - "17" mainJar: "my-java-app-{{projectVersion}}.jar" mainClass: "com.example.MyApp" native: graalvm: active: ALWAYS imageName: "my-app" mainClass: "com.example.MyApp" deploy: maven: mavenCentral: active: ALWAYS sonatype: url: "https://s01.oss.sonatype.org/service/local"

Platform-Specific Distribution Examples

1. Homebrew Tap (macOS/Linux)

JReleaser can automatically generate and publish Homebrew formulae:

distributions: app: type: JAVA_BINARY brew: active: ALWAYS tap: owner: "myorganization" name: "homebrew-tap" formulaName: "my-java-app" dependencies: - "openjdk@17" install: | libexec.install "my-app.jar" bin.write_jar_script libexec/"my-app.jar", "my-app"

2. SDKMAN! Integration

Publish your application as an SDK candidate:

distributions: app: sdkman: active: ALWAYS candidate: "myapp" command: "MY_APP_COMMAND" url: "https://github.com/myorganization/my-java-app/releases/download/v{{projectVersion}}/my-app-{{projectVersion}}.zip" hashtag: "#java"

3. Chocolatey (Windows)

Create and publish Windows packages:

distributions: app: chocolatey: active: ALWAYS packageName: "my-java-app" title: "My Java Application" version: "{{projectVersion}}" dependencies: - "openjdk" iconUrl: "https://my-app.com/icon.png"

4. Docker Distribution

Build and publish multi-architecture Docker images:

distributions: app: type: SINGLE_JAR docker: active: ALWAYS spec: name: "myorganization/my-java-app" tags: - "latest" - "{{projectVersion}}" buildx: enabled: true platforms: - "linux/amd64" - "linux/arm64"

GraalVM Native Image Support

JReleaser seamlessly integrates with GraalVM to build and distribute native executables:

native: graalvm: active: ALWAYS imageName: "my-app" mainClass: "com.example.MyApp" args: - "--no-fallback" - "--enable-http" - "--enable-https" jdks: - "graalvm_21" distributions: native-app: type: NATIVE_IMAGE artifacts: - "target/my-app" brew: active: ALWAYS install: | bin.install "my-app"

Maven Integration

Basic Maven Configuration

Add the JReleaser plugin to your pom.xml:

<build> <plugins> <plugin> <groupId>org.jreleaser</groupId> <artifactId>jreleaser-maven-plugin</artifactId> <version>1.8.0</version> <configuration> <jreleaser> <project> <name>${project.name}</name> <version>${project.version}</version> </project> </jreleaser> </configuration> </plugin> </plugins> </build>

Maven Commands

# Dry run - see what would be executed mvn jreleaser:config # Full release mvn jreleaser:deploy # Only assemble distributions mvn jreleaser:assemble # Only publish release mvn jreleaser:release

Gradle Integration

Basic Gradle Configuration

Add to your build.gradle:

jreleaser { project { name = project.name version = project.version } release { github { owner = 'myorganization' name = project.name } } }

Gradle Commands

# Dry run ./gradlew jreleaserConfig # Full release ./gradlew jreleaserDeploy # Assemble only ./gradlew jreleaserAssemble

CI/CD Integration

GitHub Actions Example

name: Release on: push: tags: - 'v*' jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Java uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: Build project run: mvn -B package -DskipTests - name: Setup JReleaser uses: jreleaser/release-action@v2 with: version: latest - name: Full release run: jreleaser deploy env: JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} JRELEASER_SDKMAN_CONSUMER_KEY: ${{ secrets.SDKMAN_KEY }} JRELEASER_SDKMAN_CONSUMER_TOKEN: ${{ secrets.SDKMAN_TOKEN }}

GitLab CI Example

release: image: jreleaser/jreleaser:latest rules: - if: $CI_COMMIT_TAG =~ /^v/ script: - jreleaser deploy variables: JRELEASER_GITLAB_TOKEN: $GITLAB_TOKEN

Best Practices for Java Distribution

  1. Start Simple: Begin with GitHub releases, then add package managers incrementally
  2. Use Environment Variables: Store sensitive tokens in environment variables or CI secrets
  3. Version Management: Use semantic versioning and consistent tagging (v1.0.0)
  4. Multi-Platform Testing: Test distributions on all target platforms
  5. Incremental Adoption: Add one distribution channel at a time to validate the process
  6. Documentation: Include installation instructions for each distribution method
  7. Signing: Always sign your artifacts for security:
signing: active: ALWAYS armored: true mode: COMMAND command: "gpg" keyName: "[email protected]" publicKey: "{{env.HOME}}/.gnupg/pubring.gpg"

Complete Workflow Example

# 1. Prepare your release mvn clean package # 2. Dry run to validate configuration jreleaser config # 3. Assemble distributions locally jreleaser assemble # 4. Full release (if dry run looks good) jreleaser deploy

Conclusion

JReleaser transforms the complex, error-prone process of Java application distribution into a streamlined, automated workflow. By supporting multiple package managers, native images, containers, and traditional distribution methods, it provides a comprehensive solution for modern Java development.

The key advantage is consistency—once you configure JReleaser for one project, you can replicate the same professional release process across all your Java applications. This not only saves time but also ensures your users get a consistent, reliable installation experience regardless of their platform or preferred package manager.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper