---
marp: true
paginate: true
math: mathjax
theme: buutti
title: GitLab CI/CD
---

# GitLab CI/CD

<!-- headingDivider: 5 -->
<!-- class: invert -->

## Jobs

* ***Jobs*** are the individual units of work that form your CI/CD pipeline. The jobs can be anything, but usually relate to building, testing and packaging the product.
* The common thing between all jobs is that they can either ***fail*** or ***pass***. Every executable script in all operating systems return an exit code of either
	* 0 which indicates success, or
	* 1 - 255 which indicate error.
* The exit code returned from an executed job determines whether the job passes (exit code 0) or fails (exit code 1-255).
	* Failing tests typically return an exit code of 1

## Runners

* [GitLab Runners](https://docs.gitlab.com/runner/) are applications that run the jobs in your pipeline.
  * GitLab Runners can be installed to your own system and registered to be used by anyone or your group.
* If you use GitLab.com, your projects use __shared runners__ by default.
  * This is the most convenient way to run the jobs.
* _**Note:**_ To use GitLab Runners, you need to give your credit/debit card info to GitLab. You won't be charged any fees, however.

## `.gitlab-ci.yml`

<div class='columns21' markdown='1'>
<div markdown='1'>


* The entire GitLab CI/CD pipeline is controlled with a single YAML file called  `.gitlab-ci.yml`.
  * Notice that the file name starts with a dot, and is in all lowercase!
  * VSC recognizes the file name and marks it with a GitLab icon.
* Individual jobs such as *build*, *test* and *deploy* are set up by defining them in the file
* Keyword reference: [https://docs.gitlab.com/ee/ci/yaml/](https://docs.gitlab.com/ee/ci/yaml/)

</div>
<div markdown='1'>

![](imgs/gitlab-ci_cd_0.png)

```yml
# very simple .gitlab-ci.yml

test-job:
  stage: test
  script:
    - echo "Hello Pipeline!"
```

</div>
</div>

## A minimal pipeline

* We will start by creating a minimal project with a pipeline.
* The project itself will contain only a `README.md` file and a pipeline configuration `.gitlab-ci.yml` file.
* We will use this project to run our first pipelines.

### Create a project

1) Start by creating a new project  _New Project > Create blank project_ then after filling the necessary fields, _Create Project_
![w:800px](imgs/gitlab-ci_cd_1.png)
2) Clone the project locally, and open the project folder in VSC.

### Add a `.gitlab-ci.yml` file

<div class='columns21' markdown='1'>
<div markdown='1'>

3) Add a .gitlab-ci.yml file to your project.
   * We are creating a barebones project, so we'll include only one job, executed in the test stage.
   * The only thing the job does is print a message to the pipeline log.
   * We will add more complex configurations soon.
4) When the file is created, add, commit and push your changes to the repository.
   * GitLab will automatically start a new pipeline when it notices that the project has a configuration file.

</div>
<div markdown='1'>

```yml
# .gitlab-ci.yml

test-job:
  stage: test
  script:
    - echo "Hello Pipeline!"
```

</div>
</div>

### Running the pipeline

* You can observe the pipeline progress from the GitLab project *Build > Pipelines*

<div class='columns21' markdown='1'>
<div markdown='1'>

![](imgs/gitlab-ci_cd_2.png)

</div>
<div markdown='1'>

![w:306](imgs/gitlab-ci_cd_3.png)

</div>
</div>

![](imgs/gitlab-ci_cd_4.png)

### Troubleshooting

* If the job does not succeed, check the log for error messages.
  * Then modify your configuration, commit, and push again.
  * GitLab will run a new pipeline every time a project with a `.gitlab-ci.yml` file is pushed.
* You can also rerun the pipeline from *Build > Pipelines > Run pipeline* if the problem was not in the code or your configuration.
  * Examples of cases like this could be errors in the GitLab environment or problems with Azure.

## Adding environment variables

* Often our projects use ***environment variables*** in the jobs that pipelines run.
* In GitLab the variables used in CI/CD are configured in *Settings > CI/CD > Variables*

<div class='columns' style='grid-template-columns: 3.5fr 1fr;' markdown='1'>
<div markdown='1'>

![](imgs/gitlab-ci_cd_5.png)

* You can add new variables using the *Add variable* button, or edit or delete existing variables using the corresponding buttons in the Actions column.

</div>
<div markdown='1'>

![](imgs/gitlab-ci_cd_6.png)

</div>
</div>

### Using Environment Variables

<div class='columns21' markdown='1'>
<div markdown='1'>

* We can use our environment variables in our configuration files by referring to their names prepended with a `$` sign.

</div>
<div markdown='1'>

```yml
# .gitlab-ci.yml

test-job:
stage: test
script:
  - echo "Hello Pipeline v.$VERSION"
```

</div>
</div>

![](imgs/gitlab-ci_cd_7.png)

## Assignment 1: A minimal pipeline
<!--_class: "exercise invert" -->

* Following the lecture instructions, create a minimal project with a pipeline configuration that writes a message to the pipeline log.
* The message should include a value that is read from an environment variable.

## Pipeline Project

### Pipeline linting

* Let's create an actual project with automatic linting.
* We will configure our pipeline to run the linter automatically on every push.
  * This makes sure all code that ends up in our repository follows our syntax standards.

1) Create a new project like you did before. Then, add some code to it.
2) Make sure your project has a linter installed and configured in such a way that the linter can be run from the terminal.
   * In JS/TS project this means `package.json` file has a script
		```json
		"lint": "eslint ."
		```

### Pipeline dependencies

<div class='columns21' markdown='1'>
<div markdown='1'>


* GitLab runs the pipelines inside Docker containers. For our linter to work, we need the container to have access to the `eslint` command.
* This means before we can run the linter, we need to install the required dependencies to our project.
* For this to work, we need to have NPM installed in our container. We need to configure the pipeline to use an image with NPM preinstalled.
* GitLab pulls the images from Docker Hub, so we can choose an appropriate image from there.

3) Configure your YML file like in the example here.

</div>
<div markdown='1'>

```yml
# .gitlab-ci.yml
lint-job:
  image: node:21
  stage: test
  script:
    - npm install
    - npm run lint
```

</div>
</div>

### Pipeline pass/fail

<div class='columns' markdown='1'>
<div markdown='1'>

![](imgs/gitlab-ci_cd_8.png)

</div>
<div markdown='1'>

![](imgs/gitlab-ci_cd_9.png)

</div>
</div>

* Now, if our code has style or syntax problems, the linter will throw an error and the pipeline will fail.
* When all the errors have been fixed, the pipeline passes.

### Automated testing

<div class='columns21' markdown='1'>
<div markdown='1'>

* If our project includes automated testing, we can run tests in a similar fashion.
* They can be run in the same job or a dedicated one.
* Each job uses a different container, so the dependencies need to be installed separately for each container!
	```yml
	# .gitlab-ci.yml
	lint-job:
	image: node:21
	stage: test
	script:
	  - npm install
	  - npm run lint
	  - npm run test
	```

</div>
<div markdown='1'>

```yml
# .gitlab-ci.yml
lint-job:
  image: node:21
  stage: test
  script:
    - npm install
    - npm run lint

test-job:
  image: node:21
  stage: test
  script:
    - npm install
    - npm run test

```

</div>
</div>

### GitLab cache

<div class='columns21' markdown='1'>
<div markdown='1'>

* In a Node project the dependencies are stored in the `node_modules` folder, created only when
  `npm install` is run.
* Therefore by default, each stage would be required to download and install the dependencies separately. This is not desirable!
* We define a ***cache*** in the `.gitlab-ci.yml` file, which allows all stages to access the folders defined in the `cache` parameter.
  * After this we only need to install the dependencies once.

</div>
<div markdown='1'>

```yml
default:
  image: node

cache:
  paths:
    - node_modules/

stages:
  - install
  - lint
  - build
  - deploy

install:
  stage: install
  script:
    - npm install

lint:
  stage: lint
  script:
    - npm run lint

build:
  stage: build
  script:
    - npm run build

deploy:
  stage: deploy
  script:
    - echo "Deploy app here"
```

</div>
</div>

### Assignment 2: Pipeline Linter
<!--_class: "exercise invert" -->

1) Create a new empty repository in GitLab. Clone it to your local machine, and initialize a NPM project into it.
2) Install and configure TypeScript and ESLint. Add some program code (a simple `Hello World` will do.)
3) Add scripts for starting the program and linting the code. Also include a script to build the code. Make sure everything works locally.

---
<!--_class: "exercise invert" -->

4) Add a `.gitlab-ci.yml` file. It should have four stages:
   * `install` (where you install the dependencies)
   * `lint` (where the linter is run)
   * `deploy` (with a placeholder logging message, we don't actually deploy anything today)
   * It should have an additional `build` stage as well.
5) Commit and push your changes. Go to GitLab and check that all the jobs run and pass.

