dri 0.4.0 → 0.6.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: 8718d907e41f0cfb472a255898ed513af0fe81ae4cfe782c7aa7d270cb0c2b9f
4
- data.tar.gz: 718f976b309b6eb0cab9f65b32e58de5876edd5507aafcc20a2f098068bb5929
3
+ metadata.gz: 9f99fb005dc6cbc12b4fe9b8b131680c09474c125d7fe396fa3b0fadd8804dba
4
+ data.tar.gz: dfa14e8f527bfed0d17a428d81ac16955251cba3ef19907016a19bc43901e768
5
5
  SHA512:
6
- metadata.gz: 643631388b378a127bca182911c9dfb304a1134b568fb71c716976f637f31f2a8d5056c2e30f0f9e2a72e7528e9e7f58a0bda4c0cbc1066efab02c3159353777
7
- data.tar.gz: 46514571671417008fe79dadc26cee33b97ad642e61822e2a2097f4e83d774290d1389f3d7d5c6a8ddf7be7a1974d733b4f5cacef3e7c0084af16a37db30ee0d
6
+ metadata.gz: f7e619cb7241e804810b8cdd97759df84c98aada62c975e0d39fa9f9125219846f5e8bddb50624ce22cc1f836006b023d49375f0a17e298d0f0086d220390e87
7
+ data.tar.gz: '09e94ad8bc08ffb95839e01456703e81f8ed262c7c925beadeb21d71a816f6632b5772b91af5700901331c5e7560af56b1398ed41ac6dc04d884f61b6ab6043b'
data/.gitignore CHANGED
@@ -13,4 +13,5 @@
13
13
 
14
14
  .dri_profile.yml
15
15
  handover_reports/*
16
+ analyze_reports/*
16
17
  .idea/
data/.gitlab-ci.yml CHANGED
@@ -1,10 +1,10 @@
1
1
  .job_base:
2
- image: ruby:2.7
2
+ image: registry.gitlab.com/gitlab-org/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3
3
3
  variables:
4
4
  BUNDLE_PATH: vendor/bundle
5
5
  BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES: "true"
6
+ BUNDLE_FROZEN: "true"
6
7
  before_script:
7
- - gem install bundler -v 2.3.9 --no-document
8
8
  - bundle install
9
9
  cache:
10
10
  key:
@@ -13,26 +13,34 @@
13
13
  - Gemfile.lock
14
14
  paths:
15
15
  - vendor/bundle
16
+ policy: pull
16
17
  rules:
17
18
  - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
18
19
  - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
19
20
 
20
21
  include:
21
22
  - project: gitlab-org/quality/pipeline-common
22
- ref: 0.3.4
23
+ ref: 0.15.1
23
24
  file:
24
25
  - /ci/gem-release.yml
26
+ - /ci/ref-update.gitlab-ci.yml
27
+
28
+ variables:
29
+ RUBY_VERSION: "2.7"
25
30
 
26
31
  stages:
27
32
  - build
28
33
  - test
29
34
  - deploy
35
+ - update
30
36
 
31
37
  build_gem:
32
38
  stage: build
33
39
  extends: .job_base
34
40
  script:
35
41
  - gem build
42
+ cache:
43
+ policy: pull-push
36
44
 
37
45
  rubocop:
38
46
  stage: test
@@ -43,13 +51,8 @@ rubocop:
43
51
  rspec:
44
52
  stage: test
45
53
  extends: .job_base
54
+ parallel:
55
+ matrix:
56
+ - RUBY_VERSION: ['2.7', '3.0']
46
57
  script:
47
- - bundle exec rspec --color
48
-
49
- rspec 3.0:
50
- stage: test
51
- extends: .job_base
52
- image: ruby:3.0
53
- script:
54
- - bundle exec rspec --color
55
-
58
+ - bundle exec rspec --force-color
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.0.2
1
+ ruby 3.0.4
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dri (0.3.1)
4
+ dri (0.6.0)
5
+ amatch (~> 0.4.1)
5
6
  gitlab (~> 4.18)
6
7
  httparty (~> 0.20.0)
7
8
  json (~> 2.6.1)
@@ -27,14 +28,17 @@ GEM
27
28
  tzinfo (~> 2.0)
28
29
  addressable (2.8.0)
29
30
  public_suffix (>= 2.0.2, < 5.0)
31
+ amatch (0.4.1)
32
+ mize
33
+ tins (~> 1.0)
30
34
  ast (2.4.2)
31
35
  coderay (1.1.3)
32
36
  concurrent-ruby (1.1.10)
33
37
  crack (0.4.5)
34
38
  rexml
35
39
  diff-lcs (1.5.0)
36
- gitlab (4.18.0)
37
- httparty (~> 0.18)
40
+ gitlab (4.19.0)
41
+ httparty (~> 0.20)
38
42
  terminal-table (>= 1.5.1)
39
43
  gitlab-styles (7.0.0)
40
44
  rubocop (~> 0.91, >= 0.91.1)
@@ -56,12 +60,16 @@ GEM
56
60
  mime-types-data (~> 3.2015)
57
61
  mime-types-data (3.2022.0105)
58
62
  minitest (5.15.0)
63
+ mize (0.4.0)
64
+ protocol (~> 2.0)
59
65
  multi_xml (0.6.0)
60
66
  parallel (1.22.1)
61
67
  parser (3.1.1.0)
62
68
  ast (~> 2.4.1)
63
69
  pastel (0.8.0)
64
70
  tty-color (~> 0.5)
71
+ protocol (2.0.0)
72
+ ruby_parser (~> 3.0)
65
73
  pry (0.14.1)
66
74
  coderay (~> 1.1)
67
75
  method_source (~> 1.0)
@@ -110,15 +118,21 @@ GEM
110
118
  rubocop (~> 0.87)
111
119
  rubocop-ast (>= 0.7.1)
112
120
  ruby-progressbar (1.11.0)
121
+ ruby_parser (3.19.1)
122
+ sexp_processor (~> 4.16)
123
+ sexp_processor (4.16.1)
113
124
  strings (0.2.1)
114
125
  strings-ansi (~> 0.2)
115
126
  unicode-display_width (>= 1.5, < 3.0)
116
127
  unicode_utils (~> 1.4)
117
128
  strings-ansi (0.2.0)
129
+ sync (0.5.0)
118
130
  terminal-table (3.0.2)
119
131
  unicode-display_width (>= 1.1.1, < 3)
120
132
  thor (1.0.1)
121
133
  timecop (0.9.5)
134
+ tins (1.31.1)
135
+ sync
122
136
  tty-box (0.7.0)
123
137
  pastel (~> 0.8)
124
138
  strings (~> 0.2.0)
@@ -162,10 +176,10 @@ DEPENDENCIES
162
176
  dri!
163
177
  gitlab-styles (~> 7.0.0)
164
178
  pry (~> 0.14.1)
165
- rake
179
+ rake (~> 13.0)
166
180
  rspec (~> 3.10.0)
167
181
  timecop (~> 0.9.1)
168
182
  webmock (~> 3.5)
169
183
 
170
184
  BUNDLED WITH
171
- 2.3.9
185
+ 2.3.16
data/README.md CHANGED
@@ -66,7 +66,9 @@ $ dri profile
66
66
  - profile
67
67
  - reports
68
68
  - [6. incidents](#6-incidents)
69
- - [7. version](#7-version)
69
+ - [7. analyze](#7-analyze)
70
+ - stacktraces
71
+ - [8. version](#8-version)
70
72
 
71
73
  #### 1. init
72
74
 
@@ -123,12 +125,19 @@ $ dri fetch dequarantines
123
125
 
124
126
  Fetches open dequarantine Merge Requests to be reviewed
125
127
 
128
+ Results are organized by environment (production, staging, staging ref and preprod).
126
129
  ```shell
127
130
  $ dri fetch featureflags
128
131
  ```
129
132
 
130
133
  Fetches a list of today's feature flag changes, including the date and time in UTC of when the change occurred as well as a link to the corresponding issue from the feature-flag-log project.
131
- Results are organized by environment (production, staging, staging ref and preprod).
134
+
135
+ ```shell
136
+ $ dri fetch pipelines
137
+ ```
138
+
139
+ Fetches a table containing last executed pipeline and its test report link for all monitored pipelines.
140
+ The timestamps are in UTC
132
141
 
133
142
  #### 4. publish
134
143
 
@@ -145,6 +154,7 @@ $ dri publish report --format=list # formats the report in a list
145
154
  $ dri publish report --format=table # formats the report in a table (default)
146
155
  $ dri publish report --dry-run # the report is only generated locally
147
156
  $ dri publish report --actions # activate the actions prompt for each failure
157
+ $ dri publish report --feature-flags # includes a summary of the feature flag changes on each environment
148
158
  ```
149
159
 
150
160
  **Note:** These options above can be combined like:
@@ -190,7 +200,17 @@ $ dri incidents
190
200
 
191
201
  Have a quick look at currently active/mitigated incidents on GitLab services.
192
202
 
193
- #### 7. version
203
+ #### 7. analyze
204
+
205
+ ```shell
206
+ $ dri analyze stacktraces
207
+ ```
208
+ Searches through any open test failure issues and publishes a report that identifies
209
+ issues that have similar stack traces.
210
+ This may be useful to identify situations where a common test failure is presenting
211
+ itself across multiple individual test cases, over a period of time.
212
+
213
+ #### 8. version
194
214
 
195
215
  ```shell
196
216
  $ dri version
data/dri.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
24
 
25
+ spec.add_dependency 'amatch', '~> 0.4.1'
25
26
  spec.add_dependency "gitlab", "~> 4.18"
26
27
  spec.add_dependency 'httparty', '~> 0.20.0'
27
28
  spec.add_dependency 'json', '~> 2.6.1'
@@ -39,7 +40,7 @@ Gem::Specification.new do |spec|
39
40
 
40
41
  spec.add_development_dependency 'gitlab-styles', '~> 7.0.0'
41
42
  spec.add_development_dependency "pry", "~> 0.14.1"
42
- spec.add_development_dependency 'rake'
43
+ spec.add_development_dependency 'rake', "~> 13.0"
43
44
  spec.add_development_dependency 'rspec', '~> 3.10.0'
44
45
  spec.add_development_dependency "timecop", "~> 0.9.1"
45
46
  spec.add_development_dependency 'webmock', '~> 3.5'
@@ -3,20 +3,28 @@
3
3
  require "httparty"
4
4
  require "json"
5
5
  require "tty-config"
6
- require 'cgi'
7
- require 'gitlab'
6
+ require "cgi"
7
+ require "gitlab"
8
8
 
9
9
  module Dri
10
- class ApiClient
11
- API_URL = 'https://gitlab.com/api/v4'
12
- TESTCASES_PROJECT_ID = '11229385'
13
- TRIAGE_PROJECT_ID = '15291320'
14
- GITLAB_PROJECT_ID = '278964'
15
- FEATURE_FLAG_LOG_PROJECT_ID = '15208716'
16
- INFRA_TEAM_PROD_PROJECT_ID = '7444821'
17
-
18
- def initialize(config)
10
+ TokenNotProvidedError = Class.new(StandardError)
11
+ class ApiClient # rubocop:disable Metrics/ClassLength
12
+ API_URL = "https://gitlab.com/api/v4"
13
+ OPS_API_URL = "https://ops.gitlab.net/api/v4"
14
+ TESTCASES_PROJECT_ID = "11229385"
15
+ TRIAGE_PROJECT_ID = "15291320"
16
+ GITLAB_PROJECT_ID = "278964"
17
+ FEATURE_FLAG_LOG_PROJECT_ID = "15208716"
18
+ INFRA_TEAM_PROD_PROJECT_ID = "7444821"
19
+ def initialize(config, ops = false)
19
20
  @token = config.read.dig("settings", "token")
21
+ @ops_token = config.read.dig("settings", "ops_token")
22
+ if @token.nil? || @ops_token.nil?
23
+ raise TokenNotProvidedError, "Gitlab API client cannot be initialized without both access tokens. " \
24
+ "Run dri init again or dri profile --edit"
25
+ end
26
+
27
+ @ops_instance = ops
20
28
  end
21
29
 
22
30
  # Fetch triaged failures
@@ -57,6 +65,18 @@ module Dri
57
65
  ).auto_paginate
58
66
  end
59
67
 
68
+ # Fetch issues related to failing test cases
69
+ #
70
+ # @return [Array<Gitlab::ObjectifiedHash>]
71
+ def fetch_test_failure_issues(labels: 'failure::new')
72
+ gitlab.issues(
73
+ GITLAB_PROJECT_ID,
74
+ labels: labels,
75
+ state: 'opened',
76
+ scope: "all"
77
+ ).auto_paginate
78
+ end
79
+
60
80
  # Fetch related issue mrs
61
81
  #
62
82
  # @param [Integer] issue_iid
@@ -149,18 +169,73 @@ module Dri
149
169
  gitlab.issues(INFRA_TEAM_PROD_PROJECT_ID, order_by: "updated_at", state: "opened", labels: "incident")
150
170
  end
151
171
 
172
+ # Fetch pipelines
173
+ #
174
+ # @param [Integer] project_id
175
+ # @return [Array<Gitlab::ObjectifiedHash>]
176
+ def pipelines(project_id:, options:, auto_paginate: false)
177
+ if auto_paginate
178
+ gitlab.pipelines(project_id, options).auto_paginate
179
+ else
180
+ gitlab.pipelines(project_id, options)
181
+ end
182
+ end
183
+
184
+ # Fetch single pipeline
185
+ #
186
+ # @param [Integer] project_id
187
+ # @param [Integer] pipeline_id
188
+ # @return [<Gitlab::ObjectifiedHash>]
189
+ def pipeline(project_id, pipeline_id)
190
+ gitlab.pipeline(project_id, pipeline_id)
191
+ end
192
+
193
+ # Fetch test report from a pipeline
194
+ #
195
+ # @param [Integer] project_id
196
+ # @param [Integer] pipeline_id
197
+ # @return [<Gitlab::ObjectifiedHash>]
198
+ def pipeline_test_report(project_id, pipeline_id)
199
+ gitlab.pipeline_test_report(project_id, pipeline_id)
200
+ end
201
+
202
+ # Fetch pipeline bridges/downstream pipelines
203
+ #
204
+ # @param [Integer] project_id
205
+ # @param [Integer] pipeline_id
206
+ # @return [Array<Gitlab::ObjectifiedHash>]
207
+ def pipeline_bridges(project_id, pipeline_id, options = {})
208
+ gitlab.pipeline_bridges(project_id, pipeline_id, options).auto_paginate
209
+ end
210
+
211
+ # Fetch jobs from a pipeline
212
+ #
213
+ # @param [Integer] project_id
214
+ # @param [Integer] pipeline_id
215
+ # @return [Array<Gitlab::ObjectifiedHash>]
216
+ def pipeline_jobs(project_id, pipeline_id, options = {})
217
+ gitlab.pipeline_jobs(project_id, pipeline_id, options).auto_paginate
218
+ end
219
+
152
220
  private
153
221
 
154
- attr_reader :token
222
+ attr_reader :token, :ops_token
155
223
 
156
224
  # Gitlab client
157
225
  #
158
226
  # @return [Gitlab::Client]
159
227
  def gitlab
160
- @gitlab ||= Gitlab.client(
161
- endpoint: API_URL,
162
- private_token: token
163
- )
228
+ if @ops_instance
229
+ @ops_client ||= Gitlab.client(
230
+ endpoint: OPS_API_URL,
231
+ private_token: ops_token
232
+ )
233
+ else
234
+ @gitlab_client ||= Gitlab.client(
235
+ endpoint: API_URL,
236
+ private_token: token
237
+ )
238
+ end
164
239
  end
165
240
  end
166
241
  end
data/lib/dri/cli.rb CHANGED
@@ -82,5 +82,8 @@ module Dri
82
82
 
83
83
  require_relative 'commands/publish'
84
84
  register Dri::Commands::Publish, 'publish', 'publish [SUBCOMMAND]', 'Publish report for handover'
85
+
86
+ require_relative 'commands/analyze'
87
+ register Dri::Commands::Analyze, 'analyze', 'analyze [SUBCOMMAND]', 'Analysis of test failures and issues'
85
88
  end
86
89
  end
data/lib/dri/command.rb CHANGED
@@ -26,14 +26,14 @@ module Dri
26
26
  config = TTY::Config.new
27
27
  config.filename = ".dri_profile"
28
28
  config.extname = ".yml"
29
- config.append_path Dir.pwd
30
29
  config.append_path Dir.home
30
+ config.append_path Dir.pwd
31
31
  config
32
32
  end
33
33
  end
34
34
 
35
- def api_client
36
- ApiClient.new(config)
35
+ def api_client(ops: false)
36
+ ApiClient.new(config, ops)
37
37
  end
38
38
 
39
39
  def profile
@@ -52,6 +52,10 @@ module Dri
52
52
  @token ||= profile["settings"]["token"]
53
53
  end
54
54
 
55
+ def ops_token
56
+ @ops_token ||= profile["settings"]["ops_token"]
57
+ end
58
+
55
59
  def timezone
56
60
  @timezone ||= profile["settings"]["timezone"]
57
61
  end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+ require_relative '../../utils/table'
5
+ require 'amatch'
6
+ require 'fileutils'
7
+
8
+ module Dri
9
+ module Commands
10
+ class Analyze
11
+ class StackTraces < Dri::Command
12
+ include Amatch
13
+ include Dri::Utils::Table
14
+
15
+ def initialize(options)
16
+ @options = options
17
+ @labels = options[:labels] || 'failure::new'
18
+ @similarity_score_threshold = options[:similarity_score_threshold] || 0.9
19
+ end
20
+
21
+ def execute(input: $stdin, output: $stdout) # rubocop:disable Metrics/AbcSize
22
+ verify_config_exists
23
+ logger.info "#{Time.now.utc} Fetching issues"
24
+
25
+ data = []
26
+
27
+ spinner.run do
28
+ response = api_client.fetch_test_failure_issues(labels: @labels)
29
+ logger.info "#{Time.now.utc} API response completed"
30
+
31
+ if response.empty?
32
+ logger.info 'There are no failure::new issues identified!'
33
+ exit 0
34
+ end
35
+
36
+ data = identify_similar_issues(response)
37
+
38
+ logger.info "#{Time.now.utc} Processing Data Complete"
39
+ end
40
+
41
+ similar_stack_traces = data.each_with_object([]) do |item, similar_items|
42
+ next if item[:stack_trace].empty?
43
+ next if item[:related_errors].length < 2
44
+
45
+ similar_items << [item[:stack_trace], item[:related_errors]]
46
+ end
47
+
48
+ FileUtils.mkdir_p("#{Dir.pwd}/analyze_reports/stacktraces")
49
+ report_path = "analyze_reports/stacktraces/report-#{Time.now.utc.to_i}.md"
50
+ write_report(similar_stack_traces, report_path)
51
+ logger.success "Analyze StackTraces report is ready at: #{report_path}"
52
+
53
+ errors_count = similar_stack_traces.count
54
+ issues_count = similar_stack_traces.sum { |st| st[1].size }
55
+ output.puts "Found #{errors_count} common errors across a combination of #{issues_count} issues"
56
+ end
57
+
58
+ private
59
+
60
+ def write_report(similar_stack_traces, report_path)
61
+ File.open(report_path, 'a') do |out_file|
62
+ out_file.puts "## Stack Trace Analysis"
63
+ similar_stack_traces.each do |st|
64
+ out_file.puts "### StackTrace"
65
+ out_file.puts st[0]
66
+ out_file.puts "### Related issues"
67
+ st[1].each { |err| out_file.puts "* #{err}" }
68
+ out_file.puts "-" * 80
69
+ end
70
+ end
71
+ end
72
+
73
+ def identify_similar_issues(response)
74
+ data = []
75
+ response.each do |item|
76
+ stack_trace = extract_stack_trace(item['description'])
77
+
78
+ data.find(-> { add_stack_trace(data, stack_trace) }) do |row|
79
+ calc_similarity(row[:stack_trace], stack_trace) >= @similarity_score_threshold
80
+ end[:related_errors].append(item['web_url'])
81
+ end
82
+ data
83
+ end
84
+
85
+ def add_stack_trace(data, st)
86
+ obj = { stack_trace: st, related_errors: [] }
87
+ data.append(obj)
88
+ obj
89
+ end
90
+
91
+ def extract_stack_trace(description)
92
+ stack_trace = description[/```(.*)```/m]
93
+ # Remove common patterns that may impact matching
94
+ stack_trace&.gsub!(/^*Correlation Id: \S*$/, '')
95
+ stack_trace&.gsub!(/^*Sentry Url: \S*$/, '')
96
+ stack_trace&.gsub!(/^*Kibana Url: \S*$/, '')
97
+ stack_trace || ''
98
+ end
99
+
100
+ def calc_similarity(s1, s2)
101
+ JaroWinkler.new(s1).match(s2)
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Dri
6
+ module Commands
7
+ class Analyze < Thor
8
+ namespace :analyze
9
+
10
+ desc 'stacktraces', 'Identify commonalities and patterns between stack traces'
11
+ method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
12
+ def stacktraces(*)
13
+ return invoke :help, %w[stacktraces] if options[:help]
14
+
15
+ require_relative 'analyze/stack_traces'
16
+ Dri::Commands::Analyze::StackTraces.new(options).execute
17
+ end
18
+ end
19
+ end
20
+ end
@@ -13,8 +13,9 @@ module Dri
13
13
  SORT_BY_OPTIONS = {
14
14
  title: 0,
15
15
  triaged: 1,
16
- author: 2,
17
- url: 3
16
+ environment: 2,
17
+ author: 3,
18
+ url: 4
18
19
  }.freeze
19
20
 
20
21
  def initialize(options)
@@ -25,14 +26,16 @@ module Dri
25
26
  def execute(input: $stdin, output: $stdout) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
26
27
  verify_config_exists
27
28
 
29
+ urgent_environments = %w[canary canary.staging]
30
+
28
31
  title = add_color('Title', :bright_yellow)
29
32
  triaged = add_color('Triaged?', :bright_yellow)
33
+ environment = add_color('Environment', :bright_yellow)
30
34
  author = add_color('Author', :bright_yellow)
31
35
  url = add_color('URL', :bright_yellow)
32
36
 
33
37
  failures = []
34
- urgent = []
35
- labels = [title, triaged, author, url]
38
+ labels = [title, triaged, environment, author, url]
36
39
  triaged_counter = 0
37
40
 
38
41
  logger.info "Fetching today's failures..."
@@ -50,6 +53,12 @@ module Dri
50
53
  author = failure.to_h.dig('author', 'username')
51
54
  url = failure.web_url
52
55
  triaged = add_color('x', :red)
56
+ envs = failure.labels.select { |l| l.include?('found:') }.map do |l|
57
+ env = l.split(':').last.gsub('.gitlab.com', '')
58
+
59
+ env == 'gitlab.com' ? 'production' : env
60
+ end
61
+ urgent = urgent_environments.all? { |env| envs.include?(env) }
53
62
 
54
63
  emoji_awards = api_client.fetch_awarded_emojis(failure.iid).find do |e|
55
64
  e.name == emoji && e.to_h.dig('user', 'username') == username
@@ -61,32 +70,27 @@ module Dri
61
70
  end
62
71
 
63
72
  if @options[:urgent]
64
- labels = failure.labels
65
-
66
- labels.each do |label|
67
- if label.include?('found:canary.gitlab.com' && 'found:canary.staging.gitlab.com')
68
- urgent << [title, triaged, author, url]
69
- end
70
- end
73
+ failures << [title, triaged, envs.first, author, url] if urgent
74
+ else
75
+ failures << [title, triaged, envs.first, author, url]
71
76
  end
72
-
73
- failures << [title, triaged, author, url]
74
-
75
- failures.sort_by! { |e| e[SORT_BY_OPTIONS[@options[:sort_by].to_sym]] } if @options[:sort_by]
76
77
  end
77
- end
78
78
 
79
- if @options[:urgent]
80
- print_table(labels, urgent, alignments: [:left, :center, :center, :left])
81
- output.puts(<<~MSG)
82
- Found: #{urgent.size} urgent failures, occurring in both canary.gitlab.com and canary.staging.gitlab.com.
83
- MSG
84
- else
85
- print_table(labels, failures, alignments: [:left, :center, :center, :left])
86
- output.puts(<<~MSG)
87
- Found: #{failures.size} failures, of these #{triaged_counter} have been triaged with a #{emoji}.
88
- MSG
79
+ failures.sort_by! { |failure| failure[SORT_BY_OPTIONS[@options[:sort_by]&.to_sym || :environment]] }
89
80
  end
81
+
82
+ msg = if @options[:urgent]
83
+ <<~MSG
84
+ Found: #{failures.size} urgent failures, occurring in both canary.gitlab.com and canary.staging.gitlab.com.
85
+ MSG
86
+ else
87
+ <<~MSG
88
+ Found: #{failures.size} failures, of these #{triaged_counter} have been triaged with a #{emoji}.
89
+ MSG
90
+ end
91
+
92
+ print_table(labels, failures, alignments: [:left, :center, :center, :left])
93
+ output.puts(msg)
90
94
  end
91
95
  end
92
96
  end