spec/lib/release_tools/public_release/release_spec.rb (348 lines of code) (raw):
# frozen_string_literal: true
require 'spec_helper'
describe ReleaseTools::PublicRelease::Release do
include MetadataHelper
let(:client) { class_spy(ReleaseTools::GitlabClient) }
let(:version) { ReleaseTools::Version.new('1.2.3') }
let(:release) do
Klass.new(version, client: client)
end
before do
stub_const(
'Klass',
Class.new do
include ReleaseTools::PublicRelease::Release
attr_reader :version, :client, :release_metadata
def initialize(
version,
client: ReleaseTools::GitlabClient,
release_metadata: ReleaseTools::ReleaseMetadata.new
)
@version = version
@client = client
@release_metadata = release_metadata
end
def project
ReleaseTools::Project::GitlabCe
end
def source_for_target_branch
'master'
end
end
)
end
describe '#project_path' do
context 'for a regular release' do
it 'returns the regular project path' do
expect(release.project_path).to eq(release.project.path)
end
end
context 'for a patch release' do
it 'returns the security path' do
ReleaseTools::SharedStatus.as_security_release do
expect(release.project_path).to eq(release.project.security_path)
end
end
end
end
describe '#tag_name' do
it 'returns the name of the tag to create' do
expect(release.tag_name).to eq('v1.2.3')
end
end
describe '#target_branch' do
it 'returns the name of the target branch' do
expect(release.target_branch).to eq('1-2-stable')
end
end
describe '#create_target_branch' do
it 'creates the target branch' do
expect(client)
.to receive(:find_or_create_branch)
.with('1-2-stable', 'master', release.project_path)
without_dry_run { release.create_target_branch }
end
context 'with-dry-run' do
it 'skips API call' do
expect(client).not_to receive(:find_or_create_branch)
release.create_target_branch
end
end
end
describe '#notify_slack' do
it 'sends a notification to Slack' do
expect(ReleaseTools::Slack::TagNotification)
.to receive(:release)
.with(release.project, "v#{release.version}")
release.notify_slack(release.project, release.version)
end
end
describe '#commit_version_files' do
context 'when a version file exists' do
it 'updates it' do
branch = 'master'
allow(client)
.to receive(:file_contents)
.with(release.project_path, 'VERSION', branch)
.and_return("1.2.3\n")
allow(client)
.to receive(:create_commit)
.with(
release.project_path,
branch,
'Update VERSION files',
[{ action: 'update', file_path: 'VERSION', content: '4.5.6' }]
)
release.commit_version_files(branch, { 'VERSION' => '4.5.6' })
expect(client).to have_received(:create_commit)
end
it 'supports skipping of CI builds' do
branch = 'master'
allow(client)
.to receive(:file_contents)
.with(release.project_path, 'VERSION', branch)
.and_return("1.2.3\n")
allow(client)
.to receive(:create_commit)
.with(
release.project_path,
branch,
"Update VERSION files\n\n[ci skip]",
[{ action: 'update', file_path: 'VERSION', content: '4.5.6' }]
)
release
.commit_version_files(branch, { 'VERSION' => '4.5.6' }, skip_ci: true)
expect(client).to have_received(:create_commit)
end
it 'supports skipping of Merge Train syncs' do
branch = 'master'
allow(client)
.to receive(:file_contents)
.with(release.project_path, 'VERSION', branch)
.and_return("1.2.3\n")
allow(client)
.to receive(:create_commit)
.with(
release.project_path,
branch,
"Update VERSION files\n\n[merge-train skip]",
[{ action: 'update', file_path: 'VERSION', content: '4.5.6' }]
)
release.commit_version_files(
branch,
{ 'VERSION' => '4.5.6' },
skip_merge_train: true
)
expect(client).to have_received(:create_commit)
end
it 'supports skipping of Merge Train syncs and CI builds' do
branch = 'master'
allow(client)
.to receive(:file_contents)
.with(release.project_path, 'VERSION', branch)
.and_return("1.2.3\n")
allow(client)
.to receive(:create_commit)
.with(
release.project_path,
branch,
"Update VERSION files\n\n[ci skip]\n[merge-train skip]",
[{ action: 'update', file_path: 'VERSION', content: '4.5.6' }]
)
release.commit_version_files(
branch,
{ 'VERSION' => '4.5.6' },
skip_ci: true,
skip_merge_train: true
)
expect(client).to have_received(:create_commit)
end
end
context 'when a version file does not exist' do
it 'creates it' do
branch = 'master'
allow(client)
.to receive(:file_contents)
.and_raise(gitlab_error(:NotFound))
allow(client)
.to receive(:create_commit)
.with(
release.project_path,
branch,
'Update VERSION files',
[{ action: 'create', file_path: 'VERSION', content: '4.5.6' }]
)
release.commit_version_files(branch, { 'VERSION' => '4.5.6' })
expect(client).to have_received(:create_commit)
end
end
context 'when there are no changes' do
it 'does not commit anything' do
branch = 'master'
allow(client)
.to receive(:file_contents)
.with(release.project_path, 'VERSION', branch)
.and_return("4.5.6\n")
allow(client).to receive(:create_commit)
release.commit_version_files(branch, { 'VERSION' => '4.5.6' })
expect(client).not_to have_received(:create_commit)
end
end
context 'when there are no changes but the new version contains a trailing newline' do
it 'does not commit anything' do
branch = 'master'
allow(client)
.to receive(:file_contents)
.with(release.project_path, 'VERSION', branch)
.and_return("4.5.6\n")
allow(client).to receive(:create_commit)
release.commit_version_files(branch, { 'VERSION' => "4.5.6\n" })
expect(client).not_to have_received(:create_commit)
end
end
end
describe '#last_production_commit' do
context 'when release_metadata_as_source is disabled' do
it 'returns the sha from the deployments' do
allow(release)
.to receive(:last_production_commit_deployments)
.and_return('1234abcd')
expect(release)
.not_to receive(:last_production_commit_metadata)
expect(release.last_production_commit).to eq('1234abcd')
end
end
context 'when release_metadata_as_source is enabled' do
before do
enable_feature('release_metadata_as_source')
end
it 'compares the two commits' do
allow(release)
.to receive_messages(last_production_commit_deployments: '1234abcd', last_production_commit_metadata: '1234abcd')
expect(release.last_production_commit).to eq('1234abcd')
end
context 'when the commits are different' do
it 'returns the commit from deployments' do
allow(release)
.to receive_messages(last_production_commit_deployments: 'foo', last_production_commit_metadata: 'baz')
expect(release.last_production_commit).to eq('foo')
end
end
end
end
describe '#last_production_commit_deployments' do
context 'when there are production deployments' do
it 'returns the SHA of the last deployment' do
expect(client)
.to receive(:deployments)
.with(release.project.path, 'gprd', { status: 'success' })
.and_return([double(:deploy, sha: '123')])
expect(release.last_production_commit_deployments).to eq('123')
end
end
context 'when there are no production deployments' do
it 'raises RuntimeError' do
expect(client)
.to receive(:deployments)
.with(release.project.path, 'gprd', { status: 'success' })
.and_return([])
expect do
release.last_production_commit_deployments
end.to raise_error(RuntimeError)
end
end
end
describe '#last_production_commit_metadata' do
let(:client) { class_spy(ReleaseTools::GitlabOpsClient) }
before do
stub_const('ReleaseTools::GitlabOpsClient', client)
product_version = ReleaseTools::ProductVersion.new('42.1.2021110116')
allow(client)
.to receive(:deployments)
.and_return([create(:deployment, :success)])
allow(ReleaseTools::ProductVersion)
.to receive(:from_metadata_sha)
.and_return(product_version)
allow(product_version)
.to receive(:metadata)
.and_return(build_metadata(gitlab_sha: '1234abcd'))
end
it 'fetches sha from metadata' do
allow(release)
.to receive(:project)
.and_return(ReleaseTools::Project::GitlabEe)
expect(release.last_production_commit_metadata).to eq('1234abcd')
end
end
describe '#notify_stable_branch_creation' do
let(:version) { ReleaseTools::Version.new('1.2.0-rc42') }
let(:release_managers_schedule) do
instance_spy(
ReleaseTools::ReleaseManagers::Schedule,
active_version: ReleaseTools::Version.new('1.2.0')
)
end
let(:release) { KlassGitaly.new(version, client: client) }
before do
stub_const('KlassGitaly',
Class.new(Klass) do
def project
ReleaseTools::Project::Gitaly
end
end)
allow(ReleaseTools::ReleaseManagers::Schedule)
.to receive(:new)
.and_return(release_managers_schedule)
allow(ReleaseTools::GitlabClient)
.to receive(:find_branch)
.and_return(build(:branch))
end
context 'with valid version, project and branch' do
it 'calls Security::NotifyStableBranchCreation' do
expect(ReleaseTools::Security::NotifyStableBranchCreation)
.to receive(:new)
.with(
instance_of(ReleaseTools::Security::IssueCrawler),
ReleaseTools::Project::Gitaly,
'1-2-stable'
)
.and_return(instance_spy(ReleaseTools::Security::NotifyStableBranchCreation, execute: nil))
release.notify_stable_branch_creation
end
end
context 'with non managed project' do
before do
stub_const('KlassGitaly',
Class.new(Klass) do
def project
ReleaseTools::Project::GitlabCe
end
end)
end
it 'does not call NotifyStableBranchCreation' do
expect(ReleaseTools::Security::NotifyStableBranchCreation)
.not_to receive(:new)
release.notify_stable_branch_creation
end
end
context 'with non RC version' do
let(:version) { ReleaseTools::Version.new('1.2.0') }
it 'does not call NotifyStableBranchCreation' do
expect(ReleaseTools::Security::NotifyStableBranchCreation)
.not_to receive(:new)
release.notify_stable_branch_creation
end
end
context 'when branch does not exist' do
before do
allow(ReleaseTools::GitlabClient)
.to receive(:find_branch)
.and_return(nil)
end
it 'does not call NotifyStableBranchCreation' do
expect(ReleaseTools::Security::NotifyStableBranchCreation)
.not_to receive(:new)
release.notify_stable_branch_creation
end
end
end
end