gitlab_quality-test_tooling 1.8.0 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +8 -6
- data/exe/post-to-slack +25 -2
- data/lib/gitlab_quality/test_tooling/failed_jobs_table.rb +35 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/failed_jobs.rb +32 -0
- data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +2 -3
- data/lib/gitlab_quality/test_tooling/report/report_as_issue.rb +9 -0
- data/lib/gitlab_quality/test_tooling/report/slow_test_issue.rb +9 -1
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7c32e6b11eb72bf1f453827d3c8c0dd98a8d37788c22000566fac4fe9b0daab
|
4
|
+
data.tar.gz: 573ea110d2d1bea41f217f135f6dfe57a814ecc261142813e4dbbda17f813e0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7f5b809cd506c860edba93bc45c932183106da000b2eb14ae7149aa5d7a820502971fd5a26747664be1921334af3b1041a668fb128f8b4c739507ac63f850f2
|
7
|
+
data.tar.gz: 1a428ce49770515622c810bfaf2e918df2223b7ed23f38958d994de6bef4c591c5fd10efc7be071d1fdf6cde38eacc8598126b612fde62c300a01c0e9608c83a
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -130,11 +130,13 @@ Usage: exe/update-screenshot-paths [options]
|
|
130
130
|
|
131
131
|
```shell
|
132
132
|
Purpose: Create slow test issues from JSON RSpec report files
|
133
|
-
Usage: exe/slow-test-
|
133
|
+
Usage: exe/slow-test-issues [options]
|
134
134
|
-i, --input-files INPUT_FILES JSON RSpec report files JSON
|
135
135
|
-p, --project PROJECT Can be an integer or a group/project string
|
136
136
|
-t, --token TOKEN A valid access token with `api` scope and Maintainer permission in PROJECT
|
137
|
-
|
137
|
+
-r RELATED_ISSUES_FILE, The file path for the related issues
|
138
|
+
--related-issues-file
|
139
|
+
--dry-run Perform a dry-run (don't create issues)
|
138
140
|
-v, --version Show the version
|
139
141
|
-h, --help Show the usage
|
140
142
|
```
|
@@ -143,13 +145,13 @@ Usage: exe/slow-test-issue [options]
|
|
143
145
|
|
144
146
|
```shell
|
145
147
|
Purpose: Purpose: Create flaky test issues for any passed test coming from rspec-retry JSON report files
|
146
|
-
Usage: exe/flaky-test-
|
148
|
+
Usage: exe/flaky-test-issues [options]
|
147
149
|
-i, --input-files INPUT_FILES JSON rspec-retry report files
|
148
150
|
-p, --project PROJECT Can be an integer or a group/project string
|
149
151
|
-m MERGE_REQUEST_IID, An integer merge request IID
|
150
152
|
--merge_request_iid
|
151
153
|
-t, --token TOKEN A valid access token with `api` scope and Maintainer permission in PROJECT
|
152
|
-
--dry-run Perform a dry-run (don't create
|
154
|
+
--dry-run Perform a dry-run (don't create issues)
|
153
155
|
-v, --version Show the version
|
154
156
|
-h, --help Show the usage
|
155
157
|
```
|
@@ -160,11 +162,11 @@ Usage: exe/flaky-test-issue [options]
|
|
160
162
|
Purpose: Create slow test note on merge requests from JSON RSpec report files
|
161
163
|
Usage: exe/slow-test-merge-request-report-note [options]
|
162
164
|
-i, --input-files INPUT_FILES JSON RSpec report files JSON
|
163
|
-
|
165
|
+
-p, --project PROJECT Can be an integer or a group/project string
|
164
166
|
-m MERGE_REQUEST_IID, An integer merge request IID
|
165
167
|
--merge_request_iid
|
166
168
|
-t, --token TOKEN A valid access token with `api` scope and Maintainer permission in PROJECT
|
167
|
-
--dry-run Perform a dry-run (don't create
|
169
|
+
--dry-run Perform a dry-run (don't create note)
|
168
170
|
-v, --version Show the version
|
169
171
|
-h, --help Show the usage
|
170
172
|
```
|
data/exe/post-to-slack
CHANGED
@@ -8,6 +8,9 @@ require_relative "../lib/gitlab_quality/test_tooling"
|
|
8
8
|
|
9
9
|
params = {}
|
10
10
|
|
11
|
+
messages = []
|
12
|
+
gitlab_api_token = nil
|
13
|
+
|
11
14
|
options = OptionParser.new do |opts|
|
12
15
|
opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
13
16
|
|
@@ -15,16 +18,34 @@ options = OptionParser.new do |opts|
|
|
15
18
|
params[:slack_webhook_url] = slack_webhook_url
|
16
19
|
end
|
17
20
|
|
21
|
+
opts.on('-a', '--gitlab-api-token TOKEN', String, 'GitLab API token') do |token|
|
22
|
+
gitlab_api_token = token unless token.empty?
|
23
|
+
end
|
24
|
+
|
18
25
|
opts.on('-c', '--channel CHANNEL', String, 'Slack channel to post the message to') do |channel|
|
19
26
|
params[:channel] = channel
|
20
27
|
end
|
21
28
|
|
22
29
|
opts.on('-m', '--message MESSAGE', String, 'Post message to Slack') do |message|
|
23
|
-
|
30
|
+
messages << message
|
24
31
|
end
|
25
32
|
|
26
33
|
opts.on('-t', '--include-summary-table FILES', String, 'Add a test summary table based on RSpec report files (JUnit XML)') do |files|
|
27
|
-
|
34
|
+
messages << GitlabQuality::TestTooling::SummaryTable.create(input_files: files)
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('-j', '--include-failed-jobs-table', 'Add a list of failed jobs in the pipeline') do
|
38
|
+
next puts("Failed jobs table requires api token to be set via --gitlab-api-token option, skipping failed jobs table") unless gitlab_api_token
|
39
|
+
|
40
|
+
project_id = ENV['CI_PROJECT_ID']&.to_i || (next puts("CI_PROJECT_ID not set, skipping failed jobs table"))
|
41
|
+
pipeline_id = ENV['CI_PIPELINE_ID']&.to_i || (next puts("CI_PIPELINE_ID not set, skipping failed jobs table"))
|
42
|
+
|
43
|
+
jobs = GitlabQuality::TestTooling::GitlabClient::FailedJobs.new(token: gitlab_api_token, project_id: project_id, pipeline_id: pipeline_id).fetch
|
44
|
+
next if jobs.empty?
|
45
|
+
|
46
|
+
messages << GitlabQuality::TestTooling::FailedJobsTable.create(jobs: jobs)
|
47
|
+
rescue StandardError => e
|
48
|
+
puts "Failed to fetch failed jobs. #{e.class}: #{e.message}"
|
28
49
|
end
|
29
50
|
|
30
51
|
opts.on('-u', '--username USERNAME', String, 'Username to use for the Slack message') do |username|
|
@@ -50,6 +71,8 @@ options = OptionParser.new do |opts|
|
|
50
71
|
opts.parse(ARGV)
|
51
72
|
end
|
52
73
|
|
74
|
+
params[:message] = messages.join("\n\n")
|
75
|
+
|
53
76
|
if params.any?
|
54
77
|
GitlabQuality::TestTooling::Slack::PostToSlack.new(**params).invoke!
|
55
78
|
else
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'table_print'
|
4
|
+
|
5
|
+
module GitlabQuality
|
6
|
+
module TestTooling
|
7
|
+
module FailedJobsTable
|
8
|
+
class << self
|
9
|
+
# Create table with formatted list of failed jobs
|
10
|
+
#
|
11
|
+
# @param [Array<Gitlab::ObjectifiedHash>] jobs
|
12
|
+
# @return [String]
|
13
|
+
def create(jobs:)
|
14
|
+
"```\n#{TablePrint::Printer.table_print(collect_results(jobs))}\n```\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Format list of failed jobs
|
20
|
+
#
|
21
|
+
# @param [Array<Gitlab::ObjectifiedHash>] jobs
|
22
|
+
# @return [Array]
|
23
|
+
def collect_results(jobs)
|
24
|
+
jobs.sort_by(&:stage).map do |job|
|
25
|
+
{
|
26
|
+
"Job" => job.name,
|
27
|
+
"Stage" => job.stage,
|
28
|
+
"Failure Reason" => job.failure_reason
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gitlab'
|
4
|
+
|
5
|
+
module GitlabQuality
|
6
|
+
module TestTooling
|
7
|
+
module GitlabClient
|
8
|
+
class FailedJobs
|
9
|
+
def initialize(token:, project_id:, pipeline_id:)
|
10
|
+
@token = token
|
11
|
+
@project_id = project_id
|
12
|
+
@pipeline_id = pipeline_id
|
13
|
+
end
|
14
|
+
|
15
|
+
def fetch
|
16
|
+
client.pipeline_jobs(project_id, pipeline_id, scope: 'failed')
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :token, :project_id, :pipeline_id
|
22
|
+
|
23
|
+
def client
|
24
|
+
@client ||= Gitlab.client(
|
25
|
+
endpoint: Runtime::Env.gitlab_api_base,
|
26
|
+
private_token: token
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -167,9 +167,7 @@ module GitlabQuality
|
|
167
167
|
def pipeline_issues_with_similar_stacktrace(test)
|
168
168
|
search_labels = (base_issue_labels + Set.new(%w[test failure::new])).to_a
|
169
169
|
not_labels = exclude_labels_for_search.to_a
|
170
|
-
|
171
|
-
not: { labels: not_labels },
|
172
|
-
created_after: past_timestamp(2) }).select do |issue|
|
170
|
+
find_issues_created_after(past_timestamp(2), state: 'opened', labels: search_labels, not_labels: not_labels).select do |issue|
|
173
171
|
job_url_from_issue = failed_issue_job_url(issue)
|
174
172
|
|
175
173
|
next if pipeline != pipeline_env_from_job_url(job_url_from_issue)
|
@@ -196,6 +194,7 @@ module GitlabQuality
|
|
196
194
|
def failure_issues(test)
|
197
195
|
find_issues_for_test(
|
198
196
|
test,
|
197
|
+
state: 'opened',
|
199
198
|
labels: base_issue_labels + Set.new(%w[test]),
|
200
199
|
not_labels: exclude_labels_for_search
|
201
200
|
)
|
@@ -145,10 +145,19 @@ module GitlabQuality
|
|
145
145
|
def find_issues_for_test(test, labels:, not_labels: Set.new, state: nil)
|
146
146
|
search_options = { labels: labels.to_a, not: { labels: not_labels.to_a } }
|
147
147
|
search_options[:state] = state if state
|
148
|
+
search_options[:search] = test.file.to_s.empty? ? test.name : partial_file_path(test.file)
|
149
|
+
search_options[:in] = 'title'
|
148
150
|
|
149
151
|
gitlab.find_issues(options: search_options).find_all { |issue| issue_match_test?(issue, test) }
|
150
152
|
end
|
151
153
|
|
154
|
+
def find_issues_created_after(timestamp, labels:, not_labels: Set.new, state: nil)
|
155
|
+
search_options = { labels: labels.to_a, not: { labels: not_labels.to_a }, created_after: timestamp }
|
156
|
+
search_options[:state] = state if state
|
157
|
+
|
158
|
+
gitlab.find_issues(options: search_options)
|
159
|
+
end
|
160
|
+
|
152
161
|
def issue_match_test?(issue, test)
|
153
162
|
issue_title = issue.title.strip
|
154
163
|
test_file_path_found = !test.file.to_s.empty? && issue_title.include?(partial_file_path(test.file))
|
@@ -65,10 +65,18 @@ module GitlabQuality
|
|
65
65
|
"1. #{Time.new.utc.strftime('%F')}: #{test.ci_job_url} (#{ENV.fetch('CI_PIPELINE_URL', 'pipeline url is missing')})"
|
66
66
|
end
|
67
67
|
|
68
|
+
def slow_test_issues(test)
|
69
|
+
find_issues_for_test(
|
70
|
+
test,
|
71
|
+
state: 'opened',
|
72
|
+
labels: SEARCH_LABELS
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
68
76
|
def create_slow_issue(test)
|
69
77
|
puts " => Finding existing issues for slow test '#{test.name}' (run time: #{test.run_time} seconds)..."
|
70
78
|
|
71
|
-
issues =
|
79
|
+
issues = slow_test_issues(test)
|
72
80
|
|
73
81
|
if issues.blank?
|
74
82
|
issues << create_issue(test)
|
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: 1.
|
4
|
+
version: 1.9.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: 2023-
|
11
|
+
date: 2023-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|
@@ -383,6 +383,8 @@ files:
|
|
383
383
|
- exe/update-screenshot-paths
|
384
384
|
- lefthook.yml
|
385
385
|
- lib/gitlab_quality/test_tooling.rb
|
386
|
+
- lib/gitlab_quality/test_tooling/failed_jobs_table.rb
|
387
|
+
- lib/gitlab_quality/test_tooling/gitlab_client/failed_jobs.rb
|
386
388
|
- lib/gitlab_quality/test_tooling/gitlab_client/merge_request.rb
|
387
389
|
- lib/gitlab_quality/test_tooling/gitlab_client/merge_request_dry_client.rb
|
388
390
|
- lib/gitlab_quality/test_tooling/gitlab_issue_client.rb
|