Welcome to Chapter 18 of our comprehensive Java project guide! In this chapter, we’ll take a significant leap towards professional software development by implementing Continuous Integration/Continuous Deployment (CI/CD) for our “Basic To-Do List Application” using GitHub Actions. CI/CD is a set of practices that enable development teams to deliver code changes more frequently and reliably by automating the build, test, and deployment processes.

This step is crucial because it automates the repetitive tasks of building and testing our application every time a change is pushed to the repository. This ensures that new code integrations are immediately validated, catching bugs early, improving code quality, and reducing the risk of introducing regressions. By the end of this chapter, you will have a robust CI pipeline set up that automatically builds and tests your Java application on every code commit, providing immediate feedback on the health of your codebase.

Prerequisites: Before we begin, ensure you have:

  • A GitHub account and a repository for your “Basic To-Do List Application”.
  • The “Basic To-Do List Application” project (or any of your chosen Java projects) pushed to this GitHub repository. We will assume it’s a Maven-based project for this guide.
  • Familiarity with Git and GitHub basics.

Expected Outcome: Upon completing this chapter, you will have a GitHub Actions workflow configured in your repository that automatically:

  1. Checks out your project code.
  2. Sets up the correct Java Development Kit (JDK) environment.
  3. Builds your Java application using Maven.
  4. Runs all unit and integration tests defined in your project.
  5. Reports the status of the build and tests directly in GitHub.

Planning & Design: CI Workflow

Our goal is to create a simple yet effective CI workflow. The core idea is to define a series of automated steps that run whenever specific events occur in our GitHub repository, such as pushing code to a branch or opening a pull request.

Workflow Architecture:

  • Trigger Events: Push to the main branch, or any pull request opened against main.
  • Jobs: A single job named build-and-test that runs on an ubuntu-latest virtual machine.
  • Steps within the Job:
    1. Checkout Code: Get the latest version of the repository.
    2. Setup Java: Configure the correct JDK version (Java 24, as of December 2025).
    3. Cache Maven Dependencies: Speed up subsequent runs by caching downloaded dependencies.
    4. Build with Maven: Compile the project and package it.
    5. Run Tests: Execute all defined unit and integration tests.

File Structure: GitHub Actions workflows are defined in YAML files located in the .github/workflows/ directory at the root of your repository. We will create a file named java-ci.yml.

your-java-project/
├── .github/
│   └── workflows/
│       └── java-ci.yml  <-- This is where our workflow will live
├── src/
├── pom.xml
└── ... (other project files)

Step-by-Step Implementation

Let’s begin by creating our CI workflow file.

a) Setup/Configuration

First, navigate to the root of your Java project in your local development environment.

  1. Create the Workflow Directory: If it doesn’t already exist, create a directory named .github at the root of your project. Inside .github, create another directory named workflows.

    mkdir -p .github/workflows
    
  2. Create the Workflow File: Inside the newly created .github/workflows directory, create a new file named java-ci.yml.

    touch .github/workflows/java-ci.yml
    
  3. Add Basic Workflow Structure: Open java-ci.yml and add the initial structure. This defines the workflow’s name and when it should run.

    File: .github/workflows/java-ci.yml

    name: Java CI/CD
    
    # Define when the workflow should run
    on:
      push:
        branches: [ main ] # Trigger on pushes to the 'main' branch
      pull_request:
        branches: [ main ] # Trigger on pull requests targeting 'main'
    
    # Define the jobs that make up this workflow
    jobs:
      build-and-test:
        name: Build and Test
        runs-on: ubuntu-latest # Specify the runner environment
    
        # Steps sequence
        steps:
          # Step 1: Checkout the repository code
          - name: Checkout Code
            uses: actions/checkout@v4
    
          # Step 2: Set up Java Development Kit (JDK)
          - name: Set up JDK 24
            uses: actions/setup-java@v4
            with:
              java-version: '24' # Using Java 24, latest stable as of Dec 2025
              distribution: 'temurin' # Recommended distribution for GitHub Actions
              cache: 'maven' # Cache Maven dependencies for faster builds
    
          # Step 3: Build the project with Maven and run tests
          - name: Build and Test with Maven
            run: |
              echo "Building project and running tests..."
              mvn -B clean install # The -B flag for batch mode, clean install to build and run tests
              echo "Build and tests completed."
    

b) Core Implementation

Let’s break down the java-ci.yml file step by step.

  • name: Java CI/CD: This is the name that will appear in the GitHub Actions tab for your workflow. It’s descriptive and helps identify the workflow.

  • on:: This section defines the events that trigger the workflow.

    • push: branches: [ main ]: The workflow will run whenever code is pushed to the main branch.
    • pull_request: branches: [ main ]: The workflow will also run whenever a pull request is opened or updated, targeting the main branch. This is crucial for pre-merge validation.
  • jobs:: A workflow can have one or more jobs. We have a single job named build-and-test.

    • name: Build and Test: A human-readable name for the job.
    • runs-on: ubuntu-latest: Specifies the type of virtual machine that the job will run on. ubuntu-latest is a common and robust choice for Java projects.
  • steps:: This is an ordered list of tasks that the job will execute.

    • - name: Checkout Code:

      • uses: actions/checkout@v4: This is a GitHub Action that checks out your repository code onto the runner. @v4 specifies using version 4 of this action, which is the latest stable.
    • - name: Set up JDK 24:

      • uses: actions/setup-java@v4: This action sets up a Java environment.
      • with:: This block provides configuration options for the setup-java action.
        • java-version: '24': We specify Java 24, which is the latest stable version as of December 2025 according to Oracle’s CPU release notes (24.0.2 in July 2025).
        • distribution: 'temurin': Temurin (formerly AdoptOpenJDK) is a popular and open-source distribution of the JDK, widely used in CI/CD environments.
        • cache: 'maven': This is a critical optimization. It tells the setup-java action to cache Maven dependencies. The first time the workflow runs, Maven downloads all required dependencies. Subsequent runs will retrieve these dependencies from the cache, significantly speeding up the build process.
    • - name: Build and Test with Maven:

      • run: |: This executes a multi-line shell command.
        • echo "Building project and running tests...": A simple log message.
        • mvn -B clean install: This is the core command.
          • mvn: Invokes the Maven build tool.
          • -B: Batch mode. This prevents Maven from prompting for any input, which is essential for automated environments.
          • clean: Cleans the target directory, removing any previously compiled classes.
          • install: Compiles the source code, runs tests, and packages the compiled code into a JAR/WAR file, then installs it into the local Maven repository. If any tests fail, this command will exit with a non-zero status, causing the GitHub Actions job to fail.
        • echo "Build and tests completed.": Another log message.

c) Testing This Component

To test your new CI workflow:

  1. Commit and Push: Save the java-ci.yml file. Then, commit the changes to your Git repository and push them to GitHub.

    git add .github/workflows/java-ci.yml
    git commit -m "feat: Add GitHub Actions CI workflow for Java project"
    git push origin main
    
  2. Observe GitHub Actions:

    • Go to your GitHub repository in your web browser.
    • Click on the “Actions” tab.
    • You should see your “Java CI/CD” workflow listed, with a status indicating it’s running or has just completed.
    • Click on the workflow run to see the details of each step. You can expand each step to view its logs, confirming that the code was checked out, Java was set up, and Maven built and tested your project.
  3. Simulate a Failure (Optional but Recommended): To understand how failures are reported:

    • Locally, introduce a failing test into your Basic To-Do List Application (e.g., change an assertion in an existing test to assert false).
    • Commit this change and push it to your main branch.
    • Observe the “Actions” tab again. This time, the workflow run should show a red ‘X’ indicating a failure. You can drill down into the logs to see which step failed (likely “Build and Test with Maven” due to the failing test).
    • Remember to revert this failing test afterward and push again to restore a passing build.

Production Considerations

While our basic CI workflow is functional, a production-ready setup requires additional considerations:

  • Error Handling & Notifications:

    • GitHub Actions automatically marks a job as failed if any step exits with a non-zero status.
    • You can configure repository settings or use GitHub integrations (like Slack, email) to receive notifications when a workflow fails, ensuring your team is immediately aware of build breakages.
    • For more advanced error handling within the workflow, consider using if: always() or if: failure() conditions for specific steps (e.g., to upload logs or artifacts even on failure).
  • Performance Optimization:

    • Maven Dependency Caching: We’ve already included cache: 'maven' in our setup-java step. This is crucial for performance. GitHub Actions automatically manages the cache, creating a new one if dependencies change.
    • Parallelization: For larger projects with many modules, you could split the build-and-test job into multiple jobs that run in parallel (e.g., build-module-A, test-module-A, build-module-B, test-module-B).
    • Selective Testing: In some advanced scenarios, you might only run tests for modules affected by a change, though this adds complexity.
  • Security Considerations:

    • Least Privilege: Ensure your workflow only has the minimum necessary permissions. By default, actions/checkout and actions/setup-java run with the GITHUB_TOKEN which has limited permissions, usually sufficient for CI.

    • Secrets: If your build process requires sensitive information (e.g., API keys, database credentials for integration tests), use GitHub Secrets. Never hardcode sensitive data directly into your workflow file. You can access secrets using secrets.MY_SECRET_NAME.

      - name: Run Integration Tests with Secret
        env:
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }} # Accessing a GitHub Secret
        run: mvn verify -Pintegration-tests
      
    • Code Scanning: Integrate GitHub’s CodeQL or other static analysis tools into your CI workflow to automatically scan for security vulnerabilities in your code.

  • Logging and Monitoring:

    • The GitHub Actions UI provides detailed logs for each step, which is excellent for debugging.
    • For long-term monitoring and analytics, you could integrate with external logging platforms (e.g., Splunk, ELK stack) by adding steps to push build logs to these services.

Code Review Checkpoint

At this point, you have successfully configured a basic CI workflow for your Java project.

Summary of what was built:

  • A .github/workflows/java-ci.yml file was created.
  • This file defines a workflow named “Java CI/CD”.
  • The workflow is triggered on push to main and pull_request targeting main.
  • It consists of a build-and-test job running on ubuntu-latest.
  • The job includes steps to checkout code, set up Java 24 (Temurin distribution) with Maven caching, and execute a mvn clean install command to build and test the project.

Files created/modified:

  • ./github/workflows/java-ci.yml (new file)

How it integrates with existing code: This CI workflow integrates seamlessly with your existing Java project by using its pom.xml (or build.gradle if you were using Gradle) to define the build process and dependencies. It doesn’t modify your application code itself but rather automates the validation of that code.

Common Issues & Solutions

  1. Issue: Workflow fails with “command not found: mvn” or similar.

    • Cause: Maven (or Gradle) is not correctly installed or not in the system’s PATH on the runner.
    • Solution: Ensure the actions/setup-java@v4 step includes cache: 'maven' or cache: 'gradle', which helps set up the build tool. Also, verify that your pom.xml (or build.gradle) is at the root of your repository or specify its path correctly in the mvn command.
    • Prevention: Always use the setup-java action with the cache parameter set to your build tool (maven or gradle).
  2. Issue: Workflow fails during dependency download (e.g., “Could not resolve dependencies”).

    • Cause: Network issues, incorrect repository URLs in pom.xml, or issues with the Maven Central repository.
    • Solution: Check your pom.xml for any custom repository configurations that might be incorrect or unreachable. Sometimes, transient network issues occur; re-running the workflow might resolve it. If it persists, ensure your project’s dependencies are publicly available or configured correctly if they are private.
    • Debugging: Review the logs from the “Build and Test with Maven” step carefully for specific error messages related to dependency resolution.
  3. Issue: Workflow runs but tests are skipped or not found.

    • Cause: Your Maven pom.xml might not be configured to run tests (e.g., maven-surefire-plugin or maven-failsafe-plugin are missing or misconfigured), or your test files are not following standard naming conventions (e.g., *Test.java).
    • Solution: Verify your pom.xml includes the necessary plugins for running unit and integration tests. Ensure your test classes are in src/test/java and follow the *Test.java or Test*.java naming patterns.
    • Prevention: Adhere to standard Maven project structure and plugin configurations for tests from the start.

Testing & Verification

To ensure your CI setup is robust and fully functional:

  1. Verify a Successful Build:

    • Push a small, harmless change to your code (e.g., add a comment, fix a typo).
    • Observe the GitHub Actions tab. The “Java CI/CD” workflow should trigger, run successfully, and show a green checkmark.
    • Review the logs to confirm that all steps completed as expected, especially the Maven build and test execution.
  2. Verify a Failed Build (Intentional Test Failure):

    • Temporarily modify one of your existing unit tests to intentionally fail (e.g., change assertEquals(expected, actual) to assertEquals(wrongExpected, actual)).
    • Commit and push this change.
    • Observe the GitHub Actions tab. The workflow should trigger and fail with a red ‘X’.
    • Examine the logs for the “Build and Test with Maven” step to see the test failure report.
    • Crucially, revert this intentional failure and push again to ensure your main branch always has a passing CI build.
  3. Verify Pull Request Trigger:

    • Create a new branch from main (e.g., git checkout -b feature/test-ci).
    • Make a small change in this branch.
    • Push the branch to GitHub (git push origin feature/test-ci).
    • Open a Pull Request on GitHub from feature/test-ci to main.
    • Observe the “Checks” section of the Pull Request. Your “Java CI/CD” workflow should automatically trigger and run against the PR branch. This provides immediate feedback on whether the proposed changes break the build or tests before they are merged.

Summary & Next Steps

Congratulations! You have successfully implemented a Continuous Integration pipeline for your Java project using GitHub Actions. This is a foundational step for any production-ready application. You now have an automated system that validates your code changes, providing rapid feedback and maintaining code quality.

What was accomplished:

  • You learned the importance of CI/CD in modern software development.
  • You configured a GitHub Actions workflow to automatically build and test your Java application.
  • You understood how to use actions/checkout and actions/setup-java to prepare the build environment.
  • You implemented Maven commands within the workflow to compile and run tests.
  • You explored production considerations like caching, security, and error handling.

This CI setup is a crucial prerequisite for Continuous Deployment. In the next chapter, Chapter 19: Deploying to a Cloud Platform (e.g., AWS Elastic Beanstalk), we will extend this pipeline to automatically deploy our application to a cloud environment whenever a successful build occurs on the main branch. This will complete our journey from code commit to a live, accessible application.