gitlab_quality-test_tooling 1.8.0 → 1.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ee3407afd5274be75c1bb63c7f7aeeb0d2353cf62f0fb75c1393bd2103c8c1e
4
- data.tar.gz: a4965dd9d979a58c4202c6f25315d60e08d597d51322db491a077a0ae797cf34
3
+ metadata.gz: d7c32e6b11eb72bf1f453827d3c8c0dd98a8d37788c22000566fac4fe9b0daab
4
+ data.tar.gz: 573ea110d2d1bea41f217f135f6dfe57a814ecc261142813e4dbbda17f813e0e
5
5
  SHA512:
6
- metadata.gz: 1619807ec2456f02c9d4a1c09dfb0aeba598de7d51fe7a0377c1e5a9f3f9fc82cf607fa34387bb5ee1691ad28b5637e75c18c38ce068269c3bba76e3c902df22
7
- data.tar.gz: '0940057d402dfb8fbcd3a6d2d09f7ff698e26d140ebdda659fcc5347445574fa7f398b2bd91e8a39c872b0f8a9f368fa1f734e59e10262790ed2d507fb97e514'
6
+ metadata.gz: b7f5b809cd506c860edba93bc45c932183106da000b2eb14ae7149aa5d7a820502971fd5a26747664be1921334af3b1041a668fb128f8b4c739507ac63f850f2
7
+ data.tar.gz: 1a428ce49770515622c810bfaf2e918df2223b7ed23f38958d994de6bef4c591c5fd10efc7be071d1fdf6cde38eacc8598126b612fde62c300a01c0e9608c83a
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab_quality-test_tooling (1.8.0)
4
+ gitlab_quality-test_tooling (1.9.0)
5
5
  activesupport (>= 6.1, < 7.2)
6
6
  amatch (~> 0.4.1)
7
7
  gitlab (~> 4.19)
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-issue [options]
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
- --dry-run Perform a dry-run (don't create note)
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-issue [options]
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 note)
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
- --project PROJECT Can be an integer or a group/project string
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 issues)
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
- params[:message] = message
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
- params[:message] += "\n\n#{GitlabQuality::TestTooling::SummaryTable.create(input_files: files)}"
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
- gitlab.find_issues(options: { labels: search_labels,
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 = find_issues_for_test(test, labels: SEARCH_LABELS, state: 'opened')
79
+ issues = slow_test_issues(test)
72
80
 
73
81
  if issues.blank?
74
82
  issues << create_issue(test)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GitlabQuality
4
4
  module TestTooling
5
- VERSION = "1.8.0"
5
+ VERSION = "1.9.0"
6
6
  end
7
7
  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: 1.8.0
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-30 00:00:00.000000000 Z
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