Effortlessly Deliver Your Java Application as a Windows exe using GitHub Actions

Effortlessly Deliver Your Java Application as a Windows exe using GitHub Actions

When distributing Java applications, providing a professional Windows installer is crucial for a seamless installation experience. In this tutorial, we'll explore how to leverage jpackage, WIX Toolset, and GitHub Actions to automate the creation and deployment of a Windows EXE installer for your Java application. By combining these tools, you can streamline the packaging process and ensure a user-friendly installation for your software. At the end of this tutorial, we would create a release of the installer on GitHub Releases.

In other, to follow this tutorial, you need to create a working Java application that you want to convert into an exe. In my case, it’s a simple “helloworld” Java GUI application.

Prerequisites:

  • Familiarity with Java

  • A Simple GUI application in Java

  • Familiarity with GitHub Actions

  • A GitHub account

Before we start configuring our GitHub Actions workflow, we need to get familiar with some terminologies:

  1. Jpackage: jpackage is a JDK tool that simplifies the distribution of Java applications by creating self-contained executable packages for different operating systems. It allows you to generate native installers for Windows, macOS, and Linux, making it easier to distribute and install your Java applications on various platforms.

  2. WIX: WIX (Windows Installer XML) Toolset is a collection of tools for creating Windows Installer packages (MSI). It uses XML-based language to define installation logic and provides a compiler and linker for generating the final package. When combined with jpackage, WIX serves as a dependency that allows you to create professional Windows installers with custom features and a user-friendly interface for your Java applications.

Step 1: Creating a workflow file

  1. To begin, we first need to create a yml file in the root directory of the source code. This can be done manually or by typing the commands below:
mkdir .github
cd .github; mkdir workflows
cd workflows; New-Item ci-cd.yml -type file

2. Open the ci-cd.yml file and paste the workflow code below into it.

name: CI/CD Pipeline
on:
  workflow_dispatch:
  #Trigger the workflow on pull req from the prod br
  pull_request:
    types: [closed]
    branches:
      - master

jobs:
  # Build and  Deploy
  install_prerequisite:
    name: Prerequisite Install
    runs-on: windows-latest
    steps:
      - name: Checkout Code
        #checkout code
        uses: actions/checkout@v3
        #Set up JDK 17
      - name: Set up JDK
        uses: actions/setup-java@v2
        with:
          java-version: '17'
          distribution: 'adopt'

      - name: Install dotnet and wix311
        run: |
          curl -H "Connection: close" -o dotnet.exe https://download.visualstudio.microsoft.com/download/pr/56785524-dcd2-425a-8a95-3c2ee607b22f/e32ce2d12821f85c7d5e9cdee5ff5264/dotnet-sdk-6.0.411-win-x64.exe
          Start-Process ./dotnet.exe -ArgumentList "/S /v/qn"
          Expand-Archive -Path wix311.zip -DestinationPath .
          Start-Process ./wix311.exe -ArgumentList "/S /v/qn"
        shell: pwsh

      - name: Package Installer
        run: |
          curl -o jmods.zip https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_windows-x64_bin-jmods.zip
          Expand-Archive -Path jmods.zip -DestinationPath .
          mvn -B package -DskipTests --file pom.xml
          jpackage --type exe --input . --dest . --main-jar .\target\HelloWorldApp-1.0-SNAPSHOT.jar --main-class com.genkey.helloworldapp.HelloApplication --module-path ".\javafx-jmods-20.0.1" --add-modules javafx.controls,javafx.fxml --win-shortcut --win-menu

      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.TOKEN_FOR_EXE }}
        with:
          tag_name: v1.0.0  # Specify the desired tag name
          release_name: Release v1.0.0  # Specify the desired release name
          body: |
            Release v1.0.0
          draft: false
          prerelease: false

      - name: Attach Artifact to Release
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.TOKEN_FOR_EXE }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: .\HelloApplication-1.0.exe
          asset_name: HelloApplication-1.0.exe
          asset_content_type: application/octet-stream

NB: Generate a GitHub Token and store it as a secret, and use the secret name in the workflow file. In my case, I named my secret TOKEN_FOR_EXE

Step 2: Workflow Dispatch

At the top of the code is a workflow dispatch. This helps to set a condition on which the jobs or actions are run. In the case of this tutorial, the jobs will run whenever a pull request is merged to the master branch.

Step 3: Configuring Prerequisite

  1. The workflow starts with the “Prerequisite Install” job, which runs on a Windows environment.

  2. The job begins by checking out the code from the repository and setting up JDK 17 using the actions/setup-java action.

  3. Next, it installs .NET by downloading the required binaries using curl and running the installers silently.

  4. In line 30, we are extracting WIX 3.11.zip, so I am sure you are probably wondering how it got there. Unfortunately, using curl to download Wix was not successful, so I had to download it, zip it and move it to the root of the HelloWorldApp. You can download WiX311 from this link.

  5. Once this is done, the script on lines 30 and 31 will extract the zip and install the WIX exe.

  6. PowerShell (pwsh) shell is used for executing these commands.

Step 4: Packaging the Installer

  1. In this step, the job packages the application as a Windows exe installer.

  2. It begins by downloading the JavaFX jmods required for the application using curl.

  3. The jmods are extracted using Expand-Archive.

  4. Then, the Maven command mvn package is run to build the Java application, skipping tests.

  5. Finally, jpackage is used to create the exe installer by specifying the input, destination, main JAR file, main class, module path, required modules, and additional options.

Step 4: Publishing the EXE to GitHub Releases

  1. The job uses actions/upload-artifact to publish the generated exe installer as an artifact within the workflow.

  2. It creates a release using actions/create-release, specifying the desired tag name, release name, and release body.

  3. Next, it attaches the artefact (the exe installer) to ‘Releases’ on GitHub using actions/upload-release-asset.

Step 4: Download and install the exe

  1. Once the release has been created, click on it to download

  2. Double-click the installer to install it.

  3. Open the app launcher on your desktop or press the Windows key and search for the name of your app, click to launch it.

Conclusion

In this tutorial, we've covered how to set up a CI/CD pipeline using GitHub Actions to automate the creation and deployment of a Windows exe installer for a Java application. By configuring the workflow, installing prerequisites, packaging the installer with jpackage, and publishing it to GitHub Packages, you can streamline the process of creating a professional Windows installer for your Java application. With this automated pipeline, you can easily distribute your application and ensure a seamless installation experience for your users.

Remember to customize the workflow according to your specific project needs, such as adjusting the Java version, paths, and configuration options for jpackage and WIX.