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