GitLab Shared Libraries let you stop copying and pasting your CI/CD logic across dozens of repositories.
Imagine you have a fleet of microservices, each with its own .gitlab-ci.yml. You want to enforce a consistent build, test, and deploy process across all of them. Instead of manually updating each gitlab-ci.yml whenever you need to tweak a build step, you can define that logic once in a Shared Library and include it everywhere.
Here’s a typical scenario: a common build_and_test job.
Let’s say you have a Python project. Your base .gitlab-ci.yml might look like this:
include:
- project: 'my-group/ci-templates'
ref: 'main'
file: '/python/build_and_test.yml'
stages:
- build
- test
- deploy
variables:
PYTHON_VERSION: "3.11"
build_job:
stage: build
script:
- echo "Building Python project..."
- pip install --upgrade pip
- pip install -r requirements.txt
- python setup.py build
test_job:
stage: test
script:
- echo "Running tests..."
- pip install pytest
- pytest
Now, let’s look at what my-group/ci-templates/python/build_and_test.yml might contain. This is the Shared Library.
# .gitlab-ci.yml for the Shared Library project
# Path: python/build_and_test.yml
# Define base stages and common variables
stages:
- build
- test
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
# Default build job template
.build_template:
stage: build
image: python:$PYTHON_VERSION
cache:
key: "$CI_COMMIT_REF_SLUG-build"
paths:
- "$PIP_CACHE_DIR"
script:
- echo "Setting up Python environment..."
- python -m venv venv
- source venv/bin/activate
- pip install --upgrade pip
- pip install -r requirements.txt
- echo "Compiling project..."
# This script block is intentionally left open for project-specific build steps
# Project-specific build commands will be appended here.
# Default test job template
.test_template:
stage: test
image: python:$PYTHON_VERSION
cache:
key: "$CI_COMMIT_REF_SLUG-build" # Reuse build cache for tests
paths:
- "$PIP_CACHE_DIR"
script:
- echo "Setting up Python environment for testing..."
- python -m venv venv
- source venv/bin/activate
- pip install -r requirements.txt
- pip install pytest coverage
- echo "Running tests with coverage..."
# Project-specific test commands will be appended here.
# Job definitions that actually use the templates
build_job:
extends: .build_template
variables:
# Project-specific overrides or additions can go here
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" # Explicitly setting cache dir within the job
script:
- !reference [.build_template, script] # Include the base script
- echo "Running project-specific build steps..."
- python setup.py build # Example project-specific step
test_job:
extends: .test_template
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
script:
- !reference [.test_template, script]
- echo "Running project-specific tests..."
- pytest --cov=.
In the example above:
my-group/ci-templates: This is a separate GitLab project that acts as your central repository for CI/CD templates.python/build_and_test.yml: This is the actual file within the Shared Library project containing the reusable CI logic..build_templateand.test_template: These are hidden jobs (prefixed with.) that define the common logic. They useextendsto allow other jobs to inherit their settings.!reference: This GitLab CI keyword is crucial. It allows you to include specific parts of a parent job’s configuration, like thescriptsection, and then append or prepend your own commands. This is how you customize the template for each project.includedirective: In each microservice’s.gitlab-ci.yml, theincludedirective pulls in the Shared Library.project,ref(branch or tag), andfilespecify exactly which template to load.
When a pipeline runs for a microservice that includes this Shared Library:
- GitLab fetches the specified
filefrom theprojectandref. - It merges the included template’s jobs and rules with the project’s own
.gitlab-ci.yml. - Jobs like
build_jobandtest_jobin the microservice’s.gitlab-ci.ymlwillextendthe templates, inheriting theirimage,stage,cache, andscriptconfigurations. - The
!referencedirective ensures that the base script from the template is executed, followed by any project-specific commands defined in the microservice’s.gitlab-ci.yml.
This pattern allows you to maintain a single source of truth for your CI/CD pipelines, dramatically reducing duplication and making it easy to enforce standards and roll out updates across your entire organization.
The most surprising aspect of Shared Libraries is how seamlessly they integrate with standard GitLab CI features like extends and rules, allowing for deep customization without sacrificing reusability. You aren’t just including a static file; you’re inheriting and modifying dynamic job configurations.
Consider how you might want to pass project-specific configurations into a template. You can achieve this by defining variables in the top-level .gitlab-ci.yml of the including project, which are then picked up by the included template jobs.
The next logical step is to explore how to manage different versions of your Shared Libraries using Git tags and how to implement complex conditional logic using rules within your templates.