diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb
index d0ce1c5aba8b755959781898e54e7a0f05800a1e..88cec0cde9a6055ac55ca92e76b1dc9f45d51b19 100644
--- a/spec/services/ci/create_pipeline_service/rules_spec.rb
+++ b/spec/services/ci/create_pipeline_service/rules_spec.rb
@@ -7,10 +7,38 @@
   let(:ref)         { 'refs/heads/master' }
   let(:source)      { :push }
   let(:service)     { described_class.new(project, user, { ref: ref }) }
-  let(:pipeline)    { service.execute(source).payload }
+  let(:response)    { execute_service }
+  let(:pipeline)    { response.payload }
   let(:build_names) { pipeline.builds.pluck(:name) }
 
+  def execute_service(before: '00000000', variables_attributes: nil)
+    params = { ref: ref, before: before, after: project.commit(ref).sha, variables_attributes: variables_attributes }
+
+    described_class
+      .new(project, user, params)
+      .execute(source) do |pipeline|
+      yield(pipeline) if block_given?
+    end
+  end
+
   context 'job:rules' do
+    let(:regular_job) { find_job('regular-job') }
+    let(:rules_job)   { find_job('rules-job') }
+    let(:delayed_job) { find_job('delayed-job') }
+
+    def find_job(name)
+      pipeline.builds.find_by(name: name)
+    end
+
+    shared_examples 'rules jobs are excluded' do
+      it 'only persists the job without rules' do
+        expect(pipeline).to be_persisted
+        expect(regular_job).to be_persisted
+        expect(rules_job).to be_nil
+        expect(delayed_job).to be_nil
+      end
+    end
+
     before do
       stub_ci_pipeline_yaml_file(config)
       allow_next_instance_of(Ci::BuildScheduleWorker) do |instance|
@@ -95,10 +123,6 @@
     end
 
     context 'with allow_failure and exit_codes', :aggregate_failures do
-      def find_job(name)
-        pipeline.builds.find_by(name: name)
-      end
-
       let(:config) do
         <<-EOY
           job-1:
@@ -280,171 +304,1165 @@ def find_job(name)
           end
         end
       end
-    end
-  end
 
-  context 'when workflow:rules are used' do
-    before do
-      stub_ci_pipeline_yaml_file(config)
-    end
+      context 'with simple if: clauses' do
+        let(:config) do
+          <<-EOY
+            regular-job:
+              script: 'echo Hello, World!'
 
-    context 'with a single regex-matching if: clause' do
-      let(:config) do
-        <<-EOY
-          workflow:
-            rules:
-              - if: $CI_COMMIT_REF_NAME =~ /master/
-              - if: $CI_COMMIT_REF_NAME =~ /wip$/
-                when: never
-              - if: $CI_COMMIT_REF_NAME =~ /feature/
+            master-job:
+              script: "echo hello world, $CI_COMMIT_REF_NAME"
+              rules:
+                - if: $CI_COMMIT_REF_NAME == "nonexistant-branch"
+                  when: never
+                - if: $CI_COMMIT_REF_NAME =~ /master/
+                  when: manual
 
-          regular-job:
-            script: 'echo Hello, World!'
-        EOY
-      end
+            negligible-job:
+              script: "exit 1"
+              rules:
+                - if: $CI_COMMIT_REF_NAME =~ /master/
+                  allow_failure: true
 
-      context 'matching the first rule in the list' do
-        it 'saves a created pipeline' do
-          expect(pipeline).to be_created
-          expect(pipeline).to be_persisted
+            delayed-job:
+              script: "echo See you later, World!"
+              rules:
+                - if: $CI_COMMIT_REF_NAME =~ /master/
+                  when: delayed
+                  start_in: 1 hour
+
+            never-job:
+              script: "echo Goodbye, World!"
+              rules:
+                - if: $CI_COMMIT_REF_NAME
+                  when: never
+          EOY
         end
-      end
 
-      context 'matching the last rule in the list' do
-        let(:ref) { 'refs/heads/feature' }
+        context 'with matches' do
+          it 'creates a pipeline with the vanilla and manual jobs' do
+            expect(pipeline).to be_persisted
+            expect(build_names).to contain_exactly(
+              'regular-job', 'delayed-job', 'master-job', 'negligible-job'
+            )
+          end
 
-        it 'saves a created pipeline' do
-          expect(pipeline).to be_created
-          expect(pipeline).to be_persisted
+          it 'assigns job:when values to the builds' do
+            expect(find_job('regular-job').when).to eq('on_success')
+            expect(find_job('master-job').when).to eq('manual')
+            expect(find_job('negligible-job').when).to eq('on_success')
+            expect(find_job('delayed-job').when).to eq('delayed')
+          end
+
+          it 'assigns job:allow_failure values to the builds' do
+            expect(find_job('regular-job').allow_failure).to eq(false)
+            expect(find_job('master-job').allow_failure).to eq(false)
+            expect(find_job('negligible-job').allow_failure).to eq(true)
+            expect(find_job('delayed-job').allow_failure).to eq(false)
+          end
+
+          it 'assigns start_in for delayed jobs' do
+            expect(delayed_job.options[:start_in]).to eq('1 hour')
+          end
         end
-      end
 
-      context 'matching the when:never rule' do
-        let(:ref) { 'refs/heads/wip' }
+        context 'with no matches' do
+          let(:ref) { 'refs/heads/feature' }
 
-        it 'invalidates the pipeline with a workflow rules error' do
-          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
-          expect(pipeline).not_to be_persisted
+          it_behaves_like 'rules jobs are excluded'
         end
       end
 
-      context 'matching no rules in the list' do
-        let(:ref) { 'refs/heads/fix' }
+      context 'with complex if: clauses' do
+        let(:config) do
+          <<-EOY
+            regular-job:
+              script: 'echo Hello, World!'
+              rules:
+                - if: $VAR == 'present' && $OTHER || $CI_COMMIT_REF_NAME
+                  when: manual
+                  allow_failure: true
+          EOY
+        end
 
-        it 'invalidates the pipeline with a workflow rules error' do
-          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
-          expect(pipeline).not_to be_persisted
+        it 'matches the first rule' do
+          expect(pipeline).to be_persisted
+          expect(build_names).to contain_exactly('regular-job')
+          expect(regular_job.when).to eq('manual')
+          expect(regular_job.allow_failure).to eq(true)
         end
       end
     end
 
-    context 'when root variables are used' do
+    context 'changes:' do
       let(:config) do
         <<-EOY
-          variables:
-            VARIABLE: value
+          regular-job:
+            script: 'echo Hello, World!'
 
-          workflow:
+          rules-job:
+            script: "echo hello world, $CI_COMMIT_REF_NAME"
             rules:
-              - if: $VARIABLE
+              - changes:
+                - README.md
+                when: manual
+              - changes:
+                - app.rb
+                when: on_success
 
-          regular-job:
-            script: 'echo Hello, World!'
+          delayed-job:
+            script: "echo See you later, World!"
+            rules:
+              - changes:
+                - README.md
+                when: delayed
+                start_in: 4 hours
+
+          negligible-job:
+            script: "can be failed sometimes"
+            rules:
+              - changes:
+                - README.md
+                allow_failure: true
+
+          README:
+            script: "I use variables for changes!"
+            rules:
+              - changes:
+                - $CI_JOB_NAME*
+
+          changes-paths:
+            script: "I am using a new syntax!"
+            rules:
+              - changes:
+                  paths: [README.md]
         EOY
       end
 
-      context 'matching the first rule in the list' do
-        it 'saves a created pipeline' do
-          expect(pipeline).to be_created
+      context 'and matches' do
+        before do
+          allow_next_instance_of(Ci::Pipeline) do |pipeline|
+            allow(pipeline).to receive(:modified_paths).and_return(%w[README.md])
+          end
+        end
+
+        it 'creates five jobs' do
           expect(pipeline).to be_persisted
+          expect(build_names).to contain_exactly(
+            'regular-job', 'rules-job', 'delayed-job', 'negligible-job', 'README', 'changes-paths'
+          )
         end
-      end
-    end
 
-    context 'with a multiple regex-matching if: clause' do
-      let(:config) do
-        <<-EOY
-          workflow:
-            rules:
-              - if: $CI_COMMIT_REF_NAME =~ /master/
-              - if: $CI_COMMIT_REF_NAME =~ /^feature/ && $CI_COMMIT_REF_NAME =~ /conflict$/
-                when: never
-              - if: $CI_COMMIT_REF_NAME =~ /feature/
+        it 'sets when: for all jobs' do
+          expect(regular_job.when).to eq('on_success')
+          expect(rules_job.when).to eq('manual')
+          expect(delayed_job.when).to eq('delayed')
+          expect(delayed_job.options[:start_in]).to eq('4 hours')
+        end
 
-          regular-job:
-            script: 'echo Hello, World!'
-        EOY
+        it 'sets allow_failure: for negligible job' do
+          expect(find_job('negligible-job').allow_failure).to eq(true)
+        end
       end
 
-      context 'with partial match' do
-        let(:ref) { 'refs/heads/feature' }
+      context 'and matches the second rule' do
+        before do
+          allow_next_instance_of(Ci::Pipeline) do |pipeline|
+            allow(pipeline).to receive(:modified_paths).and_return(%w[app.rb])
+          end
+        end
 
-        it 'saves a created pipeline' do
-          expect(pipeline).to be_created
+        it 'includes both jobs' do
           expect(pipeline).to be_persisted
+          expect(build_names).to contain_exactly('regular-job', 'rules-job')
+        end
+
+        it 'sets when: for the created rules job based on the second clause' do
+          expect(regular_job.when).to eq('on_success')
+          expect(rules_job.when).to eq('on_success')
         end
       end
 
-      context 'with complete match' do
-        let(:ref) { 'refs/heads/feature_conflict' }
+      context 'and does not match' do
+        before do
+          allow_next_instance_of(Ci::Pipeline) do |pipeline|
+            allow(pipeline).to receive(:modified_paths).and_return(%w[useless_script.rb])
+          end
+        end
 
-        it 'invalidates the pipeline with a workflow rules error' do
-          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
-          expect(pipeline).not_to be_persisted
+        it_behaves_like 'rules jobs are excluded'
+
+        it 'sets when: for the created job' do
+          expect(regular_job.when).to eq('on_success')
+        end
+      end
+
+      context 'with paths and compare_to' do
+        let_it_be(:project) { create(:project, :empty_repo) }
+        let_it_be(:user)    { project.first_owner }
+
+        before_all do
+          project.repository.add_branch(user, 'feature_1', 'master')
+
+          project.repository.create_file(
+            user, 'file1.txt', 'file 1', message: 'Create file1.txt', branch_name: 'feature_1'
+          )
+
+          project.repository.add_branch(user, 'feature_2', 'feature_1')
+
+          project.repository.create_file(
+            user, 'file2.txt', 'file 2', message: 'Create file2.txt', branch_name: 'feature_2'
+          )
+        end
+
+        let(:changed_file) { 'file2.txt' }
+        let(:ref) { 'feature_2' }
+
+        let(:response) { execute_service(before: nil) }
+
+        context 'for jobs rules' do
+          let(:config) do
+            <<-EOY
+            job1:
+              script: exit 0
+              rules:
+                - changes:
+                    paths: [#{changed_file}]
+                    compare_to: #{compare_to}
+
+            job2:
+              script: exit 0
+            EOY
+          end
+
+          context 'when there is no such compare_to ref' do
+            let(:compare_to) { 'invalid-branch' }
+
+            it 'returns an error' do
+              expect(pipeline.errors.full_messages).to eq([
+                'Failed to parse rule for job1: rules:changes:compare_to is not a valid ref'
+              ])
+            end
+
+            context 'when the FF ci_rules_changes_compare is not enabled' do
+              before do
+                stub_feature_flags(ci_rules_changes_compare: false)
+              end
+
+              it 'ignores compare_to and changes is always true' do
+                expect(build_names).to contain_exactly('job1', 'job2')
+              end
+            end
+          end
+
+          context 'when the compare_to ref exists' do
+            let(:compare_to) { 'feature_1'}
+
+            context 'when the rule matches' do
+              it 'creates job1 and job2' do
+                expect(build_names).to contain_exactly('job1', 'job2')
+              end
+
+              context 'when the FF ci_rules_changes_compare is not enabled' do
+                before do
+                  stub_feature_flags(ci_rules_changes_compare: false)
+                end
+
+                it 'ignores compare_to and changes is always true' do
+                  expect(build_names).to contain_exactly('job1', 'job2')
+                end
+              end
+            end
+
+            context 'when the rule does not match' do
+              let(:changed_file) { 'file1.txt' }
+
+              it 'does not create job1' do
+                expect(build_names).to contain_exactly('job2')
+              end
+
+              context 'when the FF ci_rules_changes_compare is not enabled' do
+                before do
+                  stub_feature_flags(ci_rules_changes_compare: false)
+                end
+
+                it 'ignores compare_to and changes is always true' do
+                  expect(build_names).to contain_exactly('job1', 'job2')
+                end
+              end
+            end
+          end
+        end
+
+        context 'for workflow rules' do
+          let(:config) do
+            <<-EOY
+            workflow:
+              rules:
+                - changes:
+                    paths: [#{changed_file}]
+                    compare_to: #{compare_to}
+
+            job1:
+              script: exit 0
+            EOY
+          end
+
+          let(:compare_to) { 'feature_1'}
+
+          context 'when the rule matches' do
+            it 'creates job1' do
+              expect(pipeline).to be_created_successfully
+              expect(build_names).to contain_exactly('job1')
+            end
+
+            context 'when the FF ci_rules_changes_compare is not enabled' do
+              before do
+                stub_feature_flags(ci_rules_changes_compare: false)
+              end
+
+              it 'ignores compare_to and changes is always true' do
+                expect(pipeline).to be_created_successfully
+                expect(build_names).to contain_exactly('job1')
+              end
+            end
+          end
+
+          context 'when the rule does not match' do
+            let(:changed_file) { 'file1.txt' }
+
+            it 'does not create job1' do
+              expect(pipeline).not_to be_created_successfully
+              expect(build_names).to be_empty
+            end
+          end
         end
       end
     end
 
-    context 'with job rules' do
+    context 'mixed if: and changes: rules' do
       let(:config) do
         <<-EOY
-          workflow:
-            rules:
-              - if: $CI_COMMIT_REF_NAME =~ /master/
-              - if: $CI_COMMIT_REF_NAME =~ /feature/
-
           regular-job:
             script: 'echo Hello, World!'
+
+          rules-job:
+            script: "echo hello world, $CI_COMMIT_REF_NAME"
+            allow_failure: true
             rules:
-              - if: $CI_COMMIT_REF_NAME =~ /wip/
-              - if: $CI_COMMIT_REF_NAME =~ /feature/
+              - changes:
+                - README.md
+                when: manual
+              - if: $CI_COMMIT_REF_NAME == "master"
+                when: on_success
+                allow_failure: false
+
+          delayed-job:
+            script: "echo See you later, World!"
+            rules:
+              - changes:
+                - README.md
+                when: delayed
+                start_in: 4 hours
+                allow_failure: true
+              - if: $CI_COMMIT_REF_NAME == "master"
+                when: delayed
+                start_in: 1 hour
         EOY
       end
 
-      context 'where workflow passes and the job fails' do
-        let(:ref) { 'refs/heads/master' }
+      context 'and changes: matches before if' do
+        before do
+          allow_next_instance_of(Ci::Pipeline) do |pipeline|
+            allow(pipeline).to receive(:modified_paths).and_return(%w[README.md])
+          end
+        end
 
-        it 'invalidates the pipeline with an empty jobs error' do
-          expect(pipeline.errors[:base]).to include('No stages / jobs for this pipeline.')
-          expect(pipeline).not_to be_persisted
+        it 'creates two jobs' do
+          expect(pipeline).to be_persisted
+          expect(build_names)
+            .to contain_exactly('regular-job', 'rules-job', 'delayed-job')
         end
-      end
 
-      context 'where workflow passes and the job passes' do
-        let(:ref) { 'refs/heads/feature' }
+        it 'sets when: for all jobs' do
+          expect(regular_job.when).to eq('on_success')
+          expect(rules_job.when).to eq('manual')
+          expect(delayed_job.when).to eq('delayed')
+          expect(delayed_job.options[:start_in]).to eq('4 hours')
+        end
 
-        it 'saves a created pipeline' do
-          expect(pipeline).to be_created
-          expect(pipeline).to be_persisted
+        it 'sets allow_failure: for all jobs' do
+          expect(regular_job.allow_failure).to eq(false)
+          expect(rules_job.allow_failure).to eq(true)
+          expect(delayed_job.allow_failure).to eq(true)
         end
       end
 
-      context 'where workflow fails and the job fails' do
-        let(:ref) { 'refs/heads/fix' }
+      context 'and if: matches after changes' do
+        it 'includes both jobs' do
+          expect(pipeline).to be_persisted
+          expect(build_names).to contain_exactly('regular-job', 'rules-job', 'delayed-job')
+        end
 
-        it 'invalidates the pipeline with a workflow rules error' do
-          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
-          expect(pipeline).not_to be_persisted
+        it 'sets when: for the created rules job based on the second clause' do
+          expect(regular_job.when).to eq('on_success')
+          expect(rules_job.when).to eq('on_success')
+          expect(delayed_job.when).to eq('delayed')
+          expect(delayed_job.options[:start_in]).to eq('1 hour')
         end
       end
 
-      context 'where workflow fails and the job passes' do
+      context 'and does not match' do
         let(:ref) { 'refs/heads/wip' }
 
-        it 'invalidates the pipeline with a workflow rules error' do
-          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
-          expect(pipeline).not_to be_persisted
-        end
+        it_behaves_like 'rules jobs are excluded'
+
+        it 'sets when: for the created job' do
+          expect(regular_job.when).to eq('on_success')
+        end
+      end
+    end
+
+    context 'mixed if: and changes: clauses' do
+      let(:config) do
+        <<-EOY
+          regular-job:
+            script: 'echo Hello, World!'
+
+          rules-job:
+            script: "echo hello world, $CI_COMMIT_REF_NAME"
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                changes: [README.md]
+                when: on_success
+                allow_failure: true
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                changes: [app.rb]
+                when: manual
+        EOY
+      end
+
+      context 'with if matches and changes matches' do
+        before do
+          allow_next_instance_of(Ci::Pipeline) do |pipeline|
+            allow(pipeline).to receive(:modified_paths).and_return(%w[app.rb])
+          end
+        end
+
+        it 'persists all jobs' do
+          expect(pipeline).to be_persisted
+          expect(regular_job).to be_persisted
+          expect(rules_job).to be_persisted
+          expect(rules_job.when).to eq('manual')
+          expect(rules_job.allow_failure).to eq(false)
+        end
+      end
+
+      context 'with if matches and no change matches' do
+        it_behaves_like 'rules jobs are excluded'
+      end
+
+      context 'with change matches and no if matches' do
+        let(:ref) { 'refs/heads/feature' }
+
+        before do
+          allow_next_instance_of(Ci::Pipeline) do |pipeline|
+            allow(pipeline).to receive(:modified_paths).and_return(%w[README.md])
+          end
+        end
+
+        it_behaves_like 'rules jobs are excluded'
+      end
+
+      context 'and no matches' do
+        let(:ref) { 'refs/heads/feature' }
+
+        it_behaves_like 'rules jobs are excluded'
+      end
+    end
+
+    context 'complex if: allow_failure usages' do
+      let(:config) do
+        <<-EOY
+          job-1:
+            script: "exit 1"
+            allow_failure: true
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                allow_failure: false
+
+          job-2:
+            script: "exit 1"
+            allow_failure: true
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+                allow_failure: false
+
+          job-3:
+            script: "exit 1"
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+                allow_failure: true
+
+          job-4:
+            script: "exit 1"
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                allow_failure: false
+
+          job-5:
+            script: "exit 1"
+            allow_failure: false
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                allow_failure: true
+
+          job-6:
+            script: "exit 1"
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+                allow_failure: false
+              - allow_failure: true
+        EOY
+      end
+
+      it 'creates a pipeline' do
+        expect(pipeline).to be_persisted
+        expect(build_names).to contain_exactly('job-1', 'job-4', 'job-5', 'job-6')
+      end
+
+      it 'assigns job:allow_failure values to the builds' do
+        expect(find_job('job-1').allow_failure).to eq(false)
+        expect(find_job('job-4').allow_failure).to eq(false)
+        expect(find_job('job-5').allow_failure).to eq(true)
+        expect(find_job('job-6').allow_failure).to eq(true)
+      end
+    end
+
+    context 'complex if: allow_failure & when usages' do
+      let(:config) do
+        <<-EOY
+          job-1:
+            script: "exit 1"
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                when: manual
+
+          job-2:
+            script: "exit 1"
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                when: manual
+                allow_failure: true
+
+          job-3:
+            script: "exit 1"
+            allow_failure: true
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                when: manual
+
+          job-4:
+            script: "exit 1"
+            allow_failure: true
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                when: manual
+                allow_failure: false
+
+          job-5:
+            script: "exit 1"
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+                when: manual
+                allow_failure: false
+              - when: always
+                allow_failure: true
+
+          job-6:
+            script: "exit 1"
+            allow_failure: false
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+                when: manual
+
+          job-7:
+            script: "exit 1"
+            allow_failure: false
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+                when: manual
+              - when: :on_failure
+                allow_failure: true
+        EOY
+      end
+
+      it 'creates a pipeline' do
+        expect(pipeline).to be_persisted
+        expect(build_names).to contain_exactly(
+          'job-1', 'job-2', 'job-3', 'job-4', 'job-5', 'job-6', 'job-7'
+        )
+      end
+
+      it 'assigns job:allow_failure values to the builds' do
+        expect(find_job('job-1').allow_failure).to eq(false)
+        expect(find_job('job-2').allow_failure).to eq(true)
+        expect(find_job('job-3').allow_failure).to eq(true)
+        expect(find_job('job-4').allow_failure).to eq(false)
+        expect(find_job('job-5').allow_failure).to eq(true)
+        expect(find_job('job-6').allow_failure).to eq(false)
+        expect(find_job('job-7').allow_failure).to eq(true)
+      end
+
+      it 'assigns job:when values to the builds' do
+        expect(find_job('job-1').when).to eq('manual')
+        expect(find_job('job-2').when).to eq('manual')
+        expect(find_job('job-3').when).to eq('manual')
+        expect(find_job('job-4').when).to eq('manual')
+        expect(find_job('job-5').when).to eq('always')
+        expect(find_job('job-6').when).to eq('manual')
+        expect(find_job('job-7').when).to eq('on_failure')
+      end
+    end
+
+    context 'deploy freeze period `if:` clause' do
+      # '0 23 * * 5' == "At 23:00 on Friday."", '0 7 * * 1' == "At 07:00 on Monday.""
+      let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: '0 23 * * 5', freeze_end: '0 7 * * 1') }
+
+      context 'with 2 jobs' do
+        let(:config) do
+          <<-EOY
+          stages:
+            - test
+            - deploy
+
+          test-job:
+            script:
+              - echo 'running TEST stage'
+
+          deploy-job:
+            stage: deploy
+            script:
+              - echo 'running DEPLOY stage'
+            rules:
+              - if: $CI_DEPLOY_FREEZE == null
+          EOY
+        end
+
+        context 'when outside freeze period' do
+          it 'creates two jobs' do
+            Timecop.freeze(2020, 4, 10, 22, 59) do
+              expect(pipeline).to be_persisted
+              expect(build_names).to contain_exactly('test-job', 'deploy-job')
+            end
+          end
+        end
+
+        context 'when inside freeze period' do
+          it 'creates one job' do
+            Timecop.freeze(2020, 4, 10, 23, 1) do
+              expect(pipeline).to be_persisted
+              expect(build_names).to contain_exactly('test-job')
+            end
+          end
+        end
+      end
+
+      context 'with 1 job' do
+        let(:config) do
+          <<-EOY
+          stages:
+            - deploy
+
+          deploy-job:
+            stage: deploy
+            script:
+              - echo 'running DEPLOY stage'
+            rules:
+              - if: $CI_DEPLOY_FREEZE == null
+          EOY
+        end
+
+        context 'when outside freeze period' do
+          it 'creates two jobs' do
+            Timecop.freeze(2020, 4, 10, 22, 59) do
+              expect(pipeline).to be_persisted
+              expect(build_names).to contain_exactly('deploy-job')
+            end
+          end
+        end
+
+        context 'when inside freeze period' do
+          it 'does not create the pipeline', :aggregate_failures do
+            Timecop.freeze(2020, 4, 10, 23, 1) do
+              expect(response).to be_error
+              expect(pipeline).not_to be_persisted
+            end
+          end
+        end
+      end
+    end
+
+    context 'with when:manual' do
+      let(:config) do
+        <<-EOY
+        job-with-rules:
+          script: 'echo hey'
+          rules:
+            - if: $CI_COMMIT_REF_NAME =~ /master/
+
+        job-when-with-rules:
+          script: 'echo hey'
+          when: manual
+          rules:
+            - if: $CI_COMMIT_REF_NAME =~ /master/
+
+        job-when-with-rules-when:
+          script: 'echo hey'
+          when: manual
+          rules:
+            - if: $CI_COMMIT_REF_NAME =~ /master/
+              when: on_success
+
+        job-with-rules-when:
+          script: 'echo hey'
+          rules:
+            - if: $CI_COMMIT_REF_NAME =~ /master/
+              when: manual
+
+        job-without-rules:
+          script: 'echo this is a job with NO rules'
+        EOY
+      end
+
+      let(:job_with_rules) { find_job('job-with-rules') }
+      let(:job_when_with_rules) { find_job('job-when-with-rules') }
+      let(:job_when_with_rules_when) { find_job('job-when-with-rules-when') }
+      let(:job_with_rules_when) { find_job('job-with-rules-when') }
+      let(:job_without_rules) { find_job('job-without-rules') }
+
+      context 'when matching the rules' do
+        let(:ref) { 'refs/heads/master' }
+
+        it 'adds the job-with-rules with a when:manual' do
+          expect(job_with_rules).to be_persisted
+          expect(job_when_with_rules).to be_persisted
+          expect(job_when_with_rules_when).to be_persisted
+          expect(job_with_rules_when).to be_persisted
+          expect(job_without_rules).to be_persisted
+
+          expect(job_with_rules.when).to eq('on_success')
+          expect(job_when_with_rules.when).to eq('manual')
+          expect(job_when_with_rules_when.when).to eq('on_success')
+          expect(job_with_rules_when.when).to eq('manual')
+          expect(job_without_rules.when).to eq('on_success')
+        end
+      end
+
+      context 'when there is no match to the rule' do
+        let(:ref) { 'refs/heads/wip' }
+
+        it 'does not add job_with_rules' do
+          expect(job_with_rules).to be_nil
+          expect(job_when_with_rules).to be_nil
+          expect(job_when_with_rules_when).to be_nil
+          expect(job_with_rules_when).to be_nil
+          expect(job_without_rules).to be_persisted
+        end
+      end
+    end
+  end
+
+  context 'when workflow:rules are used' do
+    before do
+      stub_ci_pipeline_yaml_file(config)
+    end
+
+    context 'with a single regex-matching if: clause' do
+      let(:config) do
+        <<-EOY
+          workflow:
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+              - if: $CI_COMMIT_REF_NAME =~ /wip$/
+                when: never
+              - if: $CI_COMMIT_REF_NAME =~ /feature/
+
+          regular-job:
+            script: 'echo Hello, World!'
+        EOY
+      end
+
+      context 'matching the first rule in the list' do
+        it 'saves a created pipeline' do
+          expect(pipeline).to be_created
+          expect(pipeline).to be_persisted
+        end
+      end
+
+      context 'matching the last rule in the list' do
+        let(:ref) { 'refs/heads/feature' }
+
+        it 'saves a created pipeline' do
+          expect(pipeline).to be_created
+          expect(pipeline).to be_persisted
+        end
+      end
+
+      context 'matching the when:never rule' do
+        let(:ref) { 'refs/heads/wip' }
+
+        it 'invalidates the pipeline with a workflow rules error' do
+          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
+          expect(pipeline).not_to be_persisted
+        end
+      end
+
+      context 'matching no rules in the list' do
+        let(:ref) { 'refs/heads/fix' }
+
+        it 'invalidates the pipeline with a workflow rules error' do
+          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
+          expect(pipeline).not_to be_persisted
+        end
+      end
+    end
+
+    context 'when root variables are used' do
+      let(:config) do
+        <<-EOY
+          variables:
+            VARIABLE: value
+
+          workflow:
+            rules:
+              - if: $VARIABLE
+
+          regular-job:
+            script: 'echo Hello, World!'
+        EOY
+      end
+
+      context 'matching the first rule in the list' do
+        it 'saves a created pipeline' do
+          expect(pipeline).to be_created
+          expect(pipeline).to be_persisted
+        end
+      end
+    end
+
+    context 'with a multiple regex-matching if: clause' do
+      let(:config) do
+        <<-EOY
+          workflow:
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+              - if: $CI_COMMIT_REF_NAME =~ /^feature/ && $CI_COMMIT_REF_NAME =~ /conflict$/
+                when: never
+              - if: $CI_COMMIT_REF_NAME =~ /feature/
+
+          regular-job:
+            script: 'echo Hello, World!'
+        EOY
+      end
+
+      context 'with partial match' do
+        let(:ref) { 'refs/heads/feature' }
+
+        it 'saves a created pipeline' do
+          expect(pipeline).to be_created
+          expect(pipeline).to be_persisted
+        end
+      end
+
+      context 'with complete match' do
+        let(:ref) { 'refs/heads/feature_conflict' }
+
+        it 'invalidates the pipeline with a workflow rules error' do
+          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
+          expect(pipeline).not_to be_persisted
+        end
+      end
+    end
+
+    context 'with job rules' do
+      let(:config) do
+        <<-EOY
+          workflow:
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /master/
+              - if: $CI_COMMIT_REF_NAME =~ /feature/
+
+          regular-job:
+            script: 'echo Hello, World!'
+            rules:
+              - if: $CI_COMMIT_REF_NAME =~ /wip/
+              - if: $CI_COMMIT_REF_NAME =~ /feature/
+        EOY
+      end
+
+      context 'where workflow passes and the job fails' do
+        let(:ref) { 'refs/heads/master' }
+
+        it 'invalidates the pipeline with an empty jobs error' do
+          expect(pipeline.errors[:base]).to include('No stages / jobs for this pipeline.')
+          expect(pipeline).not_to be_persisted
+        end
+      end
+
+      context 'where workflow passes and the job passes' do
+        let(:ref) { 'refs/heads/feature' }
+
+        it 'saves a created pipeline' do
+          expect(pipeline).to be_created
+          expect(pipeline).to be_persisted
+        end
+      end
+
+      context 'where workflow fails and the job fails' do
+        let(:ref) { 'refs/heads/fix' }
+
+        it 'invalidates the pipeline with a workflow rules error' do
+          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
+          expect(pipeline).not_to be_persisted
+        end
+      end
+
+      context 'where workflow fails and the job passes' do
+        let(:ref) { 'refs/heads/wip' }
+
+        it 'invalidates the pipeline with a workflow rules error' do
+          expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
+          expect(pipeline).not_to be_persisted
+        end
+      end
+    end
+
+    context 'with persisted variables' do
+      let(:config) do
+        <<-EOY
+          workflow:
+            rules:
+              - if: $CI_COMMIT_REF_NAME == "master"
+
+          regular-job:
+            script: 'echo Hello, World!'
+        EOY
+      end
+
+      context 'with matches' do
+        it 'creates a pipeline' do
+          expect(pipeline).to be_persisted
+          expect(build_names).to contain_exactly('regular-job')
+        end
+      end
+
+      context 'with no matches' do
+        let(:ref) { 'refs/heads/feature' }
+
+        it 'does not create a pipeline', :aggregate_failures do
+          expect(response).to be_error
+          expect(pipeline).not_to be_persisted
+        end
+      end
+    end
+
+    context 'with pipeline variables' do
+      let(:pipeline) do
+        execute_service(variables_attributes: variables_attributes).payload
+      end
+
+      let(:config) do
+        <<-EOY
+          workflow:
+            rules:
+              - if: $SOME_VARIABLE
+
+          regular-job:
+            script: 'echo Hello, World!'
+        EOY
+      end
+
+      context 'with matches' do
+        let(:variables_attributes) do
+          [{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
+        end
+
+        it 'creates a pipeline' do
+          expect(pipeline).to be_persisted
+          expect(build_names).to contain_exactly('regular-job')
+        end
+      end
+
+      context 'with no matches' do
+        let(:variables_attributes) { {} }
+
+        it 'does not create a pipeline', :aggregate_failures do
+          expect(response).to be_error
+          expect(pipeline).not_to be_persisted
+        end
+      end
+    end
+
+    context 'with trigger variables' do
+      let(:pipeline) do
+        execute_service do |pipeline|
+          pipeline.variables.build(variables)
+        end.payload
+      end
+
+      let(:config) do
+        <<-EOY
+          workflow:
+            rules:
+              - if: $SOME_VARIABLE
+
+          regular-job:
+            script: 'echo Hello, World!'
+        EOY
+      end
+
+      context 'with matches' do
+        let(:variables) do
+          [{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
+        end
+
+        it 'creates a pipeline' do
+          expect(pipeline).to be_persisted
+          expect(build_names).to contain_exactly('regular-job')
+        end
+
+        context 'when a job requires the same variable' do
+          let(:config) do
+            <<-EOY
+              workflow:
+                rules:
+                  - if: $SOME_VARIABLE
+
+              build:
+                stage: build
+                script: 'echo build'
+                rules:
+                  - if: $SOME_VARIABLE
+
+              test1:
+                stage: test
+                script: 'echo test1'
+                needs: [build]
+
+              test2:
+                stage: test
+                script: 'echo test2'
+            EOY
+          end
+
+          it 'creates a pipeline' do
+            expect(pipeline).to be_persisted
+            expect(build_names).to contain_exactly('build', 'test1', 'test2')
+          end
+        end
+      end
+
+      context 'with no matches' do
+        let(:variables) { {} }
+
+        it 'does not create a pipeline', :aggregate_failures do
+          expect(response).to be_error
+          expect(pipeline).not_to be_persisted
+        end
+
+        context 'when a job requires the same variable' do
+          let(:config) do
+            <<-EOY
+              workflow:
+                rules:
+                  - if: $SOME_VARIABLE
+
+              build:
+                stage: build
+                script: 'echo build'
+                rules:
+                  - if: $SOME_VARIABLE
+
+              test1:
+                stage: test
+                script: 'echo test1'
+                needs: [build]
+
+              test2:
+                stage: test
+                script: 'echo test2'
+            EOY
+          end
+
+          it 'does not create a pipeline', :aggregate_failures do
+            expect(response).to be_error
+            expect(pipeline).not_to be_persisted
+          end
+        end
+      end
+    end
+
+    context 'changes' do
+      shared_examples 'comparing file changes with workflow rules' do
+        context 'when matches' do
+          before do
+            allow_next_instance_of(Ci::Pipeline) do |pipeline|
+              allow(pipeline).to receive(:modified_paths).and_return(%w[file1.md])
+            end
+          end
+
+          it 'creates the pipeline with a job' do
+            expect(pipeline).to be_persisted
+            expect(build_names).to contain_exactly('job')
+          end
+        end
+
+        context 'when does not match' do
+          before do
+            allow_next_instance_of(Ci::Pipeline) do |pipeline|
+              allow(pipeline).to receive(:modified_paths).and_return(%w[unknown])
+            end
+          end
+
+          it 'creates the pipeline with a job' do
+            expect(pipeline.errors.full_messages).to eq(['Pipeline filtered out by workflow rules.'])
+            expect(response).to be_error
+            expect(pipeline).not_to be_persisted
+          end
+        end
+      end
+
+      context 'changes is an array' do
+        let(:config) do
+          <<-EOY
+            workflow:
+              rules:
+                - changes: [file1.md]
+
+            job:
+              script: exit 0
+          EOY
+        end
+
+        it_behaves_like 'comparing file changes with workflow rules'
+      end
+
+      context 'changes:paths is an array' do
+        let(:config) do
+          <<-EOY
+            workflow:
+              rules:
+                - changes:
+                    paths: [file1.md]
+
+            job:
+              script: exit 0
+          EOY
+        end
+
+        it_behaves_like 'comparing file changes with workflow rules'
       end
     end
   end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 6f89e10da7f01cc28fc141edc756aad99a00768b..a9442b0dc6871fdf8c1cb013828737160ff2b665 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -1866,1019 +1866,6 @@ def previous_commit_sha_from_ref(ref)
         end
       end
     end
-
-    context 'when rules are used' do
-      let(:ref_name)    { 'refs/heads/master' }
-      let(:response)    { execute_service }
-      let(:pipeline)    { response.payload }
-      let(:build_names) { pipeline.builds.pluck(:name) }
-      let(:regular_job) { find_job('regular-job') }
-      let(:rules_job)   { find_job('rules-job') }
-      let(:delayed_job) { find_job('delayed-job') }
-
-      context 'with when:manual' do
-        let(:config) do
-          <<-EOY
-          job-with-rules:
-            script: 'echo hey'
-            rules:
-              - if: $CI_COMMIT_REF_NAME =~ /master/
-
-          job-when-with-rules:
-            script: 'echo hey'
-            when: manual
-            rules:
-              - if: $CI_COMMIT_REF_NAME =~ /master/
-
-          job-when-with-rules-when:
-            script: 'echo hey'
-            when: manual
-            rules:
-              - if: $CI_COMMIT_REF_NAME =~ /master/
-                when: on_success
-
-          job-with-rules-when:
-            script: 'echo hey'
-            rules:
-              - if: $CI_COMMIT_REF_NAME =~ /master/
-                when: manual
-
-          job-without-rules:
-            script: 'echo this is a job with NO rules'
-          EOY
-        end
-
-        let(:job_with_rules) { find_job('job-with-rules') }
-        let(:job_when_with_rules) { find_job('job-when-with-rules') }
-        let(:job_when_with_rules_when) { find_job('job-when-with-rules-when') }
-        let(:job_with_rules_when) { find_job('job-with-rules-when') }
-        let(:job_without_rules) { find_job('job-without-rules') }
-
-        context 'when matching the rules' do
-          let(:ref_name) { 'refs/heads/master' }
-
-          it 'adds the job-with-rules with a when:manual' do
-            expect(job_with_rules).to be_persisted
-            expect(job_when_with_rules).to be_persisted
-            expect(job_when_with_rules_when).to be_persisted
-            expect(job_with_rules_when).to be_persisted
-            expect(job_without_rules).to be_persisted
-
-            expect(job_with_rules.when).to eq('on_success')
-            expect(job_when_with_rules.when).to eq('manual')
-            expect(job_when_with_rules_when.when).to eq('on_success')
-            expect(job_with_rules_when.when).to eq('manual')
-            expect(job_without_rules.when).to eq('on_success')
-          end
-        end
-
-        context 'when there is no match to the rule' do
-          let(:ref_name) { 'refs/heads/wip' }
-
-          it 'does not add job_with_rules' do
-            expect(job_with_rules).to be_nil
-            expect(job_when_with_rules).to be_nil
-            expect(job_when_with_rules_when).to be_nil
-            expect(job_with_rules_when).to be_nil
-            expect(job_without_rules).to be_persisted
-          end
-        end
-      end
-
-      shared_examples 'rules jobs are excluded' do
-        it 'only persists the job without rules' do
-          expect(pipeline).to be_persisted
-          expect(regular_job).to be_persisted
-          expect(rules_job).to be_nil
-          expect(delayed_job).to be_nil
-        end
-      end
-
-      def find_job(name)
-        pipeline.builds.find_by(name: name)
-      end
-
-      before do
-        stub_ci_pipeline_yaml_file(config)
-        allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
-      end
-
-      context 'with simple if: clauses' do
-        let(:config) do
-          <<-EOY
-            regular-job:
-              script: 'echo Hello, World!'
-
-            master-job:
-              script: "echo hello world, $CI_COMMIT_REF_NAME"
-              rules:
-                - if: $CI_COMMIT_REF_NAME == "nonexistant-branch"
-                  when: never
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  when: manual
-
-            negligible-job:
-              script: "exit 1"
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  allow_failure: true
-
-            delayed-job:
-              script: "echo See you later, World!"
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  when: delayed
-                  start_in: 1 hour
-
-            never-job:
-              script: "echo Goodbye, World!"
-              rules:
-                - if: $CI_COMMIT_REF_NAME
-                  when: never
-          EOY
-        end
-
-        context 'with matches' do
-          it 'creates a pipeline with the vanilla and manual jobs' do
-            expect(pipeline).to be_persisted
-            expect(build_names).to contain_exactly(
-              'regular-job', 'delayed-job', 'master-job', 'negligible-job'
-            )
-          end
-
-          it 'assigns job:when values to the builds' do
-            expect(find_job('regular-job').when).to eq('on_success')
-            expect(find_job('master-job').when).to eq('manual')
-            expect(find_job('negligible-job').when).to eq('on_success')
-            expect(find_job('delayed-job').when).to eq('delayed')
-          end
-
-          it 'assigns job:allow_failure values to the builds' do
-            expect(find_job('regular-job').allow_failure).to eq(false)
-            expect(find_job('master-job').allow_failure).to eq(false)
-            expect(find_job('negligible-job').allow_failure).to eq(true)
-            expect(find_job('delayed-job').allow_failure).to eq(false)
-          end
-
-          it 'assigns start_in for delayed jobs' do
-            expect(delayed_job.options[:start_in]).to eq('1 hour')
-          end
-        end
-
-        context 'with no matches' do
-          let(:ref_name) { 'refs/heads/feature' }
-
-          it_behaves_like 'rules jobs are excluded'
-        end
-      end
-
-      context 'with complex if: clauses' do
-        let(:config) do
-          <<-EOY
-            regular-job:
-              script: 'echo Hello, World!'
-              rules:
-                - if: $VAR == 'present' && $OTHER || $CI_COMMIT_REF_NAME
-                  when: manual
-                  allow_failure: true
-          EOY
-        end
-
-        it 'matches the first rule' do
-          expect(pipeline).to be_persisted
-          expect(build_names).to contain_exactly('regular-job')
-          expect(regular_job.when).to eq('manual')
-          expect(regular_job.allow_failure).to eq(true)
-        end
-      end
-
-      context 'with changes:' do
-        let(:config) do
-          <<-EOY
-            regular-job:
-              script: 'echo Hello, World!'
-
-            rules-job:
-              script: "echo hello world, $CI_COMMIT_REF_NAME"
-              rules:
-                - changes:
-                  - README.md
-                  when: manual
-                - changes:
-                  - app.rb
-                  when: on_success
-
-            delayed-job:
-              script: "echo See you later, World!"
-              rules:
-                - changes:
-                  - README.md
-                  when: delayed
-                  start_in: 4 hours
-
-            negligible-job:
-              script: "can be failed sometimes"
-              rules:
-                - changes:
-                  - README.md
-                  allow_failure: true
-
-            README:
-              script: "I use variables for changes!"
-              rules:
-                - changes:
-                  - $CI_JOB_NAME*
-
-            changes-paths:
-              script: "I am using a new syntax!"
-              rules:
-                - changes:
-                    paths: [README.md]
-          EOY
-        end
-
-        context 'and matches' do
-          before do
-            allow_any_instance_of(Ci::Pipeline)
-              .to receive(:modified_paths).and_return(%w[README.md])
-          end
-
-          it 'creates five jobs' do
-            expect(pipeline).to be_persisted
-            expect(build_names).to contain_exactly(
-              'regular-job', 'rules-job', 'delayed-job', 'negligible-job', 'README', 'changes-paths'
-            )
-          end
-
-          it 'sets when: for all jobs' do
-            expect(regular_job.when).to eq('on_success')
-            expect(rules_job.when).to eq('manual')
-            expect(delayed_job.when).to eq('delayed')
-            expect(delayed_job.options[:start_in]).to eq('4 hours')
-          end
-
-          it 'sets allow_failure: for negligible job' do
-            expect(find_job('negligible-job').allow_failure).to eq(true)
-          end
-        end
-
-        context 'and matches the second rule' do
-          before do
-            allow_any_instance_of(Ci::Pipeline)
-              .to receive(:modified_paths).and_return(%w[app.rb])
-          end
-
-          it 'includes both jobs' do
-            expect(pipeline).to be_persisted
-            expect(build_names).to contain_exactly('regular-job', 'rules-job')
-          end
-
-          it 'sets when: for the created rules job based on the second clause' do
-            expect(regular_job.when).to eq('on_success')
-            expect(rules_job.when).to eq('on_success')
-          end
-        end
-
-        context 'and does not match' do
-          before do
-            allow_any_instance_of(Ci::Pipeline)
-              .to receive(:modified_paths).and_return(%w[useless_script.rb])
-          end
-
-          it_behaves_like 'rules jobs are excluded'
-
-          it 'sets when: for the created job' do
-            expect(regular_job.when).to eq('on_success')
-          end
-        end
-      end
-
-      context 'with changes: paths and compare_to' do
-        before_all do
-          project.repository.add_branch(user, 'feature_1', 'master')
-
-          project.repository.create_file(
-            user, 'file1.txt', 'file 1', message: 'Create file1.txt', branch_name: 'feature_1'
-          )
-
-          project.repository.add_branch(user, 'feature_2', 'feature_1')
-
-          project.repository.create_file(
-            user, 'file2.txt', 'file 2', message: 'Create file2.txt', branch_name: 'feature_2'
-          )
-        end
-
-        let(:changed_file) { 'file2.txt' }
-        let(:ref_name) { 'feature_2' }
-
-        let(:response) { execute_service(ref: ref_name, before: nil, after: project.commit(ref_name).sha) }
-
-        context 'for jobs rules' do
-          let(:config) do
-            <<-EOY
-            job1:
-              script: exit 0
-              rules:
-                - changes:
-                    paths: [#{changed_file}]
-                    compare_to: #{compare_to}
-
-            job2:
-              script: exit 0
-            EOY
-          end
-
-          context 'when there is no such compare_to ref' do
-            let(:compare_to) { 'invalid-branch' }
-
-            it 'returns an error' do
-              expect(pipeline.errors.full_messages).to eq([
-                'Failed to parse rule for job1: rules:changes:compare_to is not a valid ref'
-              ])
-            end
-
-            context 'when the FF ci_rules_changes_compare is not enabled' do
-              before do
-                stub_feature_flags(ci_rules_changes_compare: false)
-              end
-
-              it 'ignores compare_to and changes is always true' do
-                expect(build_names).to contain_exactly('job1', 'job2')
-              end
-            end
-          end
-
-          context 'when the compare_to ref exists' do
-            let(:compare_to) { 'feature_1'}
-
-            context 'when the rule matches' do
-              it 'creates job1 and job2' do
-                expect(build_names).to contain_exactly('job1', 'job2')
-              end
-
-              context 'when the FF ci_rules_changes_compare is not enabled' do
-                before do
-                  stub_feature_flags(ci_rules_changes_compare: false)
-                end
-
-                it 'ignores compare_to and changes is always true' do
-                  expect(build_names).to contain_exactly('job1', 'job2')
-                end
-              end
-            end
-
-            context 'when the rule does not match' do
-              let(:changed_file) { 'file1.txt' }
-
-              it 'does not create job1' do
-                expect(build_names).to contain_exactly('job2')
-              end
-
-              context 'when the FF ci_rules_changes_compare is not enabled' do
-                before do
-                  stub_feature_flags(ci_rules_changes_compare: false)
-                end
-
-                it 'ignores compare_to and changes is always true' do
-                  expect(build_names).to contain_exactly('job1', 'job2')
-                end
-              end
-            end
-          end
-        end
-
-        context 'for workflow rules' do
-          let(:config) do
-            <<-EOY
-            workflow:
-              rules:
-                - changes:
-                    paths: [#{changed_file}]
-                    compare_to: #{compare_to}
-
-            job1:
-              script: exit 0
-            EOY
-          end
-
-          let(:compare_to) { 'feature_1'}
-
-          context 'when the rule matches' do
-            it 'creates job1' do
-              expect(pipeline).to be_created_successfully
-              expect(build_names).to contain_exactly('job1')
-            end
-
-            context 'when the FF ci_rules_changes_compare is not enabled' do
-              before do
-                stub_feature_flags(ci_rules_changes_compare: false)
-              end
-
-              it 'ignores compare_to and changes is always true' do
-                expect(pipeline).to be_created_successfully
-                expect(build_names).to contain_exactly('job1')
-              end
-            end
-          end
-
-          context 'when the rule does not match' do
-            let(:changed_file) { 'file1.txt' }
-
-            it 'does not create job1' do
-              expect(pipeline).not_to be_created_successfully
-              expect(build_names).to be_empty
-            end
-          end
-        end
-      end
-
-      context 'with mixed if: and changes: rules' do
-        let(:config) do
-          <<-EOY
-            regular-job:
-              script: 'echo Hello, World!'
-
-            rules-job:
-              script: "echo hello world, $CI_COMMIT_REF_NAME"
-              allow_failure: true
-              rules:
-                - changes:
-                  - README.md
-                  when: manual
-                - if: $CI_COMMIT_REF_NAME == "master"
-                  when: on_success
-                  allow_failure: false
-
-            delayed-job:
-              script: "echo See you later, World!"
-              rules:
-                - changes:
-                  - README.md
-                  when: delayed
-                  start_in: 4 hours
-                  allow_failure: true
-                - if: $CI_COMMIT_REF_NAME == "master"
-                  when: delayed
-                  start_in: 1 hour
-          EOY
-        end
-
-        context 'and changes: matches before if' do
-          before do
-            allow_any_instance_of(Ci::Pipeline)
-              .to receive(:modified_paths).and_return(%w[README.md])
-          end
-
-          it 'creates two jobs' do
-            expect(pipeline).to be_persisted
-            expect(build_names)
-              .to contain_exactly('regular-job', 'rules-job', 'delayed-job')
-          end
-
-          it 'sets when: for all jobs' do
-            expect(regular_job.when).to eq('on_success')
-            expect(rules_job.when).to eq('manual')
-            expect(delayed_job.when).to eq('delayed')
-            expect(delayed_job.options[:start_in]).to eq('4 hours')
-          end
-
-          it 'sets allow_failure: for all jobs' do
-            expect(regular_job.allow_failure).to eq(false)
-            expect(rules_job.allow_failure).to eq(true)
-            expect(delayed_job.allow_failure).to eq(true)
-          end
-        end
-
-        context 'and if: matches after changes' do
-          it 'includes both jobs' do
-            expect(pipeline).to be_persisted
-            expect(build_names).to contain_exactly('regular-job', 'rules-job', 'delayed-job')
-          end
-
-          it 'sets when: for the created rules job based on the second clause' do
-            expect(regular_job.when).to eq('on_success')
-            expect(rules_job.when).to eq('on_success')
-            expect(delayed_job.when).to eq('delayed')
-            expect(delayed_job.options[:start_in]).to eq('1 hour')
-          end
-        end
-
-        context 'and does not match' do
-          let(:ref_name) { 'refs/heads/wip' }
-
-          it_behaves_like 'rules jobs are excluded'
-
-          it 'sets when: for the created job' do
-            expect(regular_job.when).to eq('on_success')
-          end
-        end
-      end
-
-      context 'with mixed if: and changes: clauses' do
-        let(:config) do
-          <<-EOY
-            regular-job:
-              script: 'echo Hello, World!'
-
-            rules-job:
-              script: "echo hello world, $CI_COMMIT_REF_NAME"
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  changes: [README.md]
-                  when: on_success
-                  allow_failure: true
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  changes: [app.rb]
-                  when: manual
-          EOY
-        end
-
-        context 'with if matches and changes matches' do
-          before do
-            allow_any_instance_of(Ci::Pipeline)
-              .to receive(:modified_paths).and_return(%w[app.rb])
-          end
-
-          it 'persists all jobs' do
-            expect(pipeline).to be_persisted
-            expect(regular_job).to be_persisted
-            expect(rules_job).to be_persisted
-            expect(rules_job.when).to eq('manual')
-            expect(rules_job.allow_failure).to eq(false)
-          end
-        end
-
-        context 'with if matches and no change matches' do
-          it_behaves_like 'rules jobs are excluded'
-        end
-
-        context 'with change matches and no if matches' do
-          let(:ref_name) { 'refs/heads/feature' }
-
-          before do
-            allow_any_instance_of(Ci::Pipeline)
-              .to receive(:modified_paths).and_return(%w[README.md])
-          end
-
-          it_behaves_like 'rules jobs are excluded'
-        end
-
-        context 'and no matches' do
-          let(:ref_name) { 'refs/heads/feature' }
-
-          it_behaves_like 'rules jobs are excluded'
-        end
-      end
-
-      context 'with complex if: allow_failure usages' do
-        let(:config) do
-          <<-EOY
-            job-1:
-              script: "exit 1"
-              allow_failure: true
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  allow_failure: false
-
-            job-2:
-              script: "exit 1"
-              allow_failure: true
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
-                  allow_failure: false
-
-            job-3:
-              script: "exit 1"
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
-                  allow_failure: true
-
-            job-4:
-              script: "exit 1"
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  allow_failure: false
-
-            job-5:
-              script: "exit 1"
-              allow_failure: false
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  allow_failure: true
-
-            job-6:
-              script: "exit 1"
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
-                  allow_failure: false
-                - allow_failure: true
-          EOY
-        end
-
-        it 'creates a pipeline' do
-          expect(pipeline).to be_persisted
-          expect(build_names).to contain_exactly('job-1', 'job-4', 'job-5', 'job-6')
-        end
-
-        it 'assigns job:allow_failure values to the builds' do
-          expect(find_job('job-1').allow_failure).to eq(false)
-          expect(find_job('job-4').allow_failure).to eq(false)
-          expect(find_job('job-5').allow_failure).to eq(true)
-          expect(find_job('job-6').allow_failure).to eq(true)
-        end
-      end
-
-      context 'with complex if: allow_failure & when usages' do
-        let(:config) do
-          <<-EOY
-            job-1:
-              script: "exit 1"
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  when: manual
-
-            job-2:
-              script: "exit 1"
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  when: manual
-                  allow_failure: true
-
-            job-3:
-              script: "exit 1"
-              allow_failure: true
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  when: manual
-
-            job-4:
-              script: "exit 1"
-              allow_failure: true
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  when: manual
-                  allow_failure: false
-
-            job-5:
-              script: "exit 1"
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
-                  when: manual
-                  allow_failure: false
-                - when: always
-                  allow_failure: true
-
-            job-6:
-              script: "exit 1"
-              allow_failure: false
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /master/
-                  when: manual
-
-            job-7:
-              script: "exit 1"
-              allow_failure: false
-              rules:
-                - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
-                  when: manual
-                - when: :on_failure
-                  allow_failure: true
-          EOY
-        end
-
-        it 'creates a pipeline' do
-          expect(pipeline).to be_persisted
-          expect(build_names).to contain_exactly(
-            'job-1', 'job-2', 'job-3', 'job-4', 'job-5', 'job-6', 'job-7'
-          )
-        end
-
-        it 'assigns job:allow_failure values to the builds' do
-          expect(find_job('job-1').allow_failure).to eq(false)
-          expect(find_job('job-2').allow_failure).to eq(true)
-          expect(find_job('job-3').allow_failure).to eq(true)
-          expect(find_job('job-4').allow_failure).to eq(false)
-          expect(find_job('job-5').allow_failure).to eq(true)
-          expect(find_job('job-6').allow_failure).to eq(false)
-          expect(find_job('job-7').allow_failure).to eq(true)
-        end
-
-        it 'assigns job:when values to the builds' do
-          expect(find_job('job-1').when).to eq('manual')
-          expect(find_job('job-2').when).to eq('manual')
-          expect(find_job('job-3').when).to eq('manual')
-          expect(find_job('job-4').when).to eq('manual')
-          expect(find_job('job-5').when).to eq('always')
-          expect(find_job('job-6').when).to eq('manual')
-          expect(find_job('job-7').when).to eq('on_failure')
-        end
-      end
-
-      context 'with deploy freeze period `if:` clause' do
-        # '0 23 * * 5' == "At 23:00 on Friday."", '0 7 * * 1' == "At 07:00 on Monday.""
-        let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: '0 23 * * 5', freeze_end: '0 7 * * 1') }
-
-        context 'with 2 jobs' do
-          let(:config) do
-            <<-EOY
-            stages:
-              - test
-              - deploy
-
-            test-job:
-              script:
-                - echo 'running TEST stage'
-
-            deploy-job:
-              stage: deploy
-              script:
-                - echo 'running DEPLOY stage'
-              rules:
-                - if: $CI_DEPLOY_FREEZE == null
-            EOY
-          end
-
-          context 'when outside freeze period' do
-            it 'creates two jobs' do
-              Timecop.freeze(2020, 4, 10, 22, 59) do
-                expect(pipeline).to be_persisted
-                expect(build_names).to contain_exactly('test-job', 'deploy-job')
-              end
-            end
-          end
-
-          context 'when inside freeze period' do
-            it 'creates one job' do
-              Timecop.freeze(2020, 4, 10, 23, 1) do
-                expect(pipeline).to be_persisted
-                expect(build_names).to contain_exactly('test-job')
-              end
-            end
-          end
-        end
-
-        context 'with 1 job' do
-          let(:config) do
-            <<-EOY
-            stages:
-              - deploy
-
-            deploy-job:
-              stage: deploy
-              script:
-                - echo 'running DEPLOY stage'
-              rules:
-                - if: $CI_DEPLOY_FREEZE == null
-            EOY
-          end
-
-          context 'when outside freeze period' do
-            it 'creates two jobs' do
-              Timecop.freeze(2020, 4, 10, 22, 59) do
-                expect(pipeline).to be_persisted
-                expect(build_names).to contain_exactly('deploy-job')
-              end
-            end
-          end
-
-          context 'when inside freeze period' do
-            it 'does not create the pipeline', :aggregate_failures do
-              Timecop.freeze(2020, 4, 10, 23, 1) do
-                expect(response).to be_error
-                expect(pipeline).not_to be_persisted
-              end
-            end
-          end
-        end
-      end
-
-      context 'with workflow rules with persisted variables' do
-        let(:config) do
-          <<-EOY
-            workflow:
-              rules:
-                - if: $CI_COMMIT_REF_NAME == "master"
-
-            regular-job:
-              script: 'echo Hello, World!'
-          EOY
-        end
-
-        context 'with matches' do
-          it 'creates a pipeline' do
-            expect(pipeline).to be_persisted
-            expect(build_names).to contain_exactly('regular-job')
-          end
-        end
-
-        context 'with no matches' do
-          let(:ref_name) { 'refs/heads/feature' }
-
-          it 'does not create a pipeline', :aggregate_failures do
-            expect(response).to be_error
-            expect(pipeline).not_to be_persisted
-          end
-        end
-      end
-
-      context 'with workflow rules with pipeline variables' do
-        let(:pipeline) do
-          execute_service(variables_attributes: variables_attributes).payload
-        end
-
-        let(:config) do
-          <<-EOY
-            workflow:
-              rules:
-                - if: $SOME_VARIABLE
-
-            regular-job:
-              script: 'echo Hello, World!'
-          EOY
-        end
-
-        context 'with matches' do
-          let(:variables_attributes) do
-            [{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
-          end
-
-          it 'creates a pipeline' do
-            expect(pipeline).to be_persisted
-            expect(build_names).to contain_exactly('regular-job')
-          end
-        end
-
-        context 'with no matches' do
-          let(:variables_attributes) { {} }
-
-          it 'does not create a pipeline', :aggregate_failures do
-            expect(response).to be_error
-            expect(pipeline).not_to be_persisted
-          end
-        end
-      end
-
-      context 'with workflow rules with trigger variables' do
-        let(:pipeline) do
-          execute_service do |pipeline|
-            pipeline.variables.build(variables)
-          end.payload
-        end
-
-        let(:config) do
-          <<-EOY
-            workflow:
-              rules:
-                - if: $SOME_VARIABLE
-
-            regular-job:
-              script: 'echo Hello, World!'
-          EOY
-        end
-
-        context 'with matches' do
-          let(:variables) do
-            [{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
-          end
-
-          it 'creates a pipeline' do
-            expect(pipeline).to be_persisted
-            expect(build_names).to contain_exactly('regular-job')
-          end
-
-          context 'when a job requires the same variable' do
-            let(:config) do
-              <<-EOY
-                workflow:
-                  rules:
-                    - if: $SOME_VARIABLE
-
-                build:
-                  stage: build
-                  script: 'echo build'
-                  rules:
-                    - if: $SOME_VARIABLE
-
-                test1:
-                  stage: test
-                  script: 'echo test1'
-                  needs: [build]
-
-                test2:
-                  stage: test
-                  script: 'echo test2'
-              EOY
-            end
-
-            it 'creates a pipeline' do
-              expect(pipeline).to be_persisted
-              expect(build_names).to contain_exactly('build', 'test1', 'test2')
-            end
-          end
-        end
-
-        context 'with no matches' do
-          let(:variables) { {} }
-
-          it 'does not create a pipeline', :aggregate_failures do
-            expect(response).to be_error
-            expect(pipeline).not_to be_persisted
-          end
-
-          context 'when a job requires the same variable' do
-            let(:config) do
-              <<-EOY
-                workflow:
-                  rules:
-                    - if: $SOME_VARIABLE
-
-                build:
-                  stage: build
-                  script: 'echo build'
-                  rules:
-                    - if: $SOME_VARIABLE
-
-                test1:
-                  stage: test
-                  script: 'echo test1'
-                  needs: [build]
-
-                test2:
-                  stage: test
-                  script: 'echo test2'
-              EOY
-            end
-
-            it 'does not create a pipeline', :aggregate_failures do
-              expect(response).to be_error
-              expect(pipeline).not_to be_persisted
-            end
-          end
-        end
-      end
-
-      context 'with workflow rules changes' do
-        shared_examples 'comparing file changes with workflow rules' do
-          context 'when matches' do
-            before do
-              allow_next_instance_of(Ci::Pipeline) do |pipeline|
-                allow(pipeline).to receive(:modified_paths).and_return(%w[file1.md])
-              end
-            end
-
-            it 'creates the pipeline with a job' do
-              expect(pipeline).to be_persisted
-              expect(build_names).to contain_exactly('job')
-            end
-          end
-
-          context 'when does not match' do
-            before do
-              allow_next_instance_of(Ci::Pipeline) do |pipeline|
-                allow(pipeline).to receive(:modified_paths).and_return(%w[unknown])
-              end
-            end
-
-            it 'creates the pipeline with a job' do
-              expect(pipeline.errors.full_messages).to eq(['Pipeline filtered out by workflow rules.'])
-              expect(response).to be_error
-              expect(pipeline).not_to be_persisted
-            end
-          end
-        end
-
-        context 'changes is an array' do
-          let(:config) do
-            <<-EOY
-              workflow:
-                rules:
-                  - changes: [file1.md]
-
-              job:
-                script: exit 0
-            EOY
-          end
-
-          it_behaves_like 'comparing file changes with workflow rules'
-        end
-
-        context 'changes:paths is an array' do
-          let(:config) do
-            <<-EOY
-              workflow:
-                rules:
-                  - changes:
-                      paths: [file1.md]
-
-              job:
-                script: exit 0
-            EOY
-          end
-
-          it_behaves_like 'comparing file changes with workflow rules'
-        end
-      end
-    end
   end
 
   describe '#execute!' do