Using github actions for maven builds

How to use github actions to create and deploy springboot jar and the corresponding docker image?

While GitHub has been the primary choice for an SCM tool for many of us, with the integration of various CI tools and ready-made actions, it is growing as an end-to-end solution for complete DevOps practices.

At my workplace, we are now using GitLab for various purposes(migrated from Bitbucket and TeamCity), and it has been a smooth migration so far. But I wanted to explore similar features in GitHub as I was already familiar with the interface and hosting almost all of my side projects there.

For this purpose, I set up a build pipeline for a springboot project. The project is dependent on another library project, so I had to build the library repository first to refer to its jar as a dependency while running a Maven build on the springboot project. I also wanted to publish the docker image of the springboot project as part of the same build pipeline.

Overview

To summarize the sequence of steps, here is an overview:

  1. Run Maven build for the library repository.
  2. Publish jar file of library module in Github maven package repository.
  3. Run Maven build for the SpringBoot repository.
  4. Publish jar file to GitHub maven package repository.
  5. Publish the docker image to the GitHub container registry. Assuming a Dockerfile is available at the root.

Building Library module

Thanks to the availability of re-usable actions like actions/checkout@v3 or actions/setup-java@v3, this step was straightforward. All I had to do was include the repository tag in distributionManagement section in the pom.xml and add a GitHub action to push the jar in the package repository.

<distributionManagement>
    <repository>
        <id>github</id>
        <name>GitHub Packages</name>
        <url>https://maven.pkg.github.com/owner/repo-name</url>
    </repository>
</distributionManagement>

And here is the github workflow that I used:

name: build
# trigger the build on push to master branch (excluding some files)
# or when a PR is raised on master branch
on:
  push:
    branches: [ master ]
    paths-ignore:
      - 'README.md'
      - '**/*.yml'
  pull_request:
    branches: [ master ]
  workflow_dispatch:
# to be used later
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
  build:
    # base image to be used for running pipeline
    runs-on: ubuntu-latest
    steps:
        # checkout code
      - name: Checkout code
        uses: actions/checkout@v3
        # setup openjdk20
      - name: Set up Java
        uses: actions/setup-java@v3
        with:
          java-version: '20'
          distribution: 'oracle'
          cache: maven
        # run maven build
      - name: Build and Test
        run: |
          mvn clean install          
        # if the current event is a push on master
        # branch, publish the corrresponding jar to 
        # github package repository
      - name: Publish JAR to GitHub Package Registry
        if: ${{ github.event_name != 'pull_request' }}
        run: mvn --batch-mode deploy
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The workflow will handle the maven build and jar push to the GitHub package repository. As mentioned in the comments, there are two triggers for this:

  1. A push to the master branch except for changes to README or a config yaml file.
  2. A pull request was raised against the master branch.

Building SpringBoot repository

It may be because I am a newbie in the GitHub actions world, but building the springboot repository was a bit more complicated due to the following reasons:

  1. It was referencing a package hosted in a private repository.
  2. Additionally, I wanted to create the docker image as well.

To start with, one needs a personal access token (PAT) with read/write permissions for the user.

Additionally, create a settings.xml file with relevant private repository details to fetch the dependent jar. This setting.xml will contain the required credentials to access the private repository.

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">

    <activeProfiles>
        <activeProfile>github</activeProfile>
    </activeProfiles>

    <profiles>
        <profile>
            <id>github</id>
            <repositories>
                <repository>
                    <id>central</id>
                    <url>https://repo1.maven.org/maven2</url>
                </repository>
                <repository>
                    <id>github</id>
                    <url>https://maven.pkg.github.com/owner/repo-name</url>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                </repository>
            </repositories>
        </profile>
    </profiles>

    <servers>
        <server>
            <id>github</id>
            <username>${GH_USERNAME}</username>
            <password>${GH_TOKEN}</password>
        </server>
    </servers>
</settings>

The GH_USERNAME and GH_TOKEN map to the current user and its personal token, respectively. Once the settings file is available in the SpringBoot repository, we create these two secrets in the same repository with the relevant details.

The next step is to trigger the GitHub action workflow and direct it to use this settings.xml to download the library jar from the private repository.

name: build
on:
  push:
    branches: [ master ]
    paths-ignore:
      - 'README.md'
      - '**/*.yml'
  pull_request:
    branches: [ master ]
  workflow_dispatch:
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Set up Java
        uses: actions/setup-java@v3
        with:
          java-version: '20'
          distribution: 'oracle'
          cache: maven
      - name: Build and Test
        run: |
            mvn clean install -s settings.xml -DGH_USERNAME=${{ secrets.GH_USERNAME }} -DGH_TOKEN=${{ secrets.GH_TOKEN }}            
      - name: Publish JAR to GitHub Package Registry
        if: ${{ github.event_name != 'pull_request' }}
        run: mvn --batch-mode deploy
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Publish Docker image to GitHub Container Registry
        if: ${{ github.event_name != 'pull_request' }}
        # some issue with the post formatting here
        # the three lines in run command are to the
        # right (two space indentation)
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin
          docker build -t ghcr.io/$GITHUB_REPOSITORY/image-name:latest .
          docker push ghcr.io/$GITHUB_REPOSITORY/image-name:latest          

Pay close attention to mvn clean install -s settings.xml -DGH_USERNAME=${{ secrets.GH_USERNAME }} -DGH_TOKEN=${{ secrets.GH_TOKEN }} where we trigger the maven build with a custom settings.xml, passing the credentials to access private repository.

And that’s it. With the help of reusable actions, we are able to run end-to-end build pipelines that deploy the final artifacts to respective repositories.

References

  1. Github actions variables
  2. Github actions - publish java packages with maven
  3. Github actions - working with maven registry
  4. Github actions - working with container registry

That is all for this post. If you want to share any feedback, please drop me an email, or contact me on any social platforms. I’ll try to respond at the earliest. Also, please consider subscribing feed for regular updates.

Be notified of new posts. Subscribe to the RSS feed.