2021-10-20 05:12:43 -04:00
---
stage: Verify
group: Pipeline Authoring
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference
---
2021-11-15 10:10:57 -05:00
# Optimize GitLab CI/CD configuration files **(FREE)**
2021-10-20 05:12:43 -04:00
2021-11-15 10:10:57 -05:00
You can reduce complexity and duplicated configuration in your GitLab CI/CD configuration
files by using:
2021-10-20 05:12:43 -04:00
2021-11-15 10:10:57 -05:00
- YAML-specific features like [anchors (`&`) ](#anchors ), aliases (`*`), and map merging (`<< `).
Read more about the various [YAML features ](https://learnxinyminutes.com/docs/yaml/ ).
- The [`extends` keyword ](#use-extends-to-reuse-configuration-sections ),
which is more flexible and readable. We recommend you use `extends` where possible.
2021-10-20 05:12:43 -04:00
## Anchors
YAML has a feature called 'anchors' that you can use to duplicate
content across your document.
Use anchors to duplicate or inherit properties. Use anchors with [hidden jobs ](../jobs/index.md#hide-jobs )
to provide templates for your jobs. When there are duplicate keys, GitLab
performs a reverse deep merge based on the keys.
2021-11-15 10:10:57 -05:00
You can use YAML anchors to merge YAML arrays.
2021-10-20 05:12:43 -04:00
You can't use YAML anchors across multiple files when using the [`include` ](index.md#include )
keyword. Anchors are only valid in the file they were defined in. To reuse configuration
from different YAML files, use [`!reference` tags ](#reference-tags ) or the
2021-11-15 10:10:57 -05:00
[`extends` keyword ](#use-extends-to-reuse-configuration-sections ).
2021-10-20 05:12:43 -04:00
The following example uses anchors and map merging. It creates two jobs,
`test1` and `test2` , that inherit the `.job_template` configuration, each
with their own custom `script` defined:
```yaml
.job_template: & job_configuration # Hidden yaml configuration that defines an anchor named 'job_configuration'
image: ruby:2.6
services:
- postgres
- redis
test1:
< < : * job_configuration # Merge the contents of the ' job_configuration ' alias
script:
- test1 project
test2:
< < : * job_configuration # Merge the contents of the ' job_configuration ' alias
script:
- test2 project
```
`&` sets up the name of the anchor (`job_configuration`), `<<` means "merge the
given hash into the current one," and `*` includes the named anchor
(`job_configuration` again). The expanded version of this example is:
```yaml
.job_template:
image: ruby:2.6
services:
- postgres
- redis
test1:
image: ruby:2.6
services:
- postgres
- redis
script:
- test1 project
test2:
image: ruby:2.6
services:
- postgres
- redis
script:
- test2 project
```
You can use anchors to define two sets of services. For example, `test:postgres`
and `test:mysql` share the `script` defined in `.job_template` , but use different
`services` , defined in `.postgres_services` and `.mysql_services` :
```yaml
.job_template: & job_configuration
script:
- test project
tags:
- dev
.postgres_services:
services: & postgres_configuration
- postgres
- ruby
.mysql_services:
services: & mysql_configuration
- mysql
- ruby
test:postgres:
< < : * job_configuration
services: *postgres_configuration
tags:
- postgres
test:mysql:
< < : * job_configuration
services: *mysql_configuration
```
The expanded version is:
```yaml
.job_template:
script:
- test project
tags:
- dev
.postgres_services:
services:
- postgres
- ruby
.mysql_services:
services:
- mysql
- ruby
test:postgres:
script:
- test project
services:
- postgres
- ruby
tags:
- postgres
test:mysql:
script:
- test project
services:
- mysql
- ruby
tags:
- dev
```
You can see that the hidden jobs are conveniently used as templates, and
`tags: [postgres]` overwrites `tags: [dev]` .
### YAML anchors for scripts
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23005) in GitLab 12.5.
You can use [YAML anchors ](#anchors ) with [script ](index.md#script ), [`before_script` ](index.md#before_script ),
and [`after_script` ](index.md#after_script ) to use predefined commands in multiple jobs:
```yaml
.some-script-before: & some-script-before
- echo "Execute this script first"
.some-script: & some-script
- echo "Execute this script second"
- echo "Execute this script too"
.some-script-after: & some-script-after
- echo "Execute this script last"
job1:
before_script:
- *some-script-before
script:
- *some-script
- echo "Execute something, for this job only"
after_script:
- *some-script-after
job2:
script:
- *some-script-before
- *some-script
- echo "Execute something else, for this job only"
- *some-script-after
```
### YAML anchors for variables
Use [YAML anchors ](#anchors ) with `variables` to repeat assignment
of variables across multiple jobs. You can also use YAML anchors when a job
requires a specific `variables` block that would otherwise override the global variables.
The following example shows how override the `GIT_STRATEGY` variable without affecting
the use of the `SAMPLE_VARIABLE` variable:
```yaml
# global variables
variables: & global-variables
SAMPLE_VARIABLE: sample_variable_value
ANOTHER_SAMPLE_VARIABLE: another_sample_variable_value
# a job that must set the GIT_STRATEGY variable, yet depend on global variables
job_no_git_strategy:
stage: cleanup
variables:
< < : * global-variables
GIT_STRATEGY: none
script: echo $SAMPLE_VARIABLE
```
2021-11-15 10:10:57 -05:00
## Use `extends` to reuse configuration sections
You can use the [`extends` keyword ](index.md#extends ) to reuse configuration in
multiple jobs. It is similar to [YAML anchors ](#anchors ), but simpler and you can
[use `extends` with `includes` ](#use-extends-and-include-together ).
`extends` supports multi-level inheritance. You should avoid using more than three levels,
but you can use as many as eleven. The following example has two levels of inheritance:
```yaml
.tests:
rules:
- if: $CI_PIPELINE_SOURCE == "push"
.rspec:
extends: .tests
script: rake rspec
rspec 1:
variables:
RSPEC_SUITE: '1'
extends: .rspec
rspec 2:
variables:
RSPEC_SUITE: '2'
extends: .rspec
spinach:
extends: .tests
script: rake spinach
```
### Exclude a key from `extends`
To exclude a key from the extended content, you must assign it to `null` , for example:
```yaml
.base:
script: test
variables:
VAR1: base var 1
test1:
extends: .base
variables:
VAR1: test1 var 1
VAR2: test2 var 2
test2:
extends: .base
variables:
VAR2: test2 var 2
test3:
extends: .base
variables: {}
test4:
extends: .base
variables: null
```
Merged configuration:
```yaml
test1:
script: test
variables:
VAR1: test1 var 1
VAR2: test2 var 2
test2:
script: test
variables:
VAR1: base var 1
VAR2: test2 var 2
test3:
script: test
variables:
VAR1: base var 1
test4:
script: test
variables: null
```
### Use `extends` and `include` together
To reuse configuration from different configuration files,
combine `extends` and [`include` ](index.md#include ).
In the following example, a `script` is defined in the `included.yml` file.
Then, in the `.gitlab-ci.yml` file, `extends` refers
to the contents of the `script` :
- `included.yml` :
```yaml
.template:
script:
- echo Hello!
```
- `.gitlab-ci.yml` :
```yaml
include: included.yml
useTemplate:
image: alpine
extends: .template
```
### Merge details
You can use `extends` to merge hashes but not arrays.
The algorithm used for merge is "closest scope wins," so
keys from the last member always override anything defined on other
levels. For example:
```yaml
.only-important:
variables:
URL: "http://my-url.internal"
IMPORTANT_VAR: "the details"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_BRANCH == "stable"
tags:
- production
script:
- echo "Hello world!"
.in-docker:
variables:
URL: "http://docker-url.internal"
tags:
- docker
image: alpine
rspec:
variables:
GITLAB: "is-awesome"
extends:
- .only-important
- .in-docker
script:
- rake rspec
```
The result is this `rspec` job:
```yaml
rspec:
variables:
URL: "http://docker-url.internal"
IMPORTANT_VAR: "the details"
GITLAB: "is-awesome"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_BRANCH == "stable"
tags:
- docker
image: alpine
script:
- rake rspec
```
In this example:
- The `variables` sections merge, but `URL: "http://docker-url.internal"` overwrites `URL: "http://my-url.internal"` .
- `tags: ['docker']` overwrites `tags: ['production']` .
- `script` does not merge, but `script: ['rake rspec']` overwrites
`script: ['echo "Hello world!"']` . You can use [YAML anchors ](yaml_optimization.md#anchors ) to merge arrays.
2021-10-20 05:12:43 -04:00
## `!reference` tags
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/266173) in GitLab 13.9.
> - `rules` keyword support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/322992) in GitLab 14.3.
Use the `!reference` custom YAML tag to select keyword configuration from other job
sections and reuse it in the current section. Unlike [YAML anchors ](#anchors ), you can
use `!reference` tags to reuse configuration from [included ](index.md#include ) configuration
files as well.
In the following example, a `script` and an `after_script` from two different locations are
reused in the `test` job:
- `setup.yml` :
```yaml
.setup:
script:
- echo creating environment
```
- `.gitlab-ci.yml` :
```yaml
include:
- local: setup.yml
.teardown:
after_script:
- echo deleting environment
test:
script:
- !reference [.setup, script]
- echo running my own command
after_script:
- !reference [.teardown, after_script]
```
In the following example, `test-vars-1` reuses all the variables in `.vars` , while `test-vars-2`
selects a specific variable and reuses it as a new `MY_VAR` variable.
```yaml
.vars:
variables:
URL: "http://my-url.internal"
IMPORTANT_VAR: "the details"
test-vars-1:
variables: !reference [.vars, variables]
script:
- printenv
test-vars-2:
variables:
MY_VAR: !reference [.vars, variables, IMPORTANT_VAR]
script:
- printenv
```
You can't reuse a section that already includes a `!reference` tag. Only one level
of nesting is supported.