jirametrics 2.29.2 → 2.29.3pre4
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/lib/jirametrics/downloader_for_data_center.rb +1 -1
- data/lib/jirametrics/exporter.rb +6 -0
- data/lib/jirametrics/file_config.rb +9 -11
- data/lib/jirametrics/issue.rb +12 -3
- data/lib/jirametrics/jira_gateway.rb +26 -7
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c97a5f29dfdbcd35bb1f57afbe46122f68fcf91d1eba479aba591d1ae7d5f37d
|
|
4
|
+
data.tar.gz: 761a96ce629f7800fddb4b828a962ca4379b5ac8a8b9c1665b0f0b131d44d5b7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dc69c58ff1e5eb6d54ca9f2d2fddc0ce121c3dc80e2af36389a9ddeeefee4a627662aab7516ae72500e81a2538673b966dc7d9b536229736e394afe51df1b386
|
|
7
|
+
data.tar.gz: 2120a9ec7a7ef3b28885dc9d04cec559331a4ce294c342b62eaf8dcda9d2554993221eeb1d36ddd34ad23142800a58baad52bd000a2f93b8694e68243fcaeb8c
|
|
@@ -25,7 +25,7 @@ class DownloaderForDataCenter < Downloader
|
|
|
25
25
|
keys_to_request = @issue_keys_pending_download[0..99]
|
|
26
26
|
@issue_keys_pending_download.reject! { |key| keys_to_request.include? key }
|
|
27
27
|
jql = "key in (#{keys_to_request.join(', ')})"
|
|
28
|
-
jira_search_by_jql(jql: jql, initial_query:
|
|
28
|
+
jira_search_by_jql(jql: jql, initial_query: true, board: board, path: path)
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
data/lib/jirametrics/exporter.rb
CHANGED
|
@@ -10,6 +10,7 @@ class Exporter
|
|
|
10
10
|
logfile_name = 'jirametrics.log'
|
|
11
11
|
logfile = File.open(logfile_name, 'w')
|
|
12
12
|
rescue Errno::EACCES
|
|
13
|
+
# FileSystem can't be used here — it hasn't been created yet (it depends on this logfile).
|
|
13
14
|
warn "Error: Cannot write to #{File.expand_path(logfile_name)}. " \
|
|
14
15
|
'Please ensure the current directory is writable.'
|
|
15
16
|
exit 1
|
|
@@ -72,18 +73,23 @@ class Exporter
|
|
|
72
73
|
|
|
73
74
|
def info key, name_filter:
|
|
74
75
|
selected = []
|
|
76
|
+
file_system.log_only = true
|
|
75
77
|
each_project_config(name_filter: name_filter) do |project|
|
|
76
78
|
project.evaluate_next_level
|
|
77
79
|
|
|
78
80
|
project.run load_only: true
|
|
79
81
|
project.issues.each do |issue|
|
|
80
82
|
selected << [project, issue] if key == issue.key
|
|
83
|
+
issue.subtasks.each do |subtask|
|
|
84
|
+
selected << [project, subtask] if key == subtask.key
|
|
85
|
+
end
|
|
81
86
|
end
|
|
82
87
|
rescue => e # rubocop:disable Style/RescueStandardError
|
|
83
88
|
# This happens when we're attempting to load an aggregated project because it hasn't been
|
|
84
89
|
# properly initialized. Since we don't care about aggregated projects, we just ignore it.
|
|
85
90
|
raise unless e.message.start_with? 'This is an aggregated project and issues should have been included'
|
|
86
91
|
end
|
|
92
|
+
file_system.log_only = false
|
|
87
93
|
|
|
88
94
|
if selected.empty?
|
|
89
95
|
file_system.log "No issues found to match #{key.inspect}"
|
|
@@ -65,22 +65,20 @@ class FileConfig
|
|
|
65
65
|
# most common usecase - the Team Dashboard from FocusedObjective.com. The rule for that one
|
|
66
66
|
# is that all empty values in the first column should be at the bottom.
|
|
67
67
|
def sort_output all_lines
|
|
68
|
-
all_lines.sort do |a, b|
|
|
69
|
-
result =
|
|
70
|
-
|
|
71
|
-
result = a[1..] <=> b[1..]
|
|
68
|
+
all_lines.each_with_index.sort do |(a, a_idx), (b, b_idx)|
|
|
69
|
+
result = if a[0] == b[0]
|
|
70
|
+
a[1..] <=> b[1..]
|
|
72
71
|
elsif a[0].nil?
|
|
73
|
-
|
|
72
|
+
1
|
|
74
73
|
elsif b[0].nil?
|
|
75
|
-
|
|
74
|
+
-1
|
|
76
75
|
else
|
|
77
|
-
|
|
76
|
+
a[0] <=> b[0]
|
|
78
77
|
end
|
|
79
78
|
|
|
80
|
-
#
|
|
81
|
-
result
|
|
82
|
-
|
|
83
|
-
end
|
|
79
|
+
# When objects aren't comparable, preserve original order for a stable sort.
|
|
80
|
+
result.nil? || result.zero? ? a_idx <=> b_idx : result
|
|
81
|
+
end.map(&:first)
|
|
84
82
|
end
|
|
85
83
|
|
|
86
84
|
def columns &block
|
data/lib/jirametrics/issue.rb
CHANGED
|
@@ -905,11 +905,20 @@ class Issue
|
|
|
905
905
|
|
|
906
906
|
history['items']&.each do |item|
|
|
907
907
|
if item['field'] == 'status' && item['to'].nil?
|
|
908
|
-
|
|
908
|
+
to_name = item['toString']
|
|
909
|
+
matches = board.possible_statuses.find_all_by_name(to_name)
|
|
910
|
+
guessed_id, id_note = if matches.length == 1
|
|
911
|
+
[matches.first.id.to_s, "Guessed id #{matches.first.id} from status name."]
|
|
912
|
+
elsif matches.length > 1
|
|
913
|
+
['0', "Multiple statuses named #{to_name.inspect} exist (ids: #{matches.map(&:id).join(', ')}); cannot disambiguate. Using id 0."]
|
|
914
|
+
else
|
|
915
|
+
['0', "No known status named #{to_name.inspect}. Using id 0."]
|
|
916
|
+
end
|
|
917
|
+
board.project_config.file_system.warning(
|
|
909
918
|
"Issue #{key} has a status change without a 'to' id " \
|
|
910
|
-
"(from #{item['fromString'].inspect} to #{
|
|
919
|
+
"(from #{item['fromString'].inspect} to #{to_name.inspect}). #{id_note}"
|
|
911
920
|
)
|
|
912
|
-
item = item.merge('to' =>
|
|
921
|
+
item = item.merge('to' => guessed_id)
|
|
913
922
|
end
|
|
914
923
|
|
|
915
924
|
@changes << ChangeItem.new(raw: item, time: created, author_raw: history['author'])
|
|
@@ -9,6 +9,10 @@ class JiraGateway
|
|
|
9
9
|
attr_accessor :ignore_ssl_errors
|
|
10
10
|
attr_reader :jira_url, :settings, :file_system
|
|
11
11
|
|
|
12
|
+
RETRYABLE_EXIT_CODES = [7, 28, 35, 56].freeze
|
|
13
|
+
MAX_RETRIES = 3
|
|
14
|
+
RETRY_DELAY_SECONDS = 5
|
|
15
|
+
|
|
12
16
|
def initialize file_system:, jira_config:, settings:
|
|
13
17
|
@file_system = file_system
|
|
14
18
|
load_jira_config(jira_config)
|
|
@@ -26,8 +30,23 @@ class JiraGateway
|
|
|
26
30
|
log_entry = sanitize_message log_entry
|
|
27
31
|
@file_system.log log_entry
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
retries = 0
|
|
34
|
+
loop do
|
|
35
|
+
stdout, stderr, status = capture3(command, stdin_data: stdin_data)
|
|
36
|
+
|
|
37
|
+
if status.success?
|
|
38
|
+
@file_system.log "Returned (stderr): #{stderr.inspect}" unless stderr == ''
|
|
39
|
+
raise 'no response from curl on stdout' if stdout == ''
|
|
40
|
+
return parse_response(command: command, result: stdout)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if RETRYABLE_EXIT_CODES.include?(status.exitstatus) && retries < MAX_RETRIES
|
|
44
|
+
retries += 1
|
|
45
|
+
@file_system.log "Transient network error (exit #{status.exitstatus}), retrying in #{RETRY_DELAY_SECONDS}s (attempt #{retries}/#{MAX_RETRIES})..."
|
|
46
|
+
sleep_between_retries
|
|
47
|
+
next
|
|
48
|
+
end
|
|
49
|
+
|
|
31
50
|
@file_system.error "Failed call with exit status #{status.exitstatus}!"
|
|
32
51
|
@file_system.error "Returned (stdout): #{stdout.inspect}"
|
|
33
52
|
@file_system.error "Returned (stderr): #{stderr.inspect}"
|
|
@@ -37,11 +56,6 @@ class JiraGateway
|
|
|
37
56
|
raise "Failed call with exit status #{status.exitstatus}. " \
|
|
38
57
|
"See #{@file_system.logfile_name} for details"
|
|
39
58
|
end
|
|
40
|
-
|
|
41
|
-
@file_system.log "Returned (stderr): #{stderr.inspect}" unless stderr == ''
|
|
42
|
-
raise 'no response from curl on stdout' if stdout == ''
|
|
43
|
-
|
|
44
|
-
parse_response(command: command, result: stdout)
|
|
45
59
|
end
|
|
46
60
|
|
|
47
61
|
def capture3 command, stdin_data:
|
|
@@ -49,6 +63,11 @@ class JiraGateway
|
|
|
49
63
|
Open3.capture3(command, stdin_data: stdin_data)
|
|
50
64
|
end
|
|
51
65
|
|
|
66
|
+
def sleep_between_retries
|
|
67
|
+
# In its own method so we can mock it out in tests
|
|
68
|
+
sleep RETRY_DELAY_SECONDS
|
|
69
|
+
end
|
|
70
|
+
|
|
52
71
|
def call_url relative_url:
|
|
53
72
|
command = make_curl_command url: "#{@jira_url}#{relative_url}"
|
|
54
73
|
exec_and_parse_response command: command, stdin_data: nil
|