Configures GitLab CI/CD test stages - `.gitlab-ci.yml` with parallel matrix, artifact reports (junit, coverage), services (postgres, redis), needs / dependencies between jobs, only/except + rules for trigger filtering, retry policy. Use when the project hosts on GitLab and the team wants idiomatic GitLab CI patterns.
73
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Configured via .gitlab-ci.yml at the repo root. Structure:
stages (sequential phases) → jobs (parallel within a
stage) → steps (a job's script: array).
.gitlab-ci.yml needs standardization.# .gitlab-ci.yml
stages:
- test
test:
stage: test
image: node:22
script:
- npm ci
- npm testDefault: runs on every push to any branch. Per-branch / per-MR
filtering via rules: (Step 7).
test:
stage: test
image: node:22
parallel:
matrix:
- NODE_VERSION: ['20', '22']
OS_IMAGE: ['node:20', 'node:22']
script:
- npm ci
- npm testparallel: matrix: runs the job once per matrix entry. Use
GitLab's matrix syntax for cross-product / explicit include for
specific combinations.
test:
stage: test
image: node:22
services:
- name: postgres:15
alias: postgres
variables:
POSTGRES_PASSWORD: test
- name: redis:7
alias: redis
variables:
DATABASE_URL: 'postgres://postgres:test@postgres:5432/postgres'
REDIS_URL: 'redis://redis:6379'
script:
- npm ci
- npm testThe service hostname is the alias field; reachable from the job
container.
test:
stage: test
script:
- npm ci
- npm test -- --reporters=default --reporters=jest-junit
artifacts:
when: always
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
paths:
- junit.xml
- coverage/
expire_in: 30 days
coverage: '/Lines\s*:\s*([0-9.]+)%/'GitLab natively renders:
coverage: regex parses %).stages:
- lint
- test
- integration
- e2e
lint:
stage: lint
script: npm run lint
unit:
stage: test
script: npm test
needs: [lint] # don't wait for stage; run as soon as lint passes
integration:
stage: integration
script: npm run test:integration
needs: [unit]
e2e:
stage: e2e
script: npm run test:e2e
needs: [integration]needs: enables DAG-style pipelines (newer GitLab feature) - jobs
run as soon as their dependencies finish, not waiting for the
full stage.
test:
retry:
max: 2
when:
- runner_system_failure
- stuck_or_timeout_failure
- api_failureDistinguish retry classes:
runner_system_failure - runner died; safe to retry.script_failure - test failed; don't retry (masks bugs).unknown_failure - case by case.rules:test:
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
changes:
- 'src/**/*'
- 'tests/**/*'
- 'package.json'
- if: $CI_COMMIT_BRANCH == 'main'
- if: $CI_PIPELINE_SOURCE == 'schedule'
script:
- npm testModern rules: replaces older only: / except: (still work but
deprecated for new config).
test:
cache:
key:
files:
- package-lock.json
paths:
- .npm/
- node_modules/
script:
- npm ci --cache .npm --prefer-offline
- npm testcache: per-job; speeds repeat installs. Pin the key to
package-lock.json so cache invalidates on dep changes.
variables:
CI: 'true'
NODE_ENV: 'test'
test:
script:
- npm test
variables:
# Job-level overrides
NODE_AUTH_TOKEN: $CI_NPM_TOKENSecrets configured in Project Settings → CI/CD → Variables (masked + protected for sensitive values).
| Anti-pattern | Why it fails | Fix |
|---|---|---|
retry: 5 on test failures | Hides flake; never resolved. | retry: when: only for runner / API failures (Step 6). |
Hardcoded credentials in .gitlab-ci.yml | Secrets in repo. | CI/CD Variables (Step 9). |
| One mega-stage with all jobs | No parallelism; slow pipeline. | Stages + needs (Step 5). |
| Missing artifact upload on failure | Failure debugging needs results. | when: always (Step 4). |
Default image: for everything | Pulls Docker image per job; slow. | Pin lightweight default (e.g., alpine:latest for non-build jobs). |
include: to split across files.docs.gitlab.com/ee/ci/.github-actions-test-jobs,
jenkinsfile-test-stages,
circleci-test-configs -
alternatives.ci-test-job-conventions - cross-CI conventions.junit-xml-analysis - JUnit XML parser.cobertura-analysis - Cobertura parser (GitLab's native coverage format).