GitLab CI
GitLab CI runs pipelines defined in .gitlab-ci.yml. Stages run sequentially, jobs within a stage run in parallel, runners pick up jobs by tags. The model is similar to GitHub Actions, with stronger templating via include and extends.
Pipeline file with stages, caching, env, deploy
EXAMPLE
# .gitlab-ci.yml
stages: [setup, test, build, deploy]
variables:
NODE_VERSION: "20"
default:
image: node:${NODE_VERSION}
cache:
key:
files: [package-lock.json]
paths: [.npm/, node_modules/]
# 1) Setup — install once, share across stages via cache
install:
stage: setup
script:
- npm ci --cache .npm --prefer-offline
artifacts:
paths: [node_modules/]
expire_in: 1 hour
# 2) Test in parallel — fan-out
lint:
stage: test
needs: [install]
script: [npm run lint]
unit:
stage: test
needs: [install]
script: [npm test -- --coverage]
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
artifacts:
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
e2e:
stage: test
needs: [install]
image: mcr.microsoft.com/playwright:v1.44.1-jammy
script:
- npx playwright install --with-deps
- npm run test:e2e
# 3) Build a Docker image — kaniko (no docker-in-docker)
build:
stage: build
needs: [unit, lint]
image:
name: gcr.io/kaniko-project/executor:v1.23.2-debug
entrypoint: [""]
rules:
- if: $CI_COMMIT_TAG
script:
- /kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile Dockerfile
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
--destination "${CI_REGISTRY_IMAGE}:latest"
# 4) Deploy to staging on main
deploy_staging:
stage: deploy
needs: [unit, lint]
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
environment:
name: staging
url: https://staging.example.com
script:
- ./deploy.sh staging
# 5) Deploy to prod on tag, manual approval gate
deploy_prod:
stage: deploy
needs: [build]
rules:
- if: $CI_COMMIT_TAG
environment:
name: production
url: https://example.com
when: manual
script:
- ./deploy.sh prod
# 6) Include shared templates — DRY across many projects
include:
- project: 'shared/ci-templates'
file: '/security.yml'
- template: 'Security/SAST.gitlab-ci.yml'
- template: 'Security/Dependency-Scanning.gitlab-ci.yml'
# 7) Extends — reuse job snippets
.test_base:
stage: test
needs: [install]
interruptible: true
lint:
extends: .test_base
script: [npm run lint]
Why it matters
GitLab’s needs: graph lets you skip the rigid stage barrier — a build job can start the moment its specific upstream jobs pass, not when the whole stage clears. Big speedup on fan-out pipelines.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
# .gitlab-ci.yml
image: node:20
stages: [test, build, deploy]
test:
stage: test
script: npm ci && npm test
build:
stage: build
script: npm run build
artifacts: { paths: [dist/] }
Try it Yourself »
Discussion
Loading…