tefoji 3.3.0 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mixins/user_functions.rb +19 -0
- data/lib/tefoji/declared_value.rb +30 -13
- data/lib/tefoji/jira_api.rb +7 -3
- data/lib/tefoji/version.rb +1 -1
- data/lib/tefoji.rb +96 -28
- metadata +26 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69675edd7a74569645d7b4124adf46b6b89fe61b6b249e8773ce3a90e37d4cbd
|
4
|
+
data.tar.gz: 9925aa98c156453976e2e54b55231bd22dfb2de0fe9867ffb385d3030a3684a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb28d7c19800cd8b61b8a30d44cb67f96c1dea95f31a40703d6fa74fe47f5865648dc433f6b757f853377fea76a3b7e053559753f6911f1ce7bdb553ea273cc3
|
7
|
+
data.tar.gz: 4ec1f5c99b091fd457482c9678c7d6b85fad114d3d212c126d1351214a0687bff5f94f19e40ebaefcc5a0298a5867fd67c70750e352c0d3556ee6966c771ebb1
|
@@ -14,6 +14,7 @@ module UserFunctions
|
|
14
14
|
%w[
|
15
15
|
chooser
|
16
16
|
conditional
|
17
|
+
counter
|
17
18
|
date_after
|
18
19
|
date_before
|
19
20
|
default
|
@@ -128,6 +129,24 @@ module UserFunctions
|
|
128
129
|
end
|
129
130
|
end
|
130
131
|
|
132
|
+
# Takes 2 optional arguments:
|
133
|
+
# initial_value (default 1), increment (default)
|
134
|
+
# Returns a Struct that starts with the initial value and post-increments every
|
135
|
+
# time it is used.
|
136
|
+
# The method name 'computed_value' is required for this form.
|
137
|
+
def counter(args)
|
138
|
+
initial_value = args[0] || 1
|
139
|
+
increment = args[1] || 1
|
140
|
+
|
141
|
+
Struct.new(:current_value, :increment) do
|
142
|
+
def computed_value
|
143
|
+
current = current_value.to_s
|
144
|
+
self.current_value += self.increment
|
145
|
+
current
|
146
|
+
end
|
147
|
+
end.new(initial_value, increment)
|
148
|
+
end
|
149
|
+
|
131
150
|
# Date `number_of_days` after `date_string`
|
132
151
|
def date_after(args)
|
133
152
|
_fail_if_unset(__method__.to_s, args)
|
@@ -1,43 +1,60 @@
|
|
1
1
|
module Tefoji
|
2
2
|
class DeclaredValue
|
3
|
-
|
3
|
+
def initialize(user_value = nil)
|
4
|
+
@value = if user_value.is_a? DeclaredValue
|
5
|
+
# prevent recursive DeclaredValues
|
6
|
+
user_value.value
|
7
|
+
else
|
8
|
+
user_value
|
9
|
+
end
|
10
|
+
end
|
4
11
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
12
|
+
# For most DeclaredValues that aren't Structs that have their one `value` method
|
13
|
+
def simple?
|
14
|
+
!@value.respond_to?(:computed_value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def value
|
18
|
+
if simple?
|
19
|
+
@value
|
20
|
+
else
|
21
|
+
@value.computed_value
|
22
|
+
end
|
9
23
|
end
|
10
24
|
|
11
25
|
def prepend(*prepended_things)
|
12
|
-
|
26
|
+
value.prepend(*prepended_things)
|
13
27
|
end
|
14
28
|
|
15
29
|
def to_i
|
16
|
-
|
30
|
+
value.to_i
|
17
31
|
end
|
18
32
|
|
19
33
|
def to_s
|
20
|
-
|
34
|
+
value.to_s
|
21
35
|
end
|
22
36
|
|
23
37
|
def to_a
|
24
|
-
|
25
|
-
|
38
|
+
current = value
|
39
|
+
if current.is_a? Array
|
40
|
+
current
|
26
41
|
else
|
27
|
-
[
|
42
|
+
[current]
|
28
43
|
end
|
29
44
|
end
|
30
45
|
|
31
46
|
def %(other)
|
32
|
-
|
47
|
+
value % other
|
33
48
|
end
|
34
49
|
|
50
|
+
# When querying @value, we don't want to invoke @value.computed_value because it may have
|
51
|
+
# unwanted side-effects.
|
35
52
|
def unset?
|
36
53
|
@value.nil?
|
37
54
|
end
|
38
55
|
|
39
56
|
def falsey?
|
40
|
-
@value.nil? || @value.empty? || @value.downcase == 'false' || @value == false
|
57
|
+
simple? && (@value.nil? || @value.empty? || @value.downcase == 'false' || @value == false)
|
41
58
|
end
|
42
59
|
|
43
60
|
def truthy?
|
data/lib/tefoji/jira_api.rb
CHANGED
@@ -4,8 +4,6 @@ require 'json'
|
|
4
4
|
require 'rest-client'
|
5
5
|
require 'uri'
|
6
6
|
|
7
|
-
RestClient.log = 'stdout'
|
8
|
-
|
9
7
|
module Tefoji
|
10
8
|
## An interface to send API request to Jira using the rest-client gem.
|
11
9
|
## Contained here are the bits of knowledge necessary to form API calls to Jira.
|
@@ -32,7 +30,7 @@ module Tefoji
|
|
32
30
|
def log_level=(level)
|
33
31
|
@log_level = level
|
34
32
|
# When debugging, turn on the RestClient @logger
|
35
|
-
RestClient.log =
|
33
|
+
RestClient.log = 'stderr' if @log_level == Logger::DEBUG
|
36
34
|
end
|
37
35
|
|
38
36
|
# Depending on how user specified their authorization we want to
|
@@ -92,6 +90,12 @@ module Tefoji
|
|
92
90
|
jira_get(search_parameters, fail_if_not_found)
|
93
91
|
end
|
94
92
|
|
93
|
+
# Get information about user in Jira
|
94
|
+
def get_components(project)
|
95
|
+
search_parameters = "project/#{project}/components"
|
96
|
+
jira_get(search_parameters)
|
97
|
+
end
|
98
|
+
|
95
99
|
# Save authentication YAML to the a file for reuse.
|
96
100
|
def save_authentication(save_file_name)
|
97
101
|
backup_file_name = "#{save_file_name}.bak"
|
data/lib/tefoji/version.rb
CHANGED
data/lib/tefoji.rb
CHANGED
@@ -43,6 +43,7 @@ module Tefoji
|
|
43
43
|
@epic_security = nil
|
44
44
|
@deferral_data = {}
|
45
45
|
@transitions_table = {}
|
46
|
+
@valid_components = {}
|
46
47
|
|
47
48
|
# Logging
|
48
49
|
@log_level = Logger::INFO
|
@@ -78,6 +79,7 @@ module Tefoji
|
|
78
79
|
@logger.debug "Declarations: #{@declarations}"
|
79
80
|
|
80
81
|
check_assignees(main_template_data)
|
82
|
+
check_components(main_template_data)
|
81
83
|
|
82
84
|
# Process 'before' templates
|
83
85
|
main_template.dig(:includes, :before)&.each do |before|
|
@@ -506,7 +508,7 @@ module Tefoji
|
|
506
508
|
.gsub('%{short_name}', raw_issue_data['short_name'].value)
|
507
509
|
|
508
510
|
# Now do generic expansion of anything else
|
509
|
-
DeclaredValue.new(
|
511
|
+
DeclaredValue.new(expand_variables_in_string(formatted))
|
510
512
|
end
|
511
513
|
|
512
514
|
# These tags must not be in the issue creation because they need to be
|
@@ -629,25 +631,23 @@ module Tefoji
|
|
629
631
|
def user_function_call(function_definition)
|
630
632
|
@logger.debug "function_definition is: #{function_definition}"
|
631
633
|
function_name = function_definition['name']
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
if argument.is_a?(Array) || arguments.is_a?(Array)
|
636
|
-
arguments = argument || function_definition['arguments']
|
637
|
-
function_arguments = arguments.map do |a|
|
638
|
-
expand_right_value(a).value
|
639
|
-
end
|
640
|
-
elsif argument.is_a?(String) || arguments.is_a?(String)
|
641
|
-
argument = function_definition['argument'] || function_definition['arguments']
|
642
|
-
function_arguments = [expand_right_value(argument).value]
|
643
|
-
else
|
644
|
-
fatal("No arguments supplied to function \"#{function_name}\"")
|
645
|
-
end
|
634
|
+
# Allow singlular or plural form for any function call
|
635
|
+
user_arguments = function_definition['argument'] || function_definition['arguments']
|
646
636
|
|
647
637
|
unless user_function_allowlist.include?(function_name)
|
648
638
|
fatal "Unknown function call: \"#{function_name}\""
|
649
639
|
end
|
650
640
|
|
641
|
+
function_arguments = if user_arguments.is_a?(Array)
|
642
|
+
user_arguments.map do |a|
|
643
|
+
expand_right_value(a).value
|
644
|
+
end
|
645
|
+
elsif user_arguments.is_a?(String)
|
646
|
+
[expand_right_value(user_arguments).value]
|
647
|
+
else
|
648
|
+
[]
|
649
|
+
end
|
650
|
+
|
651
651
|
return_value = send(function_name, function_arguments)
|
652
652
|
@logger.debug "send(#{function_name}, #{function_arguments}) => #{return_value}"
|
653
653
|
DeclaredValue.new(return_value)
|
@@ -695,11 +695,11 @@ module Tefoji
|
|
695
695
|
# Some simple cases
|
696
696
|
return value if value.is_a?(DeclaredValue)
|
697
697
|
return DeclaredValue.new(value) if value.is_a?(Numeric)
|
698
|
-
return DeclaredValue.new(
|
698
|
+
return DeclaredValue.new(expand_variables_in_string(value)) if value.is_a?(String)
|
699
699
|
|
700
700
|
# Do a user-function call
|
701
701
|
if value.is_a?(Hash) && value.key?('function')
|
702
|
-
return
|
702
|
+
return user_function_call(value['function'])
|
703
703
|
end
|
704
704
|
|
705
705
|
# Return the original hash with the values variable-expanded
|
@@ -726,21 +726,39 @@ module Tefoji
|
|
726
726
|
raise "Unimplemented condition in expand_right_value: (#{value.class}) #{value}"
|
727
727
|
end
|
728
728
|
|
729
|
-
def
|
729
|
+
def expand_variables_in_string(expandable_string)
|
730
|
+
# If there are no symbols, there's nothing to expand
|
731
|
+
return expandable_string if @declarations.nil?
|
732
|
+
|
730
733
|
# Special-case where 'foo: "%{bar}"'; this allows for passing around hashes and
|
731
734
|
# arrays without interpolation into strings
|
732
|
-
single_value = %r
|
735
|
+
single_value = %r/\A%{([^}]+)}\Z/.match(expandable_string)
|
733
736
|
return @declarations[single_value[1].to_sym] if single_value
|
734
737
|
|
735
|
-
|
738
|
+
expand_variables(expandable_string)
|
739
|
+
end
|
740
|
+
|
741
|
+
# Generic form of #expand_variables_in_string that recursively replaces
|
742
|
+
# variable forms ("%{foo}") in a string with the text of the variable from
|
743
|
+
# @declarations
|
744
|
+
def expand_variables(expandable_string)
|
745
|
+
# Any pattern starting with '%{', followed by 1 or more characters, ending with '}'
|
746
|
+
variable_match_data = %r/%{([^}]+)}/.match(expandable_string)
|
736
747
|
|
737
|
-
#
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
748
|
+
# Return the string if there's nothing to expand
|
749
|
+
return expandable_string if variable_match_data.nil?
|
750
|
+
|
751
|
+
variable_expression = variable_match_data[0]
|
752
|
+
variable_symbol = variable_match_data[1].to_sym
|
753
|
+
variable_value = @declarations[variable_symbol].value
|
754
|
+
variable_stringified_value = variable_value.to_s
|
755
|
+
|
756
|
+
if variable_value.nil?
|
757
|
+
fatal "Unknown variable: \"#{variable_expression}\""
|
742
758
|
end
|
743
|
-
|
759
|
+
|
760
|
+
modified_string = expandable_string.sub(variable_expression, variable_stringified_value)
|
761
|
+
expand_variables(modified_string)
|
744
762
|
end
|
745
763
|
|
746
764
|
# Return a list of all the unset variables in a template
|
@@ -824,7 +842,6 @@ module Tefoji
|
|
824
842
|
# reasonable way to check assignability prior to the
|
825
843
|
# epic/issue being created, we should implement it in place
|
826
844
|
# of using the get_username method.
|
827
|
-
|
828
845
|
def check_assignees(main_template_data)
|
829
846
|
valid_users = []
|
830
847
|
invalid_users_to_epics = {}
|
@@ -905,7 +922,7 @@ module Tefoji
|
|
905
922
|
|
906
923
|
def get_username_from_assignee(assignee)
|
907
924
|
# Assignee is a variable or direct input
|
908
|
-
username =
|
925
|
+
username = expand_variables_in_string(assignee) if assignee.is_a?(String)
|
909
926
|
# Assignee is a function
|
910
927
|
if assignee.is_a?(Hash) && assignee.key?('function')
|
911
928
|
username = user_function_call(assignee['function'])
|
@@ -930,6 +947,57 @@ module Tefoji
|
|
930
947
|
end
|
931
948
|
end
|
932
949
|
|
950
|
+
# Takes a Tefoji template as input and verifies that all components used in the template exist
|
951
|
+
# in the associated project.
|
952
|
+
def check_components(main_template_data)
|
953
|
+
invalid_components = Set.new
|
954
|
+
main_template_data['epics']&.each do |epic|
|
955
|
+
next unless epic['components']
|
956
|
+
|
957
|
+
project = expand_right_value(epic['project']).value
|
958
|
+
update_valid_components(project)
|
959
|
+
epic['components'].each do |component|
|
960
|
+
invalid_components.add([project, component]) unless @valid_components[project].include?(component)
|
961
|
+
end
|
962
|
+
end
|
963
|
+
|
964
|
+
if main_template_data.dig('epic', 'components')
|
965
|
+
project = expand_right_value(main_template_data['epic']['project']).value
|
966
|
+
update_valid_components(project)
|
967
|
+
main_template_data['epic']['components'].each do |component|
|
968
|
+
invalid_components.add([project, component]) unless @valid_components[project].include?(component)
|
969
|
+
end
|
970
|
+
end
|
971
|
+
|
972
|
+
main_template_data['issues'].each do |issue|
|
973
|
+
next unless issue['components']
|
974
|
+
|
975
|
+
project = if issue['project']
|
976
|
+
expand_right_value(issue['project']).value
|
977
|
+
else
|
978
|
+
expand_right_value(main_template_data['issue_defaults']['project']).value
|
979
|
+
end
|
980
|
+
update_valid_components(project)
|
981
|
+
issue['components'].each do |component|
|
982
|
+
invalid_components.add([project, component]) unless @valid_components[project].include?(component)
|
983
|
+
end
|
984
|
+
end
|
985
|
+
|
986
|
+
fail_message = "component validation failed. the following components do not exist within the given projects: \n"
|
987
|
+
invalid_components.each do |component_project, component|
|
988
|
+
fail_message += "#{component_project}, #{component} \n"
|
989
|
+
end
|
990
|
+
fatal fail_message unless invalid_components.empty?
|
991
|
+
end
|
992
|
+
|
993
|
+
def update_valid_components(project)
|
994
|
+
return if @valid_components.key?(project)
|
995
|
+
|
996
|
+
components_response = @jira_api.get_components(project)
|
997
|
+
valid_components = components_response.map { |component_data| component_data['name'] }
|
998
|
+
@valid_components[project] = valid_components
|
999
|
+
end
|
1000
|
+
|
933
1001
|
def logger_initialize(log_location)
|
934
1002
|
logger = Logger.new(log_location, progname: 'tefoji', level: @log_level)
|
935
1003
|
logger.formatter = proc do |severity, _, script_name, message|
|
metadata
CHANGED
@@ -1,150 +1,150 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tefoji
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet By Perforce
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: debug
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - ">="
|
17
18
|
- !ruby/object:Gem::Version
|
18
19
|
version: 1.0.0
|
19
|
-
name: debug
|
20
|
-
prerelease: false
|
21
20
|
type: :development
|
21
|
+
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
28
29
|
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
31
|
- - "~>"
|
31
32
|
- !ruby/object:Gem::Version
|
32
33
|
version: '13.0'
|
33
|
-
name: rake
|
34
|
-
prerelease: false
|
35
34
|
type: :development
|
35
|
+
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '13.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
42
43
|
requirement: !ruby/object:Gem::Requirement
|
43
44
|
requirements:
|
44
45
|
- - "~>"
|
45
46
|
- !ruby/object:Gem::Version
|
46
47
|
version: '3.0'
|
47
|
-
name: rspec
|
48
|
-
prerelease: false
|
49
48
|
type: :development
|
49
|
+
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
56
57
|
requirement: !ruby/object:Gem::Requirement
|
57
58
|
requirements:
|
58
59
|
- - ">="
|
59
60
|
- !ruby/object:Gem::Version
|
60
61
|
version: '0'
|
61
|
-
name: rubocop
|
62
|
-
prerelease: false
|
63
62
|
type: :development
|
63
|
+
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rake
|
70
71
|
requirement: !ruby/object:Gem::Requirement
|
71
72
|
requirements:
|
72
73
|
- - ">="
|
73
74
|
- !ruby/object:Gem::Version
|
74
75
|
version: '0'
|
75
|
-
name: rubocop-rake
|
76
|
-
prerelease: false
|
77
76
|
type: :development
|
77
|
+
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-rspec
|
84
85
|
requirement: !ruby/object:Gem::Requirement
|
85
86
|
requirements:
|
86
87
|
- - ">="
|
87
88
|
- !ruby/object:Gem::Version
|
88
89
|
version: '0'
|
89
|
-
name: rubocop-rspec
|
90
|
-
prerelease: false
|
91
90
|
type: :development
|
91
|
+
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
+
name: webmock
|
98
99
|
requirement: !ruby/object:Gem::Requirement
|
99
100
|
requirements:
|
100
101
|
- - ">="
|
101
102
|
- !ruby/object:Gem::Version
|
102
103
|
version: '0'
|
103
|
-
name: webmock
|
104
|
-
prerelease: false
|
105
104
|
type: :development
|
105
|
+
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
+
name: yard
|
112
113
|
requirement: !ruby/object:Gem::Requirement
|
113
114
|
requirements:
|
114
115
|
- - "~>"
|
115
116
|
- !ruby/object:Gem::Version
|
116
117
|
version: '0.9'
|
117
|
-
name: yard
|
118
|
-
prerelease: false
|
119
118
|
type: :development
|
119
|
+
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0.9'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
+
name: docopt
|
126
127
|
requirement: !ruby/object:Gem::Requirement
|
127
128
|
requirements:
|
128
129
|
- - "~>"
|
129
130
|
- !ruby/object:Gem::Version
|
130
131
|
version: '0.6'
|
131
|
-
name: docopt
|
132
|
-
prerelease: false
|
133
132
|
type: :runtime
|
133
|
+
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0.6'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
+
name: rest-client
|
140
141
|
requirement: !ruby/object:Gem::Requirement
|
141
142
|
requirements:
|
142
143
|
- - "~>"
|
143
144
|
- !ruby/object:Gem::Version
|
144
145
|
version: '2.1'
|
145
|
-
name: rest-client
|
146
|
-
prerelease: false
|
147
146
|
type: :runtime
|
147
|
+
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - "~>"
|
@@ -175,7 +175,7 @@ metadata:
|
|
175
175
|
homepage_uri: https://github.com/puppetlabs/tefoji
|
176
176
|
source_code_uri: https://github.com/puppetlabs/tefoji
|
177
177
|
changelog_uri: https://github.com/puppetlabs/tefoji/CHANGELOG.md
|
178
|
-
post_install_message:
|
178
|
+
post_install_message:
|
179
179
|
rdoc_options: []
|
180
180
|
require_paths:
|
181
181
|
- lib
|
@@ -190,8 +190,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
190
190
|
- !ruby/object:Gem::Version
|
191
191
|
version: '0'
|
192
192
|
requirements: []
|
193
|
-
rubygems_version: 3.
|
194
|
-
signing_key:
|
193
|
+
rubygems_version: 3.2.33
|
194
|
+
signing_key:
|
195
195
|
specification_version: 4
|
196
196
|
summary: Generate Jira issues from a YAML specification.
|
197
197
|
test_files: []
|