Commit 88bda04c authored by Yorick Peterse's avatar Yorick Peterse

Merge branch 'jts/validate-ref-on-dev-instance' into 'master'

Adds ability to validate we have a usable sha on our dev instance

See merge request !585
parents eb84a0c1 5fc49cf4
......@@ -57,6 +57,9 @@ RSpec/MultipleExpectations:
RSpec/NestedGroups:
Max: 5
RSpec/SubjectStub:
Enabled: false
# Style #######################################################################
Style/CommentAnnotation:
......
......@@ -153,22 +153,6 @@ RSpec/PredicateMatcher:
Exclude:
- 'spec/lib/release_tools/merge_request_spec.rb'
# Offense count: 49
RSpec/SubjectStub:
Exclude:
- 'spec/lib/release_tools/cherry_pick/comment_notifier_spec.rb'
- 'spec/lib/release_tools/cherry_pick/service_spec.rb'
- 'spec/lib/release_tools/issuable_spec.rb'
- 'spec/lib/release_tools/qa/issue_spec.rb'
- 'spec/lib/release_tools/qa/security_issue_spec.rb'
- 'spec/lib/release_tools/qa/services/build_qa_issue_service_spec.rb'
- 'spec/lib/release_tools/release_managers/client_spec.rb'
- 'spec/lib/release_tools/release_managers/definitions_spec.rb'
- 'spec/lib/release_tools/remote_repository_spec.rb'
- 'spec/lib/release_tools/services/auto_deploy_branch_service_spec.rb'
- 'spec/lib/release_tools/services/monthly_preparation_service_spec.rb'
- 'spec/lib/release_tools/services/security_preparation_service_spec.rb'
# Offense count: 257
# Configuration parameters: IgnoreSymbolicNames.
RSpec/VerifiedDoubles:
......
......@@ -31,62 +31,6 @@ namespace :auto_deploy do
end
end
namespace :green_master do
desc "Trigger a green master build for EE"
task :ee, [:trigger_build] do |_t, args|
project = ReleaseTools::Project::GitlabEe
commit = ReleaseTools::Commits.new(project).latest_successful
raise 'No recent master builds have green pipelines' if commit.nil?
$stdout.puts "Found EE Green Master at #{commit.id}"
versions = ReleaseTools::ComponentVersions.get(project, commit)
versions.each do |component, version|
puts "#{component}: #{version}"
end
if args.trigger_build
ReleaseTools::Pipeline.new(
project,
commit.id,
versions
).trigger
end
end
desc "Trigger a green master build for CE"
task :ce, [:trigger_build] do |_t, args|
project = ReleaseTools::Project::GitlabCe
commit = ReleaseTools::Commits.new(project).latest_successful
raise 'No recent master builds have green pipelines' if commit.nil?
$stdout.puts "Found CE Green Master at #{commit.id}"
versions = ReleaseTools::ComponentVersions.get(project, commit)
versions.each do |component, version|
puts "#{component}: #{version}"
end
if args.trigger_build
ReleaseTools::Pipeline.new(
project,
commit.id,
versions
).trigger
end
end
desc "Trigger a green master build for both CE and EE"
task :all, [:trigger_build] do |_t, args|
Rake::Task['green_master:ee'].invoke(args.trigger_build)
Rake::Task['green_master:ce'].invoke(args.trigger_build)
end
end
task :tag, [:version] do |t, args|
deprecate_as 'release:tag', t, args
end
......
......@@ -52,6 +52,7 @@ require 'release_tools/component_versions'
require 'release_tools/gitlab_client'
require 'release_tools/gitlab_dev_client'
require 'release_tools/gitlab_ops_client'
require 'release_tools/green_master'
require 'release_tools/helm/chart_file'
require 'release_tools/helm/version_manager'
require 'release_tools/helm_chart_version'
......
......@@ -31,7 +31,7 @@ module ReleaseTools
end
def latest_successful_ref(project, client = gitlab_client)
ReleaseTools::Commits.new(project, client).latest_successful.id
ReleaseTools::Commits.new(project, client: client).latest_successful.id
end
end
end
......@@ -2,16 +2,32 @@
module ReleaseTools
class Commits
attr_reader :project
MAX_COMMITS_TO_CHECK = 100
def initialize(project, client = ReleaseTools::GitlabClient)
attr_reader :project
def initialize(project, ref: 'master', client: ReleaseTools::GitlabClient)
@project = project
@ref = ref
@client = client
end
def latest_successful
filter_for_green_builds
commit_list.detect(&method(:success?))
end
# Find a commit with a passing build on production that also exists on dev
def latest_dev_green_build_commit
commit_list.each do |commit|
next unless success?(commit)
begin
# Hit the dev API with the specified commit to see if it even exists
return ReleaseTools::GitlabDevClient.commit(project, ref: commit.id)
rescue Gitlab::Error::Error
next
end
end
end
private
......@@ -19,23 +35,13 @@ module ReleaseTools
def commit_list
@commit_list ||= @client.commits(
@project.path,
per_page: 1,
ref_name: 'master'
per_page: MAX_COMMITS_TO_CHECK,
ref_name: @ref
)
end
def filter_for_green_builds
commit_counter = 0
commit_list.auto_paginate do |commit|
commit_counter += 1
if commit_counter > MAX_COMMITS_TO_CHECK
abort("Examined #{MAX_COMMITS_TO_CHECK} commits, " \
"but could not find a passing " \
"build for #{@project.path}, aborting")
end
commit = @client.commit(@project, ref: commit.id)
return commit if commit.status == "success"
end
def success?(commit)
@client.commit(@project, ref: commit.id).status == 'success'
end
end
end
......@@ -200,6 +200,10 @@ module ReleaseTools
client.create_branch(project_path(project), branch_name, ref)
end
def self.delete_branch(branch_name, project = Project::GitlabCe)
client.delete_branch(project_path(project), branch_name)
end
# Find a branch in a given project
#
# Returns a Gitlab::ObjectifiedHash object, or nil
......
# frozen_string_literal: true
module ReleaseTools
class GreenMaster
attr_reader :project
def initialize(project)
@project = project
end
def execute(args)
commit = ReleaseTools::Commits.new(project, ref: 'master')
.latest_dev_green_build_commit
raise "Unable to find a passing build for `master` on dev" if commit.nil?
$stdout.puts "Found #{project} green master at #{commit.id}"
versions = ReleaseTools::ComponentVersions.get(project, commit)
versions.each do |component, version|
$stdout.puts "#{component}: #{version}".indent(2)
end
trigger_build(commit, versions) if args.trigger_build
end
def trigger_build(commit, versions)
pipeline_id = ENV.fetch('CI_PIPELINE_IID', 'pipeline_id_unset')
branch_name = "nightly-#{pipeline_id}"
$stdout.puts "Creating branch #{branch_name}"
dev_client.create_branch(branch_name, commit.id, project)
ReleaseTools::Pipeline.new(
project,
commit.id,
versions
).trigger
$stdout.puts "Deleting branch #{branch_name}"
dev_client.delete_branch(branch_name, project)
end
private
def dev_client
ReleaseTools::GitlabDevClient
end
end
end
......@@ -40,6 +40,10 @@ module ReleaseTools
extract_path_from_remote(:dev)[:group]
end
def self.to_s
path
end
def self.extract_path_from_remote(remote_key)
raise "Invalid remote: #{remote_key}" unless self::REMOTES.key?(remote_key)
......
# frozen_string_literal: true
namespace :green_master do
desc "Find and optionally trigger a green master build for EE"
task :ee, [:trigger_build] do |_t, args|
ReleaseTools::GreenMaster
.new(ReleaseTools::Project::GitlabEe)
.execute(args)
end
desc "Find and optionally trigger a green master build for CE"
task :ce, [:trigger_build] do |_t, args|
ReleaseTools::GreenMaster
.new(ReleaseTools::Project::GitlabCe)
.execute(args)
end
desc "Trigger a green master build for both CE and EE"
task :all, [:trigger_build] do |_t, args|
Rake::Task['green_master:ee'].invoke(args.trigger_build)
Rake::Task['green_master:ce'].invoke(args.trigger_build)
end
end
This diff is collapsed.
# frozen_string_literal: true
require 'spec_helper'
describe ReleaseTools::Commits do
let(:project) { ReleaseTools::Project::GitlabCe }
# Simulate an error class from the `gitlab` gem
def api_error(klass, message)
error = double(parsed_response: double(message: message)).as_null_object
klass.new(error)
end
before do
# Reduce our fixture payload
stub_const('ReleaseTools::Commits::MAX_COMMITS_TO_CHECK', 5)
end
describe '#latest_successful' do
it 'returns the latest successful commit' do
instance = described_class.new(project)
VCR.use_cassette('commits/list') do
commit = instance.latest_successful
expect(commit.id).to eq 'a5f13e591f617931434d66263418a2f26abe3abe'
end
end
end
describe '#latest_dev_green_build_commit' do
it 'handles a missing commit on dev' do
expect(ReleaseTools::GitlabDevClient)
.to receive(:commit)
.and_raise(api_error(Gitlab::Error::NotFound, 'foo'))
instance = described_class.new(project)
VCR.use_cassette('commits/list') do
expect { instance.latest_dev_green_build_commit }.not_to raise_error
end
end
it 'returns a commit found on dev' do
allow(ReleaseTools::GitlabDevClient)
.to receive(:commit)
.and_return('foo')
instance = described_class.new(project)
VCR.use_cassette('commits/list') do
expect(instance.latest_dev_green_build_commit).to eq 'foo'
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe ReleaseTools::GreenMaster do
let(:project) { ReleaseTools::Project::GitlabCe }
let(:fake_commit) { double('Commit', id: '1234') }
subject(:service) { described_class.new(project) }
describe '#execute' do
let(:fake_commits) { spy }
before do
stub_const('ReleaseTools::Commits', fake_commits)
end
it 'raises an error without a dev commit' do
expect(fake_commits).to receive(:latest_dev_green_build_commit)
.and_return(nil)
expect { service.execute(nil) }
.to raise_error(/Unable to find a passing build/)
end
it 'fetches component versions', :silence_stdout do
expect(fake_commits).to receive(:latest_dev_green_build_commit)
.and_return(fake_commit)
expect(ReleaseTools::ComponentVersions)
.to receive(:get).with(project, fake_commit)
.and_return({})
expect(service).not_to receive(:trigger_build)
service.execute(double(trigger_build: false))
end
it 'triggers a build when specified', :silence_stdout do
expect(fake_commits).to receive(:latest_dev_green_build_commit)
.and_return(fake_commit)
expect(ReleaseTools::ComponentVersions)
.to receive(:get).with(project, fake_commit)
.and_return({})
expect(service).to receive(:trigger_build).with(fake_commit, {})
service.execute(double(trigger_build: true))
end
end
describe '#trigger_build', :silence_stdout do
let(:fake_client) { spy }
let(:fake_pipeline) { spy }
before do
# Ensure we don't actually perform branch creation or deletion
allow(service).to receive(:dev_client).and_return(fake_client)
stub_const('ReleaseTools::Pipeline', fake_pipeline)
end
it 'creates a temporary branch' do
ClimateControl.modify(CI_PIPELINE_IID: 'fake_pipeline_id') do
service.trigger_build(fake_commit, spy)
end
expect(fake_client).to have_received(:create_branch)
.with('nightly-fake_pipeline_id', fake_commit.id, project)
end
it 'triggers a pipeline' do
versions = { foo: 'foo', bar: 'bar' }
service.trigger_build(fake_commit, versions)
expect(fake_pipeline).to have_received(:new)
.with(project, fake_commit.id, versions)
expect(fake_pipeline).to have_received(:trigger)
end
it 'deletes the temporary branch' do
ClimateControl.modify(CI_PIPELINE_IID: 'fake_pipeline_id') do
service.trigger_build(fake_commit, spy)
end
expect(fake_client).to have_received(:delete_branch)
.with('nightly-fake_pipeline_id', project)
end
end
end
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