dri 0.1.3 → 0.2.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 +4 -4
- data/.gitlab-ci.yml +9 -2
- data/.rubocop.yml +53 -0
- data/.ruby-version +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +59 -7
- data/README.md +22 -0
- data/Rakefile +3 -1
- data/bin/console +1 -0
- data/dri.gemspec +25 -22
- data/exe/dri +3 -3
- data/lib/dri/api_client.rb +22 -10
- data/lib/dri/cli.rb +2 -2
- data/lib/dri/command.rb +5 -5
- data/lib/dri/commands/fetch/failures.rb +15 -14
- data/lib/dri/commands/fetch/featureflags.rb +95 -0
- data/lib/dri/commands/fetch/quarantines.rb +16 -9
- data/lib/dri/commands/fetch/testcases.rb +17 -10
- data/lib/dri/commands/fetch/triaged.rb +9 -7
- data/lib/dri/commands/fetch.rb +26 -4
- data/lib/dri/commands/init.rb +7 -5
- data/lib/dri/commands/profile.rb +14 -5
- data/lib/dri/commands/publish/report.rb +24 -9
- data/lib/dri/commands/publish.rb +4 -5
- data/lib/dri/commands/rm/emoji.rb +5 -5
- data/lib/dri/commands/rm/profile.rb +2 -2
- data/lib/dri/commands/rm.rb +0 -1
- data/lib/dri/refinements/truncate.rb +1 -1
- data/lib/dri/report.rb +56 -41
- data/lib/dri/utils/markdown_lists.rb +7 -9
- data/lib/dri/utils/table.rb +20 -0
- data/lib/dri/version.rb +3 -1
- data/lib/dri.rb +2 -0
- metadata +59 -41
@@ -1,44 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../../command'
|
4
|
-
|
5
|
-
require 'tty-table'
|
4
|
+
require_relative '../../utils/table'
|
6
5
|
|
7
6
|
module Dri
|
8
7
|
module Commands
|
9
8
|
class Fetch
|
10
9
|
class Quarantines < Dri::Command
|
10
|
+
include Dri::Utils::Table
|
11
11
|
using Refinements
|
12
12
|
|
13
|
-
def initialize(options)
|
13
|
+
def initialize(options, search: '[QUARANTINE]')
|
14
14
|
@options = options
|
15
|
+
@search = search
|
16
|
+
|
15
17
|
@today_iso_format = Time.now.strftime('%Y-%m-%dT00:00:00Z')
|
16
18
|
end
|
17
19
|
|
18
20
|
def execute(input: $stdin, output: $stdout)
|
19
21
|
verify_config_exists
|
20
|
-
|
21
22
|
title = add_color('Example name', :bright_yellow)
|
22
23
|
url = add_color('URL', :bright_yellow)
|
23
24
|
|
24
25
|
headers = [title, url]
|
25
26
|
mrs = []
|
26
27
|
|
27
|
-
logger.info
|
28
|
+
logger.info "Fetching #{@search} MRs..."
|
28
29
|
|
29
30
|
spinner.run do
|
30
|
-
response = api_client.fetch_mrs(
|
31
|
+
response = api_client.fetch_mrs(
|
32
|
+
project_id: 'gitlab-org/gitlab',
|
33
|
+
labels: 'QA,Quality',
|
34
|
+
search: @search,
|
35
|
+
in: :title,
|
36
|
+
state: :opened
|
37
|
+
)
|
31
38
|
|
32
39
|
mrs = response.each_with_object([]) do |mr, found_mrs|
|
33
|
-
title = mr['title'][13..] # remove the "[QUARANTINE] " prefix
|
40
|
+
title = mr['title'][13..].truncate(60) # remove the "[QUARANTINE] " prefix
|
34
41
|
url = mr['web_url']
|
35
42
|
|
36
43
|
found_mrs << [title, url]
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
40
|
-
|
41
|
-
output.puts "Found #{mrs.size} open
|
47
|
+
print_table(headers, mrs, alignments: [:left, :left])
|
48
|
+
output.puts "Found #{mrs.size} open #{@search} MRs"
|
42
49
|
end
|
43
50
|
end
|
44
51
|
end
|
@@ -8,13 +8,22 @@ module Dri
|
|
8
8
|
module Commands
|
9
9
|
class Fetch
|
10
10
|
class Testcases < Dri::Command
|
11
|
-
|
12
11
|
def initialize(options)
|
13
12
|
@options = options
|
14
|
-
@available_pipelines = %w
|
13
|
+
@available_pipelines = %w[
|
14
|
+
main
|
15
|
+
canary
|
16
|
+
master
|
17
|
+
nightly
|
18
|
+
production
|
19
|
+
staging-canary
|
20
|
+
staging-orchestrated
|
21
|
+
staging-ref
|
22
|
+
staging
|
23
|
+
]
|
15
24
|
end
|
16
25
|
|
17
|
-
def execute(input: $stdin, output: $stdout)
|
26
|
+
def execute(input: $stdin, output: $stdout) # rubocop:disable Metrics/AbcSize
|
18
27
|
verify_config_exists
|
19
28
|
|
20
29
|
if @options[:filter_pipelines]
|
@@ -31,22 +40,20 @@ module Dri
|
|
31
40
|
|
32
41
|
pipelines = @options[:filter_pipelines] ? filtered_pipelines : @available_pipelines
|
33
42
|
|
34
|
-
if pipelines.empty?
|
35
|
-
logger.error "No pipelines selected to continue to fetch testcases."
|
36
|
-
end
|
43
|
+
logger.error "No pipelines selected to continue to fetch testcases." if pipelines.empty?
|
37
44
|
|
38
45
|
pipelines.each do |pipeline|
|
39
46
|
logger.info "Fetching failing testcases in #{pipeline}\n"
|
40
47
|
response = api_client.fetch_failing_testcases(pipeline, state: 'opened')
|
41
|
-
|
48
|
+
|
42
49
|
output.puts "♦♦♦♦♦ #{add_color(pipeline, :black, :on_white)}♦♦♦♦♦\n\n"
|
43
50
|
|
44
51
|
response.each do |pipeline|
|
45
|
-
output.puts "#{title_label} #{pipeline[
|
52
|
+
output.puts "#{title_label} #{pipeline['title']}\n#{url_label} #{pipeline['web_url']}"
|
46
53
|
output.puts "#{divider}\n"
|
47
|
-
end
|
54
|
+
end
|
48
55
|
end
|
49
|
-
|
56
|
+
|
50
57
|
spinner.stop
|
51
58
|
end
|
52
59
|
end
|
@@ -1,22 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../../command'
|
2
|
-
|
4
|
+
require_relative '../../utils/table'
|
3
5
|
|
4
6
|
module Dri
|
5
7
|
module Commands
|
6
8
|
class Fetch
|
7
9
|
class Triaged < Dri::Command
|
10
|
+
include Dri::Utils::Table
|
11
|
+
using Refinements
|
12
|
+
|
8
13
|
def initialize(options)
|
9
14
|
@options = options
|
10
15
|
end
|
11
16
|
|
12
|
-
def execute(input: $stdin, output: $stdout)
|
17
|
+
def execute(input: $stdin, output: $stdout) # rubocop:disable Metrics/AbcSize
|
13
18
|
verify_config_exists
|
14
19
|
|
15
20
|
title = add_color('Title', :magenta)
|
16
21
|
url = add_color('URL', :magenta)
|
17
22
|
type_label = add_color('Type', :magenta)
|
18
23
|
|
19
|
-
table_labels = [
|
24
|
+
table_labels = [title, url, type_label]
|
20
25
|
failures_triaged = []
|
21
26
|
|
22
27
|
logger.info "Fetching your triaged failures..."
|
@@ -39,15 +44,12 @@ module Dri
|
|
39
44
|
type = label.gsub!('failure::', ' ').to_s if label.include? "failure::"
|
40
45
|
end
|
41
46
|
|
42
|
-
labels = triaged["labels"]
|
43
|
-
|
44
47
|
failures_triaged << [title, url, type]
|
45
48
|
end
|
46
49
|
|
47
50
|
spinner.stop
|
48
51
|
|
49
|
-
|
50
|
-
puts table.render(:ascii, resize: true, alignments: [:center, :center, :center])
|
52
|
+
print_table(table_labels, failures_triaged)
|
51
53
|
end
|
52
54
|
end
|
53
55
|
end
|
data/lib/dri/commands/fetch.rb
CHANGED
@@ -7,6 +7,18 @@ module Dri
|
|
7
7
|
class Fetch < Thor
|
8
8
|
namespace :fetch
|
9
9
|
|
10
|
+
desc 'featureflags', 'Display feature flag changes for today'
|
11
|
+
method_option :help, aliases: '-h', type: :boolean,
|
12
|
+
desc: 'Display usage information'
|
13
|
+
def featureflags(*)
|
14
|
+
if options[:help]
|
15
|
+
invoke :help, ['featureflags']
|
16
|
+
else
|
17
|
+
require_relative 'fetch/featureflags'
|
18
|
+
Dri::Commands::Fetch::FeatureFlags.new(options).execute
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
10
22
|
desc 'triaged', 'Command description...'
|
11
23
|
method_option :help, aliases: '-h', type: :boolean,
|
12
24
|
desc: 'Display usage information'
|
@@ -23,7 +35,7 @@ module Dri
|
|
23
35
|
method_option :help, aliases: '-h', type: :boolean,
|
24
36
|
desc: 'Display usage information'
|
25
37
|
method_option :filter_pipelines, type: :boolean,
|
26
|
-
|
38
|
+
desc: 'Filter by pipeline'
|
27
39
|
def testcases(*)
|
28
40
|
if options[:help]
|
29
41
|
invoke :help, ['testcases']
|
@@ -37,7 +49,7 @@ module Dri
|
|
37
49
|
method_option :help, aliases: '-h', type: :boolean,
|
38
50
|
desc: 'Display usage information'
|
39
51
|
method_option :urgent, type: :boolean,
|
40
|
-
|
52
|
+
desc: 'Shows failures that quickly escalated'
|
41
53
|
def failures(*)
|
42
54
|
if options[:help]
|
43
55
|
invoke :help, ['failures']
|
@@ -49,15 +61,25 @@ module Dri
|
|
49
61
|
|
50
62
|
desc 'quarantines', 'Display open quarantine MRs'
|
51
63
|
method_option :help, aliases: '-h', type: :boolean,
|
52
|
-
|
64
|
+
desc: 'Display usage information'
|
53
65
|
def quarantines(*)
|
54
66
|
if options[:help]
|
55
67
|
invoke :help, ['quarantines']
|
56
68
|
else
|
57
69
|
require_relative 'fetch/quarantines'
|
58
|
-
Dri::Commands::Fetch::Quarantines.new(options).execute
|
70
|
+
Dri::Commands::Fetch::Quarantines.new(options, search: '[QUARANTINE]').execute
|
59
71
|
end
|
60
72
|
end
|
73
|
+
|
74
|
+
desc 'dequarantines', 'Display open dequarantine MRs'
|
75
|
+
method_option :help, aliases: '-h', type: :boolean,
|
76
|
+
desc: 'Display usage information'
|
77
|
+
def dequarantines(*)
|
78
|
+
return invoke :help, %w[quarantines] if options[:help]
|
79
|
+
|
80
|
+
require_relative 'fetch/quarantines'
|
81
|
+
Dri::Commands::Fetch::Quarantines.new(options, search: '[DEQUARANTINE]').execute
|
82
|
+
end
|
61
83
|
end
|
62
84
|
end
|
63
85
|
end
|
data/lib/dri/commands/init.rb
CHANGED
@@ -10,11 +10,11 @@ module Dri
|
|
10
10
|
def initialize(options)
|
11
11
|
@options = options
|
12
12
|
|
13
|
-
font = TTY::Font.new(:doom)
|
13
|
+
font = TTY::Font.new(:doom)
|
14
14
|
puts pastel.yellow(font.write("DRI"))
|
15
15
|
end
|
16
16
|
|
17
|
-
def execute(input: $stdin, output: $stdout)
|
17
|
+
def execute(input: $stdin, output: $stdout) # rubocop:disable Metrics/AbcSize
|
18
18
|
output.puts "🤖 Welcome to DRI 🤖\n"
|
19
19
|
|
20
20
|
logger.info "🔎 Scanning for existing configurations...\n"
|
@@ -22,14 +22,16 @@ module Dri
|
|
22
22
|
if config.exist?
|
23
23
|
overwrite = prompt.yes?("There is already a configuration initialized. Would you like to overwrite it?")
|
24
24
|
unless overwrite
|
25
|
-
output.puts
|
25
|
+
output.puts(
|
26
|
+
"Using existing configuration. To view configuration in use try #{add_color('dri profile', :yellow)}."
|
27
|
+
)
|
26
28
|
exit 0
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
30
32
|
@username = prompt.ask("What is your GitLab username?")
|
31
33
|
@token = prompt.mask("Please provide your GitLab personal access token:")
|
32
|
-
@timezone = prompt.select("Choose your current timezone?", %w
|
34
|
+
@timezone = prompt.select("Choose your current timezone?", %w[EMEA AMER APAC])
|
33
35
|
@emoji = prompt.ask("Have a triage emoji?")
|
34
36
|
|
35
37
|
if (@emoji || @token || @username).nil?
|
@@ -43,7 +45,7 @@ module Dri
|
|
43
45
|
config.set(:settings, :emoji, value: @emoji)
|
44
46
|
config.write(force: true)
|
45
47
|
|
46
|
-
logger.success "✅ We're ready to go 🚀"
|
48
|
+
logger.success "✅ We're ready to go 🚀"
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|
data/lib/dri/commands/profile.rb
CHANGED
@@ -20,10 +20,15 @@ module Dri
|
|
20
20
|
logger.info "🔎 Looking for profiles...\n"
|
21
21
|
|
22
22
|
if config.exist?
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
frame_args = {
|
24
|
+
width: 30,
|
25
|
+
height: 10,
|
26
|
+
align: :center,
|
27
|
+
padding: 1,
|
28
|
+
border: :thick,
|
29
|
+
title: { top_left: add_color('PROFILE:', :bright_cyan) }
|
30
|
+
}
|
31
|
+
output.print TTY::Box.frame(**frame_args) { pretty_print_profile.strip }
|
27
32
|
output.puts "☝️ To modify this profile try passing #{add_color('dri profile --edit', :yellow)}.\n"
|
28
33
|
else
|
29
34
|
logger.error "Oops.. Profile not found. Try creating one using #{add_color('dri init', :yellow)}."
|
@@ -31,7 +36,11 @@ module Dri
|
|
31
36
|
end
|
32
37
|
|
33
38
|
def pretty_print_profile
|
34
|
-
|
39
|
+
<<~PROFILE
|
40
|
+
#{add_color('User:', :bright_cyan)} #{username}\n #{add_color('Token:', :bright_cyan)} #{token}
|
41
|
+
#{add_color('Timezone:', :bright_cyan)} #{timezone}
|
42
|
+
#{add_color('Emoji:', :bright_cyan)} #{emoji}
|
43
|
+
PROFILE
|
35
44
|
end
|
36
45
|
end
|
37
46
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../../command'
|
2
4
|
require_relative '../../utils/markdown_lists'
|
3
5
|
require_relative "../../report"
|
@@ -17,14 +19,14 @@ module Dri
|
|
17
19
|
@time = Time.now.to_i
|
18
20
|
end
|
19
21
|
|
20
|
-
def execute(input: $stdin, output: $stdout)
|
22
|
+
def execute(input: $stdin, output: $stdout) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
21
23
|
verify_config_exists
|
22
24
|
report = Dri::Report.new(config)
|
23
25
|
|
24
26
|
logger.info "Fetching triaged failures with award emoji #{emoji}..."
|
25
27
|
|
26
28
|
spinner.start
|
27
|
-
issues = api_client.fetch_triaged_failures(emoji: emoji, state: 'opened')
|
29
|
+
issues = api_client.fetch_triaged_failures(emoji: emoji, state: 'opened')
|
28
30
|
spinner.stop
|
29
31
|
|
30
32
|
if issues.empty?
|
@@ -33,7 +35,7 @@ module Dri
|
|
33
35
|
end
|
34
36
|
|
35
37
|
logger.info "Assembling the report... "
|
36
|
-
# sets each failure on the table
|
38
|
+
# sets each failure on the table
|
37
39
|
action_options = ["pinged SET", "reproduced", "transient", "quarantined"]
|
38
40
|
|
39
41
|
spinner.start
|
@@ -41,17 +43,27 @@ module Dri
|
|
41
43
|
actions = []
|
42
44
|
|
43
45
|
if @options[:actions]
|
44
|
-
actions = prompt.multi_select(
|
46
|
+
actions = prompt.multi_select(
|
47
|
+
"Please mark the actions on #{add_color(issue['title'], :yellow)}: ",
|
48
|
+
action_options
|
49
|
+
)
|
45
50
|
end
|
51
|
+
|
46
52
|
report.add_failure(issue, actions)
|
47
53
|
end
|
48
54
|
|
49
55
|
if @options[:format] == 'list'
|
50
56
|
# generates markdown list with failures
|
51
57
|
format_style = Utils::MarkdownLists.make_list(report.labels, report.failures) unless report.failures.empty?
|
52
|
-
else
|
58
|
+
else
|
53
59
|
# generates markdown table with rows as failures
|
54
|
-
|
60
|
+
unless report.failures.empty?
|
61
|
+
format_style = MarkdownTables.make_table(
|
62
|
+
report.labels,
|
63
|
+
report.failures,
|
64
|
+
is_rows: true, align: %w[l l l l l]
|
65
|
+
)
|
66
|
+
end
|
55
67
|
end
|
56
68
|
|
57
69
|
report.set_header(timezone, username)
|
@@ -71,7 +83,7 @@ module Dri
|
|
71
83
|
File.open(report_path, 'a') do |out_file|
|
72
84
|
out_file.puts note
|
73
85
|
end
|
74
|
-
|
86
|
+
|
75
87
|
spinner.stop
|
76
88
|
|
77
89
|
output.puts "Done! ✅\n"
|
@@ -82,10 +94,13 @@ module Dri
|
|
82
94
|
# sends note to the weekly triage report
|
83
95
|
issues = api_client.fetch_current_triage_issue
|
84
96
|
current_issue_iid = issues[0]["iid"]
|
85
|
-
|
97
|
+
|
98
|
+
api_client.post_triage_report_note(iid: current_issue_iid, body: note)
|
86
99
|
|
87
100
|
output.puts "Done! ✅\n"
|
88
|
-
logger.success
|
101
|
+
logger.success(<<~MSG)
|
102
|
+
Thanks @#{username}, your report was posted at https://gitlab.com/gitlab-org/quality/pipeline-triage/-/issues/#{current_issue_iid} 🎉
|
103
|
+
MSG
|
89
104
|
end
|
90
105
|
end
|
91
106
|
end
|
data/lib/dri/commands/publish.rb
CHANGED
@@ -5,16 +5,15 @@ require 'thor'
|
|
5
5
|
module Dri
|
6
6
|
module Commands
|
7
7
|
class Publish < Thor
|
8
|
-
|
9
8
|
namespace :publish
|
10
9
|
|
11
10
|
desc 'report', 'Generate a report'
|
12
11
|
method_option :dry_run, type: :boolean,
|
13
|
-
|
14
|
-
method_option :format, aliases: '-f', type: :string, :
|
15
|
-
|
12
|
+
desc: 'Generates a report locally'
|
13
|
+
method_option :format, aliases: '-f', type: :string, default: "table",
|
14
|
+
desc: 'Formats the report'
|
16
15
|
method_option :actions, type: :boolean,
|
17
|
-
|
16
|
+
desc: 'Updates actions on failures'
|
18
17
|
def report(*)
|
19
18
|
if options[:help]
|
20
19
|
invoke :help, ['report']
|
@@ -10,9 +10,9 @@ module Dri
|
|
10
10
|
@options = options
|
11
11
|
end
|
12
12
|
|
13
|
-
def execute(input: $stdin, output: $stdout)
|
13
|
+
def execute(input: $stdin, output: $stdout) # rubocop:disable Metrics/AbcSize
|
14
14
|
verify_config_exists
|
15
|
-
|
15
|
+
|
16
16
|
remove = prompt.yes? "Are you sure you want to remove all #{emoji} award emojis from issues?"
|
17
17
|
|
18
18
|
unless remove
|
@@ -29,7 +29,7 @@ module Dri
|
|
29
29
|
spinner.stop
|
30
30
|
|
31
31
|
issues_with_award_emoji.each do |issue|
|
32
|
-
logger.info "Removing #{emoji} from #{issue[
|
32
|
+
logger.info "Removing #{emoji} from #{issue['web_url']}..."
|
33
33
|
|
34
34
|
award_emoji_url = issue["_links"]["award_emoji"]
|
35
35
|
|
@@ -37,8 +37,8 @@ module Dri
|
|
37
37
|
|
38
38
|
emoji_found = response.find { |e| e['name'] == emoji && e['user']['username'] == username }
|
39
39
|
|
40
|
-
|
41
|
-
url = "#{award_emoji_url}/#{emoji_found[
|
40
|
+
unless emoji_found.nil?
|
41
|
+
url = "#{award_emoji_url}/#{emoji_found['id']}"
|
42
42
|
api_client.delete_award_emoji(url)
|
43
43
|
end
|
44
44
|
end
|
@@ -13,7 +13,7 @@ module Dri
|
|
13
13
|
|
14
14
|
def execute(input: $stdin, output: $stdout)
|
15
15
|
verify_config_exists
|
16
|
-
|
16
|
+
|
17
17
|
remove = prompt.yes? "Are you sure you want to remove existing profile?"
|
18
18
|
|
19
19
|
unless remove
|
@@ -22,7 +22,7 @@ module Dri
|
|
22
22
|
end
|
23
23
|
|
24
24
|
logger.info "Removing profile..."
|
25
|
-
|
25
|
+
|
26
26
|
FileUtils.rm("#{Dir.pwd}/.dri_profile.yml")
|
27
27
|
|
28
28
|
logger.success "Done ✅"
|
data/lib/dri/commands/rm.rb
CHANGED
@@ -6,7 +6,7 @@ module Refinements
|
|
6
6
|
# @param [Integer] limit the limit of characters
|
7
7
|
# @return [String] the resulting string after truncation
|
8
8
|
def truncate(limit = 50)
|
9
|
-
return
|
9
|
+
return freeze if size <= limit
|
10
10
|
|
11
11
|
# limit - 3 for the ellipses
|
12
12
|
self[0...(limit - 3)] << '...'
|