Sunday, September 17, 2023

GitHub Actions Java with Maven Example

What do you hate the most about pull requests? Is it the formatting changes that clearly add no value and just makes the diff confusing? Or a PR that breaks the build? Don't you just hate a PR that breaks the build? In this example we will automate the code review by checking for a broken build when a pull request is opened. How is this done? By the power of GitHub actions.

GitHub Actions

We'll utilize my old GitHub project, github.com/jpllosa/tdd-junit from Test-Driven Development with Spring Boot Starter Test. Clicking on the Actions tab, GitHub will offer GitHub actions if you haven't created any. If you already have an Action then hit the New workflow button. For our example repo, we should see somthing like below:

GitHub Actions Java with Maven Example
GitHub Actions Java with Maven

Clicking Configure will create a template maven.yml file in .github/workflows folder as shown below. Editing a yml file in GitHub is also a nice way to search for actions. Commit then pull to have a local copy. Let's create a development branch and a feature branch (github-action-java-maven). What we would like to happen is when a pull request into development, it would automatically trigger a build, test and create a review comment if it's a broken build.

GitHub Actions Java with Maven Example
Finding GitHub Actions

$ git pull
From https://github.com/jpllosa/tdd-junit
 * [new branch]      development -> origin/development
 * [new branch]      github-action-java-maven -> origin/github-action-java-maven
Already up to date.

$ git checkout github-action-java-maven

maven.yml

Let's edit the maven.yml template. If you're using Spring Tool Suite and can't find your file, you might have to untick *. resources. Then you should be able to see the yml file.

GitHub Actions Java with Maven Example
Spring Tool Suite Filter
GitHub Actions Java with Maven Example
Spring Tool Suite Java Element Filter
GitHub Actions Java with Maven Example
GitHub Workflow YML file

Your yml file should look like below:


name: Java CI with Maven

on:
  pull_request:
    branches:
      - 'development'

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3.6.0
      - name: Set up JDK
        uses: actions/setup-java@v3.12.0
        with:
          java-version: '8'
          distribution: 'semeru'
          cache: maven
      - name: Build with Maven
        run: mvn -B clean package
      - name: View context attributes
        if: ${{ failure() }}
        uses: actions/github-script@v6.4.1
        with:
          script: |
            console.log(context);
          github-token: ${{ secrets.GITHUB_TOKEN }}
          debug: true
      - name: Create PR Comment
        if: ${{ failure() }}
        uses: actions/github-script@v6.4.1
        with:
          script: |
            github.rest.pulls.createReview({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number,
              event: "COMMENT",
              body: "It is a mortal sin to PR a broken build! :rage:",
              comments: [],
            })
          github-token: ${{ secrets.GITHUB_TOKEN }}
          debug: true

I won't be explaining workflows in too much detail. I'll let the GitHub Docs do that for me. I will however explain this YAML file. Let's start from the top. Our workflow name is Java CI with Maven. This workflow is triggered when a pull request is opened on branch development. Our workflow run is made up of a single job, with a job ID of build. The type of machine that will process our job is the latest version of Ubuntu.

Steps, the place where all the grunt work is done. First is the Checkout. It uses the Checkout GitHub action to checkout our repository. Second it Set up JDK. It uses the Setup Java JDK GitHub action to download and set up Java version 8. Third is Build with Maven which runs the mvn -B clean package command. Fourth is the View context attributes. It uses the GitHub Script GitHub action to help us write scripts in our workflow that uses the GitHub API and the workflow run context. This step is ran when any of the previous steps has failed. The failure() is a status check function. This step is for debugging purposes only, hence the log to the console call. Fifth is the Create PR Comment action. It uses the same GitHub action as the previous one. If the Build with Maven step fails then this will create a review comment saying that it is a mortal sin to create a pull request with a broken build. LOL!

GitHub Actions in Action

Let's add a unit that fails in MathFunTest.java, push it then create a PR. Our source branch is github-action-java-maven and the target development so that the workflow gets triggered.


package com.blogspot.jpllosa;

// snipped...

@SpringBootTest
public class MathFunTest {
	// snipped...
	
	@Test
	public void testThatFails() {
		fail("Not yet implemented");
	}
}

What's the outcome? You should see something like below when creating a pull request with a broken build:

GitHub Actions Java with Maven Example
Pull Request Review Comment

Remove the failing test then push the code and you should have something like below:

GitHub Actions Java with Maven Example
GitHub Actions Success

GitHub Actions Java with Maven Summary

GitHub Actions did all the work for us. From setting up the machine with all the required Java tools. Checking out the repo and then building and running the tests. And finally creating the review comment via the GitHub Script Github action API. There you have it. An automated code review comment for those PRs breaking the build. Hope you had fun reading and applying a similar GitHub action. I know I did.