Commit 7e880cbe authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'latest-template-swapping-feature-flag' into 'master'

Add a feature flag to try to redirect to latest template [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!63144
parents 8280d729 f6b3ae37
---
name: redirect_to_latest_template_jobs_browser_performance_testing
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
rollout_issue_url:
milestone: '14.0'
type: development
group: group::pipeline authoring
default_enabled: false
---
name: redirect_to_latest_template_jobs_deploy
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332660
milestone: '14.0'
type: development
group: group::pipeline authoring
default_enabled: false
---
name: redirect_to_latest_template_security_api_fuzzing
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
rollout_issue_url:
milestone: '14.0'
type: development
group: group::pipeline authoring
default_enabled: false
---
name: redirect_to_latest_template_security_dast
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
rollout_issue_url:
milestone: '14.0'
type: development
group: group::pipeline authoring
default_enabled: false
---
name: redirect_to_latest_template_terraform
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
rollout_issue_url:
milestone: '14.0'
type: development
group: group::pipeline authoring
default_enabled: false
---
name: redirect_to_latest_template_verify_browser_performance
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
rollout_issue_url:
milestone: '14.0'
type: development
group: group::pipeline authoring
default_enabled: false
...@@ -307,6 +307,26 @@ include: ...@@ -307,6 +307,26 @@ include:
- remote: https://gitlab.com/gitlab-org/gitlab/-/raw/v13.0.1-ee/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml - remote: https://gitlab.com/gitlab-org/gitlab/-/raw/v13.0.1-ee/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
``` ```
### Use a feature flag to roll out a `latest` template
With a major version release like 13.0 or 14.0, [stable templates](#stable-version) must be
updated with their corresponding [latest template versions](#latest-version).
It may be hard to gauge the impact of this change, so use the `redirect_to_latest_template_<name>`
feature flag to test the impact on a subset of users. Using a feature flag can help
reduce the risk of reverts or rollbacks on production.
For example, to redirect the stable `Jobs/Deploy` template to its latest template in 25% of
projects on `gitlab.com`:
```shell
/chatops run feature set redirect_to_latest_template_jobs_deploy 25 --actors
```
After you're confident the latest template can be moved to stable:
1. Update the stable template with the content of the latest version.
1. Remove the corresponding feature flag.
### Further reading ### Further reading
There is an [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/17716) about There is an [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/17716) about
......
...@@ -6,6 +6,10 @@ module EE ...@@ -6,6 +6,10 @@ module EE
module GitlabCiYmlTemplate module GitlabCiYmlTemplate
extend ActiveSupport::Concern extend ActiveSupport::Concern
EE_TEMPLATES_WITH_LATEST_VERSION = {
'Verify/Browser-Performance' => true
}.freeze
class_methods do class_methods do
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
...@@ -26,6 +30,12 @@ module EE ...@@ -26,6 +30,12 @@ module EE
'Security' => 'Security' 'Security' => 'Security'
} }
end end
override :templates_with_latest_version
def templates_with_latest_version
@templates_with_latest_version ||=
super.merge(EE_TEMPLATES_WITH_LATEST_VERSION)
end
end end
end end
end end
......
...@@ -11,6 +11,14 @@ RSpec.describe "CI YML Templates" do ...@@ -11,6 +11,14 @@ RSpec.describe "CI YML Templates" do
Gitlab::Template::GitlabCiYmlTemplate.all.map(&:full_name) Gitlab::Template::GitlabCiYmlTemplate.all.map(&:full_name)
end end
before do
stub_feature_flags(
redirect_to_latest_template_terraform: false,
redirect_to_latest_template_security_dast: false,
redirect_to_latest_template_security_api_fuzzing: false,
redirect_to_latest_template_jobs_browser_performance_testing: false)
end
with_them do with_them do
let(:content) do let(:content) do
if template_name == 'Security/DAST-API.gitlab-ci.yml' if template_name == 'Security/DAST-API.gitlab-ci.yml'
......
...@@ -34,6 +34,7 @@ RSpec.describe 'Jobs/Browser-Performance-Testing.gitlab-ci.yml' do ...@@ -34,6 +34,7 @@ RSpec.describe 'Jobs/Browser-Performance-Testing.gitlab-ci.yml' do
before do before do
stub_ci_pipeline_yaml_file(template) stub_ci_pipeline_yaml_file(template)
stub_feature_flags(redirect_to_latest_template_jobs_browser_performance_testing: false)
allow_next_instance_of(Ci::BuildScheduleWorker) do |worker| allow_next_instance_of(Ci::BuildScheduleWorker) do |worker|
allow(worker).to receive(:perform).and_return(true) allow(worker).to receive(:perform).and_return(true)
......
...@@ -30,6 +30,7 @@ RSpec.describe 'Verify/Browser-Performance.gitlab-ci.yml' do ...@@ -30,6 +30,7 @@ RSpec.describe 'Verify/Browser-Performance.gitlab-ci.yml' do
before do before do
stub_ci_pipeline_yaml_file(template) stub_ci_pipeline_yaml_file(template)
stub_feature_flags(redirect_to_latest_template_verify_browser_performance: false)
allow_next_instance_of(Ci::BuildScheduleWorker) do |worker| allow_next_instance_of(Ci::BuildScheduleWorker) do |worker|
allow(worker).to receive(:perform).and_return(true) allow(worker).to receive(:perform).and_return(true)
end end
......
...@@ -6,7 +6,7 @@ module Gitlab ...@@ -6,7 +6,7 @@ module Gitlab
module External module External
module File module File
class Template < Base class Template < Base
attr_reader :location, :project attr_reader :location
SUFFIX = '.gitlab-ci.yml' SUFFIX = '.gitlab-ci.yml'
...@@ -41,7 +41,7 @@ module Gitlab ...@@ -41,7 +41,7 @@ module Gitlab
end end
def fetch_template_content def fetch_template_content
Gitlab::Template::GitlabCiYmlTemplate.find(template_name, project)&.content Gitlab::Template::GitlabCiYmlTemplate.find(template_name, context.project)&.content
end end
end end
end end
......
...@@ -5,11 +5,20 @@ module Gitlab ...@@ -5,11 +5,20 @@ module Gitlab
class GitlabCiYmlTemplate < BaseTemplate class GitlabCiYmlTemplate < BaseTemplate
BASE_EXCLUDED_PATTERNS = [%r{\.latest\.}].freeze BASE_EXCLUDED_PATTERNS = [%r{\.latest\.}].freeze
TEMPLATES_WITH_LATEST_VERSION = {
'Jobs/Deploy' => true,
'Jobs/Browser-Performance-Testing' => true,
'Security/API-Fuzzing' => true,
'Security/DAST' => true,
'Terraform' => true
}.freeze
def description def description
"# This file is a template, and might need editing before it works on your project." "# This file is a template, and might need editing before it works on your project."
end end
class << self class << self
extend ::Gitlab::Utils::Override
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
def extension def extension
...@@ -54,6 +63,31 @@ module Gitlab ...@@ -54,6 +63,31 @@ module Gitlab
excluded_patterns: self.excluded_patterns excluded_patterns: self.excluded_patterns
) )
end end
override :find
def find(key, project = nil)
if try_redirect_to_latest?(key, project)
key += '.latest'
end
super(key, project)
end
private
# To gauge the impact of the latest template,
# you can redirect the stable template to the latest template by enabling the feature flag.
# See https://docs.gitlab.com/ee/development/cicd/templates.html#versioning for more information.
def try_redirect_to_latest?(key, project)
return false unless templates_with_latest_version[key]
flag_name = "redirect_to_latest_template_#{key.underscore.tr('/', '_')}"
::Feature.enabled?(flag_name, project, default_enabled: :yaml)
end
def templates_with_latest_version
TEMPLATES_WITH_LATEST_VERSION
end
end end
end end
end end
......
...@@ -20,6 +20,13 @@ RSpec.describe 'CI YML Templates' do ...@@ -20,6 +20,13 @@ RSpec.describe 'CI YML Templates' do
all_templates - excluded_templates all_templates - excluded_templates
end end
before do
stub_feature_flags(
redirect_to_latest_template_terraform: false,
redirect_to_latest_template_security_api_fuzzing: false,
redirect_to_latest_template_security_dast: false)
end
with_them do with_them do
let(:content) do let(:content) do
if template_name == 'Security/DAST-API.gitlab-ci.yml' if template_name == 'Security/DAST-API.gitlab-ci.yml'
......
...@@ -21,6 +21,55 @@ RSpec.describe Gitlab::Template::GitlabCiYmlTemplate do ...@@ -21,6 +21,55 @@ RSpec.describe Gitlab::Template::GitlabCiYmlTemplate do
end end
end end
describe '.find' do
let_it_be(:project) { create(:project) }
let_it_be(:other_project) { create(:project) }
described_class::TEMPLATES_WITH_LATEST_VERSION.keys.each do |key|
it "finds the latest template for #{key}" do
result = described_class.find(key, project)
expect(result.full_name).to eq("#{key}.latest.gitlab-ci.yml")
expect(result.content).to be_present
end
context 'when `redirect_to_latest_template` feature flag is disabled' do
before do
stub_feature_flags("redirect_to_latest_template_#{key.underscore.tr('/', '_')}".to_sym => false)
end
it "finds the stable template for #{key}" do
result = described_class.find(key, project)
expect(result.full_name).to eq("#{key}.gitlab-ci.yml")
expect(result.content).to be_present
end
end
context 'when `redirect_to_latest_template` feature flag is enabled on the project' do
before do
stub_feature_flags("redirect_to_latest_template_#{key.underscore.tr('/', '_')}".to_sym => project)
end
it "finds the latest template for #{key}" do
result = described_class.find(key, project)
expect(result.full_name).to eq("#{key}.latest.gitlab-ci.yml")
expect(result.content).to be_present
end
end
context 'when `redirect_to_latest_template` feature flag is enabled on the other project' do
before do
stub_feature_flags("redirect_to_latest_template_#{key.underscore.tr('/', '_')}".to_sym => other_project)
end
it "finds the stable template for #{key}" do
result = described_class.find(key, project)
expect(result.full_name).to eq("#{key}.gitlab-ci.yml")
expect(result.content).to be_present
end
end
end
end
describe '#content' do describe '#content' do
it 'loads the full file' do it 'loads the full file' do
gitignore = subject.new(Rails.root.join('lib/gitlab/ci/templates/Ruby.gitlab-ci.yml')) gitignore = subject.new(Rails.root.join('lib/gitlab/ci/templates/Ruby.gitlab-ci.yml'))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment