gitlab_quality-test_tooling 1.14.0 → 1.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +73 -70
  3. data/exe/flaky-test-issues +4 -4
  4. data/lefthook.yml +13 -0
  5. data/lib/gitlab_quality/test_tooling/gitlab_client/branches_client.rb +1 -1
  6. data/lib/gitlab_quality/test_tooling/gitlab_client/commits_client.rb +6 -4
  7. data/lib/gitlab_quality/test_tooling/gitlab_client/gitlab_client.rb +12 -13
  8. data/lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb +6 -6
  9. data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_client.rb +6 -3
  10. data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_dry_client.rb +4 -2
  11. data/lib/gitlab_quality/test_tooling/report/concerns/issue_reports.rb +41 -28
  12. data/lib/gitlab_quality/test_tooling/report/concerns/utils.rb +1 -1
  13. data/lib/gitlab_quality/test_tooling/report/flaky_test_issue.rb +78 -43
  14. data/lib/gitlab_quality/test_tooling/report/generate_test_session.rb +1 -4
  15. data/lib/gitlab_quality/test_tooling/report/knapsack_report_issue.rb +0 -3
  16. data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +4 -8
  17. data/lib/gitlab_quality/test_tooling/report/report_as_issue.rb +5 -3
  18. data/lib/gitlab_quality/test_tooling/report/slow_test_issue.rb +71 -78
  19. data/lib/gitlab_quality/test_tooling/runtime/env.rb +5 -1
  20. data/lib/gitlab_quality/test_tooling/test_meta/processor/add_to_blocking_processor.rb +33 -16
  21. data/lib/gitlab_quality/test_tooling/test_meta/processor/add_to_quarantine_processor.rb +34 -19
  22. data/lib/gitlab_quality/test_tooling/test_meta/processor/meta_processor.rb +21 -0
  23. data/lib/gitlab_quality/test_tooling/test_meta/test_meta_updater.rb +70 -9
  24. data/lib/gitlab_quality/test_tooling/version.rb +1 -1
  25. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 970354551e0118a942865386f3e844762cf1fdfb06cfc02aa7bca602e55abc8f
4
- data.tar.gz: f512f45f80e5a3949b07a237c1f29c6bc73fcd8e320a92b6b3fcd0a23ccc311f
3
+ metadata.gz: b8709476aafe6dc96d2b49d1d74b18b2e3dbf6c37cb8ebb0acd631d05dbfe2ac
4
+ data.tar.gz: 26d4d54613a522014b5f3b93f38c0cb62027c2e209573727265a5c2b630a6d9d
5
5
  SHA512:
6
- metadata.gz: 639c54db934d3fb5ee5cc57264957a6ffff2714fff9dce6517701b7ff4e5ce74fca87a518f35b1e8d52852952d875f6b9aa45e79a506fbb9416d9712bba2ab95
7
- data.tar.gz: 1dbfcfab9f4161827ae2775a69f6203008cc4ea07dc23424a971c5451a354ebb28d7a698e922aecf8ca321a9612a05e5adf966c1bc0e38686c249aaa87ce6446
6
+ metadata.gz: 194294ffed245e88a96e35480485f98c1748a8417766cdca1cf737ece2b090a4e29efc444fa449f5f569e292333ba50954f7c07a72fbb2f9225c9195c4b6f411
7
+ data.tar.gz: 30cc7f3a5da1c15b5c3508e0850c007bddf9f22a85c9ec1361821be7bc330afd3869cd9fe74d3b424afc2d0ad2d180c4c0649b5f502f4740ca68f1b2c8119933
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab_quality-test_tooling (1.14.0)
5
- activesupport (>= 6.1, < 7.2)
4
+ gitlab_quality-test_tooling (1.17.0)
5
+ activesupport (>= 6.1, < 7.1)
6
6
  amatch (~> 0.4.1)
7
7
  gitlab (~> 4.19)
8
8
  http (~> 5.0)
@@ -21,15 +21,14 @@ GEM
21
21
  i18n (>= 1.6, < 2)
22
22
  minitest (>= 5.1)
23
23
  tzinfo (~> 2.0)
24
- zeitwerk (~> 2.3)
25
- addressable (2.8.4)
24
+ addressable (2.8.6)
26
25
  public_suffix (>= 2.0.2, < 6.0)
27
26
  amatch (0.4.1)
28
27
  mize
29
28
  tins (~> 1.0)
30
29
  ast (2.4.2)
31
30
  backport (1.2.0)
32
- benchmark (0.2.1)
31
+ benchmark (0.3.0)
33
32
  binding_of_caller (1.0.0)
34
33
  debug_inspector (>= 0.0.1)
35
34
  byebug (11.1.3)
@@ -41,23 +40,23 @@ GEM
41
40
  climate_control (1.2.0)
42
41
  coderay (1.1.3)
43
42
  colored2 (3.1.2)
44
- concurrent-ruby (1.2.2)
43
+ concurrent-ruby (1.2.3)
45
44
  cork (0.3.0)
46
45
  colored2 (~> 3.1)
47
46
  crack (0.4.5)
48
47
  rexml
49
- danger (9.3.0)
48
+ danger (9.4.2)
50
49
  claide (~> 1.0)
51
50
  claide-plugins (>= 0.9.2)
52
51
  colored2 (~> 3.1)
53
52
  cork (~> 0.1)
54
53
  faraday (>= 0.9.0, < 3.0)
55
54
  faraday-http-cache (~> 2.0)
56
- git (~> 1.13.0)
55
+ git (~> 1.13)
57
56
  kramdown (~> 2.3)
58
57
  kramdown-parser-gfm (~> 1.0)
59
58
  no_proxy_fix
60
- octokit (~> 5.0)
59
+ octokit (>= 4.0)
61
60
  terminal-table (>= 1, < 4)
62
61
  danger-gitlab (8.0.0)
63
62
  danger
@@ -65,37 +64,36 @@ GEM
65
64
  debug_inspector (1.2.0)
66
65
  diff-lcs (1.5.0)
67
66
  docile (1.4.0)
68
- domain_name (0.5.20190701)
69
- unf (>= 0.0.5, < 1.0.0)
67
+ domain_name (0.6.20240107)
70
68
  e2mmap (0.1.0)
71
- faraday (2.7.4)
72
- faraday-net_http (>= 2.0, < 3.1)
73
- ruby2_keywords (>= 0.0.4)
74
- faraday-http-cache (2.5.0)
69
+ faraday (2.9.0)
70
+ faraday-net_http (>= 2.0, < 3.2)
71
+ faraday-http-cache (2.5.1)
75
72
  faraday (>= 0.8)
76
- faraday-net_http (3.0.2)
77
- ffi (1.15.5)
73
+ faraday-net_http (3.1.0)
74
+ net-http
75
+ ffi (1.16.3)
78
76
  ffi-compiler (1.0.1)
79
77
  ffi (>= 1.0.0)
80
78
  rake
81
79
  formatador (1.1.0)
82
- git (1.13.2)
80
+ git (1.19.1)
83
81
  addressable (~> 2.8)
84
82
  rchardet (~> 1.8)
85
83
  gitlab (4.19.0)
86
84
  httparty (~> 0.20)
87
85
  terminal-table (>= 1.5.1)
88
- gitlab-dangerfiles (3.8.0)
86
+ gitlab-dangerfiles (3.13.0)
89
87
  danger (>= 8.4.5)
90
88
  danger-gitlab (>= 8.0.0)
91
89
  rake
92
- gitlab-styles (10.0.0)
93
- rubocop (~> 1.43.0)
90
+ gitlab-styles (10.1.0)
91
+ rubocop (~> 1.50.2)
94
92
  rubocop-graphql (~> 0.18)
95
93
  rubocop-performance (~> 1.15)
96
94
  rubocop-rails (~> 2.17)
97
- rubocop-rspec (~> 2.18)
98
- guard (2.18.0)
95
+ rubocop-rspec (~> 2.22)
96
+ guard (2.18.1)
99
97
  formatador (>= 0.2.4)
100
98
  listen (>= 2.7, < 4.0)
101
99
  lumberjack (>= 1.0.12, < 2.0)
@@ -109,7 +107,7 @@ GEM
109
107
  guard (~> 2.1)
110
108
  guard-compat (~> 1.1)
111
109
  rspec (>= 2.99.0, < 4.0)
112
- hashdiff (1.0.1)
110
+ hashdiff (1.1.0)
113
111
  http (5.1.1)
114
112
  addressable (~> 2.8)
115
113
  http-cookie (~> 1.0)
@@ -121,45 +119,48 @@ GEM
121
119
  httparty (0.21.0)
122
120
  mini_mime (>= 1.0.0)
123
121
  multi_xml (>= 0.5.2)
124
- i18n (1.13.0)
122
+ i18n (1.14.1)
125
123
  concurrent-ruby (~> 1.0)
126
- jaro_winkler (1.5.4)
127
- json (2.6.3)
124
+ jaro_winkler (1.5.6)
125
+ json (2.7.1)
128
126
  kramdown (2.4.0)
129
127
  rexml
130
128
  kramdown-parser-gfm (1.1.0)
131
129
  kramdown (~> 2.0)
132
- lefthook (1.3.11)
130
+ lefthook (1.6.1)
133
131
  listen (3.8.0)
134
132
  rb-fsevent (~> 0.10, >= 0.10.3)
135
133
  rb-inotify (~> 0.9, >= 0.9.10)
136
134
  llhttp-ffi (0.4.0)
137
135
  ffi-compiler (~> 1.0)
138
136
  rake (~> 13.0)
139
- lumberjack (1.2.8)
137
+ lumberjack (1.2.10)
140
138
  method_source (1.0.0)
141
- mini_mime (1.1.2)
142
- mini_portile2 (2.8.1)
143
- minitest (5.18.0)
139
+ mini_mime (1.1.5)
140
+ mini_portile2 (2.8.5)
141
+ minitest (5.21.2)
144
142
  mize (0.4.1)
145
143
  protocol (~> 2.0)
146
144
  multi_xml (0.6.0)
147
145
  nap (1.1.0)
148
146
  nenv (0.3.0)
147
+ net-http (0.4.1)
148
+ uri
149
149
  no_proxy_fix (0.1.2)
150
- nokogiri (1.14.3)
151
- mini_portile2 (~> 2.8.0)
150
+ nokogiri (1.16.0)
151
+ mini_portile2 (~> 2.8.2)
152
152
  racc (~> 1.4)
153
153
  notiffany (0.1.3)
154
154
  nenv (~> 0.1)
155
155
  shellany (~> 0.0)
156
- octokit (5.6.1)
156
+ octokit (8.0.0)
157
157
  faraday (>= 1, < 3)
158
158
  sawyer (~> 0.9)
159
159
  open4 (1.3.4)
160
- parallel (1.23.0)
161
- parser (3.2.2.1)
160
+ parallel (1.24.0)
161
+ parser (3.3.0.5)
162
162
  ast (~> 2.4.1)
163
+ racc
163
164
  proc_to_ast (0.1.0)
164
165
  coderay
165
166
  parser
@@ -172,20 +173,20 @@ GEM
172
173
  pry-byebug (3.10.1)
173
174
  byebug (~> 11.0)
174
175
  pry (>= 0.13, < 0.15)
175
- public_suffix (5.0.1)
176
- racc (1.6.2)
177
- rack (3.0.7)
176
+ public_suffix (5.0.4)
177
+ racc (1.7.3)
178
+ rack (3.0.8)
178
179
  rainbow (3.1.1)
179
- rake (13.0.6)
180
+ rake (13.1.0)
180
181
  rb-fsevent (0.11.2)
181
182
  rb-inotify (0.10.1)
182
183
  ffi (~> 1.0)
183
184
  rbs (2.8.4)
184
185
  rchardet (1.8.0)
185
- regexp_parser (2.8.0)
186
+ regexp_parser (2.9.0)
186
187
  reverse_markdown (2.1.1)
187
188
  nokogiri
188
- rexml (3.2.5)
189
+ rexml (3.2.6)
189
190
  rspec (3.12.0)
190
191
  rspec-core (~> 3.12.0)
191
192
  rspec-expectations (~> 3.12.0)
@@ -195,7 +196,7 @@ GEM
195
196
  rspec-expectations (3.12.3)
196
197
  diff-lcs (>= 1.2.0, < 2.0)
197
198
  rspec-support (~> 3.12.0)
198
- rspec-mocks (3.12.5)
199
+ rspec-mocks (3.12.6)
199
200
  diff-lcs (>= 1.2.0, < 2.0)
200
201
  rspec-support (~> 3.12.0)
201
202
  rspec-parameterized (1.0.0)
@@ -209,41 +210,45 @@ GEM
209
210
  rspec-parameterized-table_syntax (1.0.1)
210
211
  binding_of_caller
211
212
  rspec-parameterized-core (< 2)
212
- rspec-support (3.12.0)
213
- rubocop (1.43.0)
213
+ rspec-support (3.12.1)
214
+ rubocop (1.50.2)
214
215
  json (~> 2.3)
215
216
  parallel (~> 1.10)
216
217
  parser (>= 3.2.0.0)
217
218
  rainbow (>= 2.2.2, < 4.0)
218
219
  regexp_parser (>= 1.8, < 3.0)
219
220
  rexml (>= 3.2.5, < 4.0)
220
- rubocop-ast (>= 1.24.1, < 2.0)
221
+ rubocop-ast (>= 1.28.0, < 2.0)
221
222
  ruby-progressbar (~> 1.7)
222
223
  unicode-display_width (>= 2.4.0, < 3.0)
223
- rubocop-ast (1.28.0)
224
+ rubocop-ast (1.30.0)
224
225
  parser (>= 3.2.1.0)
225
- rubocop-capybara (2.18.0)
226
+ rubocop-capybara (2.20.0)
227
+ rubocop (~> 1.41)
228
+ rubocop-factory_bot (2.25.1)
226
229
  rubocop (~> 1.41)
227
230
  rubocop-graphql (0.19.0)
228
231
  rubocop (>= 0.87, < 2)
229
- rubocop-performance (1.17.1)
230
- rubocop (>= 1.7.0, < 2.0)
231
- rubocop-ast (>= 0.4.0)
232
- rubocop-rails (2.19.1)
232
+ rubocop-performance (1.20.2)
233
+ rubocop (>= 1.48.1, < 2.0)
234
+ rubocop-ast (>= 1.30.0, < 2.0)
235
+ rubocop-rails (2.23.1)
233
236
  activesupport (>= 4.2.0)
234
237
  rack (>= 1.1)
235
238
  rubocop (>= 1.33.0, < 2.0)
236
- rubocop-rspec (2.20.0)
237
- rubocop (~> 1.33)
239
+ rubocop-ast (>= 1.30.0, < 2.0)
240
+ rubocop-rspec (2.26.1)
241
+ rubocop (~> 1.40)
238
242
  rubocop-capybara (~> 2.17)
243
+ rubocop-factory_bot (~> 2.22)
239
244
  ruby-progressbar (1.13.0)
240
- ruby2_keywords (0.0.5)
241
- ruby_parser (3.20.3)
245
+ ruby_parser (3.21.0)
246
+ racc (~> 1.5)
242
247
  sexp_processor (~> 4.16)
243
248
  sawyer (0.9.2)
244
249
  addressable (>= 2.3.5)
245
250
  faraday (>= 0.17.3, < 3)
246
- sexp_processor (4.17.0)
251
+ sexp_processor (4.17.1)
247
252
  shellany (0.0.1)
248
253
  simplecov (0.22.0)
249
254
  docile (~> 1.1)
@@ -254,7 +259,7 @@ GEM
254
259
  simplecov (~> 0.19)
255
260
  simplecov-html (0.12.3)
256
261
  simplecov_json_formatter (0.1.4)
257
- solargraph (0.49.0)
262
+ solargraph (0.50.0)
258
263
  backport (~> 1.2)
259
264
  benchmark
260
265
  bundler (~> 2.0)
@@ -274,26 +279,24 @@ GEM
274
279
  table_print (1.5.7)
275
280
  terminal-table (3.0.2)
276
281
  unicode-display_width (>= 1.1.1, < 3)
277
- thor (1.2.1)
278
- tilt (2.1.0)
279
- timecop (0.9.6)
282
+ thor (1.3.0)
283
+ tilt (2.3.0)
284
+ timecop (0.9.8)
280
285
  tins (1.32.1)
281
286
  sync
282
287
  tzinfo (2.0.6)
283
288
  concurrent-ruby (~> 1.0)
284
- unf (0.1.4)
285
- unf_ext
286
- unf_ext (0.0.8.2)
287
- unicode-display_width (2.4.2)
288
- unparser (0.6.8)
289
+ unicode-display_width (2.5.0)
290
+ unparser (0.6.12)
289
291
  diff-lcs (~> 1.3)
290
- parser (>= 3.2.0)
292
+ parser (>= 3.2.2.4)
293
+ uri (0.13.0)
291
294
  webmock (3.7.0)
292
295
  addressable (>= 2.3.6)
293
296
  crack (>= 0.3.2)
294
297
  hashdiff (>= 0.4.0, < 2.0.0)
295
298
  yard (0.9.34)
296
- zeitwerk (2.6.7)
299
+ zeitwerk (2.6.12)
297
300
 
298
301
  PLATFORMS
299
302
  ruby
@@ -315,4 +318,4 @@ DEPENDENCIES
315
318
  webmock (= 3.7.0)
316
319
 
317
320
  BUNDLED WITH
318
- 2.4.2
321
+ 2.5.4
@@ -19,10 +19,6 @@ options = OptionParser.new do |opts|
19
19
  params[:project] = project
20
20
  end
21
21
 
22
- opts.on('-m', '--merge_request_iid MERGE_REQUEST_IID', String, 'An integer merge request IID') do |merge_request_iid|
23
- params[:merge_request_iid] = merge_request_iid
24
- end
25
-
26
22
  opts.on('--base-issue-labels BASE_ISSUE_LABELS', String,
27
23
  'Comma-separated labels (without tilde) to add to new flaky test issues') do |base_issue_labels|
28
24
  params[:base_issue_labels] = base_issue_labels.split(',')
@@ -32,6 +28,10 @@ options = OptionParser.new do |opts|
32
28
  params[:token] = token
33
29
  end
34
30
 
31
+ opts.on('-r', '--related-issues-file RELATED_ISSUES_FILE', String, 'The file path for the related issues') do |related_issues_file|
32
+ params[:related_issues_file] = related_issues_file
33
+ end
34
+
35
35
  opts.on('--dry-run', "Perform a dry-run (don't create issues)") do
36
36
  params[:dry_run] = true
37
37
  end
data/lefthook.yml CHANGED
@@ -13,3 +13,16 @@ pre-push:
13
13
  rubocop:
14
14
  run: bundle exec rubocop
15
15
  glob: '*.rb'
16
+
17
+ # Changelog git trailer for the first commit of the branch
18
+ changelog-on-first-commit:
19
+ run: |
20
+ first_commit_message=$(git log --format=%B -n 1 $(git log main..HEAD --pretty=format:"%h" | tail -1))
21
+ if ! echo ${first_commit_message} | grep "Changelog:"; then
22
+ echo Could not find a Changelog: git trailer on the first commit for this branch.
23
+ echo
24
+ echo Please add a trailer by amending the git commit message.
25
+ echo
26
+ echo See https://docs.gitlab.com/ee/development/changelog.html#overview for more info.
27
+ exit 1
28
+ fi
@@ -9,7 +9,7 @@ module GitlabQuality
9
9
  client.create_branch(project, branch_name, ref)
10
10
  end
11
11
 
12
- Runtime::Logger.debug("Created branch #{branch['name']} (#{branch['web_url']})")
12
+ Runtime::Logger.debug("Created branch #{branch['name']} (#{branch['web_url']})") if branch
13
13
  branch
14
14
  end
15
15
  end
@@ -5,11 +5,13 @@ module GitlabQuality
5
5
  module GitlabClient
6
6
  class CommitsClient < GitlabClient
7
7
  def create(branch_name, file_path, new_content, message)
8
- commit = client.create_commit(project, branch_name, message, [
9
- { action: :update, file_path: file_path, content: new_content }
10
- ])
8
+ commit = handle_gitlab_client_exceptions do
9
+ client.create_commit(project, branch_name, message, [
10
+ { action: :update, file_path: file_path, content: new_content }
11
+ ])
12
+ end
11
13
 
12
- Runtime::Logger.debug("Created commit #{commit['id']} (#{commit['web_url']}) on #{branch_name}")
14
+ Runtime::Logger.debug("Created commit #{commit['id']} (#{commit['web_url']}) on #{branch_name}") if commit
13
15
  commit
14
16
  end
15
17
  end
@@ -15,24 +15,32 @@ module GitlabQuality
15
15
  @retry_backoff = 0
16
16
  end
17
17
 
18
- def handle_gitlab_client_exceptions # rubocop:disable Metrics/AbcSize
18
+ def handle_gitlab_client_exceptions
19
19
  yield
20
20
  rescue Gitlab::Error::NotFound
21
21
  # This error could be raised in assert_user_permission!
22
22
  # If so, we want it to terminate at that point
23
23
  raise
24
24
  rescue SystemCallError, OpenSSL::SSL::SSLError, Net::OpenTimeout, Net::ReadTimeout,
25
- Gitlab::Error::InternalServerError, Gitlab::Error::Parsing => e
25
+ Gitlab::Error::InternalServerError, Gitlab::Error::BadRequest, Gitlab::Error::ResponseError, Gitlab::Error::Parsing => e
26
26
  @retry_backoff += RETRY_BACK_OFF_DELAY
27
27
 
28
28
  raise if @retry_backoff > RETRY_BACK_OFF_DELAY * MAX_RETRY_ATTEMPTS
29
29
 
30
- warn_exception(e)
30
+ warn("#{error.class.name} #{error.message}")
31
31
  warn("Sleeping for #{@retry_backoff} seconds before retrying...")
32
32
  sleep @retry_backoff
33
33
 
34
34
  retry
35
35
  rescue StandardError => e
36
+ post_exception_to_slack(e) if Runtime::Env.ci_commit_ref_name == Runtime::Env.default_branch
37
+
38
+ raise e
39
+ end
40
+
41
+ def post_exception_to_slack(error)
42
+ return unless ENV['CI_SLACK_WEBHOOK_URL']
43
+
36
44
  pipeline = Runtime::Env.pipeline_from_project_name
37
45
  channel = case pipeline
38
46
  when "canary"
@@ -42,9 +50,6 @@ module GitlabQuality
42
50
  else
43
51
  "qa-#{pipeline}"
44
52
  end
45
- error_msg = warn_exception(e)
46
-
47
- return unless Runtime::Env.ci_commit_ref_name == Runtime::Env.default_branch
48
53
 
49
54
  slack_options = {
50
55
  slack_webhook_url: ENV.fetch('CI_SLACK_WEBHOOK_URL', nil),
@@ -54,7 +59,7 @@ module GitlabQuality
54
59
  message: <<~MSG
55
60
  An unexpected error occurred while reporting test results in issues.
56
61
  The error occurred in job: #{Runtime::Env.ci_job_url}
57
- `#{error_msg}`
62
+ `#{error.class.name} #{error.message}`
58
63
  MSG
59
64
  }
60
65
  puts "Posting Slack message to channel: #{channel}"
@@ -79,12 +84,6 @@ module GitlabQuality
79
84
  private_token: token
80
85
  )
81
86
  end
82
-
83
- def warn_exception(error)
84
- error_msg = "#{error.class.name} #{error.message}"
85
- warn(error_msg)
86
- error_msg
87
- end
88
87
  end
89
88
  end
90
89
  end
@@ -48,6 +48,12 @@ module GitlabQuality
48
48
  end
49
49
  end
50
50
 
51
+ def find_issue_notes(iid:)
52
+ handle_gitlab_client_exceptions do
53
+ client.issue_notes(project, iid, order_by: 'created_at', sort: 'asc').auto_paginate
54
+ end
55
+ end
56
+
51
57
  def find_issue_discussions(iid:)
52
58
  handle_gitlab_client_exceptions do
53
59
  client.issue_discussions(project, iid, order_by: 'created_at', sort: 'asc').auto_paginate
@@ -75,12 +81,6 @@ module GitlabQuality
75
81
  end
76
82
  end
77
83
 
78
- def find_issue_notes(iid:)
79
- handle_gitlab_client_exceptions do
80
- client.issue_notes(project, iid, order_by: 'created_at', sort: 'asc')&.auto_paginate
81
- end
82
- end
83
-
84
84
  def create_issue_note(iid:, note:)
85
85
  handle_gitlab_client_exceptions do
86
86
  client.create_issue_note(project, iid, note)
@@ -5,10 +5,12 @@ module GitlabQuality
5
5
  module GitlabClient
6
6
  class MergeRequestsClient < GitlabClient
7
7
  def find_merge_request_changes(merge_request_iid:)
8
- client.merge_request_changes(project, merge_request_iid)
8
+ handle_gitlab_client_exceptions do
9
+ client.merge_request_changes(project, merge_request_iid)
10
+ end
9
11
  end
10
12
 
11
- def create_merge_request(title:, source_branch:, target_branch:, description:, labels:, assignee_id: nil)
13
+ def create_merge_request(title:, source_branch:, target_branch:, description:, labels:, assignee_id: nil, reviewer_ids: [])
12
14
  attrs = {
13
15
  source_branch: source_branch,
14
16
  target_branch: target_branch,
@@ -16,7 +18,8 @@ module GitlabQuality
16
18
  labels: labels,
17
19
  assignee_id: assignee_id,
18
20
  squash: true,
19
- remove_source_branch: true
21
+ remove_source_branch: true,
22
+ reviewer_ids: reviewer_ids
20
23
  }.compact
21
24
 
22
25
  merge_request = handle_gitlab_client_exceptions do
@@ -26,9 +26,11 @@ module GitlabQuality
26
26
  puts "The following note would have been updated id: #{id} with body: #{note} for mr_iid: #{merge_request_iid}"
27
27
  end
28
28
 
29
- def create_merge_request(title:, source_branch:, target_branch:, description:, labels:)
29
+ def create_merge_request(title:, source_branch:, target_branch:, description:, labels:, assignee_id:, reviewer_ids:)
30
30
  puts "A merge request would be created with title: #{title} " \
31
- "source_branch: #{source_branch} target_branch: #{target_branch} description: #{description} labels: #{labels}"
31
+ "source_branch: #{source_branch} target_branch: #{target_branch} " \
32
+ "description: #{description} labels: #{labels}, assignee_id: #{assignee_id}" \
33
+ "reviewer_ids: #{reviewer_ids}"
32
34
  end
33
35
  end
34
36
  end
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/object/blank'
4
+
3
5
  module GitlabQuality
4
6
  module TestTooling
5
7
  module Report
6
8
  module Concerns
7
9
  module IssueReports
8
- JOB_URL_REGEX = %r{(?<job_url>https://(?<host>[\w.]+)/(?<project_path>[\w\-./]+)/-/jobs/\d+)}
10
+ JOB_URL_REGEX = %r{(?<job_url>https://(?<host>[\w.]+)/(?<project_path>[\w\-./]+)/-/jobs/\d+)}
9
11
  FAILED_JOB_DESCRIPTION_REGEX = /First happened in #{JOB_URL_REGEX}\./m
10
- REPORT_ITEM_REGEX = /^1\. \d{4}-\d{2}-\d{2}: #{JOB_URL_REGEX} \((?<pipeline_url>.+)\)$/
12
+ REPORT_ITEM_REGEX = /^1\. \d{4}-\d{2}-\d{2}: #{JOB_URL_REGEX} \((?<pipeline_url>\S+)\)/
13
+ LATEST_REPORTS_TO_SHOW = 10
11
14
 
12
15
  def initial_reports_section(test)
13
16
  <<~REPORTS
@@ -17,22 +20,21 @@ module GitlabQuality
17
20
  REPORTS
18
21
  end
19
22
 
20
- def add_report_to_issue_description(issue, test)
21
- issue_description = issue.description
22
-
23
- # We include the number of reports in the header, for visibility.
24
- new_issue_description =
25
- if issue_description.include?('### Reports')
26
- # We count the number of existing reports.
27
- reports_count = issue_description
28
- .scan(REPORT_ITEM_REGEX)
29
- .size.to_i + 1
30
- issue_description.sub(/^### Reports.*$/, "### Reports (#{reports_count})")
31
- else # For issue with the legacy format, we add the Reports section
32
- update_legacy_issue_description(issue_description)
33
- end
23
+ def increment_reports(
24
+ current_reports_content:,
25
+ test:,
26
+ reports_section_header: '### Reports',
27
+ item_extra_content: nil,
28
+ reports_extra_content: nil)
29
+ preserved_content = current_reports_content.split(reports_section_header).first&.strip
30
+ reports = report_lines(current_reports_content) + [report_list_item(test, item_extra_content: item_extra_content)]
34
31
 
35
- [new_issue_description, report_list_item(test)].join("\n")
32
+ [
33
+ preserved_content,
34
+ "#{reports_section_header} (#{reports.size})",
35
+ reports_list(reports),
36
+ reports_extra_content
37
+ ].reject(&:blank?).compact.join("\n\n")
36
38
  end
37
39
 
38
40
  def failed_issue_job_url(issue)
@@ -49,8 +51,28 @@ module GitlabQuality
49
51
 
50
52
  private
51
53
 
52
- def report_list_item(test)
53
- "1. #{Time.new.utc.strftime('%F')}: #{test.ci_job_url} (#{ENV.fetch('CI_PIPELINE_URL', 'pipeline url is missing')})"
54
+ def report_lines(content)
55
+ content.lines.grep(REPORT_ITEM_REGEX).map(&:strip)
56
+ end
57
+
58
+ def reports_list(reports)
59
+ sorted_reports = reports.sort.reverse
60
+
61
+ if sorted_reports.size > LATEST_REPORTS_TO_SHOW
62
+ [
63
+ "Last 10 reports:",
64
+ sorted_reports[...LATEST_REPORTS_TO_SHOW].join("\n"),
65
+ "<details><summary>See #{sorted_reports.size - LATEST_REPORTS_TO_SHOW} more reports</summary>",
66
+ sorted_reports[LATEST_REPORTS_TO_SHOW..].join("\n"),
67
+ "</details>"
68
+ ].join("\n\n")
69
+ else
70
+ sorted_reports.join("\n")
71
+ end
72
+ end
73
+
74
+ def report_list_item(test, item_extra_content: nil)
75
+ "1. #{Time.new.utc.strftime('%F')}: #{test.ci_job_url} (#{ENV.fetch('CI_PIPELINE_URL', 'pipeline url is missing')}) #{item_extra_content}".strip
54
76
  end
55
77
 
56
78
  def job_urls_from_description(issue_description, regex)
@@ -60,15 +82,6 @@ module GitlabQuality
60
82
  end
61
83
  end
62
84
 
63
- def update_legacy_issue_description(issue_description)
64
- test_captures = issue_description.scan(JOB_URL_REGEX)
65
- reports_count = test_captures.size.to_i + 1
66
-
67
- updated_description = "#{issue_description}\n\n### Reports (#{reports_count})\n"
68
- updated_description = [updated_description, *test_captures_to_report_items(test_captures)].join("\n") unless test_captures.empty?
69
- updated_description
70
- end
71
-
72
85
  def test_captures_to_report_items(test_captures)
73
86
  test_captures.map do |ci_job_url, _, _|
74
87
  report_list_item(GitlabQuality::TestTooling::TestResult::JsonTestResult.new(
@@ -16,7 +16,7 @@ module GitlabQuality
16
16
  end
17
17
 
18
18
  def new_issue_title(test)
19
- "#{partial_file_path(test.file)} | #{search_safe(test.name)}".strip
19
+ "[Test] #{partial_file_path(test.file)} | #{search_safe(test.name)}".strip
20
20
  end
21
21
 
22
22
  def partial_file_path(path)