spec/lib/release_tools/public_release/gitlab_release_spec.rb (628 lines of code) (raw):
# frozen_string_literal: true
require 'spec_helper'
require 'release_tools/tasks'
describe ReleaseTools::PublicRelease::GitlabRelease do
describe '#initialize' do
it 'converts CE versions to EE versions' do
version = ReleaseTools::Version.new('42.0.0')
release = described_class.new(version)
expect(release.version).to eq('42.0.0-ee')
end
end
describe '#execute' do
let(:version) { ReleaseTools::Version.new('42.0.0-ee') }
subject(:release) { described_class.new(version) }
it 'runs the release' do
ce_tag = double(:tag, name: 'v42.0.0')
ee_tag = double(:tag, name: 'v42.0.0-ee')
expect(release).to receive(:create_ce_target_branch)
expect(release).to receive(:create_ee_target_branch)
expect(release).to receive(:update_monthly_tag_metrics)
expect(release).to receive(:update_managed_component_versions)
expect(release).to receive(:compile_changelogs)
expect(release).to receive(:update_versions)
expect(release).to receive(:wait_for_ee_to_ce_sync)
expect(release).to receive(:create_ce_commit_to_run_ci)
expect(release).to receive(:create_ce_tag).and_return(ce_tag)
expect(release).to receive(:create_ee_tag).and_return(ee_tag)
expect(release).to receive(:start_new_minor_release)
expect(release)
.to receive(:add_release_data_for_tags)
.with(ce_tag, ee_tag)
expect(release)
.to receive(:notify_slack)
.with(ReleaseTools::Project::GitlabCe, version.to_ce)
expect(release)
.to receive(:notify_slack)
.with(ReleaseTools::Project::GitlabEe, version)
without_dry_run { release.execute }
end
context 'with dry-run mode' do
it 'skips most of the tasks' do
expect(release).to receive(:create_ce_target_branch)
expect(release).to receive(:create_ee_target_branch)
expect(release).not_to receive(:update_managed_component_versions)
expect(release).not_to receive(:compile_changelogs)
expect(release).not_to receive(:update_versions)
expect(release).not_to receive(:wait_for_ee_to_ce_sync)
expect(release).not_to receive(:create_ce_commit_to_run_ci)
expect(release).not_to receive(:create_ce_tag)
expect(release).not_to receive(:create_ee_tag)
expect(release).not_to receive(:update_monthly_tag_metrics)
expect(release).not_to receive(:start_new_minor_release)
expect(release).not_to receive(:add_release_data_for_tags)
expect(release).not_to receive(:notify_slack)
expect(release).not_to receive(:notify_slack)
release.execute
end
end
end
describe '#create_ce_target_branch' do
let(:client) { class_spy(ReleaseTools::GitlabClient) }
let(:version) { ReleaseTools::Version.new('42.1.0-ee') }
subject(:release) { described_class.new(version, client: client) }
it 'creates the CE target branch from the last stable branch' do
expect(client)
.to receive(:find_or_create_branch)
.with('42-1-stable', '42-0-stable', release.ce_project_path)
without_dry_run do
release.create_ce_target_branch
end
end
context 'with dry-run mode' do
it 'skips api call' do
expect(client).not_to receive(:find_or_create_branch)
release.create_ce_target_branch
end
end
end
describe '#create_ee_target_branch' do
let(:client) { class_spy(ReleaseTools::GitlabClient) }
let(:version) { ReleaseTools::Version.new('42.1.0-ee') }
it 'calls notify_stable_branch_creation' do
release = described_class.new(version, client: client, commit: 'foo')
expect(release).to receive(:notify_stable_branch_creation)
without_dry_run do
release.create_ee_target_branch
end
end
context 'with dry run enabled' do
it 'calls notify_stable_branch_creation' do
release = described_class.new(version, client: client, commit: 'foo')
expect(release).to receive(:notify_stable_branch_creation)
release.create_ee_target_branch
end
end
context 'when a custom commit is specified' do
it 'creates the EE target branch from the custom commit' do
release = described_class.new(version, client: client, commit: 'foo')
expect(client)
.to receive(:find_or_create_branch)
.with('42-1-stable-ee', 'foo', release.project_path)
without_dry_run do
release.create_ee_target_branch
end
end
end
context "when a custom commit isn't specified" do
it 'creates the EE target branch from the last production commit' do
release = described_class.new(version, client: client)
expect(release)
.to receive(:last_production_commit)
.and_return('123abc')
expect(client)
.to receive(:find_or_create_branch)
.with('42-1-stable-ee', '123abc', release.project_path)
without_dry_run do
release.create_ee_target_branch
end
end
end
context 'with dry-run mode' do
it 'skips api call' do
release = described_class.new(version, client: client, commit: 'foo')
expect(client).not_to receive(:find_or_create_branch)
release.create_ee_target_branch
end
end
end
describe '#update_monthly_tag_metrics' do
before do
version = ReleaseTools::Version.new('16.10')
allow(ReleaseTools::GitlabReleasesGemClient)
.to receive(:version_for_date)
.and_return(version)
end
context 'when tagging a patch release' do
let(:client) { class_spy(ReleaseTools::GitlabClient) }
let(:version) { ReleaseTools::Version.new('16.10.1-ee') }
subject(:release) { described_class.new(version, client: client) }
it 'does nothing' do
expect(ReleaseTools::Metrics::MonthlyReleaseStatus).not_to receive(:new)
without_dry_run do
release.update_monthly_tag_metrics
end
end
end
context 'when tagging a minor release' do
let(:client) { class_spy(ReleaseTools::GitlabClient) }
let(:version) { ReleaseTools::Version.new('16.10.0') }
subject(:release) { described_class.new(version, client: client) }
it 'does nothing' do
expect(ReleaseTools::Metrics::MonthlyReleaseStatus).not_to receive(:new)
without_dry_run do
release.update_monthly_tag_metrics
end
end
end
context 'when tagging a release candidate' do
let(:client) { class_spy(ReleaseTools::GitlabClient) }
let(:version) { ReleaseTools::Version.new('16.10.0-rc42') }
subject(:release) { described_class.new(version, client: client) }
it 'updates the monthly release status metric' do
expect(ReleaseTools::Metrics::MonthlyReleaseStatus).to receive(:new).with(status: :tagged_rc)
.and_return(instance_double(ReleaseTools::Metrics::MonthlyReleaseStatus, execute: true))
without_dry_run do
release.update_monthly_tag_metrics
end
end
end
context 'when tagging an older release candidate' do
let(:client) { class_spy(ReleaseTools::GitlabClient) }
let(:version) { ReleaseTools::Version.new('16.9.0-rc42') }
subject(:release) { described_class.new(version, client: client) }
it 'does nothing' do
expect(ReleaseTools::Metrics::MonthlyReleaseStatus).not_to receive(:new)
without_dry_run do
release.update_monthly_tag_metrics
end
end
end
end
describe '#update_managed_component_versions' do
before do
enable_feature(:fetch_managed_component_version)
end
context 'when releasing an RC' do
it 'does nothing' do
version = ReleaseTools::Version.new('42.1.0-rc42-ee')
release = described_class.new(version)
expect(release).not_to receive(:commit_version_files)
release.update_managed_component_versions
end
end
context 'when releasing a non-RC release' do
let(:version) { ReleaseTools::Version.new('42.1.0-ee') }
let(:release) { described_class.new(version) }
context 'with fetch_managed_component_version feature flag enabled' do
it "updates managed version file with the version from the component" do
expect(release.client)
.to receive(:file_contents)
.with('gitlab-org/gitaly', 'VERSION', '42-1-stable')
.and_return('9.0.0')
expect(release.client)
.to receive(:file_contents)
.with('gitlab-org/gitlab-pages', 'VERSION', '42-1-stable')
.and_return('42.1.0')
expect(release.logger).to receive(:warn).with(
'Managed component version does not match version being released',
{
project: 'gitlab-org/gitaly',
component_version: '9.0.0',
release_version: '42.1.0'
}
)
expect(release)
.to receive(:commit_version_files)
.with(
'42-1-stable-ee',
{
'GITALY_SERVER_VERSION' => '9.0.0',
'GITLAB_PAGES_VERSION' => '42.1.0'
},
{
message: "Update managed components version to 42.1.0",
skip_ci: true
}
)
release.update_managed_component_versions
end
end
context 'with fetch_managed_component_version feature flag disabled' do
before do
disable_feature(:fetch_managed_component_version)
end
it "updates managed version file with the version from the release" do
expect(release.client)
.not_to receive(:file_contents)
expect(release)
.to receive(:commit_version_files)
.with(
'42-1-stable-ee',
{
'GITALY_SERVER_VERSION' => '42.1.0',
'GITLAB_PAGES_VERSION' => '42.1.0'
},
{
message: "Update managed components version to 42.1.0",
skip_ci: true
}
)
release.update_managed_component_versions
end
end
context 'with release_the_kas feature flag enabled' do
it 'updates KAS with the version from the release' do
enable_feature(:fetch_managed_component_version, :release_the_kas)
expect(release.client)
.not_to receive(:file_contents)
.with('gitlab-org/cluster-integration/gitlab-agent', 'VERSION', '42-1-stable')
expect(release.client)
.to receive(:file_contents)
.with('gitlab-org/gitaly', 'VERSION', '42-1-stable')
.and_return('9.0.0')
expect(release.client)
.to receive(:file_contents)
.with('gitlab-org/gitlab-pages', 'VERSION', '42-1-stable')
.and_return('42.1.0')
expect(release)
.to receive(:commit_version_files)
.with(
'42-1-stable-ee',
{
'GITALY_SERVER_VERSION' => '9.0.0',
'GITLAB_PAGES_VERSION' => '42.1.0',
'GITLAB_KAS_VERSION' => '42.1.0'
},
{
message: "Update managed components version to 42.1.0",
skip_ci: true
}
)
release.update_managed_component_versions
end
end
end
end
describe '#wait_for_ee_to_ce_sync' do
before do
pipeline = double(:pipeline, web_url: 'foo', id: 1)
allow(ReleaseTools::GitlabOpsClient)
.to receive(:create_pipeline)
.with(
ReleaseTools::Project::MergeTrain,
{
MERGE_FOSS: '1',
SOURCE_PROJECT: 'gitlab-org/gitlab',
SOURCE_BRANCH: '42-1-stable-ee',
TARGET_PROJECT: 'gitlab-org/gitlab-foss',
TARGET_BRANCH: '42-1-stable'
}
)
.and_return(pipeline)
end
it 'returns when EE has been synced to CE' do
pipeline = double(:pipeline, status: 'success')
version = ReleaseTools::Version.new('42.1.0-ee')
release = described_class.new(version)
expect(ReleaseTools::GitlabOpsClient)
.to receive(:pipeline)
.with(ReleaseTools::Project::MergeTrain, 1)
.and_return(pipeline)
expect { release.wait_for_ee_to_ce_sync }.not_to raise_error
end
it 'raises when the pipeline fails' do
pipeline = double(:pipeline, status: 'failed')
version = ReleaseTools::Version.new('42.1.0-ee')
release = described_class.new(version)
expect(ReleaseTools::GitlabOpsClient)
.to receive(:pipeline)
.with(ReleaseTools::Project::MergeTrain, 1)
.and_return(pipeline)
expect { release.wait_for_ee_to_ce_sync }
.to raise_error(described_class::PipelineFailed)
end
it 'raises when the pipeline does not succeed in a timely manner' do
pipeline = double(:pipeline, status: 'running')
version = ReleaseTools::Version.new('42.1.0-ee')
release = described_class.new(version)
stub_const(
'ReleaseTools::PublicRelease::GitlabRelease::WAIT_SYNC_INTERVALS',
Array.new(15, 0)
)
expect(ReleaseTools::GitlabOpsClient)
.to receive(:pipeline)
.with(ReleaseTools::Project::MergeTrain, 1)
.exactly(16)
.times
.and_return(pipeline)
expect { release.wait_for_ee_to_ce_sync }
.to raise_error(described_class::PipelineTooSlow)
end
it 'raises when the pipeline is waiting for resources' do
pipeline = double(:pipeline, status: 'waiting_for_resource')
version = ReleaseTools::Version.new('42.1.0-ee')
release = described_class.new(version)
stub_const(
'ReleaseTools::PublicRelease::GitlabRelease::WAIT_SYNC_INTERVALS',
Array.new(15, 0)
)
expect(ReleaseTools::GitlabOpsClient)
.to receive(:pipeline)
.with(ReleaseTools::Project::MergeTrain, 1)
.exactly(16)
.times
.and_return(pipeline)
expect { release.wait_for_ee_to_ce_sync }
.to raise_error(described_class::PipelineTooSlow)
end
it 'retries the operation when the pipeline is still running' do
version = ReleaseTools::Version.new('42.1.0-ee')
release = described_class.new(version)
stub_const(
'ReleaseTools::PublicRelease::GitlabRelease::WAIT_SYNC_INTERVALS',
Array.new(15, 0)
)
expect(ReleaseTools::GitlabOpsClient)
.to receive(:pipeline)
.with(ReleaseTools::Project::MergeTrain, 1)
.twice
.and_return(
double(:pipeline, status: 'running'),
double(:pipeline, status: 'success')
)
expect { release.wait_for_ee_to_ce_sync }.not_to raise_error
end
end
describe '#create_ce_commit_to_run_ci' do
let(:release) { described_class.new(ReleaseTools::Version.new('42.1.0-ee')) }
context 'when the last commit includes "ci skip"' do
it 'creates a new empty commit' do
commit = double(:commit, message: "Foo\n[ci skip]")
expect(release.client)
.to receive(:commit)
.with('gitlab-org/gitlab-foss', { ref: '42-1-stable' })
.and_return(commit)
expect(release.client)
.to receive(:create_commit)
.with(
'gitlab-org/gitlab-foss',
'42-1-stable',
an_instance_of(String),
[
{
action: 'update',
file_path: 'VERSION',
content: '42.1.0'
}
]
)
release.create_ce_commit_to_run_ci
end
end
context "when the last commit doesn't include \"ci skip\"" do
it "doesn't create a new commit" do
commit = double(:commit, message: 'Bump version to X')
expect(release.client)
.to receive(:commit)
.with('gitlab-org/gitlab-foss', { ref: '42-1-stable' })
.and_return(commit)
expect(release.client).not_to receive(:create_commit)
release.create_ce_commit_to_run_ci
end
end
end
describe '#compile_changelogs' do
it 'compiles the changelog for a stable release' do
client = class_spy(ReleaseTools::GitlabClient)
version = ReleaseTools::Version.new('42.0.0-ee')
release = described_class.new(version, client: client)
compiler = instance_spy(ReleaseTools::ChangelogCompiler)
expect(ReleaseTools::ChangelogCompiler)
.to receive(:new)
.with(release.project.canonical_or_security_path, { client: client })
.and_return(compiler)
expect(compiler)
.to receive(:compile)
.with(version, { branch: '42-0-stable-ee' })
release.compile_changelogs
end
it 'does not compile the changelog for an RC' do
client = class_spy(ReleaseTools::GitlabClient)
version = ReleaseTools::Version.new('42.0.0-rc42-ee')
release = described_class.new(version, client: client)
expect(ReleaseTools::ChangelogCompiler).not_to receive(:new)
release.compile_changelogs
end
end
describe '#update_versions' do
it 'updates the VERSION files for CE and EE' do
version = ReleaseTools::Version.new('42.0.0-ee')
release = described_class.new(version)
expect(release)
.to receive(:commit_version_files)
.with(
'42-0-stable-ee',
{ 'VERSION' => '42.0.0-ee' },
{
skip_ci: false,
skip_merge_train: true
}
)
expect(release)
.to receive(:commit_version_files)
.with(
'42-0-stable',
{ 'VERSION' => '42.0.0' },
{
project: release.ce_project_path,
skip_ci: true
}
)
release.update_versions
end
end
describe '#start_new_minor_release' do
context 'when releasing a patch release' do
it 'does nothing' do
version = ReleaseTools::Version.new('42.1.1-ee')
release = described_class.new(version)
expect(release).not_to receive(:commit_version_files)
release.start_new_minor_release
end
end
context 'when releasing a new minor release' do
it 'updates the VERSION files on the source branches' do
version = ReleaseTools::Version.new('42.0.0-ee')
release = described_class.new(version)
allow(version).to receive_messages(to_upcoming_pre_release: '42.1.0-pre', to_ce: double(to_upcoming_pre_release: '42.1.0-pre'))
expect(release)
.to receive(:commit_version_files)
.with('master', { 'VERSION' => '42.1.0-pre' }, { skip_ci: true })
expect(release)
.to receive(:commit_version_files)
.with(
'master',
{ 'VERSION' => '42.1.0-pre' },
{
project: release.ce_project_path,
skip_ci: true
}
)
release.start_new_minor_release
end
end
end
describe '#create_ce_tag' do
let(:fake_metrics) { instance_spy(ReleaseTools::Metrics::Client) }
let(:version) { ReleaseTools::Version.new('42.0.0-ee') }
let(:release) { described_class.new(version) }
before do
allow(ReleaseTools::Metrics::Client).to receive(:new).and_return(fake_metrics)
end
context 'when the tag already exists' do
it 'returns the tag' do
tag = double(:tag)
allow(release.client)
.to receive(:tag)
.with(
release.ce_project_path,
{ tag: version.to_ce.tag }
)
.and_return(tag)
expect(release.client)
.not_to receive(:create_tag)
expect(fake_metrics).not_to receive(:inc)
expect(release.create_ce_tag).to eq(tag)
release.create_ce_tag
end
end
context 'when the tag does not exist' do
before do
tag = double(:tag)
allow(release.client)
.to receive(:tag)
.with(
release.ce_project_path,
{ tag: version.to_ce.tag }
)
.and_raise(gitlab_error(:NotFound))
allow(release.client).to receive(:create_tag).and_return(tag)
allow(ReleaseTools::SharedStatus)
.to receive(:critical_patch_release?)
.and_return(false)
end
it 'creates the tag for CE' do
expect(release.client)
.to receive(:create_tag)
.with(
release.ce_project_path,
version.to_ce.tag,
'42-0-stable',
'Version v42.0.0'
)
expect(ReleaseTools::Metrics::Client.new).to receive(:inc)
release.create_ce_tag
end
end
end
describe '#create_ee_tag' do
it 'creates the tag for EE' do
version = ReleaseTools::Version.new('42.0.0-ee')
release = described_class.new(version)
expect(release.client)
.to receive(:find_or_create_tag)
.with(
release.project_path,
version.tag,
'42-0-stable-ee',
{ message: 'Version v42.0.0-ee' }
)
release.create_ee_tag
end
end
describe '#add_release_data_for_tags' do
it 'adds the release data for CE and EE' do
version = ReleaseTools::Version.new('42.0.0-ee')
release = described_class.new(version)
ce_tag = double(:tag, name: 'foo', commit: double(:commit, id: 'a'))
ee_tag = double(:tag, name: 'bar', commit: double(:commit, id: 'b'))
expect(release.release_metadata).to receive(:add_release).with(
{
name: 'gitlab-ce',
version: '42.0.0',
sha: 'a',
ref: 'foo',
tag: true
}
)
expect(release.release_metadata).to receive(:add_release).with(
{
name: 'gitlab-ee',
version: '42.0.0',
sha: 'b',
ref: 'bar',
tag: true
}
)
release.add_release_data_for_tags(ce_tag, ee_tag)
end
end
describe '#project' do
it 'returns the project to release' do
version = ReleaseTools::Version.new('42.0.0-ee')
release = described_class.new(version)
expect(release.project).to eq(ReleaseTools::Project::GitlabEe)
end
end
describe '#ce_target_branch' do
it 'returns the target branch for CE' do
version = ReleaseTools::Version.new('42.0.0-ee')
release = described_class.new(version)
expect(release.ce_target_branch).to eq('42-0-stable')
end
end
describe '#ce_project_path' do
it 'returns the path to CE' do
version = ReleaseTools::Version.new('42.0.0-ee')
release = described_class.new(version)
expect(release.ce_project_path)
.to eq(ReleaseTools::Project::GitlabCe.canonical_or_security_path)
end
end
describe '#source_for_target_branch' do
let(:version) { ReleaseTools::Version.new('42.0.0-ee') }
context 'when a custom commit is specified' do
it 'returns the commit' do
release = described_class.new(version, commit: 'foo')
expect(release.source_for_target_branch).to eq('foo')
end
end
context 'when no custom commit is specified' do
it 'returns the SHA of the last deployment' do
release = described_class.new(version)
allow(release).to receive(:last_production_commit).and_return('foo')
expect(release.source_for_target_branch).to eq('foo')
end
end
end
end