What is CI/CD

  • CI - Continuous Integration
    • Improve: Source Code Delivery, Build, Test (Unit and Integration)
  • CD - Continuous Delivery/ Deployment
    • Improve: Deployment, User acceptance testing,

CI/CD Tools

  • Jenkins
    • Open-source mostly focused on CI
  • Bamboo
    • A CI tool and part of JIRA & bitbucket
  • CircleCI
    • Good for cross-platform environments (e.g., mobile apps)
  • Gitlab CI/CD pipelines
    • Integrates with gitlab, various types of pipelines (basic, DAG, multi-project, etc.)

CI/CD Pipeline Best Practices:

  • Pipelines could change depending on the development process, teams, production, CI/CD tools, etc.
  • Write the pipeline and determine which part can be automated (a lot of it)
  • Some stages are necessary in your pipeline, e.g., testing, peer code review (pull requests), etc.
  • Separate the stage environments for better management (e.g., use a separate branch on git)
  • Devise metrics for transitioning to CD (e.g. test coverage)

Revisit Branching Strategies in git

Branching strategy is a set of rules an policies for creating branches, maintaining them, and merging them to optimize the agility of teams and quality of code

Branching Strategies - GitFlow

  • Master: main stable branch
  • Develop: main branch for development
  • Feature: for new features branched off develop
  • Release: for production
  • release (testing), merged back to master & develop
  • Hotfix: for fixing bugs

GitFlow Pros and Cons

Pros:

  • Allows parallel development, production, and fixing bugs
  • Organizes the work of the developers naturally
  • Allows handling multiple versions (not crucial if using git tags)

Cons:

  • Complicated pipelines due to multiple merges
  • If a test fails in the master branch it could be difficult to pinpoint the branch that hosts the issue
  • Not recommended for amateur teams & for agile CI/CD

Branching Strategies - Github Flow

  • Master branch: stable deployable branch
  • Feature branches: temporary branches off the master branch to isolate the work of developers. Merged back into the master branch

Github Flow Pros and Cons

Pros:

  • Much simpler compared to gitflow
  • Allows for agile development and short production cycles
  • Allows for fast feedback and easier debugging
  • Ideal for small teams

Cons:

  • Not suitable for handling multiple versions
  • More likely to have an unstable master branch due to lack of develop/release branches (requires good testing pipelines before merging into the master branch)
  • Number of merge conflicts grows with bigger teams and slows CI/CD

Branching Strategies - Gitlab Flow

(Middle Ground between Github Flow and Git Flow)

  • Master branch: act as the develop branch
  • Environment branches: the middle branches before merging to the production branch (staging/pre-production)
  • Production branch: the ‘downstream’ stable code in use

Gitlab Flow Pros and Cons

Pros:

  • Provides proper isolation between production and development
  • Allows for staging environments (testing vs production)
  • Great for when the timing of the releases is not under control (iOS)

Cons:

  • Requires proper setup of the staging & QA pipelines
  • Could be complicated with large teams

Branching Strategies - Trunk-based Development

  • Master branch: core “trunk” that received quick and frequent merges (e.g., once a day). Stable anytime
  • Feature Branches: short-lived temporary branches that are merged quickly into the trunk (sometimes implemented with flags)

Trunk-based Development Pros and Cons

Pros:

  • Avoids merge conflicts by forcing developers to integrate their changes quickly
  • Encourages CI/CD due to faster integration and releases
  • Enhances collaboration between the team members

Cons:

  • Suited for more senior teams
  • Requires careful management of features and multiple versions
  • Requires streamlining of merging and integration phases (massive pipelines)

Building Systems

Assume we have the following files:

  • A file containing the class complex: complex.hpp
  • A file containing the class calVar: calc.hpp
  • A file containing the function parseLine: parse.cpp
  • A file containing the main function: calculator.cpp

To compile the entire project:

1
g++ -o calc.out main.cpp parse.cpp

What if the directory structure of the project changed?

  • ./PROJECT/ : the project directory
  • ./PROJECT/include/ : header files
  • ./PROJECT/src/ : the source code files
  • ./PROJECT/bin/ : the executables

Compiling from the project directory:

1
g++ -o bin/calc.out src/main.cpp src/parse.cpp -I include
  • What if the dependencies changes? For example, using different versions of g++
  • If we change any of the files means compiling and linking all the files again
    • We only need to recompile the changed file
  • Is there a way to streamline the building process in an efficient way?

Building Systems Tool

A software build system is the usage of a set of tools for building software applications

  • Putting together several parts of the software efficiently
  • Automating the build as a pipeline in our CI/CD
  • Each part might be in a different programming language
  • We may want to build is for different target machines (execution environment)

GNU-MAKE

Uses a Makefile to issues commands what pieces need to be [re]compiled and [re]linked.

What is a Makefile: a set of rules in the following format:

1
2
3
4
target ... : prerequisites ...
recipe
...
...

A makefile example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SRC = ./src/
INE = ./include/
BIN = ./bin/
CFLAG = -I

calc : $(SRC)calculator.cpp $(SRC)parse.cpp
g++ -o $(BIN)calc.out $(SRC)calculator.cpp $(SRC)parse.cpp $(CFLAG)$(INC)
parse:
g++ -o $(BIN)parse.o $(SRC)parse.cpp
parseTest: $(SRC)parse.cpp
g++ -o $(BIN)parseTest.out $(SRC)parseTest.cpp $(SRC)parse.cpp

clean:
rm $(SRC)*.o

To run them:

1
2
3
make
make calc
make parse

Another makefile example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
calc.out: main.cpp parse.cpp
g++ -o calc.out main.cpp parse.cpp -I.
# Using variables
CXX=g++
CFLAGS=-I.
calc.out: main.o parse.o
$(CXX) -o calc.out main.o parse.o $(CFLAGS)
# Adding dependencies
DEPS = calc.hpp
CXX=g++
CFLAGS=-I.\include
calc.out: main.o parse.o $(DEPS)
$(CXX) -o calc.out main.o parse.o $(CFLAG)
# Cleaning
clean:
rm *.o
# Better cleaning!
.PHONY: clean
clean:
rm *.o

But Makefiles are tedious to make.

A better way is to use a Build System Generator:

  • A tool that generates native build system files
    • E.g. CMake

CMake

It uses CMake scripting (declarative) language to describe the build

The developer edits the CMakeLists.txt

  • invokes CMake but should never edit the generated files by CMake (e.g., Makefiles)

CMake does not compile (remember it is a build system generator), the underlying build system (e.g., make) does, and you have to run that explicitly after running CMake

CMakeLists.txt Example

1
2
3
4
5
6
cmake_minimum_required(VERSION 3.10)
project(calc)
include_directories(include)
file(GLOB SOURCES "src/*.cpp")
set(CMAKE_CXX_STANDARD 11)
add_executable(calc ${SOURCES})

Another example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# put the minimum required version for CMake
cmake_minimum_required(VERSION 3.10)

# information about your project.
# Only name (first argument) is mandatory the rest is optional
project(calc
VERSION 0.1
DESCRIPTION "A complex calculator that just adds!"
LANGUAGES CXX
)

# Sets the version of the C++ compiler
set(CMAKE_CXX_STANDARD 11)

# the include directory
include_directories(include)

# Add the source files to SOURCES as a variable
file(GLOB SOURCES "src/*.cpp")

# Adds the executable
# the first argument is the name of the executable file
add_executable(calc ${SOURCES})


https://github.com/TheErk/CMake-tutorial

https://vinesmsuic.github.io/cmake-tuto/