1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
---
stage: Verify
group: Pipeline Authoring
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
---
# Contribute to the CI/CD configuration
## Glossary
- **CI/CD configuration**: The YAML file that defines the CI/CD configuration for a project.
- **keyword**: Each keyword in the CI/CD configuration.
- **entry**: An `Entry` class that represents a keyword in the CI/CD configuration.
Not every keyword in the CI/CD configuration is represented by an `Entry` class.
We create `Entry` classes for keywords that have a complex structure or reusable parts.
For example;
- The `image` keyword is represented by the [`Entry::Image`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/config/entry/image.rb) class.
- The `name` subkeyword of the `image` keyword is not represented by an `Entry` class.
- The `pull_policy` subkeyword of the `image` keyword is represented by the [`Entry::PullPolicy`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/config/entry/pull_policy.rb) class.
## Adding New Keywords
CI config keywords are added in the [`lib/gitlab/ci/config/entry`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/config/entry) directory.
For EE-specific changes, use the [`ee/lib/gitlab/ci/config/entry`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/lib/gitlab/ci/config/entry)
or [`ee/lib/ee/gitlab/ci/config/entry`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/lib/ee/gitlab/ci/config/entry) directory.
### Inheritance
An entry is represented by a class that inherits from;
- `Entry::Node`: for simple keywords.
(e.g. [`Entry::Stage`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/config/entry/stage.rb))
- `Entry::Simplifiable`: for keywords that have multiple structures.
For example, [`Entry::Retry`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/config/entry/retry.rb) can be a simple number or a hash configuration.
- `Entry::ComposableArray`: for keywords that have a list of single-type sub-elements.
For example, [`Entry::Includes`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/config/entry/includes.rb) has a list of `Entry::Include` elements.
- `Entry::ComposableHash`: for keywords that have single-type sub-elements with user-defined keys.
For example, [`Entry::Variables`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/config/entry/variables.rb) has a list of `Entry::Variable` elements with user-defined keys.
### Helper Classes
The following helper classes are available for use in entries:
- `Entry::Validatable`: Enables the `validations` block in an entry class and provides validations.
- `Entry::Attributable`: Enables the `attributes` method in an entry class. It creates these methods for each attribute; `xxx`, `has_xxx?`, `has_xxx_value?`.
- `Entry::Configurable`: Enables the `entry` method in an entry class. It creates these methods for each entry; `xxx_defined?`, `xxx_entry`, `xxx_value`.
### The `value` Method
The `value` method is the main method of an entry class. It returns the actual value of the entry.
By default, from the `Entry::Node` class, the `value` method returns the hash configuration of the entry unless it has nested entries.
It can be useful for simple entries. For example, `Entry::Paths` has an array of strings as its value. So, it can return the array of strings directly.
In some keywords, we override the `value` method. In this method, we return what and how we want to return from the entry.
The usage of `Entry::Attributable` and `Entry::Configurable` may have a significant role here. For example,
in `Entry::Secret`, we have this;
```ruby
attributes %i[vault file token].freeze
entry :vault, Entry::Vault::Secret
entry :file, ::Gitlab::Config::Entry::Boolean
def value
{
vault: vault_value,
file: file_value,
token: token
}.compact
end
```
- `vault_value` is the value of the nested `vault` entry.
- `file_value` is the value of the nested `file` entry.
- `token` is the value of the basic `token` attribute.
**It is important** that we should always use the `xxx_value` method to get the value of a nested entry.
## Feature Flag Usage
When adding new CI/CD configuration keywords, it is important to use feature flags to control the rollout of the change.
This allows us to test the change in production without affecting all users. For more information, see the [feature flags documentation](../feature_flags/index.md).
### Feature Flag Actor
In entry classes, we have no access to the current project or user. However, it's discouraged to use feature flags without [an actor](../feature_flags/index.md#feature-actors).
To solve this problem, we have three options;
1. Use `Feature.enabled?(:feature_flag, Feature.current_request)`.
1. Use `YamlProcessor::FeatureFlags.enabled?(:feature_flag)`
1. Do not use feature flags in entry classes and use them in other parts of the code.
## Testing and Validation
When adding or modifying an entry, the corresponding spec file must be either added or updated.
Besides, to have a fully integrated test, it's also important to add/modify tests in the `spec/lib/gitlab/ci/yaml_processor_spec.rb` file or
the files in `spec/lib/gitlab/ci/yaml_processor/test_cases/*` directory.
|