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 +4 -4
- data/Gemfile.lock +7 -4
- data/abide_dev_utils.gemspec +2 -2
- data/lib/abide_dev_utils/cem/generate/reference.rb +13 -3
- data/lib/abide_dev_utils/cli/jira.rb +45 -32
- data/lib/abide_dev_utils/jira/client.rb +69 -0
- data/lib/abide_dev_utils/jira/client_builder.rb +107 -0
- data/lib/abide_dev_utils/jira/dry_run.rb +136 -0
- data/lib/abide_dev_utils/jira/finder.rb +68 -0
- data/lib/abide_dev_utils/jira/helper.rb +41 -0
- data/lib/abide_dev_utils/jira/issue_builder.rb +110 -0
- data/lib/abide_dev_utils/jira.rb +117 -170
- data/lib/abide_dev_utils/output.rb +8 -0
- data/lib/abide_dev_utils/version.rb +1 -1
- metadata +15 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba0491c8fbcaa0a69015c410a2aa7e61ac61e6b30caca6fb5b334d22fd77069c
|
4
|
+
data.tar.gz: 119c837a5ea897957e63744ec3f38a7567222d7dbb5b952302ec71cd70aafc24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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 (>=
|
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 (
|
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 (
|
279
|
+
fast_gettext (>= 2.0)
|
277
280
|
gem-release
|
278
281
|
github_changelog_generator
|
279
282
|
pry
|
data/abide_dev_utils.gemspec
CHANGED
@@ -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', '>=
|
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', '
|
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
|
-
@
|
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
|
-
@
|
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
|
-
|
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['
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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', '
|
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
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
data/lib/abide_dev_utils/jira.rb
CHANGED
@@ -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.
|
18
|
-
|
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
|
-
|
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
|
-
|
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
|
166
|
-
|
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
|
-
|
40
|
+
client.create(:subtask, parent: parent, summary: s)
|
175
41
|
progress.increment
|
176
42
|
end
|
177
43
|
end
|
178
44
|
end
|
179
45
|
|
180
|
-
|
181
|
-
|
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
|
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
|
-
|
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
|
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.
|
203
|
-
|
204
|
-
|
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
|
-
|
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
|
-
|
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:
|
100
|
+
total: final_to_create.count,
|
218
101
|
format: PROGRESS_BAR_FORMAT)
|
219
|
-
|
220
|
-
abrev =
|
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
|
-
|
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.
|
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
|
-
|
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 = "#{
|
236
|
-
if summary_exist?(new_epic_summary, i_attrs)
|
237
|
-
issue
|
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}'?"
|
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
|
-
|
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
|
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
|
-
|
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
|
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.
|
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-
|
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:
|
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:
|
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: '
|
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: '
|
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.
|
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
|