Getting started with GitHub Actions

GitHub Actions is a flexible and powerful automation service that allows you to automate, customize, and execute workflows directly in your GitHub repository. These workflows can automate various tasks such as building, testing, deploying, and publishing software. In this article we will learn how to get started with GitHub Actions by building a simple Maven project.

Purpose of GitHub Actions in CI/CD:

Continuous Integration (CI) and Continuous Deployment (CD) are development practices aimed at improving software quality, reducing risk, and increasing development speed by integrating and deploying code frequently. GitHub Actions is an excellent tool for implementing CI/CD pipelines because it allows you to automate these processes directly within your GitHub repository.

Here’s a general overview of how GitHub Actions fit into CI/CD:

  1. Continuous Integration (CI): Every time a developer pushes new code to a branch, GitHub Actions can automatically run a series of tests (such as unit tests, integration tests, etc.) to ensure that the new code does not introduce any errors. If the tests pass, the code is considered “green” and can be safely merged into the main branch.
  2. Continuous Deployment (CD): Once the code has been merged into the main branch, GitHub Actions can automatically deploy the application to a staging or production environment, depending on the configuration. This allows for fast and reliable deployment of new features and bug fixes.

Creating your first GitHub WorkFlow

You can create your first workflow by clicking on Actions in the list of options under your GitHub Repository:

github actions tutorial

In the next screen, you will see a list of built-in workflows. You can filter them, for example with the “maven” keyword. Then choose the following Java with Maven basic workflow:

github actions step-by-step tutorial

Click Configure. As a result, GitHub will prepare a basic Workflow in the .github/workflows of your Repository.

The Basic Maven Workflow

When you create your Maven Workflow you will get a default WorkFlow which contains a single Job and consists of some steps:

name: Java CI with Maven

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

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'
        cache: maven
    - name: Build with Maven
      run: mvn -B package --file pom.xml

    # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
    - name: Update dependency graph
      uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6

Let’s break down the key components of this workflow:

  1. on: This specifies the events that trigger the workflow. In this case, the workflow is triggered on push events to the main branch and pull request events targeting the main branch.
  2. jobs: This section defines the jobs to be executed as part of the workflow.
    • build: This is the name of the job. In this case, there is only one job defined in the workflow.
      • runs-on: This specifies the type of virtual machine to run the job on. In this case, the job runs on an ubuntu-latest machine.
      • steps: This section lists the individual steps or actions to be executed as part of the job.
        • actions/checkout@v3: This step executes the GitHub checkout action. The @v3 specifies the version of the GitHub Action. In this case, it’s version 3 of the checkout action.
        • actions/setup-java@v3: This step sets up the Java environment using the setup-java action. It specifies Java version 17, using the Temurin distribution, and caching the Maven dependencies.
        • mvn -B package --file pom.xml: This step builds the Maven project by running the mvn package command. -B specifies batch mode, which means that Maven will run without any interactive prompts, and --file pom.xml specifies the location of the POM file.
        • advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6: This is an optional step that uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts for the repository. It uses a specific version of the maven-dependency-submission-action GitHub Action.
  3. Dependency Graph Update (Optional): The last step in the workflow updates the full dependency graph to GitHub. This is an optional step that aims to improve the quality of Dependabot alerts in the repository.

Next, click on Commit Changes. This will commit the workflow on your Repository and also trigger the execution:

Github actions example with maven

You can get further details, such as the Maven logs, by clicking on the steps in the Run Execution:

github actions tutorial

Caching the Maven repository

In this section, we will learn one improvement we can apply to our workflow. By using the action cache we can cache the result of the Maven build:

    - name: Cache the Maven packages to speed up build
      uses: actions/cache@v1
      with:
        path: ~/.m2
        key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
        restore-keys: ${{ runner.os }}-m2  

When the step runs, it checks if there is a cache matching the specified key. If a cache is found, it restores the cache to the specified path (~/.m2 in this case), which prevents Maven from downloading packages that are already in the cache. This can significantly reduce the build time, especially for large projects with many dependencies. If no cache is found, Maven will download the dependencies and store them in the cache for future builds.

Uploading Artifacts

As a general rule, a CI/CD Workflow will eventually publish successfully built artifacts to the next stage. For example to UAT or Production environment. In this basic example, we will show a minimalist approach which is to upload the archive in a staging folder:

      - name: Create staging directory
        run: mkdir -p staging
      - name: Copy JAR files to staging directory
        run: cp target/*.jar staging/
      - name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: java-artifacts
          path: staging/*.jar

The tasks are pretty-much self-explanatory:

  • mkdir -p staging: This step creates a directory named staging in the workspace.
  • cp target/*.jar staging/: This copies all JAR files from the target directory (where Maven builds the artifacts) to the newly created staging directory.
  • Upload artifacts: This step uses the actions/upload-artifact action to upload the contents of the staging directory (JAR files) as an artifact named java-artifacts.

Add Manual Workflow Dispatch

The workflow_dispatch event is a trigger available in GitHub Actions that allows you to manually initiate a workflow execution from the GitHub UI or the GitHub CLI. This provides flexibility and control over when your workflow runs, independent of automatic triggers like push events or pull requests.

In order to add a new trigger for the Workflow Dispatch event, just add the the list of triggers the following one:

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  workflow_dispatch: # Add a new trigger for the workflow_dispatch event   

After you add the worflow_dispatch event, you should be able to see in your WorkFlow definition the option to trigger it manually.

github action step-by-step tutorial

Just click on Run workflow and it will start building your project.

Adding conditions in the Workflow

Finally, we will discuss here how do add some basic conditions in our Workflow so that we can embed a logic during the execution. For the sake of simplicity, we will just show how to add a basic if condition that executes a simple Task ( an echo command) if the user started the WorkFlow Manually.

Here’s the final version of the Workflow which contains the conditional execution before attempting the build:

name: Java CI with Maven

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  workflow_dispatch: # Add a new trigger for the workflow_dispatch event    

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'
        cache: maven
    - name: Log starting event
      if: ${{ github.event_name == 'workflow_dispatch' }}
      run: |
        echo "Manual Build!"        
    - name: Build with Maven
      run: mvn -B package --file pom.xml
    - name: Create staging directory
      run: mkdir -p staging
    - name: Copy JAR files to staging directory
      run: cp target/*.jar staging/
    - name: Upload artifacts
      uses: actions/upload-artifact@v3
      with:
        name: java-artifacts
        path: staging/*.jar    

Run again the Workflow manually. Then, you should be able to see in the Workflow log for the Build the following trace:

Run echo "Manual Build!"        
  echo "Manual Build!"        
  shell: /usr/bin/bash -e {0}
  env:
    JAVA_HOME: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.10-7/x64
    JAVA_HOME_17_X64: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.10-7/x64
Manual Build!

Conclusion

This article has explored the powerful capabilities of GitHub Actions in building and automating your Maven projects.
By mastering these techniques, you can establish robust and efficient CI pipelines within your GitHub repositories. This empowers you to automate repetitive tasks, ensure code quality, and streamline your development workflow.

You can find the example Workflow here: https://github.com/fmarchioni/githubactiondemo