Github: locally invoked release process for a Gradle built Java Spring Boot project

The GitHub “Release” page for a repository can provide your consumers a convenient way to download a binary version of your software as well as track the latest changes and enhancements.

In this article, I will show how to invoke a local release process for a Java Spring Boot jar built with Gradle.  A new Github release will be created based on semantic tags (e.g. v1.0.1) that includes the relevant commits as release notes and the Spring Boot jar as an asset.

If you want to instead use an automated Github Actions workflow to generate the release, see my other article here.

Prerequisites

  • Install the Github CLI
  • Install a Java compiler
    • sudo apt search openjdk-* | grep ‘^openjdk\-‘ | grep ‘\-jdk’
    • sudo apt install openjdk-xx-jdk

Calculate Semantic version tag

Our process will be to create a semantic tag for every release (e.g. v1.0.1).   If there is already a semantic tag defined, we will increment its minor patch to get the next release (e.g. v1.0.1 -> v1.0.2).

We need to discover if the current branch is already semantically tagged.  If it is, then we will increment the minor patch.  If not, then we assume we are at “v1.0.0”

# get current list of semantic tags
git tag --sort=-committerdate | grep ^v

# get latest semantic tag, will be empty if none defined yet
semantic_version=$(git tag --sort=-committerdate | grep ^v | grep -Po '^v[0-9]*.[0-9]*.[0-9]*' | head -n1)

# if non-empty, calculate next patch version
major_minor=$(echo "$semantic_version" | cut -d'.' -f1-2)
patch=$(echo "$semantic_version" | cut -d'.' -f3)
((patch++))
newtag="${major_minor}.${patch}"

# if latest semantic_version was empty, that means we are starting fresh
[ -n "$semantic_version" ] || newtag="v1.0.0"

echo "The new semantic tag is: $newtag"

Gradle build of Spring Boot jar

You can view my build.gradle for an example, or you can build a project from scratch using https://start.spring.io/

# should have a 'bootJar' task
./gradlew tasks | grep bootJar

# build jar without version name in suffix
./gradlew bootJar -Pversion=''

# get path to boot jar for uploading to release later
artifact_name=$(cat settings.gradle | grep 'rootProject.name' | awk '{print $3}' | tr -d "'")
boot_jar=build/libs/$artifact_name.jar
[ -f $boot_jar ] && echo "boot_jar exists as $boot_jar" || echo "cannot find $boot_jar"

Generate Release notes

Each release has a set of optional release notes so consumers can see what has changed, been fixed, etc.

For our small, single contributor project we just want the release notes to be based on direct commits to the repository.

# commits between latest HEAD and the previous version
git log HEAD...${semantic_version} --pretty="- %s " | tee /tmp/$newtag.log

# if semantic_version was empty and newtag=v1.0.0
git log --pretty="- %s " | tee /tmp/$newtag.log

Note: the Github CLI has a ‘generate-notes‘ flag that will create release notes based on merged pull requests.

Create Github Release

Finally, create the release in the Github repository.

# commit all changes
git commit -a -m "changes for new tag $newtag"

# create new tag and push
git tag $newtag && git push origin $newtag

# pull all commits
git push

# create release, attach Go binary
gh release create $newtag -F /tmp/$newtag.log $boot_jar

Now if you go to your Github repo’s “Releases” section, you should see something similar to the screenshot below with release notes and the Spring Boot jar as an asset.

I have wrapped up the logic in this article into a Bash script, create_new_gh_release.sh

 

REFERENCES

stackoverflow, git commits between tags

cli.github.com, Github CLI manual

docs.github.com, Releases

spring.start.io – starter for Spring Boot Project, provides entire project skeleton for gradle

NOTES

Example of starting Spring Boot project with Gradle using spring.start.io

# variables to be used in project creation
id=spring-boot-example
artifact_id="${id//-}"
SpringAppClassName=SpringMain
version="0.0.1-SNAPSHOT"
groupId="org.fabianlee"

# send parameters that create zip containing SpringBoot project
curl https://start.spring.io/starter.zip \
    -d type=gradle-project \
    -d dependencies=web,prometheus,devtools,actuator \
    -d javaVersion=11 \
    -d bootVersion=2.7.0 \
    -d groupId=$groupId \
    -d artifactId=$artifact_id \
    -d name=$SpringAppClassName \
    -d baseDir=$id \
    -d version=$version \
    -o $id.zip

# unzip archive to directory
unzip $id.zip
cd $id
chmod +x ./gradlew

# validate jar build is successful, with no errors
./gradlew bootJar