About GitLab
GitLab is a popular web service for collaborative software development and maintenance. You can work with Git repositories, manage tasks, discuss changes with your team, write wiki documentation, evaluate quality, release versions, and even monitor running programs — all of this in one place.
In Brief
GitLab CI is a tool integrated into GitLab for automating routine tasks that arise during software development. The range of such tasks is vast and varies from project to project, but the main ones are testing, static analysis, code style checking, and application deployment. GitLab CI is a competitor to another popular tool, GitHub Actions. These two services are largely similar, but there are some differences.
Example
Suppose we agreed in the team on special code formatting rules using EditorConfig, installed it as a dev dependency, and made it available with the command npm run editorconfig
. We can run the check every time before committing, but there will always be situations when this is forgotten, and incorrectly formatted code will end up in the repository. Here, GitLab CI/CD comes to the rescue — we just need to create a file .gitlab-ci.yml at the root of the project with the following content:
EditorConfig: image: node:lts script: - npm ci - npm run editorconfig
EditorConfig: image: node:lts script: - npm ci - npm run editorconfig
And now, every time new code enters the repository, it will be checked for compliance with the rules, and any errors will be visible in the GitLab interface.
How to Use
Key Concepts
The main entity in GitLab CI/CD is the pipeline — a conveyor consisting of:
- jobs, describing what needs to be done;
- stages, indicating when or in what sequence to perform the jobs.
Jobs in one stage usually run in parallel. If all jobs complete successfully, execution moves to the next stage, and so on. If any job fails, execution stops, and the entire pipeline (usually) is considered failed.
Creating .gitlab-ci.yml
GitLab CI is fully configured using a single file in YAML format, which should be created at the root of the project — .gitlab-ci.yml.
Jobs often can have similar properties, such as the environment image in which actions are performed, preliminary commands, etc. To avoid repeating them every time, they need to be declared in the default
section. If a particular job needs different parameters, you can specify them within that job, and they will override the global parameters.
First, you need to specify the Docker image (more on this in the article “What is Docker”), in which jobs will be executed. In most cases, the official Node.js image node
will suffice — this means our commands will run inside a Linux operating system with Node.js, npm, and even Yarn installed. You can read about the letters lts in the section on Node.js versioning.
default: image: node:lts
default: image: node:lts
Setting Up Preparation Commands
When working with CI/CD in front-end projects, it is often necessary to install dependencies before performing the main action. For this, we can specify them in the before
section — these commands will be executed in each job before the main action.
default: image: node:lts before_script: - npm -v - npm install
default: image: node:lts before_script: - npm -v - npm install
Specifying Stages
Suppose we want to run code base checks first using EditorConfig and Stylelint, and then, if both complete successfully, run tests. In this example, we can identify two stages: code style and tests. Define the stages using the keyword stages
:
stages: - Code Style - Tests
stages: - Code Style - Tests
Describing Jobs and Specifying Commands
Now let's specify all three jobs. For this, we first state the job name, specify its stage using the keyword stage
, and pass a list of commands in script
. In our example, each job will run one npm script.
default: image: node:lts before_script: - npm -v - npm cistages: - Code Style - TestsEditorConfig: stage: Code Style script: - npm run editorconfigStylelint: stage: Code Style script: - npm run stylelintAutomated Tests: stage: Tests script: - npm run test
default: image: node:lts before_script: - npm -v - npm ci stages: - Code Style - Tests EditorConfig: stage: Code Style script: - npm run editorconfig Stylelint: stage: Code Style script: - npm run stylelint Automated Tests: stage: Tests script: - npm run test
Here’s a schematic representation of the above configuration:

Advanced Usage
Manual Run
If we want to run a specific job manually, we need to add when
:
job: script: npm run deploy when: manual
job: script: npm run deploy when: manual
Continuing on Failure
By default, if any job fails, the entire pipeline is marked as failed and the remaining jobs will not execute. However, there are situations where you would want to avoid this behavior. For example, we added a job with tests in a newly released version of Node.js and simply want to see problems that may need to be fixed in the future. Here, allow
comes in handy:
job: image: node:latest script: npm run test allow_failure: true
job: image: node:latest script: npm run test allow_failure: true
Conditional Job Execution
GitLab provides access to a large number of environment variables with useful information. For example, $
contains the current branch, $
— the short commit hash, $
— the source of the current pipeline invocation, and so on. Using these, we can run specific jobs under certain conditions. To do this, you need to declare one or more rules
sections.
This job will only execute for commits to the main
branch:
job: script: npm run deploy-to-production rules: - if: '$CI_COMMIT_BRANCH == "main"'
job: script: npm run deploy-to-production rules: - if: '$CI_COMMIT_BRANCH == "main"'
Scheduled Runs
Unlike GitHub Actions, in GitLab CI/CD, scheduling pipelines is set up only in the web interface. To do this, open the repository page and select CI/CD → Schedules. A list of existing rules and a button to add a new one will appear. In the addition form, you can specify the rule name, choose an interval from a list or specify your own in the Cron syntax. The last important field is the branch — when the rule is triggered, the pipeline will run as if code were pushed to that branch. The difference is that the variable $
will contain the value schedule.
Series of Jobs
Another typical task is to run tests on different versions of Node.js. You can create a job for each version manually, or you can specify a list of variables:
Unit Tests: script: node -v image: ${NODE_VERSION} parallel: matrix: - NODE_VERSION: ["node:14", "node:16", "node:17"]
Unit Tests: script: node -v image: ${NODE_VERSION} parallel: matrix: - NODE_VERSION: ["node:14", "node:16", "node:17"]
In the example above, we declared a list NODE
with three elements. GitLab will create three jobs named: “Unit Tests [node:14]”, “Unit Tests [node:16]”, and “Unit Tests [node:17]”, then replace all instances of the variable NODE
in each job. Therefore, image
in each job will be different.