Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moving scripts into a separate file in gitlab-ci.yml to avoid code duplication and including it from several files

I am trying to set up a CI with minimal code duplication using .gitlab-ci.yml.

With that, I am separating the configuration in separate files and reusing parts of it that are common.

I have a separate repository with Gitlab CI settings: gitlab-ci and several projects that use it to form their own CI pipelines.

Contents of gitlab-ci repository

template_jobs.yml:

.sample:
  rules:
    -  if: '$CI_PIPELINE_SOURCE == "push"'
       when: on_success
    -  when: never

jobs_architectureA.yml:

include:
  - local: '/template_jobs.yml'

.script_core: &script_core
  -  echo "Running stage"

test_archA:
  extends:
    - .sample
  stage: test
  tags:
    - architectureA
  script:
    - *script_core

jobs_architectureB.yml:

include:
  - local: '/template_jobs.yml'

.script_core: &script_core
  -  echo "Running stage"

test_archB:
  extends:
    - .sample
  stage: test
  tags:
    - architectureB
  script:
    - *script_core

Project with code contents:

In the actual project (separate repositories per project, and I have a lot of them), I have the following:

.gitlab-ci.yml:

stages:
  - test

include:
  - project: 'gitlab-ci'
    file: '/jobs_architectureA.yml'
  - project: 'gitlab-ci'
    file: '/jobs_architectureB.yml'

This configuration works fine and allows to include only some architectures for some modules while sharing rules between the job templates.

However, it's easy to notice one code duplication: both jobs_architectureA.yml and jobs_architectureB.yml contain a common section:

.script_core: &script_core
  -  echo "Running stage"

It would be ideal to move it into a separate file: template_scripts.yml and include from both jobs_architectureA.yml* and jobs_architectureB.yml. However, that results in the invalid YAML (at least from Gitlab's point of view).

With that, I make a conclusion that I can share the rules as the mechanism of their usage is via extends keyword; however, I am not able to do it with the scripts: as it uses &/* anchoring mechanic on the YAML level.

Ideally, I want something along the lines of:

Contents of the ideal (conceptually) gitlab-ci repository

template_jobs.yml:

.sample:
  rules:
    -  if: '$CI_PIPELINE_SOURCE == "push"'
       when: on_success
    -  when: never

template_scripts.yml:

.script_core: &script_core
  -  echo "Running stage"

jobs_architectureA.yml:

include:
  - local: '/template_jobs.yml'
  - local: '/template_scripts.yml'

test_archA:
  extends:
    - .sample
  stage: test
  tags:
    - architectureA
  script:
    - *script_core  # this becomes invalid, as script_core is in the other file, even though it is included at the top

jobs_architectureB.yml:

include:
  - local: '/template_jobs.yml'
  - local: '/template_scripts.yml'

test_archB:
  extends:
    - .sample
  stage: test
  tags:
    - architectureB
  script:
    - *script_core # this becomes invalid, as script_core is in the other file, even though it is included at the top

  • Am I doing something wrong?
  • Am I hitting a limitation of the Gitlab mechanic? Is it the implementation of the include directive in this specific YML type, that limits me?
  • Do I have options to achieve something close to the desired behaviour?

Note, while this might not look like a big deal, in reality, I have many more pieces to the scripts, and the actual script is much larger. Thus, currently, it is duplicated code all over the place which is very prone to mistakes.

like image 524
Anton Menshov Avatar asked Oct 15 '25 14:10

Anton Menshov


2 Answers

my solution is to not include template_jobs.yml and template_scripts.yml directly in jobs_architectureA.yml but only in the "final" .gitlab-ci.yml

taking you exemple, /template_jobs.yml//template_scripts.yml do not change.

jobs_architectureA.yml loses the include:

test_archA:
  extends:
    - .sample
  stage: test
  tags:
    - architectureB
  script:
    - *script_core # this becomes invalid, as script_core is in the other file, even though it is included at the top

and .gitlab-ci.yml becomes:

stages:
  - test

include:
  - local: '/template_jobs.yml'
  - local: '/template_scripts.yml'
  - project: 'gitlab-ci'
    file: '/jobs_architectureA.yml'
  - project: 'gitlab-ci'
    file: '/jobs_architectureB.yml'
like image 61
Cyril Jouve Avatar answered Oct 18 '25 06:10

Cyril Jouve


in reality, I have many more pieces to the scripts, and the actual script is much larger

Adding to Cyril's solution, GitLab 13.12 (May 2021) can help scale those includes:

Support wildcards when including YAML CI/CD configuration files

The includes: keyword for CI/CD pipelines lets you break one long .gitlab-ci.yml file into multiple smaller files to increase readability.
It also makes it easier to reuse configuration in multiple places.

Frequently there are multiple files included into a single pipeline, and they all might be stored in the same place.

In this release, we add support to use the * wildcard with the local includes: keyword. You can now make your includes: sections more dynamic, less verbose, and easier to read, check out how we are dogfooding it in GitLab.

https://about.gitlab.com/images/13_12/wildcard1.png -- Support wildcards when including YAML CI/CD configuration files

See Documentation and Issue.

like image 41
VonC Avatar answered Oct 18 '25 06:10

VonC