tefoji 1.2.0 → 2.0.0
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/lib/mixins/user_functions.rb +15 -7
- data/lib/tefoji/jira_api.rb +31 -11
- data/lib/tefoji.rb +40 -74
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c181a5680af5eebeab13d8a78d1e5430df0f3e7535cd9083771af8fbff6d3d06
|
4
|
+
data.tar.gz: 9e0e8b9dcbc36cdbc093a038bd67d4b38f56197bf25695329d78f15c273e5ace
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40b84da52997e8df28012872982e3e50c2f3b18da3a2f80650d08c0f45525a3fa6803e1c9fdca9a82e6461c67882357ddc77d85125b39000176930bb6ccf3464
|
7
|
+
data.tar.gz: 6396ee3ff93fd5214500ebe2913d5eddce18968741532e8c7a95b4547a4ffef7251fd9cf0667a30236503b76d1468f44e650f2816ca8815e0f8d6dbaf7891593
|
@@ -27,6 +27,7 @@ module UserFunctions
|
|
27
27
|
prompt_only
|
28
28
|
random
|
29
29
|
release_type
|
30
|
+
scrum_team
|
30
31
|
split
|
31
32
|
]
|
32
33
|
end
|
@@ -51,10 +52,10 @@ module UserFunctions
|
|
51
52
|
PE_INTERNAL: 'PE',
|
52
53
|
POOLER: 'POOLER',
|
53
54
|
PROJECT_CENTRAL: 'PC',
|
55
|
+
PUPPET: 'PUP',
|
54
56
|
PUPPETDB: 'PDB',
|
55
57
|
PUPPETSERVER: 'SERVER',
|
56
58
|
PUPPET_AGENT: 'PA',
|
57
|
-
PUPPET: 'PUP',
|
58
59
|
QUALITY_ENGINEERING: 'QENG',
|
59
60
|
RELEASE_ENGINEERING: 'RE',
|
60
61
|
SLV: 'SLV',
|
@@ -67,17 +68,23 @@ module UserFunctions
|
|
67
68
|
|
68
69
|
def jira_sprints
|
69
70
|
{
|
70
|
-
|
71
|
-
|
71
|
+
DUMPLING_READY_TO_WORK: 5515,
|
72
|
+
RE_KANBAN: 3150
|
72
73
|
}
|
73
74
|
end
|
74
75
|
|
75
76
|
def jira_statuses
|
77
|
+
# READY_FOR_ENGINEERING deprecated and translated to ACCEPTED
|
76
78
|
{
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
79
|
+
ACCEPTED: 11,
|
80
|
+
READY_FOR_ENGINEERING: 11,
|
81
|
+
ON_HOLD: 21,
|
82
|
+
UNDER_INVESTIGATION: 31,
|
83
|
+
TESTING: 41,
|
84
|
+
IN_PROGRESS: 51,
|
85
|
+
IN_REVIEW: 61,
|
86
|
+
CLOSED: 71,
|
87
|
+
CANCELED: 81
|
81
88
|
}
|
82
89
|
end
|
83
90
|
|
@@ -184,6 +191,7 @@ module UserFunctions
|
|
184
191
|
def jira_team(args)
|
185
192
|
_jira_key('team', jira_teams, args[0])
|
186
193
|
end
|
194
|
+
alias scrum_team jira_team
|
187
195
|
|
188
196
|
# Return the first n fields of '.' - delimited version string
|
189
197
|
def n_digit_version(args)
|
data/lib/tefoji/jira_api.rb
CHANGED
@@ -192,6 +192,12 @@ module Tefoji
|
|
192
192
|
|
193
193
|
private
|
194
194
|
|
195
|
+
## BUG BUG 'type' should ALWAYS be defined but we have a number of assumptions in
|
196
|
+
# this file that just check for the field existence and not the value.
|
197
|
+
def epic?(issue_data)
|
198
|
+
issue_data.key?('type') && issue_data['type'].casecmp?(ISSUE_EPIC)
|
199
|
+
end
|
200
|
+
|
195
201
|
def jira_get(jira_request_path, fail_if_not_found = true)
|
196
202
|
# Jira likes to send complete URLs for responses. Handle
|
197
203
|
# the case where we've received a 'self' query from Jira with
|
@@ -321,6 +327,28 @@ module Tefoji
|
|
321
327
|
end
|
322
328
|
end
|
323
329
|
|
330
|
+
# Backward compatiblity: translate 'team' to 'scrum_team'
|
331
|
+
scrum_team = issue_data['scrum_team'] || issue_data['team']
|
332
|
+
if scrum_team
|
333
|
+
if epic?(issue_data)
|
334
|
+
jira_fields['components'] = [{ FIELD_NAME => scrum_team }]
|
335
|
+
else
|
336
|
+
jira_fields['customfield_11500'] = { FIELD_VALUE => scrum_team }
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# We're only allowed multiple teams (as 'components') in epics. For ordinary
|
341
|
+
# issues take only the first of the list
|
342
|
+
scrum_teams = issue_data['scrum_teams'] || issue_data['teams']
|
343
|
+
if scrum_teams
|
344
|
+
processed_teams = scrum_teams.to_a.flatten.reject { |t| t =~ /^\s*$/ }
|
345
|
+
if epic?(issue_data)
|
346
|
+
jira_fields['components'] = processed_teams.map { |team| { FIELD_NAME => team } }
|
347
|
+
else
|
348
|
+
jira_fields['customfield_11500'] = { FIELD_VALUE => processed_teams.first }
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
324
352
|
# Default issue type to ISSUE_TASK if it isn't already set
|
325
353
|
jira_fields['issuetype'] = { FIELD_NAME => ISSUE_TASK }
|
326
354
|
|
@@ -350,7 +378,7 @@ module Tefoji
|
|
350
378
|
if issue_data['type']
|
351
379
|
jira_fields['issuetype'] = { FIELD_NAME => issue_data['type'] }
|
352
380
|
# If this is an epic, we need to add an epic name
|
353
|
-
if
|
381
|
+
if epic?(issue_data)
|
354
382
|
jira_fields['customfield_10011'] = issue_data['epic_name'] || issue_data['summary']
|
355
383
|
end
|
356
384
|
end
|
@@ -408,7 +436,7 @@ module Tefoji
|
|
408
436
|
if issue_data['type']
|
409
437
|
jira_fields['issuetype'] = { FIELD_NAME => issue_data['type'] }
|
410
438
|
# If this is an epic, we need to add an epic name
|
411
|
-
if
|
439
|
+
if epic?(issue_data)
|
412
440
|
jira_fields['customfield_10007'] = issue_data['epic_name'] || issue_data['summary']
|
413
441
|
end
|
414
442
|
end
|
@@ -416,15 +444,7 @@ module Tefoji
|
|
416
444
|
if issue_data['story_points']
|
417
445
|
jira_fields['customfield_10002'] = issue_data['story_points'].to_i
|
418
446
|
end
|
419
|
-
|
420
|
-
jira_fields['customfield_14200'] = { FIELD_VALUE => issue_data['team'] }
|
421
|
-
end
|
422
|
-
if issue_data['teams']
|
423
|
-
teams = issue_data['teams'].to_a.flatten.reject { |t| t =~ /^\s*$/ }
|
424
|
-
unless teams.empty?
|
425
|
-
jira_fields['customfield_14201'] = teams.map { |team| { FIELD_VALUE => team } }
|
426
|
-
end
|
427
|
-
end
|
447
|
+
|
428
448
|
if issue_data['subteam']
|
429
449
|
jira_fields['customfield_11700'] = [issue_data['subteam']]
|
430
450
|
end
|
data/lib/tefoji.rb
CHANGED
@@ -40,7 +40,6 @@ module Tefoji
|
|
40
40
|
@issue_counter = 1
|
41
41
|
|
42
42
|
# Changable internal states
|
43
|
-
@feature_issue = nil
|
44
43
|
@default_target_epic = nil
|
45
44
|
@epic_security = nil
|
46
45
|
@deferral_data = {}
|
@@ -116,9 +115,7 @@ module Tefoji
|
|
116
115
|
@logger.info "- Processing \"#{template_name}\" template"
|
117
116
|
@template_data = template_data
|
118
117
|
|
119
|
-
if @template_data.key?('
|
120
|
-
generate_feature_with_issues
|
121
|
-
elsif @template_data.key?('epics')
|
118
|
+
if @template_data.key?('epics')
|
122
119
|
generate_epics_with_issues('epics')
|
123
120
|
elsif @template_data.key?('epic')
|
124
121
|
generate_epics_with_issues('epic')
|
@@ -239,9 +236,6 @@ module Tefoji
|
|
239
236
|
|
240
237
|
yaml_data = YAML.safe_load(raw_yaml, filename: included_template_path)
|
241
238
|
|
242
|
-
# In included templates, we don't include features.
|
243
|
-
yaml_data.delete('feature')
|
244
|
-
|
245
239
|
# In included templates, we don't include epics unless they are specifically kept.
|
246
240
|
unless include_epics
|
247
241
|
yaml_data.delete('epics')
|
@@ -285,33 +279,6 @@ module Tefoji
|
|
285
279
|
@jira_api.save_authentication(@jira_auth_file, @jira_cloud)
|
286
280
|
end
|
287
281
|
|
288
|
-
# Generate a feature, a list of epics, and associated issues
|
289
|
-
def generate_feature_with_issues
|
290
|
-
@feature_issue = generate_feature
|
291
|
-
generate_epics('epics').each do |epic|
|
292
|
-
@default_target_epic = epic
|
293
|
-
generate_ordinary_issues
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
# Generate a 'feature' issue
|
298
|
-
def generate_feature
|
299
|
-
feature_to_do = @template_data['feature']
|
300
|
-
|
301
|
-
feature = variable_substitute(feature_to_do)
|
302
|
-
feature['type'] = JiraApi::ISSUE_NEW_FEATURE
|
303
|
-
|
304
|
-
short_name = feature['short_name'] || feature['summary']
|
305
|
-
|
306
|
-
@feature_issue = @jira_api.create_issue(feature, @jira_cloud)
|
307
|
-
@deferral_data[short_name.to_s] = {
|
308
|
-
'jira' => @feature_issue,
|
309
|
-
'raw' => feature
|
310
|
-
}
|
311
|
-
@logger.info "Feature issue: #{@feature_issue['key']}"
|
312
|
-
@feature_issue
|
313
|
-
end
|
314
|
-
|
315
282
|
def generate_epics_with_issues(epic_key)
|
316
283
|
epics = generate_epics(epic_key)
|
317
284
|
epics.each do |epic|
|
@@ -372,7 +339,6 @@ module Tefoji
|
|
372
339
|
epic_issue = @jira_api.create_issue(epic, @jira_cloud)
|
373
340
|
epic_issue['short_name'] = short_name
|
374
341
|
@logger.info 'Epic: %16s [%s]' % [epic_issue['key'], short_name]
|
375
|
-
@jira_api.link_issues(@feature_issue['key'], epic_issue['key']) if @feature_issue
|
376
342
|
@deferral_data[short_name.to_s] = {
|
377
343
|
'jira' => epic_issue,
|
378
344
|
'raw' => epic
|
@@ -822,40 +788,27 @@ module Tefoji
|
|
822
788
|
end
|
823
789
|
|
824
790
|
def missing_expected_keys?(template_uri, template)
|
825
|
-
expected_keys = %w[epic epics
|
791
|
+
expected_keys = %w[epic epics issues]
|
826
792
|
return false if (template.keys & expected_keys).any?
|
827
793
|
|
828
794
|
@logger.error "Cannot find any known template keywords in \"#{template_uri}\""
|
829
795
|
return true
|
830
796
|
end
|
831
797
|
|
832
|
-
# Iterates through the assignees in a template and checks if they
|
833
|
-
#
|
798
|
+
# Iterates through the assignees in a template and checks if they
|
799
|
+
# exist on jira. If any assignee in the template does not exist,
|
800
|
+
# it will fail, outputting a message saying which users failed and
|
834
801
|
# what epics/issues they are associated with.
|
835
|
-
# NOTE: This check does not guarantee a user is assignable, so there may be cases when a user passes
|
836
|
-
# the check but still fails when being assigned a ticket/epic. If we find a reasonable way to
|
837
|
-
# check assignability prior to the epic/issue being created, we should implement it in place of
|
838
|
-
# using the get_username method.
|
839
|
-
def check_assignees(main_template_data)
|
840
|
-
valid_users = []
|
841
802
|
|
842
|
-
|
843
|
-
|
844
|
-
|
803
|
+
# NOTE: This check does not guarantee a user is assignable, so
|
804
|
+
# there may be cases when a user passes the check but still
|
805
|
+
# fails when being assigned a ticket/epic. If we find a
|
806
|
+
# reasonable way to check assignability prior to the
|
807
|
+
# epic/issue being created, we should implement it in place
|
808
|
+
# of using the get_username method.
|
845
809
|
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
update_user_validity(assignee_username, feature['summary'], valid_users, invalid_users_to_features)
|
850
|
-
end
|
851
|
-
|
852
|
-
if main_template_data.dig('feature', 'assignee')
|
853
|
-
assignee_username = get_username_from_assignee(main_template_data['feature']['assignee'])
|
854
|
-
unless valid_users.include?(assignee_username)
|
855
|
-
update_user_validity(assignee_username, main_template_data['feature']['summary'], valid_users,
|
856
|
-
invalid_users_to_features)
|
857
|
-
end
|
858
|
-
end
|
810
|
+
def check_assignees(main_template_data)
|
811
|
+
valid_users = []
|
859
812
|
|
860
813
|
invalid_users_to_epics = {}
|
861
814
|
main_template_data['epics']&.each do |epic|
|
@@ -864,22 +817,35 @@ module Tefoji
|
|
864
817
|
assignee_username = get_username_from_assignee(epic['assignee'])
|
865
818
|
next if valid_users.include?(assignee_username)
|
866
819
|
|
867
|
-
update_user_validity(
|
820
|
+
update_user_validity(
|
821
|
+
assignee_username,
|
822
|
+
epic['summary'],
|
823
|
+
valid_users,
|
824
|
+
invalid_users_to_epics
|
825
|
+
)
|
868
826
|
end
|
869
827
|
|
870
828
|
if main_template_data.dig('epic', 'assignee')
|
871
829
|
assignee_username = get_username_from_assignee(main_template_data['epic']['assignee'])
|
872
830
|
unless valid_users.include?(assignee_username)
|
873
|
-
update_user_validity(
|
874
|
-
|
831
|
+
update_user_validity(
|
832
|
+
assignee_username,
|
833
|
+
main_template_data['epic']['summary'],
|
834
|
+
valid_users,
|
835
|
+
invalid_users_to_epics
|
836
|
+
)
|
875
837
|
end
|
876
838
|
end
|
877
839
|
|
878
840
|
invalid_users_to_issue_defaults = {}
|
879
841
|
if main_template_data.dig('issue_defaults', 'assignee')
|
880
|
-
assignee_username = get_username_from_assignee(
|
842
|
+
assignee_username = get_username_from_assignee(
|
843
|
+
main_template_data['issue_defaults']['assignee']
|
844
|
+
)
|
881
845
|
unless valid_users.include?(assignee_username)
|
882
|
-
update_user_validity(
|
846
|
+
update_user_validity(
|
847
|
+
assignee_username, 'issue_defaults', valid_users, invalid_users_to_issue_defaults
|
848
|
+
)
|
883
849
|
end
|
884
850
|
end
|
885
851
|
|
@@ -890,19 +856,18 @@ module Tefoji
|
|
890
856
|
assignee_username = get_username_from_assignee(issue['assignee'])
|
891
857
|
next if valid_users.include?(assignee_username)
|
892
858
|
|
893
|
-
update_user_validity(
|
859
|
+
update_user_validity(
|
860
|
+
assignee_username,
|
861
|
+
issue['summary'],
|
862
|
+
valid_users,
|
863
|
+
invalid_users_to_issues
|
864
|
+
)
|
894
865
|
end
|
895
|
-
unless
|
896
|
-
invalid_users_to_epics.empty? &&
|
866
|
+
unless invalid_users_to_epics.empty? &&
|
897
867
|
invalid_users_to_issue_defaults.empty? &&
|
898
868
|
invalid_users_to_issues.empty?
|
899
869
|
invalid_user_message = "Invalid assignees:\n"
|
900
870
|
end
|
901
|
-
unless invalid_users_to_features.empty?
|
902
|
-
invalid_users_to_features.each do |assignee, feature|
|
903
|
-
invalid_user_message += "Assignee #{assignee} associated with features #{feature}\n"
|
904
|
-
end
|
905
|
-
end
|
906
871
|
unless invalid_users_to_epics.empty?
|
907
872
|
invalid_users_to_epics.each do |assignee, epic|
|
908
873
|
invalid_user_message += "Assignee #{assignee} associated with epics #{epic}\n"
|
@@ -930,7 +895,8 @@ module Tefoji
|
|
930
895
|
end
|
931
896
|
|
932
897
|
unless username
|
933
|
-
fatal "invalid assignee format for issue: #{issue['short_name']} failed:
|
898
|
+
fatal "invalid assignee format for issue: #{issue['short_name']} failed: ",
|
899
|
+
'must be a function or a string'
|
934
900
|
end
|
935
901
|
|
936
902
|
username = username.value unless username.is_a?(String)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tefoji
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet Labs
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry-byebug
|