Commit 87f9cb8c authored by Mark Fletcher's avatar Mark Fletcher Committed by Rémy Coutable

Make previous test detection work for other projects and hosts

parent c4f6712e
...@@ -1263,16 +1263,12 @@ ...@@ -1263,16 +1263,12 @@
.rails:rules:detect-previous-failed-tests: .rails:rules:detect-previous-failed-tests:
rules: rules:
- <<: *if-not-canonical-namespace
when: never
- <<: *if-merge-request-labels-run-all-rspec - <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request - <<: *if-merge-request
changes: *code-backstage-patterns changes: *code-backstage-patterns
.rails:rules:rerun-previous-failed-tests: .rails:rules:rerun-previous-failed-tests:
rules: rules:
- <<: *if-not-canonical-namespace
when: never
- <<: *if-merge-request-labels-run-all-rspec - <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request - <<: *if-merge-request
changes: *code-backstage-patterns changes: *code-backstage-patterns
......
...@@ -20,7 +20,7 @@ require_relative 'api/default_options' ...@@ -20,7 +20,7 @@ require_relative 'api/default_options'
# Push into expected format for failed tests # Push into expected format for failed tests
class PipelineTestReportBuilder class PipelineTestReportBuilder
def initialize(options) def initialize(options)
@project = options.delete(:project) @target_project = options.delete(:target_project)
@mr_id = options.delete(:mr_id) || Host::DEFAULT_OPTIONS[:mr_id] @mr_id = options.delete(:mr_id) || Host::DEFAULT_OPTIONS[:mr_id]
@instance_base_url = options.delete(:instance_base_url) || Host::DEFAULT_OPTIONS[:instance_base_url] @instance_base_url = options.delete(:instance_base_url) || Host::DEFAULT_OPTIONS[:instance_base_url]
@output_file_path = options.delete(:output_file_path) @output_file_path = options.delete(:output_file_path)
...@@ -40,38 +40,38 @@ class PipelineTestReportBuilder ...@@ -40,38 +40,38 @@ class PipelineTestReportBuilder
end end
end end
private def previous_pipeline
# Top of the list will always be the current pipeline
# Second from top will be the previous pipeline
pipelines_for_mr.sort_by { |a| -Time.parse(a['created_at']).to_i }[1]
end
attr_reader :project, :mr_id, :instance_base_url, :output_file_path private
def project_api_base_url attr_reader :target_project, :mr_id, :instance_base_url, :output_file_path
"#{instance_base_url}/api/v4/projects/#{CGI.escape(project)}"
end
def project_base_url def pipeline_project_api_base_url(pipeline)
"#{instance_base_url}/#{project}" "#{instance_base_url}/api/v4/projects/#{pipeline['project_id']}"
end end
def previous_pipeline def target_project_api_base_url
# Top of the list will always be the current pipeline "#{instance_base_url}/api/v4/projects/#{CGI.escape(target_project)}"
# Second from top will be the previous pipeline
pipelines_for_mr.sort_by { |a| -Time.parse(a['created_at']).to_i }[1]
end end
def pipelines_for_mr def pipelines_for_mr
fetch("#{project_api_base_url}/merge_requests/#{mr_id}/pipelines") fetch("#{target_project_api_base_url}/merge_requests/#{mr_id}/pipelines")
end end
def failed_builds_for_pipeline(pipeline_id) def failed_builds_for_pipeline(pipeline)
fetch("#{project_api_base_url}/pipelines/#{pipeline_id}/jobs?scope=failed&per_page=100") fetch("#{pipeline_project_api_base_url(pipeline)}/pipelines/#{pipeline['id']}/jobs?scope=failed&per_page=100")
end end
# Method uses the test suite endpoint to gather test results for a particular build. # Method uses the test suite endpoint to gather test results for a particular build.
# Here we request individual builds, even though it is possible to supply multiple build IDs. # Here we request individual builds, even though it is possible to supply multiple build IDs.
# The reason for this; it is possible to lose the job context and name when requesting multiple builds. # The reason for this; it is possible to lose the job context and name when requesting multiple builds.
# Please see for more info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69053#note_709939709 # Please see for more info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69053#note_709939709
def test_report_for_build(pipeline_id, build_id) def test_report_for_build(pipeline, build_id)
fetch("#{project_base_url}/-/pipelines/#{pipeline_id}/tests/suite.json?build_ids[]=#{build_id}") fetch("#{pipeline['web_url']}/tests/suite.json?build_ids[]=#{build_id}")
end end
def build_test_report_json_for_pipeline(pipeline) def build_test_report_json_for_pipeline(pipeline)
...@@ -82,7 +82,7 @@ class PipelineTestReportBuilder ...@@ -82,7 +82,7 @@ class PipelineTestReportBuilder
puts "Discovered last failed pipeline (#{pipeline['id']}) for MR!#{mr_id}" puts "Discovered last failed pipeline (#{pipeline['id']}) for MR!#{mr_id}"
failed_builds_for_test_stage = failed_builds_for_pipeline(pipeline['id']).select do |failed_build| failed_builds_for_test_stage = failed_builds_for_pipeline(pipeline).select do |failed_build|
failed_build['stage'] == 'test' failed_build['stage'] == 'test'
end end
...@@ -92,7 +92,7 @@ class PipelineTestReportBuilder ...@@ -92,7 +92,7 @@ class PipelineTestReportBuilder
test_report['suites'] ||= [] test_report['suites'] ||= []
failed_builds_for_test_stage.each do |failed_build| failed_builds_for_test_stage.each do |failed_build|
test_report['suites'] << test_report_for_build(pipeline['id'], failed_build['id']) test_report['suites'] << test_report_for_build(pipeline, failed_build['id'])
end end
end end
...@@ -127,8 +127,8 @@ if $0 == __FILE__ ...@@ -127,8 +127,8 @@ if $0 == __FILE__
options = Host::DEFAULT_OPTIONS.dup options = Host::DEFAULT_OPTIONS.dup
OptionParser.new do |opts| OptionParser.new do |opts|
opts.on("-p", "--project PROJECT", String, "Project where to find the merge request(defaults to $CI_PROJECT_ID)") do |value| opts.on("-t", "--target-project TARGET_PROJECT", String, "Project where to find the merge request") do |value|
options[:project] = value options[:target_project] = value
end end
opts.on("-m", "--mr-id MR_ID", String, "A merge request ID") do |value| opts.on("-m", "--mr-id MR_ID", String, "A merge request ID") do |value|
......
...@@ -94,11 +94,14 @@ function retrieve_previous_failed_tests() { ...@@ -94,11 +94,14 @@ function retrieve_previous_failed_tests() {
local rspec_pg_regex="${2}" local rspec_pg_regex="${2}"
local rspec_ee_pg_regex="${3}" local rspec_ee_pg_regex="${3}"
local pipeline_report_path="test_results/previous/test_reports.json" local pipeline_report_path="test_results/previous/test_reports.json"
local project_path="gitlab-org/gitlab"
# Used to query merge requests. This variable reflects where the merge request has been created
local target_project_path="${CI_MERGE_REQUEST_PROJECT_PATH}"
local instance_url="${CI_SERVER_URL}"
echo 'Attempting to build pipeline test report...' echo 'Attempting to build pipeline test report...'
scripts/pipeline_test_report_builder.rb --instance-base-url "https://gitlab.com" --project "${project_path}" --mr-id "${CI_MERGE_REQUEST_IID}" --output-file-path "${pipeline_report_path}" scripts/pipeline_test_report_builder.rb --instance-base-url "${instance_url}" --target-project "${target_project_path}" --mr-id "${CI_MERGE_REQUEST_IID}" --output-file-path "${pipeline_report_path}"
echo 'Generating failed tests lists...' echo 'Generating failed tests lists...'
......
...@@ -9,30 +9,39 @@ RSpec.describe PipelineTestReportBuilder do ...@@ -9,30 +9,39 @@ RSpec.describe PipelineTestReportBuilder do
subject do subject do
described_class.new( described_class.new(
project: 'gitlab-org/gitlab', target_project: 'gitlab-org/gitlab',
mr_id: '999', mr_id: '999',
instance_base_url: 'https://gitlab.com', instance_base_url: 'https://gitlab.com',
output_file_path: output_file_path output_file_path: output_file_path
) )
end end
let(:mr_pipelines) do let(:failed_pipeline_url) { 'pipeline2_url' }
[
{ let(:failed_pipeline) do
'status' => 'running',
'created_at' => DateTime.now.to_s
},
{ {
'status' => 'failed', 'status' => 'failed',
'created_at' => (DateTime.now - 5).to_s 'created_at' => (DateTime.now - 5).to_s,
'web_url' => failed_pipeline_url
}
end
let(:current_pipeline) do
{
'status' => 'running',
'created_at' => DateTime.now.to_s,
'web_url' => 'pipeline1_url'
} }
]
end end
let(:mr_pipelines) { [current_pipeline, failed_pipeline] }
let(:failed_build_id) { 9999 }
let(:failed_builds_for_pipeline) do let(:failed_builds_for_pipeline) do
[ [
{ {
'id' => 9999, 'id' => failed_build_id,
'stage' => 'test' 'stage' => 'test'
} }
] ]
...@@ -62,10 +71,48 @@ RSpec.describe PipelineTestReportBuilder do ...@@ -62,10 +71,48 @@ RSpec.describe PipelineTestReportBuilder do
before do before do
allow(subject).to receive(:pipelines_for_mr).and_return(mr_pipelines) allow(subject).to receive(:pipelines_for_mr).and_return(mr_pipelines)
allow(subject).to receive(:failed_builds_for_pipeline).and_return(failed_builds_for_pipeline) allow(subject).to receive(:failed_builds_for_pipeline).and_return(failed_builds_for_pipeline)
end
describe '#previous_pipeline' do
let(:fork_pipeline_url) { 'fork_pipeline_url' }
let(:fork_pipeline) do
{
'status' => 'failed',
'created_at' => (DateTime.now - 5).to_s,
'web_url' => fork_pipeline_url
}
end
before do
allow(subject).to receive(:test_report_for_build).and_return(test_report_for_build) allow(subject).to receive(:test_report_for_build).and_return(test_report_for_build)
end end
context 'pipeline in a fork project' do
let(:mr_pipelines) { [current_pipeline, fork_pipeline] }
it 'returns fork pipeline' do
expect(subject.previous_pipeline).to eq(fork_pipeline)
end
end
context 'pipeline in target project' do
it 'returns failed pipeline' do
expect(subject.previous_pipeline).to eq(failed_pipeline)
end
end
end
describe '#test_report_for_latest_pipeline' do describe '#test_report_for_latest_pipeline' do
it 'fetches builds from pipeline related to MR' do
expect(subject).to receive(:fetch).with("#{failed_pipeline_url}/tests/suite.json?build_ids[]=#{failed_build_id}").and_return(failed_builds_for_pipeline)
subject.test_report_for_latest_pipeline
end
context 'canonical pipeline' do
before do
allow(subject).to receive(:test_report_for_build).and_return(test_report_for_build)
end
context 'no previous pipeline' do context 'no previous pipeline' do
let(:mr_pipelines) { [] } let(:mr_pipelines) { [] }
...@@ -134,4 +181,5 @@ RSpec.describe PipelineTestReportBuilder do ...@@ -134,4 +181,5 @@ RSpec.describe PipelineTestReportBuilder do
end end
end end
end end
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