abide_dev_utils 0.16.1 → 0.17.1

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: 9ba88038da5800b45085201557c28a7d4096027230083e0c8d5a811a7fd1de5a
4
- data.tar.gz: 78da6fd7887fdb50a7de4505b4dd95b6fa2e24400dc6b5419dcc44aaa1f15d24
3
+ metadata.gz: ba0491c8fbcaa0a69015c410a2aa7e61ac61e6b30caca6fb5b334d22fd77069c
4
+ data.tar.gz: 119c837a5ea897957e63744ec3f38a7567222d7dbb5b952302ec71cd70aafc24
5
5
  SHA512:
6
- metadata.gz: 3e1577583c8fcc597f75e02d3ef5edee126984f8daaea8fc98741c718c0254044767189acf24e0e21ba2b60607f4042c1f0c52be934dbc129d270008042f8bf5
7
- data.tar.gz: eb8b1038e223efeec1f7a2b3d0d0ee0f04cba49f385b58c8de5e1dfe8c2d935dac0d760faf65d8ab45f0b539f4729ea9195926d0cce1d4fe294c19f6e426e879
6
+ metadata.gz: 492066b562bee8dd3c3d3b465396a569d66a66200018bb74e606c8f3a78320853ecc71a8f30ee0c4f18194dbdc0d8679ff8c32fd38fbaf7936267eae1b35d5eb
7
+ data.tar.gz: 2766d7c84c887b573cd9cb23f5d2aa869a4f238e5208ab2377832827e88d44056d180226a31c93ee242ed954d6aaf65b0e075d282b65ba53ea475ae1adb8e45a
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- abide_dev_utils (0.16.1)
4
+ abide_dev_utils (0.17.1)
5
5
  cmdparse (~> 3.0)
6
6
  facterdb (>= 1.21)
7
7
  google-cloud-storage (~> 1.34)
8
8
  hashdiff (~> 1.0)
9
9
  jira-ruby (~> 2.2)
10
10
  nokogiri (~> 1.13)
11
- puppet (>= 6.23)
11
+ puppet (>= 7.0.0)
12
12
  puppet-strings (>= 2.7)
13
13
  ruby-progressbar (~> 1.11)
14
14
  selenium-webdriver (~> 4.0.0.beta4)
@@ -69,7 +69,7 @@ GEM
69
69
  faraday-http-cache (2.3.0)
70
70
  faraday (>= 0.8)
71
71
  faraday-net_http (2.0.3)
72
- fast_gettext (1.8.0)
72
+ fast_gettext (2.3.0)
73
73
  fiber-local (1.0.0)
74
74
  gem-release (2.2.2)
75
75
  github_changelog_generator (1.16.4)
@@ -137,6 +137,8 @@ GEM
137
137
  multi_json (1.15.0)
138
138
  multipart-post (2.3.0)
139
139
  nio4r (2.5.8)
140
+ nokogiri (1.15.2-arm64-darwin)
141
+ racc (~> 1.4)
140
142
  nokogiri (1.15.2-x86_64-darwin)
141
143
  racc (~> 1.4)
142
144
  nokogiri (1.15.2-x86_64-linux)
@@ -265,6 +267,7 @@ GEM
265
267
  yard (0.9.34)
266
268
 
267
269
  PLATFORMS
270
+ arm64-darwin-22
268
271
  x86_64-darwin-19
269
272
  x86_64-darwin-20
270
273
  x86_64-linux
@@ -273,7 +276,7 @@ DEPENDENCIES
273
276
  abide_dev_utils!
274
277
  bundler
275
278
  console
276
- fast_gettext (~> 1.8)
279
+ fast_gettext (>= 2.0)
277
280
  gem-release
278
281
  github_changelog_generator
279
282
  pry
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
34
34
  # Prod dependencies
35
35
  spec.add_dependency 'nokogiri', '~> 1.13'
36
36
  spec.add_dependency 'cmdparse', '~> 3.0'
37
- spec.add_dependency 'puppet', '>= 6.23'
37
+ spec.add_dependency 'puppet', '>= 7.0.0'
38
38
  spec.add_dependency 'puppet-strings', '>= 2.7'
39
39
  spec.add_dependency 'jira-ruby', '~> 2.2'
40
40
  spec.add_dependency 'ruby-progressbar', '~> 1.11'
@@ -56,7 +56,7 @@ Gem::Specification.new do |spec|
56
56
  spec.add_development_dependency 'rubocop-ast', '~> 1.4'
57
57
  spec.add_development_dependency 'rubocop-performance', '~> 1.9'
58
58
  spec.add_development_dependency 'rubocop-i18n', '~> 3.0'
59
- spec.add_development_dependency 'fast_gettext', '~> 1.8'
59
+ spec.add_development_dependency 'fast_gettext', '>= 2.0'
60
60
 
61
61
  # For more information and examples about making a new gem, checkout our
62
62
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -382,7 +382,12 @@ module AbideDevUtils
382
382
  # @valid_level is populated in verify_profile_and_level_selections from the fact that we've given
383
383
  # the generator a list of levels we want to use. If we didn't give it a list of levels, then we
384
384
  # want to use all of the levels that the control supports from @control.
385
- @md.add_ul('Supported Levels:')
385
+ if @framework == 'stig'
386
+ @md.add_ul('Supported MAC Levels:')
387
+ else
388
+ @md.add_ul('Supported Levels:')
389
+ end
390
+
386
391
  if @valid_level.empty?
387
392
  @control.levels.each do |l|
388
393
  @md.add_ul(@md.code(l), indent: 1)
@@ -400,7 +405,12 @@ module AbideDevUtils
400
405
  # @valid_profile is populated in verify_profile_and_level_selections from the fact that we've given
401
406
  # the generator a list of profiles we want to use. If we didn't give it a list of profiles, then we
402
407
  # want to use all of the profiles that the control supports from @control.
403
- @md.add_ul('Supported Profiles:')
408
+ if @framework == 'stig'
409
+ @md.add_ul('Supported Confidentiality:')
410
+ else
411
+ @md.add_ul('Supported Profiles:')
412
+ end
413
+
404
414
  if @valid_profile.empty?
405
415
  @control.profiles.each do |l|
406
416
  @md.add_ul(@md.code(l), indent: 1)
@@ -413,7 +423,7 @@ module AbideDevUtils
413
423
  end
414
424
 
415
425
  def control_alternate_ids_builder
416
- return if @framework == 'stig'
426
+ # return if @framework == 'stig'
417
427
 
418
428
  @md.add_ul('Alternate Config IDs:')
419
429
  @control.alternate_ids.each do |l|
@@ -6,9 +6,6 @@ require 'abide_dev_utils/jira'
6
6
 
7
7
  module Abide
8
8
  module CLI
9
- CONFIG = AbideDevUtils::Config
10
- JIRA = AbideDevUtils::Jira
11
-
12
9
  class JiraCommand < CmdParse::Command
13
10
  CMD_NAME = 'jira'
14
11
  CMD_SHORT = 'Commands related to Jira tickets'
@@ -38,11 +35,9 @@ module Abide
38
35
  end
39
36
 
40
37
  def execute
41
- client = JIRA.client
42
- myself = JIRA.myself(client)
43
- return if myself.attrs['displayName'].empty?
38
+ return if AbideDevUtils::Jira.client.myself.attrs['displayName'].empty?
44
39
 
45
- Abide::CLI::OUTPUT.simple("Successfully authenticated user #{myself.attrs['name']}!")
40
+ Abide::CLI::OUTPUT.simple("Successfully authenticated user #{AbideDevUtils::Jira.client.myself.attrs['displayName']}!")
46
41
  end
47
42
  end
48
43
 
@@ -59,8 +54,7 @@ module Abide
59
54
  end
60
55
 
61
56
  def execute(issue)
62
- client = JIRA.client(options: {})
63
- issue = client.Issue.find(issue)
57
+ issue = AbideDevUtils::Jira.client.find(:issue, issue)
64
58
  console = @data[:file].nil?
65
59
  out_json = issue.attrs.select { |_, v| !v.nil? || !v.empty? }
66
60
  Abide::CLI::OUTPUT.json(out_json, console: console, file: @data[:file])
@@ -83,13 +77,15 @@ module Abide
83
77
  end
84
78
 
85
79
  def execute(project, summary, *subtasks)
86
- client = JIRA.client(options: {})
87
- issue = JIRA.new_issue(client, project, summary)
80
+ issue = AbideDevUtils::Jira.client.create(:issue, project: project, summary: summary)
88
81
  Abide::CLI::OUTPUT.simple("Successfully created #{issue.attrs['key']}")
89
82
  return if subtasks.nil? || subtasks.empty?
90
83
 
91
84
  Abide::CLI::OUTPUT.simple('Creatings subtasks...')
92
- JIRA.bulk_new_subtask(client, JIRA.issue(client, issue.attrs['key']), subtasks) unless subtasks.empty?
85
+ subtasks.each do |sum|
86
+ subtask = AbideDevUtils::Jira.client.create(:subtask, parent: issue, summary: sum)
87
+ Abide::CLI::OUTPUT.simple("Successfully created #{subtask.attrs['key']}")
88
+ end
93
89
  end
94
90
  end
95
91
 
@@ -107,11 +103,8 @@ module Abide
107
103
 
108
104
  def execute(report, project)
109
105
  Abide::CLI::VALIDATE.file(report)
110
- @data[:dry_run] = false if @data[:dry_run].nil?
111
- client = JIRA.client(options: {})
112
- proj = JIRA.project(client, project)
113
106
  File.open(report) do |f|
114
- JIRA.new_issues_from_coverage(client, proj, JSON.parse(f.read), dry_run: @data[:dry_run])
107
+ AbideDevUtils::Jira.new_issues_from_coverage(project, JSON.parse(f.read), dry_run: @data[:dry_run])
115
108
  end
116
109
  end
117
110
  end
@@ -125,16 +118,38 @@ module Abide
125
118
  short_desc(CMD_SHORT)
126
119
  long_desc(CMD_LONG)
127
120
  argument_desc(PATH: 'An XCCDF file', PROJECT: 'A Jira project')
128
- options.on('-d', '--dry-run', 'Print to console instead of saving objects') { |_| @data[:dry_run] = true }
121
+ options.on('-d', '--dry-run', 'Runs through mock issue creation. Useful for testing, but not reliable for knowing what exactly will be created. Use --explain for more accurate information.') do
122
+ @data[:dry_run] = true
123
+ end
124
+ options.on('-x', '--explain', 'Shows a report of all the controls that will and won\'t be created as issues, and why. DOES NOT create issues.') do
125
+ @data[:explain] = true
126
+ end
129
127
  options.on('-e [EPIC]', '--epic [EPIC]', 'If given, tasks will be created and assigned to this epic. Takes form <PROJECT>-<NUM>') { |e| @data[:epic] = e }
128
+ options.on('-l [LEVEL]', '--level [LEVEL]', 'Only create tasks for rules belonging to the matching level. Takes a string that is treated as RegExp') do |x|
129
+ @data[:level] = x
130
+ end
131
+ options.on('-p [PROFILE]', '--profile [PROFILE]', 'Only create tasks for rules belonging to the matching profile. Takes a string that is treated as RegExp') do |x|
132
+ @data[:profile] = x
133
+ end
130
134
  end
131
135
 
132
136
  def execute(path, project)
133
137
  Abide::CLI::VALIDATE.file(path)
134
- @data[:dry_run] = false if @data[:dry_run].nil?
135
- client = JIRA.client(options: {})
136
- proj = JIRA.project(client, project)
137
- JIRA.new_issues_from_xccdf(client, proj, path, epic: @data[:epic], dry_run: @data[:dry_run])
138
+ # Each control gets assigned labels based on the levels and profiles it supports.
139
+ # Those labels all take the form "level_<level>_<profile>". This allows us to
140
+ # filter the controls we want to create tasks for by level and profile.
141
+ @data[:label_include] = nil
142
+ @data[:label_include] = "level_#{@data[:level]}_" if @data[:level]
143
+ @data[:label_include] = "#{@data[:label_include]}#{@data[:profile]}" if @data[:profile]
144
+ Abide::CLI::OUTPUT.simple "Label include: #{@data[:label_include]}"
145
+ AbideDevUtils::Jira.new_issues_from_xccdf(
146
+ project,
147
+ path,
148
+ epic: @data[:epic],
149
+ dry_run: @data[:dry_run],
150
+ explain: @data[:explain],
151
+ label_include: @data[:label_include],
152
+ )
138
153
  end
139
154
  end
140
155
 
@@ -167,17 +182,15 @@ module Abide
167
182
  def execute(path1, path2, project)
168
183
  Abide::CLI::VALIDATE.file(path1)
169
184
  Abide::CLI::VALIDATE.file(path2)
170
- @data[:dry_run] = false if @data[:dry_run].nil?
171
- client = JIRA.client(options: {})
172
- proj = JIRA.project(client, project)
173
- JIRA.new_issues_from_xccdf_diff(client,
174
- proj,
175
- path1,
176
- path2,
177
- epic: @data[:epic],
178
- dry_run: @data[:dry_run],
179
- auto_approve: @data[:auto_approve],
180
- diff_opts: @data[:diff_opts])
185
+ AbideDevUtils::Jira.new_issues_from_xccdf_diff(
186
+ project,
187
+ path1,
188
+ path2,
189
+ epic: @data[:epic],
190
+ dry_run: @data[:dry_run],
191
+ auto_approve: @data[:auto_approve],
192
+ diff_opts: @data[:diff_opts],
193
+ )
181
194
  end
182
195
  end
183
196
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'client_builder'
4
+ require_relative 'dry_run'
5
+ require_relative 'finder'
6
+ require_relative 'helper'
7
+ require_relative 'issue_builder'
8
+ require_relative '../config'
9
+ require_relative '../errors/jira'
10
+
11
+ module AbideDevUtils
12
+ module Jira
13
+ class Client
14
+ extend DryRun
15
+
16
+ dry_run :create, :find, :myself
17
+
18
+ attr_accessor :default_project
19
+ attr_reader :config
20
+
21
+ def initialize(dry_run: false, **options)
22
+ @dry_run = dry_run
23
+ @options = options
24
+ @config = AbideDevUtils::Config.config_section('jira')
25
+ @default_project = @config[:default_project]
26
+ @client = nil
27
+ @finder = nil
28
+ @issue_builder = nil
29
+ @helper = nil
30
+ end
31
+
32
+ def myself
33
+ @myself ||= finder.myself
34
+ end
35
+
36
+ def find(type, id)
37
+ raise ArgumentError, "Invalid type #{type}" unless finder.respond_to?(type.to_sym)
38
+
39
+ finder.send(type.to_sym, id)
40
+ end
41
+
42
+ def create(type, **fields)
43
+ issue_builder.create(type, **fields)
44
+ end
45
+
46
+ def helper
47
+ @helper ||= Helper.new(self, dry_run: @dry_run)
48
+ end
49
+
50
+ def translate_issue_custom_field(name)
51
+ IssueBuilder::CUSTOM_FIELDS[name] || IssueBuilder::CUSTOM_FIELDS.invert[name]
52
+ end
53
+
54
+ private
55
+
56
+ def client
57
+ @client ||= ClientBuilder.new(@config, **@options).build
58
+ end
59
+
60
+ def finder
61
+ @finder ||= Finder.new(client)
62
+ end
63
+
64
+ def issue_builder
65
+ @issue_builder ||= IssueBuilder.new(client, finder)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jira-ruby'
4
+ require_relative '../prompt'
5
+
6
+ module AbideDevUtils
7
+ module Jira
8
+ class ClientBuilder
9
+ def initialize(config, **options)
10
+ @options = options
11
+ @config = config
12
+ end
13
+
14
+ def username
15
+ find_option_value(:username)
16
+ end
17
+
18
+ def username=(username)
19
+ @options[:username] = username
20
+ end
21
+
22
+ def password
23
+ if find_option_value(:password)
24
+ '********'
25
+ else
26
+ nil
27
+ end
28
+ end
29
+
30
+ def password=(password)
31
+ @options[:password] = password
32
+ end
33
+
34
+ def site
35
+ find_option_value(:site)
36
+ end
37
+
38
+ def site=(site)
39
+ @options[:site] = site
40
+ end
41
+
42
+ def context_path
43
+ find_option_value(:context_path, default: '')
44
+ end
45
+
46
+ def context_path=(context_path)
47
+ @options[:context_path] = context_path
48
+ end
49
+
50
+ def auth_type
51
+ find_option_value(:auth_type, default: :basic)
52
+ end
53
+
54
+ def auth_type=(auth_type)
55
+ @options[:auth_type] = auth_type
56
+ end
57
+
58
+ def http_debug
59
+ find_option_value(:http_debug, default: false)
60
+ end
61
+
62
+ def http_debug=(http_debug)
63
+ @options[:http_debug] = http_debug
64
+ end
65
+
66
+ def build
67
+ JIRA::Client.new({
68
+ username: find_option_value(:username, prompt: true),
69
+ password: find_option_value(:password, prompt: true),
70
+ site: find_option_value(:site, prompt: 'Jira site URL'),
71
+ context_path: find_option_value(:context_path, default: ''),
72
+ auth_type: find_option_value(:auth_type, default: :basic),
73
+ http_debug: find_option_value(:http_debug, default: false),
74
+ })
75
+ end
76
+
77
+ private
78
+
79
+ def find_option_value(key, default: nil, prompt: nil)
80
+ if prompt
81
+ find_option_value_or_prompt(key, prompt)
82
+ else
83
+ find_option_value_or_default(key, default)
84
+ end
85
+ end
86
+
87
+ def find_option_val(key)
88
+ @options[key] || @config[key] || ENV["JIRA_#{key.to_s.upcase}"]
89
+ end
90
+
91
+ def find_option_value_or_prompt(key, prompt = 'Enter value')
92
+ case key
93
+ when /password/i
94
+ find_option_val(key) || AbideDevUtils::Prompt.password
95
+ when /username/i
96
+ find_option_val(key) || AbideDevUtils::Prompt.username
97
+ else
98
+ find_option_val(key) || AbideDevUtils::Prompt.single_line(prompt)
99
+ end
100
+ end
101
+
102
+ def find_option_value_or_default(key, default)
103
+ find_option_val(key) || default
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../output'
4
+
5
+ module AbideDevUtils
6
+ module Jira
7
+ module DryRun
8
+ def dry_run(*method_names)
9
+ method_names.each do |method_name|
10
+ proxy = Module.new do
11
+ define_method(method_name) do |*args, **kwargs|
12
+ if !!@dry_run
13
+ case method_name
14
+ when %r{^create}
15
+ AbideDevUtils::Output.simple("DRY RUN: #{self.class.name}##{method_name}(#{args[0]}, #{kwargs.map { |k, v| "#{k}: #{v.inspect}" }.join(', ')})")
16
+ sleep 0.1
17
+ return DummyIssue.new if args[0].match?(%r{^issue$})
18
+ return DummySubtask.new if args[0].match?(%r{^subtask$})
19
+ when %r{^find}
20
+ AbideDevUtils::Output.simple("DRY RUN: #{self.class.name}##{method_name}(#{args[0]}, #{kwargs.inspect})")
21
+ return DummyIssue.new if args[0].match?(%r{^issue$})
22
+ return DummySubtask.new if args[0].match?(%r{^subtask$})
23
+ return DummyProject.new if args[0].match?(%r{^project$})
24
+ return [] if args[0].match?(%r{^issues_by_jql$})
25
+
26
+ "Dummy #{args[0].capitalize}"
27
+ else
28
+ AbideDevUtils::Output.simple("DRY RUN: #{self.class.name}##{method_name}(#{args.map(&:inspect).join(', ')}, #{kwargs.map { |k, v| "#{k}: #{v.inspect}" }.join(', ')})")
29
+ end
30
+ else
31
+ super(*args, **kwargs)
32
+ end
33
+ end
34
+ end
35
+ self.prepend(proxy)
36
+ end
37
+ end
38
+
39
+ def dry_run_simple(*method_names)
40
+ method_names.each do |method_name|
41
+ proxy = Module.new do
42
+ define_method(method_name) do |*args, **kwargs|
43
+ return if !!@dry_run
44
+
45
+ super(*args, **kwargs)
46
+ end
47
+ end
48
+ self.prepend(proxy)
49
+ end
50
+ end
51
+
52
+ def dry_run_return_true(*method_names)
53
+ method_names.each do |method_name|
54
+ proxy = Module.new do
55
+ define_method(method_name) do |*args, **kwargs|
56
+ return true if !!@dry_run
57
+
58
+ super(*args, **kwargs)
59
+ end
60
+ end
61
+ self.prepend(proxy)
62
+ end
63
+ end
64
+
65
+ def dry_run_return_false(*method_names)
66
+ method_names.each do |method_name|
67
+ proxy = Module.new do
68
+ define_method(method_name) do |*args, **kwargs|
69
+ return false if !!@dry_run
70
+
71
+ super(*args, **kwargs)
72
+ end
73
+ end
74
+ self.prepend(proxy)
75
+ end
76
+ end
77
+
78
+ class Dummy
79
+ attr_reader :dummy
80
+
81
+ def initialize(*_args, **_kwargs)
82
+ @dummy = true
83
+ end
84
+ end
85
+
86
+ class DummyIssue < Dummy
87
+ attr_reader :summary, :key
88
+
89
+ def initialize(summary = 'Dummy Issue', key = 'DUM-111')
90
+ super
91
+ @summary = summary
92
+ @key = key
93
+ end
94
+
95
+ def labels
96
+ @labels ||= ['abide_dev_utils']
97
+ end
98
+
99
+ def attrs
100
+ {
101
+ 'fields' => {
102
+ 'project' => 'dummy',
103
+ 'priority' => 'dummy',
104
+ },
105
+ }
106
+ end
107
+ end
108
+
109
+ class DummySubtask < DummyIssue
110
+ def initialize(summary = 'Dummy Subtask', key = 'DUM-222')
111
+ super(summary, key)
112
+ end
113
+
114
+ def attrs
115
+ {
116
+ 'fields' => {
117
+ 'project' => 'dummy',
118
+ 'priority' => 'dummy',
119
+ 'parent' => DummyIssue.new,
120
+ },
121
+ }
122
+ end
123
+ end
124
+
125
+ class DummyProject < Dummy
126
+ attr_reader :key, :issues
127
+
128
+ def initialize(key = 'DUM', issues = [DummyIssue.new, DummySubtask.new])
129
+ super
130
+ @key = 'DUM'
131
+ @issues = [DummyIssue.new, DummySubtask.new]
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../errors/jira'
4
+
5
+ module AbideDevUtils
6
+ module Jira
7
+ class Finder
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ def myself
13
+ client.User.myself
14
+ end
15
+
16
+ # @param id [String] The project key or ID
17
+ def project(id)
18
+ return id if id.is_a?(client.Project.target_class)
19
+
20
+ client.Project.find(id)
21
+ end
22
+
23
+ # @param id [String] The issue key or summary
24
+ def issue(id)
25
+ return id if id.is_a?(client.Issue.target_class)
26
+
27
+ client.Issue.find(id)
28
+ rescue URI::InvalidURIError
29
+ iss = client.Issue.all.find { |i| i.summary == id }
30
+ raise AbideDevUtils::Errors::Jira::FindIssueError, id if iss.nil?
31
+
32
+ iss
33
+ end
34
+
35
+ # @param jql [String] The JQL query
36
+ # @return [Array<JIRA::Resource::Issue>]
37
+ def issues_by_jql(jql)
38
+ client.Issue.jql(jql, max_results: 1000)
39
+ end
40
+
41
+ # @param id [String] The issuetype ID or name
42
+ def issuetype(id)
43
+ return id if id.is_a?(client.Issuetype.target_class)
44
+
45
+ if id.match?(%r{^\d+$})
46
+ client.Issuetype.find(id)
47
+ else
48
+ client.Issuetype.all.find { |i| i.name == id }
49
+ end
50
+ end
51
+
52
+ # @param id [String] The priority ID or name
53
+ def priority(id)
54
+ return id if id.is_a?(client.Priority.target_class)
55
+
56
+ if id.match?(%r{^\d+$})
57
+ client.Priority.find(id)
58
+ else
59
+ client.Priority.all.find { |i| i.name == id }
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ attr_reader :client
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dry_run'
4
+
5
+ module AbideDevUtils
6
+ module Jira
7
+ class Helper
8
+ extend DryRun
9
+
10
+ dry_run_simple :add_issue_label
11
+ dry_run_return_false :summary_exist?
12
+
13
+ def initialize(client, dry_run: false)
14
+ @client = client
15
+ @dry_run = dry_run
16
+ end
17
+
18
+ # @param project [JIRA::Resource::Project, String]
19
+ def all_project_issues_attrs(project)
20
+ project = @client.find(:project, project)
21
+ project.issues.collect(&:attrs)
22
+ end
23
+
24
+ # @param issue [JIRA::Resource::Issue, String]
25
+ # @param label [String]
26
+ def add_issue_label(issue, label)
27
+ issue = @client.find(:issue, issue)
28
+ return if issue.labels.include?(label)
29
+
30
+ issue.labels << label
31
+ issue.save
32
+ end
33
+
34
+ # @param summary [String]
35
+ # @param issue_attrs [Array<Hash>]
36
+ def summary_exist?(summary, issue_attrs)
37
+ issue_attrs.any? { |attrs| attrs['fields'].key?('summary') && attrs['fields']['summary'] == summary }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../errors/jira'
4
+
5
+ module AbideDevUtils
6
+ module Jira
7
+ class IssueBuilder
8
+ CUSTOM_FIELDS = {
9
+ 'epic_link' => 'customfield_10014',
10
+ 'epic_name' => 'customfield_10011',
11
+ }.freeze
12
+
13
+ FIELD_DEFAULTS = {
14
+ 'issuetype' => 'Task',
15
+ 'priority' => 'Medium',
16
+ 'labels' => ['abide_dev_utils'],
17
+ }.freeze
18
+
19
+ REQUIRED_FIELDS = %w[project summary].freeze
20
+
21
+ def initialize(client, finder)
22
+ @client = client
23
+ @finder = finder
24
+ end
25
+
26
+ def can_create?(type)
27
+ respond_to?("create_#{type}".to_sym, true)
28
+ end
29
+
30
+ def create(type, **fields)
31
+ type = type.to_s.downcase.to_sym
32
+ raise ArgumentError, "Invalid type \"#{type}\"; no method \"create_#{type}\"" unless can_create?(type)
33
+
34
+ fields = process_fields(fields)
35
+ send("create_#{type}".to_sym, **fields)
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :client
41
+
42
+ def create_issue(**fields)
43
+ iss = client.Issue.build
44
+ iss.save({ 'fields' => fields })
45
+ iss
46
+ rescue StandardError => e
47
+ raise AbideDevUtils::Errors::Jira::CreateIssueError, e
48
+ end
49
+
50
+ def create_subtask(**fields)
51
+ fields['parent'] = find_if_not_type(:issue, client.Issue.target_class, fields['parent'])
52
+ issue_fields = fields['parent'].attrs['fields']
53
+ fields['project'] = issue_fields['project']
54
+ fields['issuetype'] = find_if_not_type(:issuetype, client.Issuetype.target_class, 'Sub-task')
55
+ fields['priority'] = issue_fields['priority']
56
+ iss = client.Issue.build
57
+ iss.save({ 'fields' => fields })
58
+ iss
59
+ rescue StandardError => e
60
+ raise AbideDevUtils::Errors::Jira::CreateSubtaskError, e
61
+ end
62
+
63
+ def process_fields(fields)
64
+ fields = fields.dup
65
+ normalize_field_keys!(fields)
66
+ validate_required_fields!(fields)
67
+ normalize_field_values(fields)
68
+ end
69
+
70
+ def validate_required_fields!(fields)
71
+ missing = REQUIRED_FIELDS.reject { |f| fields.key?(f) }
72
+ raise "Missing required field(s) \"#{missing}\"; present fields: \"#{fields.keys}\"" unless missing.empty?
73
+ end
74
+
75
+ def normalize_field_keys!(fields)
76
+ fields.transform_keys! { |k| k.to_s.downcase }
77
+ fields.transform_keys! { |k| CUSTOM_FIELDS[k] || k }
78
+ end
79
+
80
+ def normalize_field_values(fields)
81
+ fields = FIELD_DEFAULTS.merge(fields).map do |k, v|
82
+ v = case k
83
+ when 'labels'
84
+ v.is_a?(Array) ? v : [v]
85
+ when 'issuetype'
86
+ find_if_not_type(:issuetype, client.Issuetype.target_class, v)
87
+ when 'parent'
88
+ find_if_not_type(:issue, client.Issue.target_class, v)
89
+ when 'priority'
90
+ find_if_not_type(:priority, client.Priority.target_class, v)
91
+ when 'epic_link', CUSTOM_FIELDS['epic_link']
92
+ find_if_not_type(:issue, client.Issue.target_class, v)&.key || v
93
+ when 'project'
94
+ find_if_not_type(:project, client.Project.target_class, v)
95
+ else
96
+ v
97
+ end
98
+ [k, v]
99
+ end
100
+ fields.to_h
101
+ end
102
+
103
+ def find_if_not_type(typesym, typeklass, obj)
104
+ return obj if obj.is_a?(typeklass)
105
+
106
+ @finder.send(typesym, obj)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -5,6 +5,7 @@ require 'abide_dev_utils/output'
5
5
  require 'abide_dev_utils/prompt'
6
6
  require 'abide_dev_utils/config'
7
7
  require 'abide_dev_utils/errors/jira'
8
+ require_relative 'jira/client'
8
9
 
9
10
  module AbideDevUtils
10
11
  module Jira
@@ -14,236 +15,182 @@ module AbideDevUtils
14
15
  UPD_EPIC_SUMMARY_PREFIX = '::BENCHMARK UPDATE::'
15
16
  PROGRESS_BAR_FORMAT = '%a %e %P% Created: %c of %C'
16
17
 
17
- def self.project(client, project)
18
- client.Project.find(project)
19
- end
20
-
21
- def self.issue(client, issue)
22
- client.Issue.find(issue)
23
- rescue URI::InvalidURIError
24
- iss = client.Issue.all.find { |i| i.summary == issue }
25
- raise ERRORS::FindIssueError, issue unless iss
26
-
27
- iss
28
- end
29
-
30
- def self.myself(client)
31
- client.User.myself
32
- end
33
-
34
- def self.issuetype(client, id)
35
- if id.match?(%r{^\d+$})
36
- client.Issuetype.find(id)
37
- else
38
- client.Issuetype.all.find { |i| i.name == id }
39
- end
40
- end
41
-
42
- def self.priority(client, id)
43
- if id.match?(%r{^\d+$})
44
- client.Priority.find(id)
45
- else
46
- client.Priority.all.find { |i| i.name == id }
47
- end
48
- end
49
-
50
- def self.all_project_issues_attrs(project)
51
- raw_issues = project.issues
52
- raw_issues.collect(&:attrs)
53
- end
54
-
55
- def self.add_issue_label(iss, label, dry_run: false)
56
- return if dry_run || iss.labels.include?(label)
57
-
58
- iss.labels << profile_summary
59
- iss.save
60
- end
61
-
62
- def self.new_issue(client, project, summary, description: nil, labels: ['abide_dev_utils'], epic: nil, dry_run: false)
63
- if dry_run
64
- sleep(0.2)
65
- return Dummy.new(summary)
66
- end
67
- fields = {}
68
- fields['summary'] = summary
69
- fields['project'] = project(client, project)
70
- fields['issuetype'] = issuetype(client, 'Task')
71
- fields['priority'] = priority(client, '3')
72
- fields['description'] = description if description
73
- fields['labels'] = labels
74
- epic = issue(client, epic) if epic && !epic.is_a?(JIRA::Resource::Issue)
75
- fields['customfield_10006'] = epic.key if epic # Epic_Link
76
- iss = client.Issue.build
77
- raise ERRORS::CreateIssueError, iss.attrs unless iss.save({ 'fields' => fields })
78
-
79
- iss
80
- end
81
-
82
- def self.new_epic(client, project, summary, dry_run: false)
83
- AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Creating epic '#{summary}'")
84
- if dry_run
85
- sleep(0.2)
86
- return Dummy.new(summary)
87
- end
88
- fields = {
89
- 'summary' => summary,
90
- 'project' => project(client, project),
91
- 'issuetype' => issuetype(client, 'Epic'),
92
- 'customfield_10007' => summary, # Epic Name
93
- }
94
- iss = client.Issue.build
95
- raise ERRORS::CreateEpicError, iss.attrs unless iss.save({ 'fields' => fields })
96
-
97
- iss
98
- end
99
-
100
- # This should probably be threaded in the future
101
- def self.bulk_new_issue(client, project, summaries, dry_run: false)
102
- summaries.each { |s| new_issue(client, project, s, dry_run: dry_run) }
103
- end
104
-
105
- def self.new_subtask(client, issue, summary, dry_run: false)
106
- if dry_run
107
- sleep(0.2)
108
- return Dummy.new
109
- end
110
- issue_fields = issue.attrs['fields']
111
- fields = {}
112
- fields['parent'] = issue
113
- fields['summary'] = summary
114
- fields['project'] = issue_fields['project']
115
- fields['issuetype'] = issuetype(client, '5')
116
- fields['priority'] = issue_fields['priority']
117
- subtask = client.Issue.build
118
- raise ERRORS::CreateSubtaskError, subtask.attrs unless subtask.save({ 'fields' => fields })
119
-
120
- subtask
121
- end
18
+ def self.client(memo: true, dry_run: false, **options)
19
+ return AbideDevUtils::Jira::Client.new(dry_run: dry_run, **options) unless memo
122
20
 
123
- def self.bulk_new_subtask(client, issue, summaries, dry_run: false)
124
- summaries.each do |s|
125
- new_subtask(client, issue, s, dry_run: dry_run)
126
- end
127
- end
128
-
129
- def self.client(options: {})
130
- opts = merge_options(options)
131
- return client_from_prompts if opts.empty?
132
-
133
- opts[:username] = AbideDevUtils::Prompt.username if opts[:username].nil?
134
- opts[:password] = AbideDevUtils::Prompt.password if opts[:password].nil?
135
- opts[:site] = AbideDevUtils::Prompt.single_line('Jira URL') if opts[:site].nil?
136
- opts[:context_path] = '' if opts[:context_path].nil?
137
- opts[:auth_type] = :basic if opts[:auth_type].nil?
138
- JIRA::Client.new(opts)
139
- end
140
-
141
- def self.client_from_prompts(http_debug: false)
142
- options = {}
143
- options[:username] = AbideDevUtils::Prompt.username
144
- options[:password] = AbideDevUtils::Prompt.password
145
- options[:site] = AbideDevUtils::Prompt.single_line('Jira URL')
146
- options[:context_path] = ''
147
- options[:auth_type] = :basic
148
- options[:http_debug] = http_debug
149
- JIRA::Client.new(options)
150
- end
151
-
152
- def self.project_from_prompts(http_debug: false)
153
- client = client_from_prompts(http_debug)
154
- project = AbideDevUtils::Prompt.single_line('Project').upcase
155
- client.Project.find(project)
21
+ @client ||= AbideDevUtils::Jira::Client.new(dry_run: dry_run, **options)
156
22
  end
157
23
 
158
24
  def self.new_issues_from_coverage(client, project, report, dry_run: false)
159
25
  dr_prefix = dry_run ? 'DRY RUN: ' : ''
160
- i_attrs = all_project_issues_attrs(project)
26
+ client(dry_run: dry_run) # Initializes the client if needed
27
+ i_attrs = client.helper.all_project_issues_attrs(project)
161
28
  rep_sums = summaries_from_coverage_report(report)
162
29
  rep_sums.each do |k, v|
163
- next if summary_exist?(k, i_attrs)
30
+ next if client.helper.summary_exist?(k, i_attrs)
164
31
 
165
- parent = new_issue(client, project.attrs['key'], k.to_s, dry_run: dry_run)
166
- AbideDevUtils::Output.simple("#{dr_prefix}Created parent issue #{k}")
167
- parent_issue = issue(client, parent.attrs['key']) unless parent.respond_to?(:dummy)
32
+ AbideDevUtils::Output.simple("#{dr_prefix}Creating parent issue #{k}...")
33
+ parent = client.create(:issue, project: project, summary: k.to_s)
168
34
  AbideDevUtils::Output.simple("#{dr_prefix}Creating subtasks, this can take a while...")
169
35
  progress = AbideDevUtils::Output.progress(title: "#{dr_prefix}Creating Subtasks", total: nil)
170
36
  v.each do |s|
171
- next if summary_exist?(s, i_attrs)
37
+ next if client.helper.summary_exist?(s, i_attrs)
172
38
 
173
39
  progress.title = "#{dr_prefix}#{s}"
174
- new_subtask(client, parent_issue, s, dry_run: dry_run)
40
+ client.create(:subtask, parent: parent, summary: s)
175
41
  progress.increment
176
42
  end
177
43
  end
178
44
  end
179
45
 
180
- def self.new_issues_from_xccdf(client, project, xccdf_path, epic: nil, dry_run: false)
181
- i_attrs = all_project_issues_attrs(project)
46
+ ToCreateData = Struct.new(:summary, :labels, :should_create, :metadata)
47
+
48
+ def self.new_issues_from_xccdf(project, xccdf_path, epic: nil, dry_run: false, explain: false, label_include: nil)
49
+ client(dry_run: dry_run) # Initializes the client if needed
50
+ i_attrs = client.helper.all_project_issues_attrs(project)
182
51
  xccdf = AbideDevUtils::XCCDF::Benchmark.new(xccdf_path)
183
52
  # We need to get the actual epic Issue object, or create it if it doesn't exist
184
53
  epic = if epic.nil?
185
54
  new_epic_summary = "#{COV_PARENT_SUMMARY_PREFIX}#{xccdf.title}"
186
- if summary_exist?(new_epic_summary, i_attrs)
187
- issue(client, new_epic_summary)
55
+ if client.helper.summary_exist?(new_epic_summary, i_attrs)
56
+ client.find(:issue, new_epic_summary)
188
57
  else
189
58
  unless AbideDevUtils::Prompt.yes_no("#{dr_prefix(dry_run)}Create new epic '#{new_epic_summary}'?")
190
59
  AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Aborting")
191
60
  exit(0)
192
61
  end
193
- new_epic(client, project.key, new_epic_summary, dry_run: dry_run)
62
+ client.create(:issue, project: project, summary: new_epic_summary, issuetype: 'Epic', epic_name: new_epic_summary)
194
63
  end
195
64
  else
196
- issue(client, epic)
65
+ client.find(:issue, epic)
197
66
  end
198
67
  # Now we need to find out which issues we need to create for the benchmark
199
68
  # The profiles that the control belongs to will be added as an issue label
200
- to_create = {}
69
+ to_create = []
201
70
  summaries_from_xccdf(xccdf).each do |profile_summary, control_summaries|
202
- control_summaries.reject { |s| summary_exist?(s, i_attrs) }.each do |control_summary|
203
- if to_create.key?(control_summary)
204
- to_create[control_summary] << profile_summary.split.join('_').downcase
71
+ control_summaries.each do |control_summary|
72
+ existing_to_create = to_create.find { |tc| tc.summary == control_summary }
73
+ if existing_to_create
74
+ existing_to_create.labels << profile_summary.split.join('_').downcase
205
75
  else
206
- to_create[control_summary] = [profile_summary.split.join('_').downcase]
76
+ new_to_create = ToCreateData.new(
77
+ summary: control_summary,
78
+ labels: [profile_summary.split.join('_').downcase],
79
+ should_create: true,
80
+ metadata: {
81
+ epic_key: epic.key,
82
+ project: project,
83
+ },
84
+ )
85
+ to_create << new_to_create
207
86
  end
208
87
  end
209
88
  end
210
89
 
211
- unless AbideDevUtils::Prompt.yes_no("#{dr_prefix(dry_run)}Create #{to_create.keys.count} new Jira issues?")
90
+ final_to_create = filter_to_create(to_create, label_include, project, epic)
91
+
92
+ return explain_this(to_create) if explain
93
+
94
+ unless AbideDevUtils::Prompt.yes_no("#{dr_prefix(dry_run)}Create #{final_to_create.count} new Jira issues?")
212
95
  AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Aborting")
213
96
  exit(0)
214
97
  end
215
98
 
216
99
  progress = AbideDevUtils::Output.progress(title: "#{dr_prefix(dry_run)}Creating issues",
217
- total: to_create.keys.count,
100
+ total: final_to_create.count,
218
101
  format: PROGRESS_BAR_FORMAT)
219
- to_create.each do |control_summary, labels|
220
- abrev = control_summary.length > 40 ? control_summary[0..60] : control_summary
102
+ final_to_create.each do |tc|
103
+ abrev = tc.summary.length > 40 ? tc.summary[0..60] : tc.summary
221
104
  progress.log("#{dr_prefix(dry_run)}Creating #{abrev}...")
222
- new_issue(client, project.key, control_summary, labels: labels, epic: epic, dry_run: dry_run)
105
+ client.create(:issue, project: project, summary: tc.summary, labels: tc.labels, epic_link: epic)
223
106
  progress.increment
224
107
  end
225
108
  progress.finish
226
109
  AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Done creating tasks in Epic '#{epic.summary}'")
227
110
  end
228
111
 
229
- def self.new_issues_from_xccdf_diff(client, project, xccdf1_path, xccdf2_path, epic: nil, dry_run: false, auto_approve: false, diff_opts: {})
112
+ def self.filter_to_create(to_create, label_include, project, epic)
113
+ not_already_exists = filter_already_exists(to_create, project, epic)
114
+ AbideDevUtils::Output.simple(
115
+ "Filtered out #{to_create.count - not_already_exists.count} issues that already existed",
116
+ )
117
+ only_label_include = filter_label_include(not_already_exists, label_include)
118
+ AbideDevUtils::Output.simple(
119
+ "Filtered out #{not_already_exists.count - only_label_include.count} issues that didn't include the label #{label_include}",
120
+ )
121
+ only_label_include
122
+ end
123
+
124
+ def self.filter_already_exists(to_create, project, epic)
125
+ AbideDevUtils::Output.simple('Checking if issues already exist...')
126
+ project = client.find(:project, project)
127
+ epic = client.find(:issue, epic)
128
+ issues = client.find(:issues_by_jql, "project = \"#{project.key}\" AND 'Epic Link' = \"#{epic.key}\"")
129
+ to_create.reject do |tc|
130
+ if issues.any? { |i| i.summary == tc.summary }
131
+ tc.metadata[:already_exists] = true
132
+ tc.should_create = false
133
+ true
134
+ else
135
+ tc.metadata[:already_exists] = false
136
+ false
137
+ end
138
+ end
139
+ end
140
+
141
+ # If we have a label_include, we need to filter out any controls that don't have that label
142
+ def self.filter_label_include(to_create, label_include)
143
+ return to_create if label_include.nil?
144
+
145
+ AbideDevUtils::Output.simple("Filtering out controls that don't match label include: #{label_include}")
146
+ to_create.select do |tc|
147
+ if tc.labels.any? { |l| l.match?(label_include) }
148
+ tc.metadata[:label_include] = true
149
+ true
150
+ else
151
+ tc.metadata[:label_include] = false
152
+ tc.should_create = false
153
+ false
154
+ end
155
+ end
156
+ end
157
+
158
+ def self.explain_this(to_create)
159
+ should_create = to_create.select(&:should_create)
160
+ should_not_create = to_create.reject(&:should_create)
161
+ AbideDevUtils::Output.simple(AbideDevUtils::Output.simple_section_separator('EXPLAIN'))
162
+ AbideDevUtils::Output.simple("Will create #{should_create.count} issues")
163
+ AbideDevUtils::Output.simple("Will not create #{should_not_create.count} issues")
164
+ AbideDevUtils::Output.simple(AbideDevUtils::Output.simple_section_separator('WILL CREATE'))
165
+ should_create.each do |tc|
166
+ AbideDevUtils::Output.simple("\"#{tc.summary}\"; labels: #{tc.labels}; metadata: #{tc.metadata}")
167
+ end
168
+ AbideDevUtils::Output.simple(AbideDevUtils::Output.simple_section_separator('WILL NOT CREATE'))
169
+ should_not_create.each do |tc|
170
+ AbideDevUtils::Output.simple("\"#{tc.summary}\"; labels: #{tc.labels}; metadata: #{tc.metadata}")
171
+ end
172
+ exit(0)
173
+ end
174
+
175
+ def self.new_issues_from_xccdf_diff(project, xccdf1_path, xccdf2_path, epic: nil, dry_run: false, auto_approve: false, diff_opts: {})
230
176
  require 'abide_dev_utils/xccdf/diff'
231
177
  diff = AbideDevUtils::XCCDF::Diff::BenchmarkDiff.new(xccdf1_path, xccdf2_path, diff_opts)
232
- i_attrs = all_project_issues_attrs(project)
178
+ client(dry_run: dry_run) # Initializes the client if needed
179
+ i_attrs = client.helper.all_project_issues_attrs(project)
233
180
  # We need to get the actual epic Issue object, or create it if it doesn't exist
234
181
  epic = if epic.nil?
235
- new_epic_summary = "#{UPD_EPIC_SUMMARY_PREFIX}#{diff.this.title}: v#{diff.this.version} -> #{diff.other.version}"
236
- if summary_exist?(new_epic_summary, i_attrs)
237
- issue(client, new_epic_summary)
182
+ new_epic_summary = "#{COV_PARENT_SUMMARY_PREFIX}#{xccdf.title}"
183
+ if client.helper.summary_exist?(new_epic_summary, i_attrs)
184
+ client.find(:issue, new_epic_summary)
238
185
  else
239
- unless AbideDevUtils::Prompt.yes_no("#{dr_prefix(dry_run)}Create new epic '#{new_epic_summary}'?", auto_approve: auto_approve)
186
+ unless AbideDevUtils::Prompt.yes_no("#{dr_prefix(dry_run)}Create new epic '#{new_epic_summary}'?")
240
187
  AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Aborting")
241
188
  exit(0)
242
189
  end
243
- new_epic(client, project.key, new_epic_summary, dry_run: dry_run)
190
+ client.create(:issue, project: project, summary: new_epic_summary, issuetype: 'Epic', epic_name: new_epic_summary)
244
191
  end
245
192
  else
246
- issue(client, epic)
193
+ client.find(:issue, epic)
247
194
  end
248
195
  to_create = {}
249
196
  diff.diff[:rules].each do |key, val|
@@ -285,7 +232,7 @@ module AbideDevUtils
285
232
  format: PROGRESS_BAR_FORMAT)
286
233
  approved_create.each do |summary, description|
287
234
  progress.log("#{dr_prefix(dry_run)}Creating #{summary}...")
288
- new_issue(client, project.key, summary, description: description, labels: [], epic: epic, dry_run: dry_run)
235
+ client.create(:issue, project: project, summary: summary, description: description, labels: [], epic_link: epic)
289
236
  progress.increment
290
237
  end
291
238
  progress.finish
@@ -10,6 +10,14 @@ require 'abide_dev_utils/files'
10
10
  module AbideDevUtils
11
11
  module Output
12
12
  FWRITER = AbideDevUtils::Files::Writer.new
13
+ def self.simple_section_separator(section_text, sepchar: '#', width: 60, **_)
14
+ section_text = section_text.to_s
15
+ section_text = section_text[0..width - 4] if section_text.length > width
16
+ section_text = " #{section_text} "
17
+ section_sep_line = sepchar * width
18
+ [section_sep_line, section_text.center(width, sepchar), section_sep_line].join("\n")
19
+ end
20
+
13
21
  def self.simple(msg, stream: $stdout, **_)
14
22
  case msg
15
23
  when Hash
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AbideDevUtils
4
- VERSION = "0.16.1"
4
+ VERSION = "0.17.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abide_dev_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.1
4
+ version: 0.17.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - abide-team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-07 00:00:00.000000000 Z
11
+ date: 2023-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '6.23'
47
+ version: 7.0.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '6.23'
54
+ version: 7.0.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: puppet-strings
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -322,16 +322,16 @@ dependencies:
322
322
  name: fast_gettext
323
323
  requirement: !ruby/object:Gem::Requirement
324
324
  requirements:
325
- - - "~>"
325
+ - - ">="
326
326
  - !ruby/object:Gem::Version
327
- version: '1.8'
327
+ version: '2.0'
328
328
  type: :development
329
329
  prerelease: false
330
330
  version_requirements: !ruby/object:Gem::Requirement
331
331
  requirements:
332
- - - "~>"
332
+ - - ">="
333
333
  - !ruby/object:Gem::Version
334
- version: '1.8'
334
+ version: '2.0'
335
335
  description: Provides a CLI with helpful utilities for developing compliance Puppet
336
336
  code
337
337
  email:
@@ -404,6 +404,12 @@ files:
404
404
  - lib/abide_dev_utils/files.rb
405
405
  - lib/abide_dev_utils/gcloud.rb
406
406
  - lib/abide_dev_utils/jira.rb
407
+ - lib/abide_dev_utils/jira/client.rb
408
+ - lib/abide_dev_utils/jira/client_builder.rb
409
+ - lib/abide_dev_utils/jira/dry_run.rb
410
+ - lib/abide_dev_utils/jira/finder.rb
411
+ - lib/abide_dev_utils/jira/helper.rb
412
+ - lib/abide_dev_utils/jira/issue_builder.rb
407
413
  - lib/abide_dev_utils/markdown.rb
408
414
  - lib/abide_dev_utils/mixins.rb
409
415
  - lib/abide_dev_utils/output.rb
@@ -463,7 +469,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
463
469
  - !ruby/object:Gem::Version
464
470
  version: '0'
465
471
  requirements: []
466
- rubygems_version: 3.4.19
472
+ rubygems_version: 3.4.18
467
473
  signing_key:
468
474
  specification_version: 4
469
475
  summary: Helper utilities for developing compliance Puppet code