# Automating Confidence: Setting Up Continuous Integration with GitHub Actions for a Java Project

Hearing about **CI/CD** (Continuous Integration / Continuous Deployment) at work these days is very common. Sometimes, though, we nod along without fully understanding what it's all about — or why it's valuable.

I’m a big fan of KISS (Keep it Simple Sweety) so in this article, we’ll break it down in a way that’s easy to grasp — and more importantly, we’ll **put it into practice** with a real-world example using a Spring Boot Java project. By the end, you won’t just *know* what Continuous Integration is — you’ll have it running in your Java project, backed by GitHub Actions. 💥 I also include a few pro tips to take it even further along &lt;3

## 🔄 What Is Continuous Integration?

**Continuous Integration (CI)** is the practice of automatically building and testing your code every time you make a change — whether it’s a push to your branch or a pull request into `main`.

### ✅ CI helps you:

* Catch bugs early
    
* Ensure the codebase is always in a working state
    
* Avoid “works on my machine” scenarios
    
* Move fast without breaking things
    

Instead of relying on manual testing or guessing if things work, CI makes sure every change is validated automatically.

---

## 🚀 What About Continuous Deployment?

**Continuous Deployment (CD)** is the next step: it automatically deploys your app to production (or a staging environment) *after* a successful CI run.

CD helps you:

* Release faster
    
* Eliminate manual deployment errors
    
* Deliver features with confidence
    

> ⚠️ In this article, we’ll focus only on the **CI part**

---

## 🔧 Popular CI/CD Tools

When setting up a CI/CD pipeline, there are several tools commonly used across the industry. Some of the most popular include:

* **GitHub Actions** – tightly integrated with GitHub, easy to set up (used in this article)
    
* **GitLab CI/CD** – powerful CI/CD built into GitLab
    
* **Jenkins** – highly configurable open-source automation server
    
* **CircleCI** – known for speed and cloud-based pipelines
    
* **Travis CI** – once popular with open source, still in use today
    
* **Bitbucket Pipelines** – Atlassian’s CI/CD solution
    

In this article, we’ve chosen **GitHub Actions** because it integrates directly with GitHub repositories and is simple to get started with.

## 💪 How GitHub Actions Makes CI Easy

GitHub Actions is GitHub’s built-in automation platform. It allows you to define custom workflows that run **on every push, pull request, or even on a schedule**.

For example, You can create a workflow that:

* Installs Java
    
* Builds your app using Maven
    
* Runs the unit and integration tests
    

All of this will happen *automatically* every time you push to `main` or open a pull request.

---

## 💧 Setting Up CI for a Java Project (Step-by-Step)

### 1\. Create a GitHub Actions workflow file

In your project root, create a new file at:

```plaintext
.github/workflows/ci.yml
```

This will define the steps GitHub will take to build and test your project. Here's a breakdown of the key parts:

### 2\. Define when your workflow runs

```plaintext
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
```

This means the workflow will run on any push or pull request targeting the `main` branch.

### 3\. Set up the job and environment

```plaintext
jobs:
  build-and-test:
    runs-on: ubuntu-latest

    env:
      API_SECRET_KEY: ${{ secrets.API_SECRET_KEY }}
      USERNAME: ${{ secrets.USERNAME }}
      PASSWORD: ${{ secrets.PASSWORD }}
```

We define a job called `build-and-test` that runs on the latest Ubuntu image. We also load secret environment variables that we’ll define later.

### 4\. Add the build steps

```plaintext
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

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

      - name: Build and run tests
        run: mvn clean verify --batch-mode
```

* `checkout@v3`: Clones your code into the runner
    
* `setup-java@v3`: Installs Java 21
    
* `mvn clean verify`: Builds your app and runs the tests
    

### 5\. Push your workflow file

Once committed to the repository, GitHub Actions immediately starts running it on every push or pull request targeting the `main` branch.

You can monitor the results by going to your GitHub repository and clicking on the **"Actions"** tab. There, you'll see your workflow listed (by the name you gave it in `ci.yml`), along with its run history, status, and logs for each step. GitHub Actions immediately starts running it on every push or pull request targeting the `main` branch.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744830592666/38cb475a-3d5e-475b-bf8d-6c176e2d7602.png align="center")

---

## 🔐 Securing Environment Variables with GitHub Secrets

My app uses JWT authentication, so I needed to provide a secret key, username, and password even for tests. Rather than hardcoding them (which is risky), I used **GitHub Secrets**.

### Why use GitHub Secrets?

* ✅ Prevent leaking sensitive data (tokens, passwords, API keys)
    
* ✅ Encrypted by GitHub
    
* ✅ Only exposed to the workflow at runtime — meaning they are securely injected during the execution of the workflow and are never visible in the UI, stored in logs, or accessible after the job ends
    

### How to configure them:

1. Go to your GitHub repository → **Settings**
    
2. Click **Secrets and variables** → **Actions**
    
3. Add secrets like:
    
    * `API_SECRET_KEY`
        
    * `USERNAME`
        
    * `PASSWORD`
        

Now in your YAML workflow, you can use them like this:

```plaintext
env:
  API_SECRET_KEY: ${{ secrets.API_SECRET_KEY }}
```

---

## ✅ See this in action

Once I updated my workflow and secrets, I opened this PR:  
🔗 [Set up CI workflow with GitHub Actions, JWT-secured tests, and test config](https://github.com/mdjc/blog-posts-app/pull/1)

As soon as I opened the PR, GitHub ran the CI workflow. My tests passed ✅, my secrets were used securely 🔒, and the PR showed a green checkmark — all automatically.

> 💡 **Pro Tip 1:** Since GitHub Actions runs directly in your repository, it's a good idea to make workflow changes on a feature or development branch first. Once you're sure everything works, you can open a pull request to `main`. This approach helps you debug more freely and keeps your commit history cleaner.

> 💡 **Pro Tip 2:** Once your CI is up and running, consider enabling a **branch protection rule** on your `main` branch. This ensures that only pull requests with passing builds can be merged — a simple way to prevent broken code from reaching production and to keep your main branch clean and stable.

---

## 🧩 What Is the GitHub Actions Marketplace?

The [GitHub Actions Marketplace](https://github.com/marketplace?type=actions) is where you can find **reusable actions** built by the community and GitHub itself.

Some useful actions I used:

* `actions/checkout@v3` – pulls your code into the workflow
    
* `actions/setup-java@v3` – installs Java
    
* `actions/cache@v4` – caches dependencies to speed up builds
    

You can also find actions for linting, Docker builds, deployments, notifications, and more. Think of it like a plugin marketplace for automation.

---

## Final Thoughts

CI isn’t just for big teams. Even if you’re working solo (like I am), it’s worth it. It gives you confidence that your app has been **validated automatically** and that the code you just added **didn’t break anything**. It helps you move fast, ship more reliably, and maintain a healthy codebase over time.

Now, every time I push to `main` or open a PR, I get that little green ✅ — and behind it, the assurance that everything’s working as expected.

### 🚀 Ready to Try It Yourself?

If you haven’t set up CI for your project yet, this is a great moment to start.  
Keep it simple, secure your secrets, and let GitHub Actions help you catch issues early and ship with confidence.
