Search code examples
inheritancegitlabgitlab-ciextendsgitlab-ci.yml

Which job properties get merged vs overwritten when using `extends` in GitLab CI?


In GitLab CI, you can use the extends keyword in order to inherit properties from another job or template.

One example is:

.tests:
  script: rake test
  stage: test
  only:
    refs:
      - branches

rspec:
  extends: .tests
  script: rake rspec
  only:
    variables:
      - $RSPEC

Which results in:

rspec:
  script: rake rspec
  stage: test
  only:
    refs:
      - branches
    variables:
      - $RSPEC

But as you can see, it is not obvious how each property gets merged. Properties like "only" appear to get merged and other properties like script appear to get overwritten instead.

I'm trying to understand exactly which properties get overwritten and which properties get merged (and how). Is there a complete list?

Something like this maybe?

Property Behavior when used with extends
script Overwritten
only Merged
except Merged
variables Merged
before_script Overwritten
after_script Overwritten
needs Merged
cache Merged (gets converted from map to list of maps)
artifacts Merged (deep)
services ?
when ?
rules ?
... ...

Solution

  • As documented in merge details, hashes (key, value pairs) get merged, but any other value like arrays are overwritten by the extending job. It works simply by using the deep_merge method which is part of the Ruby on Rails framework.

    The actual logic, looks like this:

    # File activesupport/lib/active_support/core_ext/hash/deep_merge.rb, line 23
      def deep_merge!(other_hash, &block)
        merge!(other_hash) do |key, this_val, other_val|
          if this_val.is_a?(Hash) && other_val.is_a?(Hash)
            this_val.deep_merge(other_val, &block)
          elsif block_given?
            block.call(key, this_val, other_val)
          else
            other_val
          end
        end
      end
    

    This means it can be important how you write your YAML, as some configurations accept multiple types. For example variables: may be a hash of keys and values (or hash of keys and hash of configuration options). It can also be an array of hashes. These different options for writing variables: will merge differently when used with extends:. The same goes for many other keys, like only: and except:.