dri 0.1.0 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb31c263acfb2d576ed72028d207df2d36d76b1965ac00aa5228fbdd991a7524
4
- data.tar.gz: 179c9389a9b3ce00a9f4ff357da3d1f4fdf3af9b22ec96a8d582dfb7d35bf17a
3
+ metadata.gz: c1c589c770547e0142cb5b2942316e41af19cad293ed2e141d95e54a7379f741
4
+ data.tar.gz: b1cc881757273745c40485c469b34abb642a8edcda18ae1baf22bfe80ed8bacd
5
5
  SHA512:
6
- metadata.gz: d38a554fad6c98a72992c2d5c97eae39f6ecebce78ff466174f20074a4aa74b436ee6fcff6192ad4800515056fd9e53ec049a5aa1f6dfa90fdcafa2489376256
7
- data.tar.gz: 2f2994a17800bd2d1fbf02ed9724e9b0428e4ded12ae9516135d19a6b529982d3e36d2148e6ef9df4f6bc740355555831ed035b440d30d89f94fdf814e3a1a99
6
+ metadata.gz: 0fa73e3af14a31bcf96f919c1b37465695a52a7db5d0fcaedf8ee36e51c817bf4c789c3fe24bc2169de0b385b83d0ec92ae716f8da6fbb55cb134c6219a0b26a
7
+ data.tar.gz: 880ae4a81c350dcfe7b49c9db00c3760d7f98b56383b7516d0c890dae24034fc3d8ebb86f37592c8d65655c3cbe8671bddf3c26327d49a6006b0e9f5f0313afa
data/.gitignore CHANGED
@@ -6,9 +6,11 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /vendor/bundle
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
12
13
 
13
14
  .dri_profile.yml
14
- handover_reports/*
15
+ handover_reports/*
16
+ .idea/
data/.gitlab-ci.yml CHANGED
@@ -1,28 +1,40 @@
1
- image: ruby:3.0.2
1
+ .job_base:
2
+ image: ruby:3.1
3
+ variables:
4
+ BUNDLE_PATH: vendor/bundle
5
+ BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES: "true"
6
+ before_script:
7
+ - bundle install
8
+ cache:
9
+ key:
10
+ files:
11
+ - dri.gemspec
12
+ - Gemfile.lock
13
+ paths:
14
+ - vendor/bundle
15
+ rules:
16
+ - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
17
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
18
+
19
+ include:
20
+ - project: gitlab-org/quality/pipeline-common
21
+ ref: 0.3.4
22
+ file:
23
+ - /ci/gem-release.yml
2
24
 
3
25
  stages:
4
26
  - build
5
27
  - test
6
28
  - deploy
7
29
 
8
- before_script:
9
- - gem install bundler:2.2.22
10
- - bundle install
11
-
12
30
  build_gem:
13
31
  stage: build
32
+ extends: .job_base
14
33
  script:
15
34
  - gem build
16
35
 
17
36
  rspec:
18
37
  stage: test
38
+ extends: .job_base
19
39
  script:
20
40
  - bundle exec rspec
21
-
22
- deploy:
23
- stage: deploy
24
- script:
25
- - gem push dri*.gem
26
- rules:
27
- - if: '$CI_COMMIT_TAG'
28
- when: always
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.0.2
data/Gemfile CHANGED
@@ -2,6 +2,3 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in dri.gemspec
4
4
  gemspec
5
-
6
- gem "rake", "~> 12.0"
7
- gem "rspec", "~> 3.0"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dri (0.1.0)
4
+ dri (0.1.2)
5
5
  httparty (~> 0.20.0)
6
6
  json (~> 2.6.1)
7
7
  markdown-tables (~> 1.1.1)
@@ -39,19 +39,19 @@ GEM
39
39
  public_suffix (4.0.6)
40
40
  rake (12.3.3)
41
41
  rexml (3.2.5)
42
- rspec (3.11.0)
43
- rspec-core (~> 3.11.0)
44
- rspec-expectations (~> 3.11.0)
45
- rspec-mocks (~> 3.11.0)
46
- rspec-core (3.11.0)
47
- rspec-support (~> 3.11.0)
48
- rspec-expectations (3.11.0)
42
+ rspec (3.10.0)
43
+ rspec-core (~> 3.10.0)
44
+ rspec-expectations (~> 3.10.0)
45
+ rspec-mocks (~> 3.10.0)
46
+ rspec-core (3.10.1)
47
+ rspec-support (~> 3.10.0)
48
+ rspec-expectations (3.10.1)
49
49
  diff-lcs (>= 1.2.0, < 2.0)
50
- rspec-support (~> 3.11.0)
51
- rspec-mocks (3.11.0)
50
+ rspec-support (~> 3.10.0)
51
+ rspec-mocks (3.10.2)
52
52
  diff-lcs (>= 1.2.0, < 2.0)
53
- rspec-support (~> 3.11.0)
54
- rspec-support (3.11.0)
53
+ rspec-support (~> 3.10.0)
54
+ rspec-support (3.10.2)
55
55
  strings (0.2.1)
56
56
  strings-ansi (~> 0.2)
57
57
  unicode-display_width (>= 1.5, < 3.0)
@@ -98,10 +98,10 @@ PLATFORMS
98
98
 
99
99
  DEPENDENCIES
100
100
  dri!
101
- rake (~> 12.0)
102
- rspec (~> 3.0)
101
+ rake
102
+ rspec (~> 3.10.0)
103
103
  timecop (~> 0.9.1)
104
104
  webmock (~> 3.5)
105
105
 
106
106
  BUNDLED WITH
107
- 2.1.4
107
+ 2.3.9
data/README.md CHANGED
@@ -7,9 +7,9 @@ Welcome to your new gem! DRI is a CLI ready to help:
7
7
  - Fetching failing test cases by pipeline
8
8
  - Reporting at the end of the timezone shift
9
9
  - View today's newest and untriaged failures
10
- - Take actions on failures
10
+ - Mark actions on failures
11
11
 
12
- ... and more.
12
+ ... and more
13
13
 
14
14
  ## Installation
15
15
 
@@ -55,10 +55,13 @@ $ dri profile
55
55
  - [3. fetch](#3-fetch)
56
56
  - failures
57
57
  - testcases
58
+ - triaged
58
59
  - [4. publish](#4-publish)
59
60
  - report
60
61
  - [5. rm](#5-rm)
61
62
  - emoji
63
+ - profile
64
+ - reports
62
65
  - [6. version](#6-version)
63
66
 
64
67
  #### 1. init
@@ -87,6 +90,9 @@ $ dri fetch failures
87
90
  Fetches today's opened failures and lists them according to their triage status.
88
91
  Helpful to understand if there are missing recent failures to be reviewed.
89
92
 
93
+ To surface most urgent issues pass the `--urgent` flag to see issues that are both
94
+ in `canary` and `staging-canary` pipelines just during today's timespan.
95
+
90
96
  ```shell
91
97
  $ dri fetch testcases
92
98
  ```
@@ -95,6 +101,12 @@ Fetches test cases that failing currently and groups them by pipeline.
95
101
  To filter the pipelines, pass the `--filter-pipelines` options to multi-select the pipelines you wish to consult.
96
102
  (Use `Space` to select an option)
97
103
 
104
+ ```shell
105
+ $ dri fetch triaged
106
+ ```
107
+
108
+ Fetches triaged failures which use the triage emoji specified in `dri profile`.
109
+
98
110
  #### 4. publish
99
111
 
100
112
  ```shell
@@ -109,8 +121,22 @@ Publishes a handover report on the latest triage issue, in the [pipeline-triage]
109
121
  $ dri publish report --format=list # formats the report in a list
110
122
  $ dri publish report --format=table # formats the report in a table (default)
111
123
  $ dri publish report --dry-run # the report is only generated locally
124
+ $ dri publish report --actions # activate the actions prompt for each failure
112
125
  ```
113
126
 
127
+ **Note:** These options above can be combined like:
128
+
129
+ ```shell
130
+ $ dri publish report --format=list --dry-run --actions
131
+ ```
132
+
133
+ **Actions**
134
+
135
+ When using `--actions` a set of actions are prompt on each failure. Use `Space` to
136
+ mark several actions: `pinged SET`, `quarantined`, `reproduced`, `transient`.
137
+ Hitting `Enter` without a selected action will skip marking the actions for a given
138
+ failure.
139
+
114
140
  #### 5. rm
115
141
 
116
142
  ```shell
@@ -119,6 +145,20 @@ $ dri rm emoji
119
145
 
120
146
  Removes the triage emoji from all triaged issues.
121
147
 
148
+ ```shell
149
+ $ dri rm reports
150
+ ```
151
+
152
+ When using `$ dri publish report --dry-run` to download the reports locally instead
153
+ of uploading to the latest Pipeline Triage Issue, an `/handover_reports/` folder
154
+ created to store these reports. This command removes this folder and subsequent reports stored in it.
155
+
156
+ ```shell
157
+ $ dri rm profile
158
+ ```
159
+
160
+ Removes the profile currently in use.
161
+
122
162
  #### 6. version
123
163
 
124
164
  ```shell
data/dri.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
 
10
10
  spec.summary = %q{CLI app to help triage GitLab QA pipelines}
11
11
  spec.homepage = 'https://gitlab.com/gitlab-org/quality/dri'
12
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
13
13
 
14
14
  # Specify which files should be added to the gem when it is released.
15
15
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency "tty-editor", "~> 0.6"
31
31
  spec.add_dependency "pastel", "~> 0.8.0"
32
32
  spec.add_dependency "thor", "~> 1.0.1"
33
- spec.add_dependency "markdown-tables", "~> 1.1.1"
33
+ spec.add_dependency "markdown-tables", "~> 1.1.1"
34
34
  spec.add_dependency 'json', '~> 2.6.1'
35
35
  spec.add_dependency 'httparty', '~> 0.20.0'
36
36
 
@@ -3,13 +3,14 @@
3
3
  require "httparty"
4
4
  require "json"
5
5
  require "tty-config"
6
+ require 'cgi'
6
7
 
7
8
  module Dri
8
9
  class ApiClient
9
-
10
+
10
11
  API_URL = 'https://gitlab.com/api/v4'
11
12
  TESTCASES_PROJECT_ID = '11229385'
12
- TRIAGE_PROJECT_ID = '34185524'
13
+ TRIAGE_PROJECT_ID = '15291320'
13
14
  GITLAB_PROJECT_ID = '278964'
14
15
 
15
16
  def initialize(config)
@@ -50,6 +51,24 @@ module Dri
50
51
  fetch_json(url.join)
51
52
  end
52
53
 
54
+ # Fetch MRs
55
+ #
56
+ # @see https://docs.gitlab.com/ee/api/merge_requests.html for all passable options
57
+ #
58
+ # @param [Hash<String>] options
59
+ # @option options [String] state
60
+ # @option options [String] order_by
61
+ # @option options [String] sort
62
+ # @option options [String] milestone
63
+ # @option options [String] labels
64
+ def fetch_mrs(project_id:, **options)
65
+ uri = URI(API_URL)
66
+ uri.query = HTTParty::HashConversions.to_params(options)
67
+ uri.path = File.join(uri.path, 'projects', CGI.escape(project_id), 'merge_requests') # CGI.escape('gitlab-org/gitlab') => 'gitlab-org%2Fgitlab'
68
+
69
+ fetch_json(uri.to_s)
70
+ end
71
+
53
72
  def fetch_current_triage_issue
54
73
  url = ["#{API_URL}/projects/"]
55
74
  url << "#{TRIAGE_PROJECT_ID}/issues?state=opened"
@@ -97,7 +116,7 @@ module Dri
97
116
  }
98
117
  }
99
118
 
100
- response = HTTParty.post(url, options)
119
+ response = HTTParty.post(url, options)
101
120
  handle_response(response)
102
121
  end
103
122
 
@@ -122,4 +141,4 @@ module Dri
122
141
  handle_response(JSON.parse(response.body))
123
142
  end
124
143
  end
125
- end
144
+ end
data/lib/dri/command.rb CHANGED
@@ -2,6 +2,8 @@
2
2
  require_relative 'command'
3
3
  require_relative 'api_client'
4
4
 
5
+ require 'dri/refinements/truncate'
6
+
5
7
  require "tty-config"
6
8
  require "pastel"
7
9
  require 'forwardable'
@@ -8,6 +8,8 @@ module Dri
8
8
  module Commands
9
9
  class Fetch
10
10
  class Failures < Dri::Command
11
+ using Refinements
12
+
11
13
  def initialize(options)
12
14
  @options = options
13
15
  @today_iso_format = Time.now.strftime('%Y-%m-%dT00:00:00Z')
@@ -22,46 +24,60 @@ module Dri
22
24
  url = add_color('URL', :bright_yellow)
23
25
 
24
26
  failures = []
27
+ urgent = []
25
28
  labels = [ title, triaged, author, url ]
26
29
  triaged_counter = 0
27
30
 
28
- logger.info "Fetching today\'s failures..."
29
-
30
- spinner.run do
31
+ logger.info "Fetching today's failures..."
32
+
33
+ spinner.run do
31
34
 
32
35
  response = api_client.fetch_failures(date: @today_iso_format, state: 'opened')
33
36
 
34
37
  if response.nil?
35
- logger.info "Life is great, there are no new failures today!"
38
+ logger.info 'Life is great, there are no new failures today!'
36
39
  exit 0
37
40
  end
38
41
 
39
42
  response.each do |failure|
40
- title = truncate(failure["title"], 60)
41
- author = failure["author"]["username"]
42
- url = failure["web_url"]
43
- award_emoji_url = failure["_links"]["award_emoji"]
43
+ title = failure['title'].truncate(60)
44
+ author = failure['author']['username']
45
+ url = failure['web_url']
46
+ award_emoji_url = failure['_links']['award_emoji']
44
47
  triaged = add_color('x', :red)
45
48
 
46
- emoji_awards = api_client.fetch_awarded_emojis(award_emoji_url)
49
+ emoji_awards = api_client.fetch_awarded_emojis(award_emoji_url).find do |e|
50
+ e['name'] == emoji && e['user']['username'] == username
51
+ end
47
52
 
48
- triaged = add_color('✓', :green) && triaged_counter += 1 if emoji_awards.find do |e|
49
- e['name'] == emoji && e['user']['username'] == @username
53
+ if emoji_awards
54
+ triaged = add_color('', :green)
55
+ triaged_counter += 1
56
+ end
57
+
58
+ if @options[:urgent]
59
+ labels = failure['labels']
60
+
61
+ labels.each do |label|
62
+ if label.include? ('found:canary.gitlab.com' && 'found:canary.staging.gitlab.com')
63
+ urgent << [title, triaged, author, url]
64
+ end
65
+ end
50
66
  end
51
67
 
52
68
  failures << [title, triaged, author, url]
53
69
  end
54
70
  end
55
71
 
56
- table = TTY::Table.new(labels, failures)
57
- puts table.render(:ascii, resize: true, alignments: [:center, :center, :center, :center])
58
- output.puts "\nFound: #{failures.size} failures, of these #{triaged_counter} have been triaged with a #{emoji}."
59
- end
60
-
61
- private
62
-
63
- def truncate(string, max)
64
- string.length > max ? "#{string[0...max]}..." : string
72
+ if @options[:urgent]
73
+ table = TTY::Table.new(labels, urgent)
74
+ puts table.render(:ascii, resize: true, alignments: [:center, :center, :center, :center])
75
+ output.puts "\nFound: #{urgent.size} urgent failures, occurring in both canary.gitlab.com and canary.staging.gitlab.com."
76
+ else
77
+ table = TTY::Table.new(labels, failures)
78
+ puts table.render(:ascii, resize: true, alignments: [:center, :center, :center, :center])
79
+ output.puts "\nFound: #{failures.size} failures, of these #{triaged_counter} have been triaged with a #{emoji}."
80
+ end
65
81
  end
66
82
  end
67
83
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+
5
+ require 'tty-table'
6
+
7
+ module Dri
8
+ module Commands
9
+ class Fetch
10
+ class Quarantines < Dri::Command
11
+ using Refinements
12
+
13
+ def initialize(options)
14
+ @options = options
15
+ @today_iso_format = Time.now.strftime('%Y-%m-%dT00:00:00Z')
16
+ end
17
+
18
+ def execute(input: $stdin, output: $stdout)
19
+ verify_config_exists
20
+
21
+ title = add_color('Example name', :bright_yellow)
22
+ url = add_color('URL', :bright_yellow)
23
+
24
+ headers = [title, url]
25
+ mrs = []
26
+
27
+ logger.info 'Fetching Quarantine MRs...'
28
+
29
+ spinner.run do
30
+ response = api_client.fetch_mrs(project_id: 'gitlab-org/gitlab', labels: 'QA,Quality', search: '[QUARANTINE]', in: :title, state: :opened)
31
+
32
+ mrs = response.each_with_object([]) do |mr, found_mrs|
33
+ title = mr['title'][13..] # remove the "[QUARANTINE] " prefix
34
+ url = mr['web_url']
35
+
36
+ found_mrs << [title, url]
37
+ end
38
+ end
39
+
40
+ puts TTY::Table.new(headers, mrs).render(:ascii, resize: true, alignments: %i[left center])
41
+ output.puts "Found #{mrs.size} open Quarantine MRs"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -31,6 +31,10 @@ module Dri
31
31
 
32
32
  pipelines = @options[:filter_pipelines] ? filtered_pipelines : @available_pipelines
33
33
 
34
+ if pipelines.empty?
35
+ logger.error "No pipelines selected to continue to fetch testcases."
36
+ end
37
+
34
38
  pipelines.each do |pipeline|
35
39
  logger.info "Fetching failing testcases in #{pipeline}\n"
36
40
  response = api_client.fetch_failing_testcases(pipeline, state: 'opened')
@@ -0,0 +1,55 @@
1
+ require_relative '../../command'
2
+ require 'tty-table'
3
+
4
+ module Dri
5
+ module Commands
6
+ class Fetch
7
+ class Triaged < Dri::Command
8
+ def initialize(options)
9
+ @options = options
10
+ end
11
+
12
+ def execute(input: $stdin, output: $stdout)
13
+ verify_config_exists
14
+
15
+ title = add_color('Title', :magenta)
16
+ url = add_color('URL', :magenta)
17
+ type_label = add_color('Type', :magenta)
18
+
19
+ table_labels = [ title, url, type_label ]
20
+ failures_triaged = []
21
+
22
+ logger.info "Fetching your triaged failures..."
23
+ spinner.start
24
+
25
+ response = api_client.fetch_triaged_failures(emoji: emoji, state: 'opened')
26
+
27
+ if response.empty?
28
+ logger.info "There are no failures triaged yet with #{add_color(emoji, :black, :on_white)}."
29
+ exit 0
30
+ end
31
+
32
+ response.each do |triaged|
33
+ title = triaged["title"].truncate(70)
34
+ url = triaged["web_url"]
35
+ labels = triaged["labels"]
36
+ type = ""
37
+
38
+ labels.each do |label|
39
+ type = label.gsub!('failure::', ' ').to_s if label.include? "failure::"
40
+ end
41
+
42
+ labels = triaged["labels"]
43
+
44
+ failures_triaged << [title, url, type]
45
+ end
46
+
47
+ spinner.stop
48
+
49
+ table = TTY::Table.new(table_labels,failures_triaged)
50
+ puts table.render(:ascii, resize: true, alignments: [:center, :center, :center])
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -7,6 +7,18 @@ module Dri
7
7
  class Fetch < Thor
8
8
  namespace :fetch
9
9
 
10
+ desc 'triaged', 'Command description...'
11
+ method_option :help, aliases: '-h', type: :boolean,
12
+ desc: 'Display usage information'
13
+ def triaged(*)
14
+ if options[:help]
15
+ invoke :help, ['triaged']
16
+ else
17
+ require_relative 'fetch/triaged'
18
+ Dri::Commands::Fetch::Triaged.new(options).execute
19
+ end
20
+ end
21
+
10
22
  desc 'testcases', 'Display failing testcases'
11
23
  method_option :help, aliases: '-h', type: :boolean,
12
24
  desc: 'Display usage information'
@@ -24,6 +36,8 @@ module Dri
24
36
  desc 'failures', 'Display failures opened today'
25
37
  method_option :help, aliases: '-h', type: :boolean,
26
38
  desc: 'Display usage information'
39
+ method_option :urgent, type: :boolean,
40
+ desc: 'Shows failures that quickly escalated'
27
41
  def failures(*)
28
42
  if options[:help]
29
43
  invoke :help, ['failures']
@@ -32,6 +46,18 @@ module Dri
32
46
  Dri::Commands::Fetch::Failures.new(options).execute
33
47
  end
34
48
  end
49
+
50
+ desc 'quarantines', 'Display open quarantine MRs'
51
+ method_option :help, aliases: '-h', type: :boolean,
52
+ desc: 'Display usage information'
53
+ def quarantines(*)
54
+ if options[:help]
55
+ invoke :help, ['quarantines']
56
+ else
57
+ require_relative 'fetch/quarantines'
58
+ Dri::Commands::Fetch::Quarantines.new(options).execute
59
+ end
60
+ end
35
61
  end
36
62
  end
37
63
  end
@@ -3,12 +3,13 @@
3
3
  require_relative '../command'
4
4
 
5
5
  require "tty-font"
6
- require "pastel"
7
6
 
8
7
  module Dri
9
8
  module Commands
10
9
  class Init < Dri::Command
11
10
  def initialize(options)
11
+ @options = options
12
+
12
13
  font = TTY::Font.new(:doom)
13
14
  puts pastel.yellow(font.write("DRI"))
14
15
  end
@@ -21,7 +22,7 @@ module Dri
21
22
  if config.exist?
22
23
  overwrite = prompt.yes?("There is already a configuration initialized. Would you like to overwrite it?")
23
24
  unless overwrite
24
- output.puts "Using existing configuration. To view configuration in use try #{add_color('dri profile', :yellow)}."
25
+ output.puts "Using existing configuration. To view configuration in use try #{add_color('dri profile', :yellow)}."
25
26
  exit 0
26
27
  end
27
28
  end
@@ -34,9 +34,16 @@ module Dri
34
34
 
35
35
  logger.info "Assembling the report... "
36
36
  # sets each failure on the table
37
+ action_options = ["pinged SET", "reproduced", "transient", "quarantined"]
38
+
37
39
  spinner.start
38
40
  issues.each do |issue|
39
- report.add_failure(issue)
41
+ actions = []
42
+
43
+ if @options[:actions]
44
+ actions = prompt.multi_select("Please mark the actions on #{add_color(issue['title'], :yellow)}: ", action_options)
45
+ end
46
+ report.add_failure(issue, actions)
40
47
  end
41
48
 
42
49
  if @options[:format] == 'list'
@@ -78,7 +85,7 @@ module Dri
78
85
  response = api_client.post_triage_report_note(iid: current_issue_iid, body: note)
79
86
 
80
87
  output.puts "Done! ✅\n"
81
- logger.success "Thanks @#{username}, your report was posted at https://gitlab.com/gitlab-org/quality/dri/-/issues/#{current_issue_iid} 🎉"
88
+ logger.success "Thanks @#{username}, your report was posted at https://gitlab.com/gitlab-org/quality/pipeline-triage/-/issues/#{current_issue_iid} 🎉"
82
89
  end
83
90
  end
84
91
  end
@@ -13,6 +13,8 @@ module Dri
13
13
  desc: 'Generates a report locally'
14
14
  method_option :format, aliases: '-f', type: :string, :default => "table",
15
15
  desc: 'Formats the report'
16
+ method_option :actions, type: :boolean,
17
+ desc: 'Updates actions on failures'
16
18
  def report(*)
17
19
  if options[:help]
18
20
  invoke :help, ['report']
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+ require 'fileutils'
5
+
6
+ module Dri
7
+ module Commands
8
+ class Rm
9
+ class Profile < Dri::Command
10
+ def initialize(options)
11
+ @options = options
12
+ end
13
+
14
+ def execute(input: $stdin, output: $stdout)
15
+ verify_config_exists
16
+
17
+ remove = prompt.yes? "Are you sure you want to remove existing profile?"
18
+
19
+ unless remove
20
+ logger.info "Profile kept in place 👍"
21
+ exit 0
22
+ end
23
+
24
+ logger.info "Removing profile..."
25
+
26
+ FileUtils.rm("#{Dir.pwd}/.dri_profile.yml")
27
+
28
+ logger.success "Done ✅"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+ require 'fileutils'
5
+
6
+ module Dri
7
+ module Commands
8
+ class Rm
9
+ class Reports < Dri::Command
10
+ def initialize(options)
11
+ @options = options
12
+ end
13
+
14
+ def execute(input: $stdin, output: $stdout)
15
+ FileUtils.rm_rf("#{Dir.pwd}/handover_reports")
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -8,6 +8,30 @@ module Dri
8
8
 
9
9
  namespace :rm
10
10
 
11
+ desc 'profile', 'Command description...'
12
+ method_option :help, aliases: '-h', type: :boolean,
13
+ desc: 'Display usage information'
14
+ def profile(*)
15
+ if options[:help]
16
+ invoke :help, ['profile']
17
+ else
18
+ require_relative 'rm/profile'
19
+ Dri::Commands::Rm::Profile.new(options).execute
20
+ end
21
+ end
22
+
23
+ desc 'reports', 'Command description...'
24
+ method_option :help, aliases: '-h', type: :boolean,
25
+ desc: 'Display usage information'
26
+ def reports(*)
27
+ if options[:help]
28
+ invoke :help, ['reports']
29
+ else
30
+ require_relative 'rm/reports'
31
+ Dri::Commands::Rm::Reports.new(options).execute
32
+ end
33
+ end
34
+
11
35
  desc 'emoji', 'Remove triage emoji from all failures'
12
36
  method_option :help, aliases: '-h', type: :boolean,
13
37
  desc: 'Display usage information'
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Refinements
4
+ refine String do
5
+ # Truncate string to limit of _n_ characters
6
+ # @param [Integer] limit the limit of characters
7
+ # @return [String] the resulting string after truncation
8
+ def truncate(limit = 50)
9
+ return self.freeze if self.size <= limit
10
+
11
+ # limit - 3 for the ellipses
12
+ self[0...(limit - 3)] << '...'
13
+ end
14
+ end
15
+ end
data/lib/dri/report.rb CHANGED
@@ -17,7 +17,7 @@ module Dri
17
17
  @header = "# #{timezone}, #{@weekday} - #{@date}\n posted by: @#{username}"
18
18
  end
19
19
 
20
- def add_failure(failure)
20
+ def add_failure(failure, actions_opts = [])
21
21
  iid = failure["iid"]
22
22
  title = failure["title"]
23
23
  link = failure["web_url"]
@@ -38,10 +38,10 @@ module Dri
38
38
  assigned_status = assigned?(assignees)
39
39
  pipelines = filter_pipeline_labels(labels)
40
40
 
41
- linked_pipelines = link_pipelines(iid, pipelines)
41
+ linked_pipelines = link_pipelines(iid, pipelines, description)
42
42
 
43
43
  actions = ""
44
- actions.concat actions_status_template(failure_type, assigned_status)
44
+ actions.concat actions_status_template(failure_type, assigned_status, actions_opts)
45
45
  actions.concat actions_fixes_template(related_mrs)
46
46
 
47
47
  @failures << [title, emojified_link, linked_pipelines, stack_trace, actions]
@@ -49,28 +49,75 @@ module Dri
49
49
 
50
50
  private
51
51
 
52
- def link_pipelines(iid, pipelines)
52
+ def link_pipelines(iid, pipelines, description)
53
+ linked = []
54
+ label_pipeline_map = {
55
+ 'gitlab.com' => '/quality/production',
56
+ 'canary.gitlab.com' => '/quality/canary',
57
+ # 'canary.staging.gitlab.com' => '',
58
+ 'main' => '/gitlab-org/gitlab-qa-mirror',
59
+ 'master' => '/gitlab-org/gitlab-qa-mirror',
60
+ 'nightly' => '/quality/nightly',
61
+ 'pre.gitlab.com' => '/quality/preprod',
62
+ 'staging-ref' => '/quality/staging-ref',
63
+ 'staging.gitlab.com' => '/quality/staging',
64
+ 'release' => '/quality/release'
65
+ }
66
+
53
67
  failure_notes = @api_client.fetch_failure_notes(issue_iid: iid)
54
68
 
55
- linked = ""
69
+ return if pipelines.empty?
56
70
 
57
71
  pipelines.each do |pipeline|
58
- failure_notes.each do |note|
59
- if note["body"].include? pipeline
60
- pipeline_link = URI.extract(note["body"], %w(https))
61
- pipeline_link_sanitized = pipeline_link.join.strip
62
- pipeline = "[#{pipeline}](#{pipeline_link_sanitized})"
63
- break
64
- end
72
+ next if !label_pipeline_map.has_key? pipeline
73
+
74
+ pipeline_in_notes_found = false
75
+ pipeline_link = ''
76
+ pipeline_link_sanitized = ''
77
+ pipeline_markdown = ''
78
+
79
+ failure_notes.each do |note|
80
+ if note["body"].include? label_pipeline_map.fetch(pipeline)
81
+ pipeline_in_notes_found = true
82
+ pipeline_link = URI.extract(note["body"], %w(https))
83
+ break
65
84
  end
85
+ end
86
+
87
+ unless pipeline_in_notes_found
88
+ links_description = URI.extract(description, %w(https))
89
+ pipeline_link = links_description.select { |link| link.include? label_pipeline_map.fetch(pipeline) }
90
+ end
66
91
 
67
- linked << pipeline
92
+ if !pipeline_link.empty?
93
+ pipeline_link_sanitized = pipeline_link.join.strip.chop
94
+ pipeline_markdown = "[#{pipeline.gsub(/.gitlab.com/, '')}](#{pipeline_link_sanitized})"
95
+ linked << pipeline_markdown
96
+ end
68
97
  end
69
- linked
98
+ linked.join(', ')
70
99
  end
71
100
 
72
- def actions_status_template(failure_type, assigned_status)
73
- "<i>Status:</i><ul><li>#{failure_type}</li><li>#{assigned_status}</li><li>[ ] notified SET</li><li>[ ] quarantined</li></ul>"
101
+ def actions_status_template(failure_type, assigned_status, actions_opts)
102
+ notified_set = ''
103
+ quarantined = ''
104
+ reproduced = ''
105
+ transient = ''
106
+
107
+ notified_set = '<li>[x] notified SET</li>' if actions_opts.include? 'pinged SET'
108
+ quarantined = '<li>[x] quarantined</li>' if actions_opts.include? 'quarantined'
109
+ reproduced = '<li>[x] reproduced</li>' if actions_opts.include? 'reproduced'
110
+ transient = '<li>[x] transient</li>' if actions_opts.include? 'transient'
111
+
112
+ action_status = "<i>Status:</i><ul>"
113
+ action_status << "<li>#{failure_type}</li>"
114
+ action_status << "</li><li>#{assigned_status}</li>"
115
+ action_status << notified_set
116
+ action_status << quarantined
117
+ action_status << reproduced
118
+ action_status << transient
119
+
120
+ action_status
74
121
  end
75
122
 
76
123
  def actions_fixes_template(related_mrs)
@@ -90,10 +137,10 @@ module Dri
90
137
  pipelines = []
91
138
 
92
139
  labels.each do |label|
93
- matchers = { 'found:' => ' ', '.gitlab.com' => ' ' }
140
+ matchers = { 'found:' => ' '}
94
141
 
95
142
  if label.include? "found:"
96
- pipeline = label.gsub(/found:|.gitlab.com/) { |match| matchers[match] }
143
+ pipeline = label.gsub(/found:/) { |match| matchers[match] }
97
144
  pipelines << pipeline.strip
98
145
  end
99
146
  end
data/lib/dri/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dri
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dri
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab Quality
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-10 00:00:00.000000000 Z
11
+ date: 2022-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tty-config
@@ -248,7 +248,7 @@ dependencies:
248
248
  - - "~>"
249
249
  - !ruby/object:Gem::Version
250
250
  version: 0.9.1
251
- description:
251
+ description:
252
252
  email:
253
253
  - quality+dri@gitlab.com
254
254
  executables:
@@ -260,6 +260,7 @@ files:
260
260
  - ".gitignore"
261
261
  - ".gitlab-ci.yml"
262
262
  - ".rspec"
263
+ - ".tool-versions"
263
264
  - Gemfile
264
265
  - Gemfile.lock
265
266
  - LICENSE.txt
@@ -275,13 +276,18 @@ files:
275
276
  - lib/dri/command.rb
276
277
  - lib/dri/commands/fetch.rb
277
278
  - lib/dri/commands/fetch/failures.rb
279
+ - lib/dri/commands/fetch/quarantines.rb
278
280
  - lib/dri/commands/fetch/testcases.rb
281
+ - lib/dri/commands/fetch/triaged.rb
279
282
  - lib/dri/commands/init.rb
280
283
  - lib/dri/commands/profile.rb
281
284
  - lib/dri/commands/publish.rb
282
285
  - lib/dri/commands/publish/report.rb
283
286
  - lib/dri/commands/rm.rb
284
287
  - lib/dri/commands/rm/emoji.rb
288
+ - lib/dri/commands/rm/profile.rb
289
+ - lib/dri/commands/rm/reports.rb
290
+ - lib/dri/refinements/truncate.rb
285
291
  - lib/dri/report.rb
286
292
  - lib/dri/utils/markdown_lists.rb
287
293
  - lib/dri/version.rb
@@ -289,7 +295,7 @@ homepage: https://gitlab.com/gitlab-org/quality/dri
289
295
  licenses:
290
296
  - MIT
291
297
  metadata: {}
292
- post_install_message:
298
+ post_install_message:
293
299
  rdoc_options: []
294
300
  require_paths:
295
301
  - lib
@@ -297,7 +303,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
297
303
  requirements:
298
304
  - - ">="
299
305
  - !ruby/object:Gem::Version
300
- version: 2.5.0
306
+ version: 2.7.0
301
307
  required_rubygems_version: !ruby/object:Gem::Requirement
302
308
  requirements:
303
309
  - - ">="
@@ -305,7 +311,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
305
311
  version: '0'
306
312
  requirements: []
307
313
  rubygems_version: 3.1.6
308
- signing_key:
314
+ signing_key:
309
315
  specification_version: 4
310
316
  summary: CLI app to help triage GitLab QA pipelines
311
317
  test_files: []