Automating Java Application Builds and Releases with GitHub Actions

Introduction

Agasthi Sankalana
7 min readNov 5, 2024

--

  • Briefly introduce the topic of Continuous Integration and Continuous Deployment (CI/CD).
  • Explain the benefits of using GitHub Actions for automating builds and releases, particularly for Spring Boot applications.
  • Mention what readers will learn from the blog.

Prerequisites

  • Basic knowledge of Git and GitHub.
  • Familiarity with Spring Boot and Gradle (or Maven, if applicable).
  • A GitHub repository with a Spring Boot application.

Step 1: Setting Up Your Spring Boot Application

  • Describe how to create a new Spring Boot application using Spring Initializr or an existing project.
  • Include any essential configurations in application.properties (like versioning if applicable).

Step 2: Create a GitHub Actions Workflow

1. Navigate to the Actions Tab:

  • Instruct the reader to go to their GitHub repository and click on the “Actions” tab.

2. Set Up a New Workflow:

  • Click on the “New Workflow” Button:
  • Select a Pre-defined Workflow Template:
  • After selecting a template, GitHub will generate a starter YAML file in the .github/workflows directory. Instruct the readers to modify this template as necessary to suit their project’s needs. They can refer to the YAML example you provided earlier for specific configurations.
  • rename the workflow file (e.g., ci-cd.yml) for clarity.
  • Explain that after the template is generated, the reader should review the contents and make adjustments, such as changing the trigger events, setting the Java version, or modifying the build steps to use Gradle commands.

Step 3: Define the Workflow

  • Provide an example workflow file and explain each section:
name: Java CI|CD with Gradle

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Grant execute permission for Gradlew
run: chmod +x ./gradlew

- name: Build and Test with Gradle
run: ./gradlew clean build test # Ensures code is compiled and tested

- name: Upload Build Artifact
uses: actions/upload-artifact@v3
with:
name: build-artifact
path: build/libs/*.jar # Uploads the .jar file for use in the release stage
  • Explain the on section: Triggers for the workflow (push and pull request to the master branch).
  • Describe the jobs section:
  • runs-on The environment where the workflow runs.
  • steps Individual steps in the job, including checking out the code, setting up JDK, and building the project.
  • The permissions key allows us to specify the permissions for the job. Here, we're giving write access to the repository's contents.
  • The actions/checkout action retrieves the repository code, making it available for subsequent steps.
  • Next, we need to set up the Java Development Kit (JDK) required for our Gradle build. Here, we are using JDK 21 from the Temurin distribution:
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
  • This step ensures that the correct version of Java is available in the workflow environment.
  • Once the JDK is set up, we proceed to configure Gradle, which is the build automation tool we will use:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

Before building the project, we need to ensure that the Gradle wrapper script (gradlew) has execute permissions:

- name: Grant execute permission for Gradlew
run: chmod +x ./gradlew
  • This step is crucial, especially on Unix-based systems, to allow the script to be executed.
  • Now, we can build and test our project using Gradle. This step compiles the code, runs tests, and generates build artifacts:
- name: Build and Test with Gradle
run: ./gradlew clean build test # Ensures code is compiled and tested
  • The clean command cleans up any previous builds, build compiles the code, and test runs the unit tests. If any tests fail, the workflow will stop, indicating issues in the code.
  • Finally, after building the project, we upload the generated JAR files as build artifacts:
- name: Upload Build Artifact
uses: actions/upload-artifact@v3
with:
name: build-artifact
path: build/libs/*.jar
  • This step ensures that the built JAR files are stored and can be accessed later. The artifacts can be useful for deployments or further testing.

Step 4: Generate and Release Artifacts

  • Explain how to generate a version tag and release artifacts, such as JAR files:
  release:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/master'
permissions:
contents: write

steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.TOKEN }}

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Download Build Artifact
uses: actions/download-artifact@v3
with:
name: build-artifact
path: ./artifact # Specify the directory where the artifact will be downloaded

- name: Generate Version Tag
id: generate_tag
run: |
APP_VERSION=$(grep '^app.version=' src/main/resources/application.properties | cut -d'=' -f2)
TAG="v-${APP_VERSION}"
echo "tag=${TAG}" >> $GITHUB_ENV

- name: Create Tag
run: |
git tag ${{ env.tag }}
git push origin ${{ env.tag }}
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}

- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: ./artifact/*.jar # Use the path where the artifact was downloaded
tag_name: ${{ env.tag }}
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
  • The release job is defined to run on the ubuntu-latest runner. It specifies that it depends on the successful completion of the build job and will only run when changes are pushed to the master branch.
release:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/master'
permissions:
contents: write
  • needs: build: This indicates that the release job will only execute if the build job has completed successfully.
  • if: github.ref == 'refs/heads/master': This condition ensures the job runs only when the event is triggered from the master branch.
  • permissions: contents: write: This grants permission to write to the repository's contents, necessary for tagging.
  • This uses the actions/checkout action to retrieve the code. The token input is used to authenticate the action with a GitHub token stored in the repository secrets for security.
- uses: actions/checkout@v4
with:
token: ${{ secrets.TOKEN }}

The ${{ secrets.TOKEN }} notation in GitHub Actions is used to access sensitive information securely. In this case, it refers to a GitHub secret named TOKEN. Secrets are encrypted environment variables that GitHub makes available to your workflows, allowing you to store sensitive data like API tokens, passwords, or any other confidential information without exposing them in your code.

  • For more detailed instructions and examples, you can refer to the official GitHub documentation on configuring secrets: How to Configure a GitHub Secret
  • Next, we set up the Java Development Kit:
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
  • This step ensures that JDK 21 is available for the subsequent tasks, such as creating the release.
  • After setting up the JDK, we need to download the previously built artifacts from the build job:
- name: Download Build Artifact
uses: actions/download-artifact@v3
with:
name: build-artifact
path: ./artifact # Specify the directory where the artifact will be downloaded
  • Here, we use the actions/download-artifact action to retrieve the JAR files generated during the build process and place them in the specified directory (./artifact).
  • Before creating a Git tag, we need to determine the application version. This step extracts the version number from the application.properties file:
app.version=1.0.3
- name: Generate Version Tag
id: generate_tag
run: |
APP_VERSION=$(grep '^app.version=' src/main/resources/application.properties | cut -d'=' -f2)
TAG="v-${APP_VERSION}"
echo "tag=${TAG}" >> $GITHUB_ENV
  • grep '^app.version=' src/main/resources/application.properties: This command searches for the line starting with app.version= in the properties file to find the version number.
  • cut -d'=' -f2: This extracts the version number by splitting the line at the = character.
  • echo "tag=${TAG}" >> $GITHUB_ENV: This stores the generated tag in the environment variable tag, making it available for subsequent steps.
  • Once the version tag is generated, the next step is to create a Git tag in the repository:
- name: Create Tag
run: |
git tag ${{ env.tag }}
git push origin ${{ env.tag }}
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
  • git tag ${{ env.tag }}: This command creates a new tag in the repository using the version tag generated in the previous step.
  • git push origin ${{ env.tag }}: This pushes the newly created tag to the remote repository on GitHub.
  • Finally, we create a GitHub release using the tagged version and the downloaded artifacts:
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: ./artifact/*.jar # Use the path where the artifact was downloaded
tag_name: ${{ env.tag }}
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
  • uses: softprops/action-gh-release@v1: This action is used to create a release on GitHub.
  • files: ./artifact/*.jar: This specifies the JAR files that will be attached to the release. The files downloaded in the previous step will be uploaded here.
  • tag_name: ${{ env.tag }}: This associates the release with the newly created tag.
  • env: GITHUB_TOKEN: ${{ secrets.TOKEN }}: Again, we use the GitHub token for authentication to ensure the action can create a release.
  • ou can check the running status of your actions and the releases created by your workflow in the “Actions” tab of your GitHub repository. This tab will provide you with a detailed view of all the workflows that have been executed, including their status (success or failure), logs, and any artifacts generated.
  • Additionally, you can view the releases created by your workflow under the “Releases” section, which will list all the tagged releases along with the corresponding artifacts, such as your built .jar files.
  • For more information on managing actions and releases, you can refer to GitHub’s official documentation on GitHub Actions and Releases. 🚀
  • Now you can release your Java project with GitHub automations! By setting up workflows in GitHub Actions, you have streamlined the build and release process. You’ve learned how to configure automated builds, run tests, and create releases with tagged versions of your project.
  • This automation not only saves time but also enhances your workflow efficiency, allowing you to focus more on coding and less on manual deployment processes. For more detailed information on setting up GitHub Actions and managing releases, check out the official documentation on GitHub Actions and Managing releases. 🚀

Happy coding 😁, and enjoy your journey with GitHub! 🙋‍♂️

--

--

Agasthi Sankalana
Agasthi Sankalana

Written by Agasthi Sankalana

Blogger | Gamer 🎮 | Backend Dev | Devops | SLIIT 🎓

No responses yet