gitlab_quality-test_tooling 2.9.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +2 -1
- data/Gemfile.lock +9 -1
- data/README.md +44 -3
- data/exe/failed-test-issues +53 -8
- data/exe/feature-readiness-checklist +61 -0
- data/exe/feature-readiness-evaluation +62 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_epic.rb +94 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_issue.rb +92 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_merge_request.rb +139 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/concerns/issue_concern.rb +34 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/concerns/work_item_concern.rb +128 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/evaluation.rb +82 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/operational_readiness_check.rb +82 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/gitlab_graphql_client.rb +54 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb +38 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/issues_dry_client.rb +3 -3
- data/lib/gitlab_quality/test_tooling/gitlab_client/labels_client.rb +13 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_client.rb +21 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_dry_client.rb +0 -10
- data/lib/gitlab_quality/test_tooling/gitlab_client/work_items_client.rb +277 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/work_items_dry_client.rb +25 -0
- data/lib/gitlab_quality/test_tooling/labels_inference.rb +4 -0
- data/lib/gitlab_quality/test_tooling/report/failed_test_issue.rb +6 -6
- data/lib/gitlab_quality/test_tooling/report/feature_readiness/report_on_epic.rb +174 -0
- data/lib/gitlab_quality/test_tooling/report/health_problem_reporter.rb +120 -20
- data/lib/gitlab_quality/test_tooling/report/knapsack_report_issue.rb +1 -1
- data/lib/gitlab_quality/test_tooling/report/prepare_stage_reports.rb +1 -1
- data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +1 -1
- data/lib/gitlab_quality/test_tooling/runtime/env.rb +11 -6
- data/lib/gitlab_quality/test_tooling/test_metrics_exporter/support/gcs_tools.rb +18 -5
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +32 -2
@@ -15,13 +15,29 @@ module GitlabQuality
|
|
15
15
|
class HealthProblemReporter < ReportAsIssue
|
16
16
|
include Concerns::GroupAndCategoryLabels
|
17
17
|
include Concerns::IssueReports
|
18
|
+
include TestMetricsExporter::Support::GcsTools
|
18
19
|
|
19
20
|
BASE_SEARCH_LABELS = ['test'].freeze
|
20
21
|
FOUND_IN_MR_LABEL = '~"found:in MR"'
|
21
22
|
FOUND_IN_MASTER_LABEL = '~"found:master"'
|
22
23
|
|
23
|
-
def initialize(
|
24
|
-
|
24
|
+
def initialize(
|
25
|
+
input_files: [],
|
26
|
+
dry_run: false,
|
27
|
+
issue_update_enabled: true,
|
28
|
+
gcs_enabled: false,
|
29
|
+
gcs_project_id: nil,
|
30
|
+
gcs_bucket: nil,
|
31
|
+
gcs_credentials: nil,
|
32
|
+
**kwargs)
|
33
|
+
super(input_files: input_files, dry_run: dry_run, **kwargs)
|
34
|
+
|
35
|
+
@dry_run = dry_run
|
36
|
+
@issue_update_enabled = issue_update_enabled
|
37
|
+
@gcs_enabled = gcs_enabled
|
38
|
+
@gcs_project_id = gcs_project_id
|
39
|
+
@gcs_bucket = gcs_bucket
|
40
|
+
@gcs_credentials = gcs_credentials
|
25
41
|
end
|
26
42
|
|
27
43
|
def most_recent_report_date_for_issue(issue_iid:)
|
@@ -33,6 +49,8 @@ module GitlabQuality
|
|
33
49
|
|
34
50
|
private
|
35
51
|
|
52
|
+
attr_reader :dry_run, :issue_update_enabled, :gcs_enabled, :gcs_project_id, :gcs_bucket, :gcs_credentials
|
53
|
+
|
36
54
|
def problem_type
|
37
55
|
'unhealthy'
|
38
56
|
end
|
@@ -70,43 +88,89 @@ module GitlabQuality
|
|
70
88
|
end
|
71
89
|
|
72
90
|
def run!
|
73
|
-
|
91
|
+
Runtime::Logger.info "issue creation/update is disabled." unless issue_update_enabled
|
92
|
+
Runtime::Logger.info "push to GCS is disabled." unless gcs_enabled
|
93
|
+
return unless test_reporting_enabled?
|
74
94
|
|
95
|
+
Runtime::Logger.info "Reporting tests in `#{files.join(',')}`."
|
75
96
|
TestResults::Builder.new(file_glob: files, token: token, project: project).test_results_per_file do |test_results|
|
76
|
-
|
97
|
+
Runtime::Logger.info "=> Processing #{test_results.count} tests in #{test_results.path}"
|
77
98
|
|
78
99
|
process_test_results(test_results)
|
79
100
|
end
|
80
101
|
end
|
81
102
|
|
82
103
|
def process_test_results(test_results)
|
83
|
-
|
104
|
+
applicable_tests = test_results.select { |test| test_is_applicable?(test) }
|
105
|
+
return if applicable_tests.empty?
|
106
|
+
|
107
|
+
process_issues_for_tests(applicable_tests) if issue_update_enabled
|
84
108
|
|
85
|
-
|
86
|
-
|
109
|
+
if gcs_enabled
|
110
|
+
tests_data = build_tests_data(applicable_tests)
|
111
|
+
test_results_filename = File.basename(test_results.path)
|
112
|
+
|
113
|
+
push_test_to_gcs(tests_data, test_results_filename)
|
114
|
+
end
|
87
115
|
|
88
|
-
|
116
|
+
Runtime::Logger.info " => Reported #{applicable_tests.size} #{problem_type} tests."
|
117
|
+
end
|
118
|
+
|
119
|
+
def process_issues_for_tests(tests)
|
120
|
+
tests.each do |test|
|
121
|
+
Runtime::Logger.info " => Processing issues for #{problem_type} test '#{test.name}'..."
|
122
|
+
issues = existing_test_health_issues_for_test(test)
|
123
|
+
create_or_update_test_health_issues(issues, test)
|
124
|
+
end
|
125
|
+
end
|
89
126
|
|
90
|
-
|
127
|
+
def existing_test_health_issues_for_test(test)
|
128
|
+
find_issues_by_hash(test_hash(test), state: 'opened', labels: search_labels)
|
129
|
+
end
|
91
130
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
131
|
+
def build_tests_data(tests)
|
132
|
+
tests.map do |test|
|
133
|
+
Runtime::Logger.info " => Building data for #{problem_type} test '#{test.name}'..."
|
134
|
+
issues = existing_test_health_issues_for_test(test)
|
135
|
+
test_datum(test, issues)
|
136
|
+
end
|
137
|
+
end
|
98
138
|
|
99
|
-
|
100
|
-
|
101
|
-
|
139
|
+
def create_or_update_test_health_issues(issues, test)
|
140
|
+
if issues.empty?
|
141
|
+
issues << create_issue(test)
|
142
|
+
else
|
143
|
+
# Keep issues description up-to-date
|
144
|
+
update_issues(issues, test)
|
102
145
|
end
|
103
146
|
|
104
|
-
|
147
|
+
update_reports(issues, test)
|
148
|
+
collect_issues(test, issues)
|
149
|
+
end
|
150
|
+
|
151
|
+
def push_test_to_gcs(tests_data, test_results_filename)
|
152
|
+
Runtime::Logger.info "will push the test data to GCS"
|
153
|
+
|
154
|
+
gcs_client(project_id: gcs_project_id, credentials: gcs_credentials, dry_run: dry_run).put_object(
|
155
|
+
gcs_bucket,
|
156
|
+
gcs_metrics_file_name(test_results_filename),
|
157
|
+
tests_data.to_json,
|
158
|
+
force: true,
|
159
|
+
content_type: 'application/json'
|
160
|
+
)
|
161
|
+
|
162
|
+
Runtime::Logger.info "Successfully pushed the #{gcs_metrics_file_name(test_results_filename)} file to the GCS bucket."
|
163
|
+
end
|
164
|
+
|
165
|
+
def gcs_metrics_file_name(test_results_filename)
|
166
|
+
today = Time.now.strftime('%Y-%m-%d')
|
167
|
+
|
168
|
+
"#{today}-#{test_results_filename}"
|
105
169
|
end
|
106
170
|
|
107
171
|
def update_reports(issues, test)
|
108
172
|
issues.each do |issue|
|
109
|
-
|
173
|
+
Runtime::Logger.info " => Reporting #{problem_type} test to existing issue: #{issue.web_url}"
|
110
174
|
add_report_to_issue(issue: issue, test: test, related_issues: (issues - [issue]))
|
111
175
|
end
|
112
176
|
end
|
@@ -162,6 +226,42 @@ module GitlabQuality
|
|
162
226
|
end
|
163
227
|
end
|
164
228
|
|
229
|
+
# We report a test if we have issue update and/or push to GCS enabled.
|
230
|
+
def test_reporting_enabled?
|
231
|
+
issue_update_enabled || gcs_enabled
|
232
|
+
end
|
233
|
+
|
234
|
+
# rubocop:disable Metrics/AbcSize
|
235
|
+
def test_datum(test, issues)
|
236
|
+
feature_category = test.feature_category.to_s.strip.empty? ? nil : test.feature_category
|
237
|
+
product_group = test.product_group.to_s.strip.empty? ? nil : test.product_group
|
238
|
+
|
239
|
+
if !product_group && feature_category
|
240
|
+
labels_inference = GitlabQuality::TestTooling::LabelsInference
|
241
|
+
product_group = labels_inference.new.product_group_from_feature_category(feature_category)
|
242
|
+
end
|
243
|
+
|
244
|
+
{
|
245
|
+
feature_category: feature_category,
|
246
|
+
created_at: Time.now.utc.iso8601,
|
247
|
+
description: test.name,
|
248
|
+
filename: test.file,
|
249
|
+
gitlab_project_id: Runtime::Env.ci_project_id,
|
250
|
+
product_group: product_group,
|
251
|
+
id: test.example_id,
|
252
|
+
hash: test_hash(test),
|
253
|
+
issue_url: issues.first&.web_url,
|
254
|
+
job_id: Runtime::Env.ci_job_id,
|
255
|
+
job_web_url: test.ci_job_url,
|
256
|
+
pipeline_id: Runtime::Env.ci_pipeline_id,
|
257
|
+
pipeline_ref: Runtime::Env.ci_commit_ref_name,
|
258
|
+
pipeline_web_url: Runtime::Env.ci_pipeline_url,
|
259
|
+
stacktrace: test.full_stacktrace,
|
260
|
+
test_level: test.level
|
261
|
+
}
|
262
|
+
end
|
263
|
+
# rubocop:enable Metrics/AbcSize
|
264
|
+
|
165
265
|
def found_label
|
166
266
|
if ENV.key?('CI_MERGE_REQUEST_IID')
|
167
267
|
FOUND_IN_MR_LABEL
|
@@ -17,7 +17,7 @@ module GitlabQuality
|
|
17
17
|
include Concerns::GroupAndCategoryLabels
|
18
18
|
|
19
19
|
NEW_ISSUE_LABELS = Set.new([
|
20
|
-
'test', 'automation
|
20
|
+
'test', 'automation:bot-authored', 'type::maintenance', 'maintenance::performance',
|
21
21
|
'priority::3', 'severity::3', 'knapsack_report'
|
22
22
|
]).freeze
|
23
23
|
SEARCH_LABELS = %w[test maintenance::performance knapsack_report].freeze
|
@@ -6,7 +6,7 @@ module GitlabQuality
|
|
6
6
|
module TestTooling
|
7
7
|
module Report
|
8
8
|
class PrepareStageReports
|
9
|
-
EXTRACT_STAGE_FROM_TEST_FILE_REGEX = %r{(?:api|browser_ui)/(?:[0-9]+_)?(?<stage>[
|
9
|
+
EXTRACT_STAGE_FROM_TEST_FILE_REGEX = %r{(?:api|browser_ui)/(?:[0-9]+_)?(?<stage>[\w]+)/}i
|
10
10
|
|
11
11
|
def initialize(input_files:)
|
12
12
|
@input_files = input_files
|
@@ -318,7 +318,7 @@ module GitlabQuality
|
|
318
318
|
initialize_gitlab_ops_client
|
319
319
|
|
320
320
|
if Runtime::Env.ci_pipeline_url.include?('ops.gitlab.net')
|
321
|
-
pipeline = ops_gitlab_client.find_pipeline(
|
321
|
+
pipeline = ops_gitlab_client.find_pipeline(Runtime::Env.ci_project_path, Runtime::Env.ci_pipeline_id.to_i)
|
322
322
|
generate_ops_gitlab_diff(pipeline)
|
323
323
|
else
|
324
324
|
pipeline = gitlab.find_pipeline(project, Runtime::Env.ci_pipeline_id.to_i)
|
@@ -11,19 +11,20 @@ module GitlabQuality
|
|
11
11
|
using Rainbow
|
12
12
|
|
13
13
|
ENV_VARIABLES = {
|
14
|
-
'GITLAB_QA_ISSUE_URL' => :qa_issue_url,
|
15
14
|
'CI_COMMIT_REF_NAME' => :ci_commit_ref_name,
|
15
|
+
'CI_JOB_ID' => :ci_job_id,
|
16
16
|
'CI_JOB_NAME' => :ci_job_name,
|
17
17
|
'CI_JOB_URL' => :ci_job_url,
|
18
|
-
'CI_PROJECT_ID' => :ci_project_id,
|
19
|
-
'CI_PROJECT_NAME' => :ci_project_name,
|
20
|
-
'CI_PROJECT_PATH' => :ci_project_path,
|
21
18
|
'CI_PIPELINE_ID' => :ci_pipeline_id,
|
22
19
|
'CI_PIPELINE_NAME' => :ci_pipeline_name,
|
23
20
|
'CI_PIPELINE_URL' => :ci_pipeline_url,
|
24
|
-
'
|
21
|
+
'CI_PROJECT_ID' => :ci_project_id,
|
22
|
+
'CI_PROJECT_NAME' => :ci_project_name,
|
23
|
+
'CI_PROJECT_PATH' => :ci_project_path,
|
25
24
|
'DEPLOY_VERSION' => :deploy_version,
|
26
|
-
'
|
25
|
+
'GITLAB_QA_ISSUE_URL' => :qa_issue_url,
|
26
|
+
'QA_GITLAB_CI_TOKEN' => :gitlab_ci_token,
|
27
|
+
'SLACK_QA_CHANNEL' => :slack_qa_channel
|
27
28
|
}.freeze
|
28
29
|
|
29
30
|
ENV_VARIABLES.each do |env_name, method_name|
|
@@ -56,6 +57,10 @@ module GitlabQuality
|
|
56
57
|
env_var_value_if_defined('GITLAB_API_BASE') || ci_api_v4_url
|
57
58
|
end
|
58
59
|
|
60
|
+
def gitlab_graphql_api_base
|
61
|
+
env_var_value_if_defined('GITLAB_GRAPHQL_API_BASE')
|
62
|
+
end
|
63
|
+
|
59
64
|
def pipeline_from_project_name
|
60
65
|
%w[gitlab gitaly].any? { |str| ci_project_name.to_s.start_with?(str) } ? default_branch : ci_project_name
|
61
66
|
end
|
@@ -12,11 +12,15 @@ module GitlabQuality
|
|
12
12
|
# @param project_id [String]
|
13
13
|
# @param credentials [String]
|
14
14
|
# @return [Fog::Storage::Google]
|
15
|
-
def gcs_client(project_id:, credentials:)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
def gcs_client(project_id:, credentials:, dry_run: false)
|
16
|
+
if dry_run
|
17
|
+
GCSMockClient.new
|
18
|
+
else
|
19
|
+
Fog::Storage::Google.new(
|
20
|
+
google_project: project_id || raise("Missing Google project_id"),
|
21
|
+
**gcs_creds(credentials)
|
22
|
+
)
|
23
|
+
end
|
20
24
|
end
|
21
25
|
|
22
26
|
# GCS Credentials
|
@@ -29,6 +33,15 @@ module GitlabQuality
|
|
29
33
|
|
30
34
|
{ google_json_key_string: json_key }
|
31
35
|
end
|
36
|
+
|
37
|
+
class GCSMockClient
|
38
|
+
def initialize(*_args, **_kwargs); end
|
39
|
+
|
40
|
+
def put_object(gcs_bucket, filename, data, **_kwargs)
|
41
|
+
Runtime::Logger.info "The #{filename} file would have been pushed to the #{gcs_bucket} bucket, with this content:"
|
42
|
+
Runtime::Logger.info data
|
43
|
+
end
|
44
|
+
end
|
32
45
|
end
|
33
46
|
end
|
34
47
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab_quality-test_tooling
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab Quality
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|
@@ -206,6 +206,20 @@ dependencies:
|
|
206
206
|
- - "~>"
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: 0.6.0
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: rest-client
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - "~>"
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: 2.1.0
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - "~>"
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: 2.1.0
|
209
223
|
- !ruby/object:Gem::Dependency
|
210
224
|
name: activesupport
|
211
225
|
requirement: !ruby/object:Gem::Requirement
|
@@ -417,6 +431,8 @@ executables:
|
|
417
431
|
- detect-infrastructure-failures
|
418
432
|
- existing-test-health-issue
|
419
433
|
- failed-test-issues
|
434
|
+
- feature-readiness-checklist
|
435
|
+
- feature-readiness-evaluation
|
420
436
|
- flaky-test-issues
|
421
437
|
- generate-test-session
|
422
438
|
- knapsack-report-issues
|
@@ -448,6 +464,8 @@ files:
|
|
448
464
|
- exe/detect-infrastructure-failures
|
449
465
|
- exe/existing-test-health-issue
|
450
466
|
- exe/failed-test-issues
|
467
|
+
- exe/feature-readiness-checklist
|
468
|
+
- exe/feature-readiness-evaluation
|
451
469
|
- exe/flaky-test-issues
|
452
470
|
- exe/generate-test-session
|
453
471
|
- exe/knapsack-report-issues
|
@@ -463,18 +481,29 @@ files:
|
|
463
481
|
- lib/gitlab_quality/test_tooling.rb
|
464
482
|
- lib/gitlab_quality/test_tooling/concerns/find_set_dri.rb
|
465
483
|
- lib/gitlab_quality/test_tooling/failed_jobs_table.rb
|
484
|
+
- lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_epic.rb
|
485
|
+
- lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_issue.rb
|
486
|
+
- lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_merge_request.rb
|
487
|
+
- lib/gitlab_quality/test_tooling/feature_readiness/concerns/issue_concern.rb
|
488
|
+
- lib/gitlab_quality/test_tooling/feature_readiness/concerns/work_item_concern.rb
|
489
|
+
- lib/gitlab_quality/test_tooling/feature_readiness/evaluation.rb
|
490
|
+
- lib/gitlab_quality/test_tooling/feature_readiness/operational_readiness_check.rb
|
466
491
|
- lib/gitlab_quality/test_tooling/gitlab_client/branches_client.rb
|
467
492
|
- lib/gitlab_quality/test_tooling/gitlab_client/branches_dry_client.rb
|
468
493
|
- lib/gitlab_quality/test_tooling/gitlab_client/commits_client.rb
|
469
494
|
- lib/gitlab_quality/test_tooling/gitlab_client/commits_dry_client.rb
|
470
495
|
- lib/gitlab_quality/test_tooling/gitlab_client/gitlab_client.rb
|
496
|
+
- lib/gitlab_quality/test_tooling/gitlab_client/gitlab_graphql_client.rb
|
471
497
|
- lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb
|
472
498
|
- lib/gitlab_quality/test_tooling/gitlab_client/issues_dry_client.rb
|
473
499
|
- lib/gitlab_quality/test_tooling/gitlab_client/job_client.rb
|
474
500
|
- lib/gitlab_quality/test_tooling/gitlab_client/jobs_client.rb
|
501
|
+
- lib/gitlab_quality/test_tooling/gitlab_client/labels_client.rb
|
475
502
|
- lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_client.rb
|
476
503
|
- lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_dry_client.rb
|
477
504
|
- lib/gitlab_quality/test_tooling/gitlab_client/repository_files_client.rb
|
505
|
+
- lib/gitlab_quality/test_tooling/gitlab_client/work_items_client.rb
|
506
|
+
- lib/gitlab_quality/test_tooling/gitlab_client/work_items_dry_client.rb
|
478
507
|
- lib/gitlab_quality/test_tooling/job_trace_analyzer.rb
|
479
508
|
- lib/gitlab_quality/test_tooling/knapsack_reports/spec_run_time.rb
|
480
509
|
- lib/gitlab_quality/test_tooling/knapsack_reports/spec_run_time_report.rb
|
@@ -484,6 +513,7 @@ files:
|
|
484
513
|
- lib/gitlab_quality/test_tooling/report/concerns/results_reporter.rb
|
485
514
|
- lib/gitlab_quality/test_tooling/report/concerns/utils.rb
|
486
515
|
- lib/gitlab_quality/test_tooling/report/failed_test_issue.rb
|
516
|
+
- lib/gitlab_quality/test_tooling/report/feature_readiness/report_on_epic.rb
|
487
517
|
- lib/gitlab_quality/test_tooling/report/flaky_test_issue.rb
|
488
518
|
- lib/gitlab_quality/test_tooling/report/generate_test_session.rb
|
489
519
|
- lib/gitlab_quality/test_tooling/report/health_problem_reporter.rb
|