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 @@
...
@@ -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
...
...
scripts/pipeline_test_report_builder.rb
View file @
87f9cb8c
...
@@ -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
p
roject_base_url
def
p
ipeline_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
(
"
#{
p
roject_api_base_url
}
/pipelines/
#{
pipeline_id
}
/jobs?scope=failed&per_page=100"
)
fetch
(
"
#{
p
ipeline_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
(
"
#{
p
roject_base_url
}
/-/pipelines/
#{
pipeline_id
}
/tests/suite.json?build_ids[]=
#{
build_id
}
"
)
fetch
(
"
#{
p
ipeline
[
'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
|
...
...
scripts/rspec_helpers.sh
View file @
87f9cb8c
...
@@ -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...'
...
...
spec/scripts/pipeline_test_report_builder_spec.rb
View file @
87f9cb8c
...
@@ -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'
,
},
'created_at'
=>
(
DateTime
.
now
-
5
).
to_s
,
{
'web_url'
=>
failed_pipeline_url
'status'
=>
'failed'
,
}
'created_at'
=>
(
DateTime
.
now
-
5
).
to_s
}
]
end
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
let
(
:failed_builds_for_pipeline
)
do
[
[
{
{
'id'
=>
9999
,
'id'
=>
failed_build_id
,
'stage'
=>
'test'
'stage'
=>
'test'
}
}
]
]
...
@@ -62,75 +71,114 @@ RSpec.describe PipelineTestReportBuilder do
...
@@ -62,75 +71,114 @@ 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
)
allow
(
subject
).
to
receive
(
:test_report_for_build
).
and_return
(
test_report_for_build
)
end
end
describe
'#test_report_for_latest_pipeline'
do
describe
'#previous_pipeline'
do
context
'no previous pipeline'
do
let
(
:fork_pipeline_url
)
{
'fork_pipeline_url'
}
let
(
:mr_pipelines
)
{
[]
}
let
(
:fork_pipeline
)
do
{
'status'
=>
'failed'
,
'created_at'
=>
(
DateTime
.
now
-
5
).
to_s
,
'web_url'
=>
fork_pipeline_url
}
end
it
'returns empty hash'
do
before
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
allow
(
subject
).
to
receive
(
:test_report_for_build
).
and_return
(
test_report_for_build
)
end
end
end
context
'first pipeline scenario'
do
context
'pipeline in a fork project'
do
let
(
:mr_pipelines
)
do
let
(
:mr_pipelines
)
{
[
current_pipeline
,
fork_pipeline
]
}
[
{
it
'returns fork pipeline'
do
'status'
=>
'running'
,
expect
(
subject
.
previous_pipeline
).
to
eq
(
fork_pipeline
)
'created_at'
=>
DateTime
.
now
.
to_s
}
]
end
end
end
it
'returns empty hash'
do
context
'pipeline in target project'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
it
'returns failed pipeline'
do
expect
(
subject
.
previous_pipeline
).
to
eq
(
failed_pipeline
)
end
end
end
end
end
context
'no previous failed pipeline'
do
describe
'#test_report_for_latest_pipeline'
do
let
(
:mr_pipelines
)
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
'status'
=>
'running'
,
end
'created_at'
=>
DateTime
.
now
.
to_s
},
context
'canonical pipeline'
do
{
before
do
'status'
=>
'success'
,
allow
(
subject
).
to
receive
(
:test_report_for_build
).
and_return
(
test_report_for_build
)
'created_at'
=>
(
DateTime
.
now
-
5
).
to_s
}
]
end
end
it
'returns empty hash'
do
context
'no previous pipeline'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
let
(
:mr_pipelines
)
{
[]
}
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
end
end
end
end
context
'no failed test builds'
do
context
'first pipeline scenario'
do
let
(
:failed_builds_for_pipeline
)
do
let
(
:mr_pipelines
)
do
[
[
{
{
'id'
=>
9999
,
'status'
=>
'running'
,
'stage'
=>
'prepare'
'created_at'
=>
DateTime
.
now
.
to_s
}
}
]
]
end
it
'returns empty hash'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
end
end
end
it
'returns empty hash'
do
context
'no previous failed pipeline'
do
expect
(
subject
.
test_report_for_latest_pipeline
).
to
eq
(
"{}"
)
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
end
context
'failed pipeline and failed test builds'
do
context
'failed pipeline and failed test builds'
do
it
'returns populated test list for suites'
do
it
'returns populated test list for suites'
do
actual
=
subject
.
test_report_for_latest_pipeline
actual
=
subject
.
test_report_for_latest_pipeline
expected
=
{
expected
=
{
'suites'
=>
[
test_report_for_build
]
'suites'
=>
[
test_report_for_build
]
}.
to_json
}.
to_json
expect
(
actual
).
to
eq
(
expected
)
expect
(
actual
).
to
eq
(
expected
)
end
end
end
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