abide_dev_utils 0.16.1 → 0.17.1

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: 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