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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b85b61821add760ea7d427236007207fa161d5e2f80c04f99b8dda1c9bc6118
4
- data.tar.gz: 6afeded710d46ba016e28500ff771a8c6c16f99f68ae40d7cc4435a193261958
3
+ metadata.gz: c97a5f29dfdbcd35bb1f57afbe46122f68fcf91d1eba479aba591d1ae7d5f37d
4
+ data.tar.gz: 761a96ce629f7800fddb4b828a962ca4379b5ac8a8b9c1665b0f0b131d44d5b7
5
5
  SHA512:
6
- metadata.gz: 7fb8fd29609fb8a4f5a13c0dee6167322d91072ef7dbd438455f2cb24181aad6e5a62d226eef6e6f4787173b8ce0cd756535c6eccf94587f2675c5a3509d81c8
7
- data.tar.gz: 5875777fcb72109e91646e0346cf7a6b192061fe752dc818533225421cf82d5182ddd8d644c57a183d6b3fb938aff341c5849b43116c1728048d2f75e5bab9b9
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: false, board: board, path: path)
28
+ jira_search_by_jql(jql: jql, initial_query: true, board: board, path: path)
29
29
  end
30
30
  end
31
31
 
@@ -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 = nil
70
- if a[0] == b[0]
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
- result = 1
72
+ 1
74
73
  elsif b[0].nil?
75
- result = -1
74
+ -1
76
75
  else
77
- result = a[0] <=> b[0]
76
+ a[0] <=> b[0]
78
77
  end
79
78
 
80
- # This will only happen if one of the objects isn't comparable. Seen in production.
81
- result = -1 if result.nil?
82
- result
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
@@ -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
- board.project_config.file_system.log(
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 #{item['toString'].inspect}). Using id 0."
919
+ "(from #{item['fromString'].inspect} to #{to_name.inspect}). #{id_note}"
911
920
  )
912
- item = item.merge('to' => '0')
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
- stdout, stderr, status = capture3(command, stdin_data: stdin_data)
30
- unless status.success?
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jirametrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.29.2
4
+ version: 2.29.3pre4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Bowler