Simple recipes for Gitlab ci

Today we talk about pipelines.

Well, to be specific, how we write pipelines nowadays. For Gitlab.

What is a workflow

A sequence of automated tasks to be performed on your code to produce an expected output, that is a workflow.

Run tests? Coverage or code quality reports? artifact generation? deployment? All of it, step by step? This is a pipeline.

For gitlab-hosted projects, the pipeline is declared in a file called .gitlab-ci.yml

In this file, you declare jobs.

The simplest possible job

On a gitlab project, create a .gitlab-ci.ynl file:

# .gitlab-ci.ynl
---
simplest-job:
  script: echo 'hello world'

Whenever you push code, it runs.

1 pipeline, 2 jobs

You can declare, of course, more than one job:

# .gitlab-ci.ynl
---
ping:
  script: echo 'ping!'

pong:
  script: echo 'pong!'

The jobs will run concurrently.

Create stages for your pipeline

Gitlab supports a concept called stages. it's the easiest way to create a sequence of steps for your pipeline:

# .gitlab-ci.ynl
---
stages:
  - first
  - second
  - third

ping:
  stage: first
  script: echo 'ping!'

pong:
  stage: first
  script: echo 'pong!' 

foo:
  stage: second
  script: echo 'foo!' 

bar:
  stage: second
  script: echo 'bar!' 

result:
  stage: third
  script: |
    echo 'in the end we got:'
    ls -la

Now you know how to get parallel and sequential jobs. How about to make them talk to each other?

Run jobs, make artifacts

The way to left some intermediary product to be processed by the next job is using artifacts.

# .gitlab-ci.ynl
---
stages:
  - first
  - second
  - third

ping:
  stage: first
  script: echo 'ping!' > ping.txt
  artifacts:
    paths:
      - ping.txt

pong:
  stage: first
  script: echo 'pong!' > pong.txt
  artifacts:
    paths:
      - pong.txt

foo:
  stage: second
  script: echo 'foo!' > foo.txt
  artifacts:
    paths:
      - foo.txt

bar:
  stage: second
  script: echo 'bar!' > bar.txt
  artifacts:
    paths:
      - bar.txt

result:
  stage: third
  script: |
    echo 'in the end we got:'
    cat ping.txt
    cat pong.txt
    cat foo.txt
    cat bar.txt
    

Now you know how to connect one job output into another job input.

Define a docker image for the job

You might need distinct tools for distinct jobs. Set a docker image for a job is easy:

# .gitlab-ci.ynl
---
stages:
  - part1
  - part2

python-build:
  stage: part1
  image: python
  script:
    - python --version > python-build.txt
  artifacts:
    paths: [python-build.txt]

node-build:
  stage: part1
  image: node
  script:
    - node --version > node-build.txt 
  artifacts:
    paths: [node-build.txt]

check-results:
  stage: part2
  script:
    - echo 'results:'
    - cat node-build.txt
    - cat python-build.txt

Better control of job execution

The default trigger is a push.

However, you can tweak your pipeline to fire on other kinds of evens:

# .gitlab-ci.ynl
---
automatic:
  script: echo 'i run always.'
not-automatic:
  script: echo 'trigger me!'
  when: manual

Reusing job declarations

When things get truly bigger, makes sense to use reusable job components to ease pipeline maintenance.

Further reading

Check the complete source code here.

Check the GitHub version here.

Happy hacking!

 

2026-02-22 gitlab-ci recipes CI/CD