From daf63198e038bd2d90f65b5e66d5c7474fc61a49 Mon Sep 17 00:00:00 2001 From: Furkan Ayhan Date: Fri, 5 Jan 2024 13:00:27 +0100 Subject: [PATCH 1/3] Implement workflow:rules:auto_cancel for CI With `workflow:rules:auto_cancel`, users will be able to control their CI config's behaviors depending on some conditions (rules). In this change, besides `workflow:rules:auto_cancel:on_new_commit`, `workflow:rules:auto_cancel:on_job_failure` is also implemented. However, since the `workflow:auto_cancel:on_job_failure` syntax is not used, it is not documented. This change is behind the existing feature flag ci_workflow_auto_cancel_on_new_commit. https://gitlab.com/gitlab-org/gitlab/-/issues/436467 --- app/assets/javascripts/editor/schema/ci.json | 3 + doc/ci/yaml/index.md | 53 ++++++++++++ lib/gitlab/ci/build/rules.rb | 7 +- lib/gitlab/ci/config/entry/rules/rule.rb | 6 +- lib/gitlab/ci/config/entry/workflow.rb | 2 +- .../ci/pipeline/chain/populate_metadata.rb | 5 +- .../editor/schema/ci/ci_schema_spec.js | 8 ++ .../rules/auto_cancel/on_job_failure.yml | 7 ++ .../rules/auto_cancel/on_new_commit.yml | 7 ++ .../rules/auto_cancel/on_job_failure.yml | 7 ++ .../rules/auto_cancel/on_new_commit.yml | 7 ++ spec/lib/gitlab/ci/build/rules_spec.rb | 12 +++ .../config/entry/include/rules/rule_spec.rb | 2 +- .../gitlab/ci/config/entry/rules/rule_spec.rb | 25 +++++- spec/lib/gitlab/ci/config/entry/rules_spec.rb | 2 +- .../gitlab/ci/config/entry/workflow_spec.rb | 35 ++++++++ .../pipeline/chain/populate_metadata_spec.rb | 72 ++++++++++++++++ spec/lib/gitlab/ci/yaml_processor_spec.rb | 26 ++++++ .../workflow_auto_cancel_spec.rb | 84 +++++++++++++++++++ 19 files changed, 362 insertions(+), 8 deletions(-) create mode 100644 spec/frontend/editor/schema/ci/yaml_tests/negative_tests/workflow/rules/auto_cancel/on_job_failure.yml create mode 100644 spec/frontend/editor/schema/ci/yaml_tests/negative_tests/workflow/rules/auto_cancel/on_new_commit.yml create mode 100644 spec/frontend/editor/schema/ci/yaml_tests/positive_tests/workflow/rules/auto_cancel/on_job_failure.yml create mode 100644 spec/frontend/editor/schema/ci/yaml_tests/positive_tests/workflow/rules/auto_cancel/on_new_commit.yml diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json index 7cedd00e4916b8..16cf5ec219da04 100644 --- a/app/assets/javascripts/editor/schema/ci.json +++ b/app/assets/javascripts/editor/schema/ci.json @@ -154,6 +154,9 @@ "always", "never" ] + }, + "auto_cancel": { + "$ref": "#/definitions/workflowAutoCancel" } }, "additionalProperties": false diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 35b35567d8f80d..1213334b94dbe2 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -699,6 +699,59 @@ When the branch is something else: - Use [`inherit:variables`](#inheritvariables) in the trigger job and list the exact variables you want to forward to the downstream pipeline. +#### `workflow:rules:auto_cancel` + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/436467) in GitLab 16.8 [with a flag](../../administration/feature_flags.md) named `ci_workflow_auto_cancel_on_new_commit`. Disabled by default. + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available per project or +for your entire instance, an administrator can [enable the feature flag](../../administration/feature_flags.md) named `ci_workflow_auto_cancel_on_new_commit`. +On GitLab.com, this feature is not available. +The feature is not ready for production use. + +Use `workflow:rules:auto_cancel` to configure the behavior of +the [`workflow:auto_cancel:on_new_commit`](#workflowauto_cancelon_new_commit) feature. + +**Possible inputs**: + +- `on_new_commit`: [`workflow:auto_cancel:on_new_commit`](#workflowauto_cancelon_new_commit) + +**Example of `workflow:rules:auto_cancel`**: + +```yaml +workflow: + auto_cancel: + on_new_commit: interruptible + rules: + - if: $CI_COMMIT_REF_PROTECTED == 'true' + auto_cancel: + on_new_commit: none + - when: always # Run the pipeline in other cases + +test1: + script: sleep 10 + interruptible: false + +test2: + script: sleep 10 + interruptible: true +``` + +In this example, [`workflow:auto_cancel:on_new_commit`](#workflowauto_cancelon_new_commit) is set to `interruptible` +for non-protected branches, `none` for protected branches. + +In a non-protected branch: + +- A pipeline is running. +- A new commit is pushed to the branch. +- `test1` is continued and `test2` is canceled. + +In a protected branch: + +- A pipeline is running. +- A new commit is pushed to the branch. +- `test1` and `test2` are continued. + ## Header keywords Some keywords must be defined in a header section of a YAML configuration file. diff --git a/lib/gitlab/ci/build/rules.rb b/lib/gitlab/ci/build/rules.rb index 8b503290e6e77e..999f41ff46f95c 100644 --- a/lib/gitlab/ci/build/rules.rb +++ b/lib/gitlab/ci/build/rules.rb @@ -6,7 +6,9 @@ module Build class Rules include ::Gitlab::Utils::StrongMemoize - Result = Struct.new(:when, :start_in, :allow_failure, :variables, :needs, :errors, keyword_init: true) do + Result = Struct.new( + :when, :start_in, :allow_failure, :variables, :needs, :errors, :auto_cancel, keyword_init: true + ) do def build_attributes needs_job = needs&.dig(:job) { @@ -37,7 +39,8 @@ def evaluate(pipeline, context) start_in: matched_rule.attributes[:start_in], allow_failure: matched_rule.attributes[:allow_failure], variables: matched_rule.attributes[:variables], - needs: matched_rule.attributes[:needs] + needs: matched_rule.attributes[:needs], + auto_cancel: matched_rule.attributes[:auto_cancel] ) else Result.new(when: 'never') diff --git a/lib/gitlab/ci/config/entry/rules/rule.rb b/lib/gitlab/ci/config/entry/rules/rule.rb index f5b5323c532bee..81e67592c29dd1 100644 --- a/lib/gitlab/ci/config/entry/rules/rule.rb +++ b/lib/gitlab/ci/config/entry/rules/rule.rb @@ -27,6 +27,9 @@ class Rules::Rule < ::Gitlab::Config::Entry::Node metadata: { allowed_needs: %i[job] }, inherit: false + entry :auto_cancel, Entry::AutoCancel, + description: 'Auto-cancel configuration for the pipeline.' + validations do validates :config, presence: true validates :config, type: { with: Hash } @@ -60,7 +63,8 @@ def value config.merge( changes: (changes_value if changes_defined?), variables: (variables_value if variables_defined?), - needs: (needs_value if needs_defined?) + needs: (needs_value if needs_defined?), + auto_cancel: (auto_cancel_value if auto_cancel_defined?) ).compact end diff --git a/lib/gitlab/ci/config/entry/workflow.rb b/lib/gitlab/ci/config/entry/workflow.rb index 7aafd9ba78e461..b201989a9f75df 100644 --- a/lib/gitlab/ci/config/entry/workflow.rb +++ b/lib/gitlab/ci/config/entry/workflow.rb @@ -25,7 +25,7 @@ class Workflow < ::Gitlab::Config::Entry::Node description: 'List of evaluable Rules to determine Pipeline status.', metadata: { allowed_when: %w[always never].freeze, - allowed_keys: %i[if changes exists when start_in allow_failure variables needs].freeze + allowed_keys: %i[if changes exists when start_in allow_failure variables needs auto_cancel].freeze } entry :auto_cancel, Entry::AutoCancel, diff --git a/lib/gitlab/ci/pipeline/chain/populate_metadata.rb b/lib/gitlab/ci/pipeline/chain/populate_metadata.rb index 3ac910da75255f..8e6426be6792fc 100644 --- a/lib/gitlab/ci/pipeline/chain/populate_metadata.rb +++ b/lib/gitlab/ci/pipeline/chain/populate_metadata.rb @@ -35,7 +35,10 @@ def set_pipeline_name end def set_auto_cancel - auto_cancel = @command.yaml_processor_result.workflow_auto_cancel + auto_cancel_from_config = @command.yaml_processor_result.workflow_auto_cancel || {} + auto_cancel_from_rules = @command.workflow_rules_result&.auto_cancel || {} + + auto_cancel = auto_cancel_from_config.merge(auto_cancel_from_rules) return if auto_cancel.blank? diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js index 949cf1367ff0f2..7a37f53c7a6ec4 100644 --- a/spec/frontend/editor/schema/ci/ci_schema_spec.js +++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js @@ -40,6 +40,8 @@ import NeedsParallelMatrixYaml from './yaml_tests/positive_tests/needs_parallel_ import ScriptYaml from './yaml_tests/positive_tests/script.yml'; import WorkflowAutoCancelOnJobFailureYaml from './yaml_tests/positive_tests/workflow/auto_cancel/on_job_failure.yml'; import WorkflowAutoCancelOnNewCommitYaml from './yaml_tests/positive_tests/workflow/auto_cancel/on_new_commit.yml'; +import WorkflowRulesAutoCancelOnJobFailureYaml from './yaml_tests/positive_tests/workflow/rules/auto_cancel/on_job_failure.yml'; +import WorkflowRulesAutoCancelOnNewCommitYaml from './yaml_tests/positive_tests/workflow/rules/auto_cancel/on_new_commit.yml'; // YAML NEGATIVE TEST import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml'; @@ -68,6 +70,8 @@ import NeedsParallelMatrixWrongMatrixValueYaml from './yaml_tests/negative_tests import ScriptNegativeYaml from './yaml_tests/negative_tests/script.yml'; import WorkflowAutoCancelOnJobFailureNegativeYaml from './yaml_tests/negative_tests/workflow/auto_cancel/on_job_failure.yml'; import WorkflowAutoCancelOnNewCommitNegativeYaml from './yaml_tests/negative_tests/workflow/auto_cancel/on_new_commit.yml'; +import WorkflowRulesAutoCancelOnJobFailureNegativeYaml from './yaml_tests/negative_tests/workflow/rules/auto_cancel/on_job_failure.yml'; +import WorkflowRulesAutoCancelOnNewCommitNegativeYaml from './yaml_tests/negative_tests/workflow/rules/auto_cancel/on_new_commit.yml'; const ajv = new Ajv({ strictTypes: false, @@ -113,6 +117,8 @@ describe('positive tests', () => { ScriptYaml, WorkflowAutoCancelOnJobFailureYaml, WorkflowAutoCancelOnNewCommitYaml, + WorkflowRulesAutoCancelOnJobFailureYaml, + WorkflowRulesAutoCancelOnNewCommitYaml, }), )('schema validates %s', (_, input) => { // We construct a new "JSON" from each main key that is inside a @@ -160,6 +166,8 @@ describe('negative tests', () => { ScriptNegativeYaml, WorkflowAutoCancelOnJobFailureNegativeYaml, WorkflowAutoCancelOnNewCommitNegativeYaml, + WorkflowRulesAutoCancelOnJobFailureNegativeYaml, + WorkflowRulesAutoCancelOnNewCommitNegativeYaml, }), )('schema validates %s', (_, input) => { // We construct a new "JSON" from each main key that is inside a diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/workflow/rules/auto_cancel/on_job_failure.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/workflow/rules/auto_cancel/on_job_failure.yml new file mode 100644 index 00000000000000..11029a85189224 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/workflow/rules/auto_cancel/on_job_failure.yml @@ -0,0 +1,7 @@ +workflow: + auto_cancel: + on_job_failure: all + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + auto_cancel: + on_job_failure: unexpected_value diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/workflow/rules/auto_cancel/on_new_commit.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/workflow/rules/auto_cancel/on_new_commit.yml new file mode 100644 index 00000000000000..4c7e9be9018b5b --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/workflow/rules/auto_cancel/on_new_commit.yml @@ -0,0 +1,7 @@ +workflow: + auto_cancel: + on_new_commit: interruptible + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + auto_cancel: + on_new_commit: unexpected_value diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/workflow/rules/auto_cancel/on_job_failure.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/workflow/rules/auto_cancel/on_job_failure.yml new file mode 100644 index 00000000000000..9050566fbd3277 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/workflow/rules/auto_cancel/on_job_failure.yml @@ -0,0 +1,7 @@ +workflow: + auto_cancel: + on_job_failure: all + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + auto_cancel: + on_job_failure: none diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/workflow/rules/auto_cancel/on_new_commit.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/workflow/rules/auto_cancel/on_new_commit.yml new file mode 100644 index 00000000000000..c5ec387fe5021d --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/workflow/rules/auto_cancel/on_new_commit.yml @@ -0,0 +1,7 @@ +workflow: + auto_cancel: + on_new_commit: interruptible + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + auto_cancel: + on_new_commit: none diff --git a/spec/lib/gitlab/ci/build/rules_spec.rb b/spec/lib/gitlab/ci/build/rules_spec.rb index 99577539798c45..61bd9f41182f25 100644 --- a/spec/lib/gitlab/ci/build/rules_spec.rb +++ b/spec/lib/gitlab/ci/build/rules_spec.rb @@ -254,6 +254,18 @@ end end + context 'with auto_cancel' do + context 'with matching rule' do + let(:rule_list) { [{ if: '$VAR == null', auto_cancel: { on_new_commit: 'interruptible' } }] } + + it do + is_expected.to eq( + described_class::Result.new(when: 'on_success', auto_cancel: { on_new_commit: 'interruptible' }) + ) + end + end + end + context 'with a regexp variable matching rule' do let(:rule_list) { [{ if: '"abcde" =~ $pattern' }] } diff --git a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb index cd8e35ede61554..a9f891a7b50178 100644 --- a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'fast_spec_helper' +require 'spec_helper' require_dependency 'active_model' RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules::Rule, feature_category: :pipeline_composition do diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb index 23b1046deb74dd..d5bf532c216567 100644 --- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb @@ -13,7 +13,7 @@ let(:metadata) do { allowed_when: %w[on_success on_failure always never manual delayed], - allowed_keys: %i[if changes exists when start_in allow_failure variables needs] + allowed_keys: %i[if changes exists when start_in allow_failure variables needs auto_cancel] } end @@ -341,6 +341,23 @@ expect(subject.errors).to include(/variables config should be a hash/) end end + + context 'with an invalid auto_cancel' do + let(:config) do + { if: '$THIS == "that"', auto_cancel: { on_new_commit: 'xyz' } } + end + + before do + subject.compose! + end + + it { is_expected.not_to be_valid } + + it 'returns an error' do + expect(subject.errors).to include( + 'auto_cancel on new commit must be one of: conservative, interruptible, none') + end + end end context 'allow_failure: validation' do @@ -421,6 +438,12 @@ it { is_expected.to eq(config) } end + + context 'when it has auto_cancel' do + let(:config) { { if: '$THIS || $THAT', auto_cancel: { on_new_commit: 'interruptible' } } } + + it { is_expected.to eq(config) } + end end describe '.default' do diff --git a/spec/lib/gitlab/ci/config/entry/rules_spec.rb b/spec/lib/gitlab/ci/config/entry/rules_spec.rb index 37219c743ccac8..0113b6c1f7fb92 100644 --- a/spec/lib/gitlab/ci/config/entry/rules_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/rules_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'fast_spec_helper' +require 'spec_helper' require_dependency 'active_model' RSpec.describe Gitlab::Ci::Config::Entry::Rules, feature_category: :pipeline_composition do diff --git a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb index 01ba72ff042702..dbd2501088401b 100644 --- a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb @@ -90,6 +90,41 @@ end end end + + context 'when rules has auto_cancel' do + let(:workflow_hash) { { rules: [{ if: '$VAR', auto_cancel: { on_new_commit: 'interruptible' } }] } } + + describe '#valid?' do + it 'is valid' do + expect(config).to be_valid + end + + it 'attaches no errors' do + expect(config.errors).to be_empty + end + end + + describe '#value' do + it 'returns the config' do + expect(config.value).to eq(workflow_hash) + end + end + + context 'when auto_cancel has an invalid value' do + let(:workflow_hash) { { rules: [{ if: '$VAR', auto_cancel: { on_new_commit: 'xyz' } }] } } + + describe '#valid?' do + it 'is invalid' do + expect(config).not_to be_valid + end + + it 'returns error' do + expect(config.errors).to include( + 'rules:rule:auto_cancel on new commit must be one of: conservative, interruptible, none') + end + end + end + end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb index 732748d8c8b624..787a458f0ff4ce 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb @@ -240,6 +240,78 @@ def run_chain expect(pipeline.pipeline_metadata).not_to be_persisted end end + + context 'with workflow:rules:auto_cancel' do + context 'with auto_cancel:on_new_commit not set and rules:workflow:auto_cancel:on_new_commit set' do + let(:config) do + { + variables: { MY_VAR: my_var_value }, + workflow: { + auto_cancel: { on_job_failure: 'all' }, + rules: [{ if: '$MY_VAR == "something"', auto_cancel: { on_new_commit: 'interruptible' } }] + }, + rspec: { script: 'rspec' } + } + end + + context 'when the rule is matched' do + let(:my_var_value) { 'something' } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all') + end + end + + context 'when the rule is not matched' do + let(:my_var_value) { 'something else' } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('conservative') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all') + end + end + end + + context 'with auto_cancel:on_new_commit set and rules:workflow:auto_cancel:on_new_commit set' do + let(:config) do + { + variables: { MY_VAR: my_var_value }, + workflow: { + auto_cancel: { on_new_commit: 'interruptible' }, + rules: [{ if: '$MY_VAR == "something"', auto_cancel: { on_new_commit: 'none' } }] + }, + rspec: { script: 'rspec' } + } + end + + context 'when the rule is matched' do + let(:my_var_value) { 'something' } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('none') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + end + end + + context 'when the rule is not matched' do + let(:my_var_value) { 'something else' } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + end + end + end + end end context 'with both pipeline name and auto_cancel' do diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 844a6849c8f165..a20fc33c5327c2 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -516,6 +516,32 @@ module Ci }) end end + + context 'with rules and auto_cancel' do + let(:config) do + <<-YML + workflow: + rules: + - if: $VAR == "value" + auto_cancel: + on_new_commit: none + on_job_failure: none + + hello: + script: echo world + YML + end + + it 'parses workflow_rules' do + expect(subject.workflow_rules).to contain_exactly({ + if: '$VAR == "value"', + auto_cancel: { + on_new_commit: 'none', + on_job_failure: 'none' + } + }) + end + end end describe '#warnings' do diff --git a/spec/services/ci/create_pipeline_service/workflow_auto_cancel_spec.rb b/spec/services/ci/create_pipeline_service/workflow_auto_cancel_spec.rb index 3ad6164bd01155..7e8a3ef3d7bc49 100644 --- a/spec/services/ci/create_pipeline_service/workflow_auto_cancel_spec.rb +++ b/spec/services/ci/create_pipeline_service/workflow_auto_cancel_spec.rb @@ -60,6 +60,48 @@ 'workflow:auto_cancel on new commit must be one of: conservative, interruptible, none') end end + + context 'when using with workflow:rules' do + let(:config) do + <<~YAML + workflow: + auto_cancel: + on_new_commit: interruptible + rules: + - if: $VAR123 == "valid value" + auto_cancel: + on_new_commit: none + - when: always + + test1: + script: exit 0 + YAML + end + + before do + stub_ci_pipeline_yaml_file(config) + end + + context 'when the rule matches' do + before do + create(:ci_variable, project: project, key: 'VAR123', value: 'valid value') + end + + it 'creates a pipeline with on_new_commit' do + expect(pipeline).to be_persisted + expect(pipeline.errors).to be_empty + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('none') + end + end + + context 'when the rule does not match' do + it 'creates a pipeline with on_new_commit' do + expect(pipeline).to be_persisted + expect(pipeline.errors).to be_empty + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + end + end + end end describe 'on_job_failure' do @@ -165,5 +207,47 @@ 'workflow:auto_cancel on job failure must be one of: none, all') end end + + context 'when using with workflow:rules' do + let(:config) do + <<~YAML + workflow: + auto_cancel: + on_job_failure: none + rules: + - if: $VAR123 == "valid value" + auto_cancel: + on_job_failure: all + - when: always + + test1: + script: exit 0 + YAML + end + + before do + stub_ci_pipeline_yaml_file(config) + end + + context 'when the rule matches' do + before do + create(:ci_variable, project: project, key: 'VAR123', value: 'valid value') + end + + it 'creates a pipeline with on_job_failure' do + expect(pipeline).to be_persisted + expect(pipeline.errors).to be_empty + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all') + end + end + + context 'when the rule does not match' do + it 'creates a pipeline with on_job_failure' do + expect(pipeline).to be_persisted + expect(pipeline.errors).to be_empty + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + end + end + end end end -- GitLab From e7fa359a8962de1f2a705fb0e7266cc68f82ecc5 Mon Sep 17 00:00:00 2001 From: Furkan Ayhan Date: Tue, 9 Jan 2024 12:59:26 +0100 Subject: [PATCH 2/3] Apply doc changes --- doc/ci/yaml/index.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 1213334b94dbe2..55f4e53a6f9f09 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -728,29 +728,30 @@ workflow: on_new_commit: none - when: always # Run the pipeline in other cases -test1: +test-job1: script: sleep 10 interruptible: false -test2: +test-job2: script: sleep 10 interruptible: true ``` -In this example, [`workflow:auto_cancel:on_new_commit`](#workflowauto_cancelon_new_commit) is set to `interruptible` -for non-protected branches, `none` for protected branches. +In this example, [`workflow:auto_cancel:on_new_commit`](#workflowauto_cancelon_new_commit) +is set to `interruptible` for all jobs by default. But if a pipeline runs for a protected branch, +the rule overrides the default with `on_new_commit: none`. In a non-protected branch: - A pipeline is running. - A new commit is pushed to the branch. -- `test1` is continued and `test2` is canceled. +- `test-job1` is continued and `test-job2` is canceled. In a protected branch: - A pipeline is running. - A new commit is pushed to the branch. -- `test1` and `test2` are continued. +- `test-job1` and `test-job2` are continued. ## Header keywords -- GitLab From 9aad6b272307cf2a63a37a294262a425db684b1e Mon Sep 17 00:00:00 2001 From: Marcel Amirault Date: Wed, 10 Jan 2024 09:48:35 +0000 Subject: [PATCH 3/3] Apply 1 suggestion(s) to 1 file(s) --- doc/ci/yaml/index.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 55f4e53a6f9f09..6f3d343c0a32bf 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -739,19 +739,11 @@ test-job2: In this example, [`workflow:auto_cancel:on_new_commit`](#workflowauto_cancelon_new_commit) is set to `interruptible` for all jobs by default. But if a pipeline runs for a protected branch, -the rule overrides the default with `on_new_commit: none`. +the rule overrides the default with `on_new_commit: none`. For example, if a pipeline +is running for: -In a non-protected branch: - -- A pipeline is running. -- A new commit is pushed to the branch. -- `test-job1` is continued and `test-job2` is canceled. - -In a protected branch: - -- A pipeline is running. -- A new commit is pushed to the branch. -- `test-job1` and `test-job2` are continued. +- A non-protected branch and a new commit is pushed, `test-job1` continues to run and `test-job2` is canceled. +- A protected branch and a new commit is pushed, both `test-job1` and `test-job2` continue to run. ## Header keywords -- GitLab