Continuous Integration (CI) with GitHub Actions and Java Spring Boot


Continuous Integration (CI) with GitHub Actions and Java Spring Boot





In today’s software development landscape, Continuous Integration and Continuous Delivery (CI/CD) have become essential for teams aiming to release code quickly, efficiently, and reliably. CI/CD automates and optimizes the software delivery process, allowing teams to focus on quality and iteration speed. When working with Java Spring Boot applications, integrating CI/CD through GitHub Actions is especially powerful. GitHub Actions not only offers seamless integration but also provides robust customization options for automated testing, building, and deployment.

This guide explores how to set up a CI pipeline using GitHub Actions for a Java Spring Boot project, creating a reliable, automated workflow that integrates code changes.

Table of Contents

Why Use GitHub Actions for CI/CD with Java Spring Boot?

GitHub Actions allows developers to automate a range of tasks directly within GitHub repositories, making it an ideal tool for setting up CI/CD. Below, we’ll cover key reasons why GitHub Actions is particularly useful for Java Spring Boot applications:

  • Built-In Automation: GitHub Actions is embedded in the GitHub ecosystem, making it simple to set up and configure workflows that trigger based on repository events, such as code pushes or pull requests.
  • Scalability: GitHub Actions can support projects of varying sizes, from simple workflows for small applications to complex pipelines that deploy to multiple environments.
  • Reusable Workflows: You can create reusable workflows across multiple repositories, which is highly beneficial for teams managing multiple Spring Boot projects with similar CI/CD needs.
  • Rich Marketplace of Actions: The GitHub Marketplace offers many pre-built actions for testing, building, deployment, and monitoring, reducing setup time and ensuring best practices.



Why Use GitHub Actions for CI/CD with Java Spring Boot?

Setting Up the Java Spring Boot Project

Before setting up CI/CD, you’ll need a Java Spring Boot project. Follow these steps to initialize it:

Create a Spring Boot Project


Use Spring Initializr to set up the project:
  • Choose Java as the language and Spring Boot as the framework.
  • Select a stable version, typically the latest long-term support (LTS) release.
  • Add dependencies, such as Spring Web (for REST APIs) and Spring Boot DevTools (for easier development and testing).
  • Generate the project, download the `.zip` file, and extract it locally.
Use Spring Initializr to set up the project
Use Spring Initializr to set up the project


Configure the Build Tool

Spring Boot supports both Maven and Gradle as build tools. Configure the dependencies as follows:

  • Maven: In `pom.xml`, ensure you have the necessary dependencies, such as `spring-boot-starter-web`, `spring-boot-starter-test`, and other required libraries.
  • Gradle: In `build.gradle`, set up the Spring Boot plugin and add necessary dependencies.


Example `pom.xml` Configuration for Maven:

<properties>

<java.version>17</java.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>


<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

</dependencies>


<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>



Java project structure
Java project structure



Setting Up the GitHub Repository

To share a Spring project on GitHub, follow these steps:

Create a Repository on GitHub

  1. Log in to your [GitHub account](https://github.com).
  2. Click the + icon in the top-right corner and select New repository.
  3. Fill in the details:
    • Repository name: Give it a name (e.g., `my-spring-project`).
    • Visibility: Choose `Public` or `Private`.
    • Leave Initialize this repository with a README** unchecked.
  4. Click Create repository.

Create a Repository on GitHub
Create a Repository on GitHub




Repository on GitHub

Repository on GitHub

Set workflow permissions

To set workflow permissions on GitHub, follow these steps:

  1. Go to Your Repository:
    • Navigate to the repository where you want to configure workflow permissions.
  2. Access Repository Settings:
    • Click on the Settings tab (found in the repository menu).
  3. Locate Actions Settings:
    • Scroll down to Code and automation and select Actions.
  4. Set Workflow Permissions:
    • Under the General section, look for Workflow permissions.
  5. Choose one following option:
    • Read and write permissions: Grants workflows both read and write access to the repository's contents.
  6. Save Changes:
    • Click Save to apply the new settings.


Set workflow permissions
Set workflow permissions


Enable the Dependency Graph

To enable the Dependency Graph for a GitHub repository, follow these steps:
  1. Ensure the Repository is Public (or Private with GitHub Advanced Security)
    • The dependency graph is automatically enabled for public repositories.
    • For private repositories, it requires GitHub Advanced Security (available for Enterprise and Team plans).
  2. Enabling the Dependency Graph
    • Go to Your Repository:
    • Open the repository for which you want to enable the dependency graph.
  3. Access Repository Settings:
    • Click on the Settings tab in the repository menu.
  4. Enable Features:
    • Scroll down to the Code and automation section and look for Dependency graph.
    • If the feature is available but not enabled, click the button to activate it.
  5. Allow GitHub to Process Dependencies:
    • If prompted, grant permission for GitHub to analyze your codebase for dependencies. 
    • This is often automatic if your repository includes supported dependency files (e.g., `package.json`, `requirements.txt`, `pom.xml`).

By enabling the Dependency Graph, you gain insights into your repository's dependencies and can track vulnerabilities if paired with Dependabot alerts.


Enable the Dependency Graph
Enable the Dependency Graph


Initialize a Local Git Repository

Make sure your project is ready for version control:
  1. Open a terminal in the root directory of your Spring project.
  2. Run the following commands to initialize a Git repository:
from workspace directory execute following command line:

git clone https://github.com/<username>/my-spring-project.git
copy all files from demo.zip file to my-spring-project directory
git branch -M main
git add .
git commit -m "first commit"
git push -u origin main


git clone result
git clone result



     3. Optional: Add `.gitignore` for Java Projects

Before committing, include a `.gitignore` file to exclude unnecessary files (e.g., `target/` or `.idea/` directories).

Example `.gitignore` for a Spring project:

# Maven
target/

# IntelliJ IDEA
.idea/
*.iml

# Logs
*.log

# OS generated files
.DS_Store
Thumbs.db

Add it to your repository:

echo "/target/" >> .gitignore
git add .gitignore
git commit -m "Add .gitignore"
git push -u origin main


    4. Verify Your Repository on GitHub

Visit your GitHub repository URL to confirm that the project is successfully uploaded. 🎉


Verify Your Repository on GitHub
Verify Your Repository on GitHub



Setting Up the GitHub Actions Workflow

GitHub Actions provides a robust way to automate, build, test, and deploy your code directly from GitHub. Whether you're setting up a CI pipeline or running automated scripts, GitHub Actions can handle it all. This guide will walk you through the steps to set up a GitHub Actions workflow, highlight its key features, and provide tips for getting the most out of this powerful tool.

What Is GitHub Actions?

GitHub Actions is an integrated CI/CD platform that allows developers to automate tasks such as testing, building, and deploying code. With its YAML-based configuration and tight integration into GitHub, it eliminates the need for external tools to manage workflows.

Benefits of Using GitHub Actions

Here are some reasons why GitHub Actions has become the go-to choice for many developers:
  • Native Integration: Directly accessible within GitHub repositories.
  • Customizable Workflows: Define workflows to suit any task, from linting to full-scale deployment.
  • Reusable Components: Leverage a marketplace of pre-built actions or create your own.
  • Parallelization: Execute tasks concurrently to save time.
  • Multi-Platform Support: Run workflows on Linux, macOS, and Windows environments.



Prerequisites for Setting Up GitHub Actions

To get started, ensure you have the following:
  • GitHub repository where you'll set up workflows.
  • Basic knowledge of YAML, the configuration language used in GitHub Actions.
  • Appropriate repository access permissions for creating and editing workflows.

How GitHub Actions Work: Key Concepts

Before diving into workflow creation, it’s important to understand the building blocks of GitHub Actions:
  • Workflows: Defined in `.github/workflows` and specify automated tasks.
  • Jobs: Units of work that run sequentially or in parallel within a workflow.
  • Steps: Individual tasks executed in a job.
  • Events: Triggers such as `push`, `pull_request`, or `schedule` that initiate workflows.
  • Runners: Servers that execute the tasks in your workflows.

Creating Your First Workflow

  1. Open your repository on GitHub.
  2. Navigate to the Actions tab.
  3. Click New Workflow and select a template or create a blank file.
  4. Add your workflow code.
  5. Commit the file to the `.github/workflows` directory.
Once committed, GitHub will automatically run the workflow based on the defined triggers.

Get started workflow page
Get started workflow page


Workflow file configuration
Workflow file configuration


Your First Workflow

You can extend your workflows by adding steps, such as:

name: Java CI with Maven

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

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: Build with Maven
        run: mvn -B package --file pom.xml


Additionally, you can use pre-built actions from the [GitHub Marketplace](https://github.com/marketplace).

Update the main.yml workflow file and commit your changes. Following the commit, the workflow will trigger the build automatically. 

Successful build
Successful build


Suggested Improvements

Test Phase

Add a step to run tests explicitly before packaging:

- name: Run tests
  run: mvn test

Artifact Upload

Store build artifacts (e.g., JAR files) for use in other workflows or for debugging:

      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: my-java-app
          path: target/*.jar

Enhanced Caching

Use Maven’s cache more effectively by adding a fallback mechanism:

      - name: Cache Maven repository
        uses: actions/cache@v3
        with:
          path: ~/.m2/repository
          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
          restore-keys: |
            ${{ runner.os }}-maven

Static Code Analysis

Integrate tools like Checkstyle or SpotBugs to maintain code quality:

- name: Static Code Analysis
  run: mvn checkstyle:check

Deployment

Include a deployment step for production or staging environments. For example, deploy a Dockerized app to a cloud registry:

- name: Deploy Docker Image
run: |
docker build -t my-app:${{ github.sha }} .
docker tag my-app:${{ github.sha }} my-dockerhub-username/my-app:latest
docker push my-dockerhub-username/my-app:latest

Final Enhanced Workflow Example

Here’s the improved version of your workflow:

name: Java CI with Maven

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

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: Cache Maven repository
        uses: actions/cache@v3
        with:
          path: ~/.m2/repository
          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
          restore-keys: |
            ${{ runner.os }}-maven

      - name: Run tests
        run: mvn test

      - name: Static Code Analysis with Checkstyle
        run: mvn checkstyle:check

      - name: Static Code Analysis with SpotBugs
        run: mvn spotbugs:check

      - name: Build with Maven
        run: mvn -B package --file pom.xml

      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: my-java-app
          path: target/*.jar


This workflow is modular, scalable, and ready for advanced use cases like artifact storage and deployment.

Update pom.xml file of the Spring Boot application with the following:

<build>
    <plugins>
        <!-- Checkstyle Plugin -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-checkstyle-plugin</artifactId>
            <version>3.2.2</version>
            <configuration>
                <configLocation>${basedir}/checkstyle.xml</configLocation>
                <encoding>UTF-8</encoding>
                <failOnViolation>true</failOnViolation>
            </configuration>
            <executions>
                <execution>
                    <phase>validate</phase>
                    <goals>
                        <goal>check</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

        <!-- SpotBugs Plugin -->
        <plugin>
            <groupId>com.github.spotbugs</groupId>
            <artifactId>spotbugs-maven-plugin</artifactId>
            <version>4.8.6.6</version>
            <executions>
                <execution>
                    <goals>
                        <goal>check</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>



Include the checkstyle.xml file in the root directory of the Spring Boot project:

<?xml version="1.0"?>

<!DOCTYPE module PUBLIC

    "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"

    "https://checkstyle.org/dtds/configuration_1_3.dtd">


<module name="Checker">


    <!-- Charset of the source code -->

    <property name="charset" value="UTF-8"/>


    <!-- Maximum line length -->

    <module name="LineLength">

        <property name="max" value="120"/>

    </module>


    <module name="TreeWalker">


        <!-- Code structure checks -->

        <module name="PackageDeclaration"/>


        <!-- Import declarations -->

        <module name="AvoidStarImport"/>

        <module name="UnusedImports"/>


        <!-- Naming conventions -->

        <module name="TypeName"/>

        <module name="MethodName"/>

        <module name="LocalVariableName"/>


        <!-- Whitespace checks -->

        <module name="WhitespaceAfter"/>

        <module name="WhitespaceAround"/>

        <module name="NoWhitespaceAfter"/>

        <module name="NoWhitespaceBefore"/>


        <!-- Blocks and braces -->

        <module name="NeedBraces"/>

        <module name="EmptyBlock">

            <property name="option" value="text"/>

        </module>


        <!-- Javadoc checks -->

        <module name="JavadocMethod"/>

        <module name="JavadocType"/>

        <module name="JavadocVariable"/>


        <!-- Local variable final modifier check -->

        <module name="FinalLocalVariable"/>


        <!-- String literal equality check -->

        <module name="StringLiteralEquality"/>


    </module>

</module>




Java project structure with checkstyle file
Java project structure with checkstyle file




push changes to GitHub repository:

git pull

git add .

git commit -m “Added checkstyle”

git push -u origin main


Following the commit, the workflow will trigger the build automatically. 


Build results with checkstyle plugin
Build results with checkstyle plugin

Checkstyle results
Checkstyle results


Adding Security-Focused SCA to a CI/CD Workflow

To add security-focused Static Code Analysis (SCA) checks to your CI/CD workflow, you can use tools like SonarQube, OWASP Dependency-Check, or Snyk. These tools identify potential vulnerabilities and security risks in your code or its dependencies.

Integrating OWASP Dependency-Check

OWASP Dependency-Check is a widely used tool to identify vulnerable dependencies in your project.

Add the Maven Plugin

Include the OWASP Dependency-Check plugin in your `pom.xml`:

   <build>

       <plugins>

           <plugin>

               <groupId>org.owasp</groupId>

               <artifactId>dependency-check-maven</artifactId>

               <version>11.1.0</version>

               <executions>

                   <execution>

                       <phase>verify</phase>

                       <goals>

                           <goal>check</goal>

                       </goals>

                   </execution>

               </executions>

           </plugin>

       </plugins>

   </build>


push changes to GitHub repository:

git pull

git add .

git commit -m “Added OWASP-Check”

git push -u origin main

Run Locally

Execute the following command to check for known vulnerabilities in your dependencies:

mvn dependency-check:check

Add to GitHub Actions Workflow

Update your workflow file (`.github/workflows/ci.yml`) to include OWASP Dependency-Check:

- name: OWASP Dependency-Check
  run: mvn dependency-check:check

Update the main.yml workflow file and commit your changes. Following the commit, the workflow will trigger the build automatically. 

Build results with OWASP check
Build results with OWASP dependency check


OWASP dependency check
OWASP dependency check


Integrating SonarQube for Security Analysis

SonarQube is a comprehensive tool for analyzing code quality and identifying security vulnerabilities.

Configure SonarQube

Set up a SonarQube server (cloud or self-hosted) and generate a project-specific token.

Add SonarQube Plugin to `pom.xml`

Include the following configuration in your `pom.xml`:

 

   <build>

       <plugins>

           <plugin>

               <groupId>org.sonarsource.scanner.maven</groupId>

               <artifactId>sonar-maven-plugin</artifactId>

               <version>3.9.1.2184</version>

           </plugin>

       </plugins>

   </build>

Run SonarQube Analysis Locally

Use the following command to scan your codebase:

mvn sonar:sonar \
-Dsonar.projectKey=your_project_key \
-Dsonar.host.url=https://your-sonarqube-server-url \
-Dsonar.login=your_project_token

Add SonarQube to GitHub Actions Workflow

Update the workflow file:

- name: SonarQube Analysis
run: mvn sonar:sonar \
-Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }} \
-Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \
-Dsonar.login=${{ secrets.SONAR_TOKEN }}

Using Snyk for Dependency Security

Snyk scans your dependencies for known vulnerabilities.

Install Snyk CLI

Add the Snyk CLI as part of the workflow. Update `.github/workflows/ci.yml`:

- name: Install Snyk
run: npm install -g snyk

Authenticate Snyk

Set up your Snyk API token in GitHub secrets and authenticate:

- name: Authenticate Snyk
run: snyk auth ${{ secrets.SNYK_TOKEN }}

Run a Snyk Scan

Add a step to scan for vulnerabilities:

- name: Snyk Vulnerability Scan
run: snyk test

These tools add security-focused SCA checks to your pipeline:
  • Use OWASP Dependency-Check to identify vulnerable dependencies.
  • Integrate SonarQube for in-depth security and code quality analysis.
  • Use Snyk for real-time dependency scanning and vulnerability management.
By combining these tools, your CI/CD pipeline will provide robust protection against security vulnerabilities.

Integrating Secrets and Environment Variables

Secrets, such as API keys or passwords, can be securely stored in GitHub and accessed in workflows:
  • Go to Settings > Secrets and Variables > Actions.
  • Click New Repository Secret and add your secret.
Use secrets in your workflow like this:
env:
API_KEY: ${{ secrets.MY_API_KEY }}
run: curl -H "Authorization: Bearer $API_KEY" https://api.example.com/data

Best Practices for GitHub Actions

  • Version Control for Actions: Use specific versions (e.g., `@v3`) to prevent breaking changes.
  • Use Secrets Securely: Never hardcode sensitive information in workflows.
  • Optimize Workflow Duration: Reduce unnecessary steps and leverage caching.
  • Monitor Workflow Runs: Review logs and set up notifications for failures.

Common Issues and Troubleshooting

  • Trigger Issues: Ensure the `on` triggers match your use case.
  • Permission Denied Errors: Check token permissions in settings.
  • Workflow Stalls: Split long-running jobs into smaller units for easier debugging.

Conclusion

GitHub Actions simplifies automation and CI pipelines, helping teams focus on writing code rather than managing workflows. Start with a basic workflow, explore advanced features, and customize workflows to suit your development needs.

References


Your Feedback Matters!

Have ideas or suggestions? Follow the blog and share your thoughts in the comments.

About Me

I am passionate about IT technologies. If you’re interested in learning more or staying updated with my latest articles, feel free to connect with me on:

Feel free to reach out through any of these platforms if you have any questions!

I am excited to hear your thoughts! 👇

Comments

Popular posts from this blog

Monitoring and Logging with Prometheus: A Practical Guide

Creating a Complete CRUD API with Spring Boot 3: Authentication, Authorization, and Testing

Logging in Spring Boot 3: Best Practices for Configuration and Implementation