How to Evolve an Application to Make It More Robust and Maintainable - Setting Up the CICD


In the last article we described the tests added to our project. These tests are useful to check our development locally as we use TDD. But they are also useful to check if there are regressions. In this post we will:

  • define what the CICD is
  • How to add the CICD settings to our project

What is the CICD

The acronym CICD stands for Continuous Integration Continuous Delivery. The acronym CICDCD stands for Continuous Integration Continuous Delivery Continuous Deployment

When we talk about CICD (or CICDCD) we often also refer to the pipelines. A CICD pipeline defines a process where certain steps are executed one after the other, all with the aim of building, and verifying the application before delivering it and deploying it.

What CICD tools

There are some tools to execute CICD; we can quote:

  • Gitlab-ci
  • CircleCI
  • GitHub Action
  • Jenkins

For open sources projects as our, it’s easier to use a CICD tool integrated into a web hosting and software development management service such as GitLab or GitHub. It’s why we prefer to use GitLab, CircleCi or GitHub Actions tools as Jenkins need a separate deployment, management and hardware resources.

We also want to use a free service, it is why we don’t use a Gitlab CI; because we can’t no longer push the report from Gitlab to Github withe the free plan.

In this post we will detail these solutions but we will implement only two of them:

  • CircleCI
  • GitHub Action

Implementation of our CICD

Our desired pipeline

Through our first pipeline we want to validate :

  • build the application
  • check if the code is formatted
  • verify the non-regression of the code through the execution of test

Also we want the pipeline to be run on every commit to the develop branch and the ci branches (with prefix ci_). We could add other kind of branches: feature(feat_), fixes (fix_), documentation (doc_)

Implementation with CircleCI

First connection

CircleCI is a CICD platform which has a free plan; To be able to use CircleCI we need to:

  • Create an account


  • Set up our code


  • Configure

There are tree possibilities to link your project with your future CI:


Creation of a job

CircleCI defines “jobs” which are executables tasks; each job can be divided into steps In the first version of our CI (cf. code below) we want to:

  • build the jar file
  • run the unit tests
  • run the integration tests
  • run the system test
  1# Java Gradle CircleCI 2.0 configuration file
  2# See:
  3version: 2.1
  6# Define a job to be invoked later in a workflow.
  7# See:
 10# Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub.
 11# See:
 13# specify the version you desire here
 14- image: cimg/openjdk:17.0.5
 16      # Specify service dependencies here if necessary
 17      # CircleCI maintains a library of pre-built images
 18      # documented at
 19      # - image: circleci/postgres:9.4
 21    working_directory: ~/happraisal
 23    environment:
 24      # Customize the JVM maximum heap limit
 25      JVM_OPTS: -Xmx3200m
 26      TERM: dumb
 27    # Add steps to the job
 28    # See:
 29    steps:
 30      - checkout
 31      - attach_workspace:
 32          at: ~/happraisal
 34      # Download and cache dependencies
 35      - restore_cache:
 36          keys:
 37            - v1-dependencies-{{ checksum "build.gradle" }}
 38            # fallback to using the latest cache if no exact match is found
 39            - v1-dependencies-
 41      - run: ./gradlew assemble
 43      - save_cache:
 44          paths:
 45            - ~/.gradle
 46          key: v1-dependencies-{{ checksum "build.gradle" }}
 47      - persist_to_workspace:
 48          root: ~/happraisal
 49          paths:
 50            - ./build
 51      - store_artifacts:
 52          path: ~/happraisal/build/libs
 56- image: cimg/openjdk:17.0.5
 58    working_directory: ~/happraisal
 59    steps:
 60        - checkout
 61        - restore_cache:
 62            keys:
 63              - v1-dependencies-{{ checksum "build.gradle" }}
 64              # fallback to using the latest cache if no exact match is found
 65              - v1-dependencies-
 66        - run: ./gradlew test
 67        - save_cache:
 68            paths:
 69              - ~/.gradle
 70            key: v1-dependencies-{{ checksum "build.gradle" }}
 71        - store_test_results:
 72            path:  ~/happraisal/build/test-results
 78- image: cimg/openjdk:17.0.5
 80    working_directory: ~/happraisal
 81    steps:
 82        - checkout
 83        - restore_cache:
 84            keys:
 85              - v1-dependencies-{{ checksum "build.gradle" }}
 86              # fallback to using the latest cache if no exact match is found
 87              - v1-dependencies-
 88        - run: ./gradlew integrationTest
 89        - save_cache:
 90            paths:
 91              - ~/.gradle
 92            key: v1-dependencies-{{ checksum "build.gradle" }}
 97- image: cimg/openjdk:17.0.5
 99    working_directory: ~/happraisal
100    steps:
101        - checkout
102        - restore_cache:
103            keys:
104              - v1-dependencies-{{ checksum "build.gradle" }}
105              # fallback to using the latest cache if no exact match is found
106              - v1-dependencies-
107        - run: ./gradlew systemTest
108        - save_cache:
109            paths:
110              - ~/.gradle
111            key: v1-dependencies-{{ checksum "build.gradle" }}
114version: 2.1
117- assemble
118- unit-tests:
120- assemble
121- integration-tests:
123- assemble
124- system-tests:
126- assemble

Implementation with GitHub Actions


A Workflow: In the official documentation a workflow is defined as “a configurable automated process that will execute one or more jobs. It is set up via a yaml file. cf.

Action: “Actions are individual tasks that you can combine to create jobs and customize your workflow.” cf.

First connection

GitHub Actions is the CI tool of GitHUb. This CI is accessible from the Action button in a toolbar github_action_menu.png

During the first access, GithUb Actions displays a workflow proposal according to your project. In our case we have selected the java with gradle workflow.


Once we have selected our workflow, we are redirected to the editor proposing us a default file. It is possible to modify it and save it on a specific branch; In order to easily test the CICD without having to push on develop. It is possible to define the name of the working branch in the part push.


Creation of jobs

2  push:
3    branches:
4    - develop
5    - ci_* 

To implement the 3 steps defined above, we need to create the jobs:

  • assemble: to compile the code and create the jar.
  • spotless: to check the format of new code.
  • unit-tests: to execute the unit tests.
  • integration-tests: to execute the integration tests.
  • system-tests: to execute the system tests.

the code below shows an example of how to create a job

 2  assemble:
 3    runs-on: ubuntu-latest
 4    steps:
 5    - uses: actions/checkout@v3
 6    - uses: actions/setup-java@v3
 7      with:
 8        java-version: '17'
 9        distribution: 'temurin'
10        cache: gradle
11    - run: ./gradlew assemble --no-daemon

In the code above:

  • we create the assemble job which is based on ubuntu-latest
  • we use two actions to check the code and configure the Java compiler.
  • we run the gradle assemble job.


The first objective of this article was to test three CICD engines (GitLab, CircleCi and GitHub Actions), implementing similar pipelines. While trying to implement the CICD on these engines, we quickly encountered some limitations for the first two CICD:

As a conclusion I think that to set up a free CICD, it is preferable to use a CI integrated with its version manager such as GItHub Actions for GitHub or GitLab CICD for GitLab.

If you have any remarks on the content or the form, you can leave a comment…it is by exchanging that we progress.


What Is Code Formatting and Why Does It Matter
Introduction to APIs