Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
87f9cb8c
Commit
87f9cb8c
authored
Nov 03, 2021
by
Mark Fletcher
Committed by
Rémy Coutable
Nov 03, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make previous test detection work for other projects and hosts
parent
c4f6712e
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
137 additions
and
90 deletions
+137
-90
.gitlab/ci/rules.gitlab-ci.yml
.gitlab/ci/rules.gitlab-ci.yml
+0
-4
scripts/pipeline_test_report_builder.rb
scripts/pipeline_test_report_builder.rb
+21
-21
scripts/rspec_helpers.sh
scripts/rspec_helpers.sh
+5
-2
spec/scripts/pipeline_test_report_builder_spec.rb
spec/scripts/pipeline_test_report_builder_spec.rb
+111
-63
No files found.
.gitlab/ci/rules.gitlab-ci.yml
View file @
87f9cb8c
...
...
@@ -1263,16 +1263,12 @@
.rails:rules:detect-previous-failed-tests:
rules
:
-
<<
:
*if-not-canonical-namespace
when
:
never
-
<<
:
*if-merge-request-labels-run-all-rspec
-
<<
:
*if-merge-request
changes
:
*code-backstage-patterns
.rails:rules:rerun-previous-failed-tests:
rules
:
-
<<
:
*if-not-canonical-namespace
when
:
never
-
<<
:
*if-merge-request-labels-run-all-rspec
-
<<
:
*if-merge-request
changes
:
*code-backstage-patterns
...
...
scripts/pipeline_test_report_builder.rb
View file @
87f9cb8c
...
...
@@ -20,7 +20,7 @@ require_relative 'api/default_options'
# Push into expected format for failed tests
class
PipelineTestReportBuilder
def
initialize
(
options
)
@
project
=
options
.
delete
(
:
project
)
@
target_project
=
options
.
delete
(
:target_
project
)
@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
]
@output_file_path
=
options
.
delete
(
:output_file_path
)
...
...
@@ -40,38 +40,38 @@ class PipelineTestReportBuilder
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
"
#{
instance_base_url
}
/api/v4/projects/
#{
CGI
.
escape
(
project
)
}
"
end
attr_reader
:target_project
,
:mr_id
,
:instance_base_url
,
:output_file_path
def
p
roject_base_url
"
#{
instance_base_url
}
/
#{
project
}
"
def
p
ipeline_project_api_base_url
(
pipeline
)
"
#{
instance_base_url
}
/
api/v4/projects/
#{
pipeline
[
'project_id'
]
}
"
end
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
]
def
target_project_api_base_url
"
#{
instance_base_url
}
/api/v4/projects/
#{
CGI
.
escape
(
target_project
)
}
"
end
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
def
failed_builds_for_pipeline
(
pipeline
_id
)
fetch
(
"
#{
p
roject_api_base_url
}
/pipelines/
#{
pipeline_id
}
/jobs?scope=failed&per_page=100"
)
def
failed_builds_for_pipeline
(
pipeline
)
fetch
(
"
#{
p
ipeline_project_api_base_url
(
pipeline
)
}
/pipelines/
#{
pipeline
[
'id'
]
}
/jobs?scope=failed&per_page=100"
)
end
# 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.
# 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
def
test_report_for_build
(
pipeline
_id
,
build_id
)
fetch
(
"
#{
p
roject_base_url
}
/-/pipelines/
#{
pipeline_id
}
/tests/suite.json?build_ids[]=
#{
build_id
}
"
)
def
test_report_for_build
(
pipeline
,
build_id
)
fetch
(
"
#{
p
ipeline
[
'web_url'
]
}
/tests/suite.json?build_ids[]=
#{
build_id
}
"
)
end
def
build_test_report_json_for_pipeline
(
pipeline
)
...
...
@@ -82,7 +82,7 @@ class PipelineTestReportBuilder
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'
end
...
...
@@ -92,7 +92,7 @@ class PipelineTestReportBuilder
test_report
[
'suites'
]
||=
[]
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
...
...
@@ -127,8 +127,8 @@ if $0 == __FILE__
options
=
Host
::
DEFAULT_OPTIONS
.
dup
OptionParser
.
new
do
|
opts
|
opts
.
on
(
"-
p"
,
"--project PROJECT"
,
String
,
"Project where to find the merge request(defaults to $CI_PROJECT_ID)
"
)
do
|
value
|
options
[
:project
]
=
value
opts
.
on
(
"-
t"
,
"--target-project TARGET_PROJECT"
,
String
,
"Project where to find the merge request
"
)
do
|
value
|
options
[
:
target_
project
]
=
value
end
opts
.
on
(
"-m"
,
"--mr-id MR_ID"
,
String
,
"A merge request ID"
)
do
|
value
|
...
...
scripts/rspec_helpers.sh
View file @
87f9cb8c
...
...
@@ -94,11 +94,14 @@ function retrieve_previous_failed_tests() {
local
rspec_pg_regex
=
"
${
2
}
"
local
rspec_ee_pg_regex
=
"
${
3
}
"
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...'
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...'
...
...
spec/scripts/pipeline_test_report_builder_spec.rb
View file @
87f9cb8c
...
...
@@ -9,30 +9,39 @@ RSpec.describe PipelineTestReportBuilder do
subject
do
described_class
.
new
(
project:
'gitlab-org/gitlab'
,
target_
project:
'gitlab-org/gitlab'
,
mr_id:
'999'
,
instance_base_url:
'https://gitlab.com'
,
output_file_path:
output_file_path
)
end
let
(
:mr_pipelines
)
do
[
{
'status'
=>
'running'
,
'created_at'
=>
DateTime
.
now
.
to_s
},
{
'status'
=>
'failed'
,
'created_at'
=>
(
DateTime
.
now
-
5
).
to_s
}
]
let
(
:failed_pipeline_url
)
{
'pipeline2_url'
}
let
(
:failed_pipeline
)
do
{
'status'
=>
'failed'
,
'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
let
(
:mr_pipelines
)
{
[
current_pipeline
,
failed_pipeline
]
}
let
(
:failed_build_id
)
{
9999
}
let
(
:failed_builds_for_pipeline
)
do
[
{
'id'
=>
9999
,
'id'
=>
failed_build_id
,
'stage'
=>
'test'
}
]
...
...
@@ -62,75 +71,114 @@ RSpec.describe PipelineTestReportBuilder do
before
do
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
(
:test_report_for_build
).
and_return
(
test_report_for_build
)
end
describe
'#test_report_for_latest_pipeline'
do
context
'no previous pipeline'
do
let
(
:mr_pipelines
)
{
[]
}
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
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
end
before
do
allow
(
subject
).
to
receive
(
:test_report_for_build
).
and_return
(
test_report_for_build
)
end
context
'first pipeline scenario'
do
let
(
:mr_pipelines
)
do
[
{
'status'
=>
'running'
,
'created_at'
=>
DateTime
.
now
.
to_s
}
]
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
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
context
'pipeline in target project'
do
it
'returns failed pipeline'
do
expect
(
subject
.
previous_pipeline
).
to
eq
(
failed_pipeline
)
end
end
end
context
'no previous failed pipeline'
do
let
(
:mr_pipelines
)
do
[
{
'status'
=>
'running'
,
'created_at'
=>
DateTime
.
now
.
to_s
},
{
'status'
=>
'success'
,
'created_at'
=>
(
DateTime
.
now
-
5
).
to_s
}
]
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
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
context
'no previous pipeline'
do
let
(
:mr_pipelines
)
{
[]
}
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
end
end
end
context
'no failed test builds'
do
let
(
:failed_builds_for_pipeline
)
do
[
{
'id'
=>
9999
,
'stage'
=>
'prepare'
}
]
context
'first pipeline scenario'
do
let
(
:mr_pipelines
)
do
[
{
'status'
=>
'running'
,
'created_at'
=>
DateTime
.
now
.
to_s
}
]
end
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
end
end
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
context
'no previous failed pipeline'
do
let
(
:mr_pipelines
)
do
[
{
'status'
=>
'running'
,
'created_at'
=>
DateTime
.
now
.
to_s
},
{
'status'
=>
'success'
,
'created_at'
=>
(
DateTime
.
now
-
5
).
to_s
}
]
end
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
end
end
context
'no failed test builds'
do
let
(
:failed_builds_for_pipeline
)
do
[
{
'id'
=>
9999
,
'stage'
=>
'prepare'
}
]
end
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
end
end
end
context
'failed pipeline and failed test builds'
do
it
'returns populated test list for suites'
do
actual
=
subject
.
test_report_for_latest_pipeline
expected
=
{
'suites'
=>
[
test_report_for_build
]
}.
to_json
context
'failed pipeline and failed test builds'
do
it
'returns populated test list for suites'
do
actual
=
subject
.
test_report_for_latest_pipeline
expected
=
{
'suites'
=>
[
test_report_for_build
]
}.
to_json
expect
(
actual
).
to
eq
(
expected
)
expect
(
actual
).
to
eq
(
expected
)
end
end
end
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment