Topic 4.1
.gitlab-ci.yml: Stages and Jobs
In one line
GitLab CI/CD is configured through exactly one file, .gitlab-ci.yml, at the repository root — stages define order, jobs define the actual work, and jobs in the same stage run in parallel by default, same as everywhere else.
Key ideas
- 01
.gitlab-ci.yml, committed at the repository ROOT, is GitLab's single, complete pipeline definition — genuinely simpler in structure than GitHub Actions' potentially-many-workflow-files-per-repository model, with everything for a project's CI/CD living in exactly one file (Phase 4.4 covers splitting large configs across multiple files viainclude:, once one file genuinely becomes too large). - 02
A top-level
stages:list defines the ORDER stages run in (stages: [build, test, deploy]) — this is purely a declaration of sequence; the actual WORK happens in individually-defined JOBS, each explicitly assigned to one of these stages via its ownstage:key. - 03
A JOB is defined simply by giving it a name at the top level of the YAML, with a
script:key listing the actual shell commands to run —test-job: { stage: test, script: ["mvn test"] }is a genuinely complete, valid job definition; there's no separate nested 'steps' concept the way GitHub Actions and Jenkins both have — a job'sscriptlist IS its steps. - 04
Every job assigned to the SAME stage runs in PARALLEL by default — directly the same behavior Phase 0.2 established as the universal default, and jobs in a LATER stage automatically wait for every job in the stage(s) before it to complete successfully first, before that later stage begins at all.
- 05
artifacts:lets one job's output (a compiled JAR, test reports) be passed forward to LATER stages' jobs — directly the same concept Phase 0.2 introduced generally, GitLab's own specific keyword for declaring exactly what a job produces that should persist beyond that one job's own fresh workspace. - 06
GitLab's own web UI visualizes the entire pipeline as a genuinely clear PIPELINE GRAPH — stages as columns, jobs as nodes within them, with lines showing dependencies — arguably the clearest visual representation of Phase 0.2's stage/job model of any of the three platforms this course covers.
Code & diagrams
A genuinely complete pipeline — three stages, with artifacts passed from build to test.
stages:
- build
- test
- deploy
build-job:
stage: build
image: eclipse-temurin:21-jdk
script:
- mvn clean package
artifacts:
paths:
- target/*.jar
unit-tests:
stage: test
image: eclipse-temurin:21-jdk
script:
- mvn test
lint:
stage: test # runs in PARALLEL with unit-tests — same stage
image: eclipse-temurin:21-jdk
script:
- mvn checkstyle:check
deploy-staging:
stage: deploy
script:
- echo "Deploying $(ls target/*.jar) to staging"
# this job automatically waits for BOTH test-stage jobs to succeed firstExplain it without notes
Why do unit-tests and lint (both in this example's test stage) run in parallel, while deploy-staging waits for both to finish?
What's the practical purpose of the artifacts key on build-job in this example?
Practice
Write a .gitlab-ci.yml with at least three stages and confirm, either on a real GitLab project or by reasoning through the YAML, which jobs would run in parallel and which would wait for others.
If you have access to a real GitLab project, push a .gitlab-ci.yml and view the resulting Pipeline graph in GitLab's UI, matching what you see against your own YAML's stages and jobs.
Trade-offs
- ↔
GitLab's single-file, script-list job model is genuinely simpler to read for a small-to-medium pipeline than GitHub Actions' or Jenkins' equivalents — but as a pipeline grows to have genuinely many jobs and stages, one large file can become unwieldy, which is exactly the problem Phase 4.4's
include:mechanism (splitting configuration across multiple files) exists to solve, mirroring GitHub Actions' own reusable-workflow motivation from Phase 2.3.
Done when you can
I can write a .gitlab-ci.yml with multiple stages and jobs correctly assigned to each.
I understand jobs in the same stage run in parallel, while later stages wait for earlier ones.
I can use artifacts to pass a job's output forward to a later stage.