Gitlab CI definition might get really complex. In order to reduce redundancy you can use many useful features and syntax sugar, like !reference.

Although Gitlab CI’s YAML syntax is not as friendly as many may expect, it can do wonders. Reference tags are one of these features that can help you with organising your CI definition in maintainable way.

Imagine this simple scenario:

job1:
  script:
    - echo "Diff base is '$CI_MERGE_REQUEST_DIFF_BASE_SHA'"
    - GIT_DIFF_CHANGED_FILES=$(git diff --diff-filter=ACMR --name-only ${CI_MERGE_REQUEST_DIFF_BASE_SHA}...)
    - echo "$(echo $GIT_DIFF_CHANGED_FILES | wc -l) file(s) changed"

job2:
  script:
    - echo "Diff base is '$CI_MERGE_REQUEST_DIFF_BASE_SHA'"
    - GIT_DIFF_CHANGED_FILES=$(git diff --diff-filter=ACMR --name-only ${CI_MERGE_REQUEST_DIFF_BASE_SHA}...)
    - echo $GIT_DIFF_CHANGED_FILES

Determining Git diff is redundant, it is exactly the same in both jobs. It may lead to inconsistencies between jobs if something needs to be improved - one may be updated, while the other may not. We can simply fix it with !reference:

.diff:
  script:
    - echo "Diff base is '$CI_MERGE_REQUEST_DIFF_BASE_SHA'"
    - GIT_DIFF_CHANGED_FILES=$(git diff --diff-filter=ACMR --name-only ${CI_MERGE_REQUEST_DIFF_BASE_SHA}...)

job1:
  script:
    - !reference [.diff, script]
    - echo "$(echo $GIT_DIFF_CHANGED_FILES | wc -l) file(s) changed"

job2:
  script:
    - !reference [.diff, script]
    - echo $GIT_DIFF_CHANGED_FILES

Now common logic is extracted to some template (job starting with .) and referenced in other scripts. If diff logic needs to be changed, it will affect both jobs. Even though this can be achieved also with anchors, !reference has one big advantage: referenced section can be defined and used in different files, while anchor usage is limited only to file where it’s defined.

Until today, I thought that !reference has one drawback: lack of nesting. Documentation states:

You can’t reuse a section that already includes a !reference tag. Only one level of nesting is supported.

But it seems to be working on Gitlab 14.10, so I’ve created an issue and asked for providing current information (and most probably updating docs). We found it out after coincidence, colleague just used nested references because he didn’t know that it can’t be done 😅. Now, we can improve some parts of our Gitlab CI definition, but probably will wait for an answer in the issue to know actual limitations (if any).

Happy referencing!