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