In order to talk about CI/CD, we consider it necessary to introduce a broader concept which, for obvious reasons of space and coherence, we cannot go into more than a brief paragraph. The concept we must mention is DevOps. Let us see what is meant by DevOps.
DevOps: the basics
The term ‘DevOps’ combines ‘Development’ and ‘Operations’. DevOps encompasses many aspects and practices for managing software production processes. Technically, DevOps indicates a set of approaches and values, rather than a list of concrete practices to be used. DevOps must be declined in a concrete context, in the various software production and management teams. As reported here by RedHat: DevOps describes the approaches to be taken to accelerate the processes that enable an idea (such as a new software feature, enhancement request or bug fix) to move from development to deployment in a production environment, where it can provide value to the user. Such approaches require frequent communication between the development and operational teams. In this context, CI/CD is an approach that is part of DevOps.
What CI/CD means
CI/CD (Continuous Integration/Continuous Delivery – Deployment) is a software development approach that involves continuously and automatically building, testing and delivering the developed code. CI/CD allows for the automatic integration of changes built into the source code through the use of pipelines, i.e. automation of software build, test and deployment operations. As RedHat states here: The CI/CD approach is a fundamental component of the DevOps methodology, the purpose of which is to increase collaboration between development and operations teams. Both methods focus on automating code integration processes, thus accelerating the passage of an idea (such as a new feature, bug fix or enhancement request) from the development phase to the deployment phase, in a production environment where it can offer added value to the user. The CI/CD includes steps that integrate the operations to be performed to test, compile and integrate new developments into a software; the team’s workflow adapts to the use of pipelines to optimise its productivity and minimise time and errors in the software release phase. Let us look at some of the classic steps that are integrated into a CI/CD pipeline, bearing in mind that they are not the only ones possible and that the pipeline must adapt to the needs of the software being developed and the team working on it.
Build
The build is the step that creates some type of executable from the source code of the application. One type of software often used today is called dockerised. Docker relies on certain features of the Linux kernel to allow the same software and its dependencies to run in different environments. The software build often generates Docker images, which are the software that will later be executed by Docker; they have the advantage of being portable, self-contained and easy to replicate.
Test
The test phase in the CI/CD pipeline is fundamental: it ensures that the code integrated in the application does not contain regressions or easily detected bugs. Usually, the test phase is carried out on the source that is created by the build phase, or the test phase is premised on the build itself, depending on the characteristics of the software itself. The pipeline usually provides that, if the tests fail, the release is stopped; otherwise, the pipeline continues to the next phase.
Versioning
Software versioning involves saving a history of the changes made to the software. Usually, some system similar to Git is used to version the source code; since Git is distributed, one often relies on some centralised system such as GitHub or GitLab to maintain an official version of the project. Furthermore, versioning involves assigning a version, usually represented in some alphanumeric form, to the source code and executables generated from it. It is possible to integrate the versioning phase automatically into a pipeline. The software version is often assigned using Semantic Versioning, which has the form M.m.p, where M is the major version, m is the minor version and p is the patch.
Pipeline
A pipeline, as mentioned above, is a procedure that automates the CI/CD process and with it the release of software from new source code. The objective of the pipeline is to enable developers to perform releases automatically, thus easing the workload on the Ops and Systems teams.
Pipeline in GitLab
GitLab is a DevOps platform that allows pipelines to be easily integrated into projects. GitLab integrates source code versioning via Git, saving it on centralised servers that guarantee shared and official versions, CI/CD tools and pipelines, and software control and security practices. Including a pipeline in a GitLab project is very simple: just create a file called .gitlab-ci-yml and place it in the project’s root directory. The .gitlab-ci.yml file describes the pipeline in terms of stages, jobs and the various corresponding scripts. The stage is the implementation of the phases as presented above: build, version generation, test or others; a job is a concrete task that executes a part of the stage. A stage may consist of several jobs, which are executed in parallel; the different stages, on the other hand, are carried out sequentially. Finally, the script implements the specific commands related to each job; in addition to the script, the script includes indications of the environment and execution mode. The stages of the pipeline and their scripts are executed by default by GitLab runners (processes that provide an environment in which to execute the commands provided by the script). Of course, it is possible to configure on-premises runners. To this end, please refer to the GitLab documentation. An example pipeline whose sole purpose is to introduce the file syntax is:stages:
- build
- test
- deploy
build_job:
stage: build
script:
- echo "Building"
version_job:
stage: build
script:
- echo "Versioning"
test_job:
stage: test
script:
- echo "Testing"
deploy_job:
stage: deploy
script:
- echo "Deploying"The pipeline file indicates three stages, in our case: build, test and deploy. You can enter an arbitrary number of stages with the desired name. Each section then describes jobs. The deploy_job, for example, is executed in the deploy stage and consists of the echo script ‘Deploying’ (which prints the string Deploying as standard output). It is possible to decide to execute the deployment job only for a certain branch of the versioned code. To do so, the configuration must be:
deploy:
only:
- master
stage: deploy
script:
- echo "Deploying"
An Example
An example of a pipeline versioning code when making a merge request in the following .gitlab-ci.yml file. stages: build: only: – master stage: build script: – echo “Building” version: stage: build before_script: – git config user.email “${GITLAB_USER_EMAIL}” – git config user.name “${GITLAB_USER_NAME}” – git remote add versioning-origin https://oauth2:${GITLAB_ACCESS_TOKEN}@gitlab.com/${CI_PROJECT_PATH} script: – echo “Versioning” – git fetch –tags – OLD_VERSION=`git describe –abbrev=0 –tags` – NEW_VERSION=`bash change_version.sh $OLD_VERSION` – git tag -a $NEW_VERSION -m “Auto-tagged” – git push versioning-origin $NEW_VERSION rules: – if: $CI_MERGE_REQUEST_ID test: only: – master stage: test script: – echo “Testing” deploy: only: – master stage: deploy script: – echo “Deploying” Come si può vedere, i comandi seguono la sintassi di Bash. La sezione
- build
- test
- deploy indica a GitLab di controllare la variabile d’ambiente predefinita CI_MERGE_REQUEST_ID, che specifica se la pipeline corrente è stata lanciata da una Merge Request. In questa pipeline si richiama lo script change_version.sh che dovrebbe essere salvato nella repository del progetto per essere correttamente eseguito.
rules:
- if: $CI_MERGE_REQUEST_ID


