tefoji 1.2.0 → 2.1.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 +55 -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: 2c2caf0e00b4916388f8bfb1db2b6ad61317558ed7495d2b39573670c3d7ebc1
|
4
|
+
data.tar.gz: 11f6804530ed3bdcdfa68ef5caeee1f34c070695646b228fa5842493a060732e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed85494595e4f21db5a8cd26f8b556e29e496481611c6a1322621c495151b94d9ea253450ca2dc0db79493bf8bb74bf0e7c0674ed54ded1974614347e9f523be
|
7
|
+
data.tar.gz: 4c00b11dcb3ef0179838b5bbcac8fdeb1c808905fcf6f9f3236150999cb7a2f696171ff539ad1d6653252f3eabef01664dc36785855aa1d395684d3504646d80
|
@@ -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,22 @@ 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
|
+
|
333
|
+
if scrum_team
|
334
|
+
field_name, field_value = scrum_team_rules(issue_data, scrum_team)
|
335
|
+
jira_fields[field_name] = field_value
|
336
|
+
end
|
337
|
+
|
338
|
+
# Backward compatiblity: translate 'teams' to 'scrum_teams'
|
339
|
+
scrum_teams = issue_data['scrum_teams'] || issue_data['teams']
|
340
|
+
|
341
|
+
if scrum_teams
|
342
|
+
field_name, field_value = scrum_teams_rules(issue_data, scrum_team)
|
343
|
+
jira_fields[field_name] = field_value
|
344
|
+
end
|
345
|
+
|
324
346
|
# Default issue type to ISSUE_TASK if it isn't already set
|
325
347
|
jira_fields['issuetype'] = { FIELD_NAME => ISSUE_TASK }
|
326
348
|
|
@@ -340,6 +362,36 @@ module Tefoji
|
|
340
362
|
end
|
341
363
|
end
|
342
364
|
|
365
|
+
# There are now some funky rules for setting the 'scrum_team' (previously 'team')
|
366
|
+
# and 'scrum_teams' field in different issues.
|
367
|
+
#
|
368
|
+
# For epics, put the scrum team in the 'components' field.
|
369
|
+
# Except for a couple of projects, put the scrum team in customfield_11500
|
370
|
+
# For Puppet Agent (PA) and Puppet Server (SERVER) projects, put the scrum team in
|
371
|
+
# customfield_14200
|
372
|
+
def scrum_team_rules(issue_data, scrum_team)
|
373
|
+
if epic?(issue_data)
|
374
|
+
return ['components', [{ FIELD_NAME => scrum_team }]]
|
375
|
+
end
|
376
|
+
|
377
|
+
customfield = 'customfield_11500'
|
378
|
+
if %w[PA SERVER].include?(issue_data['project'].value)
|
379
|
+
customfield = 'customfield_14200'
|
380
|
+
end
|
381
|
+
|
382
|
+
return [customfield, { FIELD_VALUE => scrum_team }]
|
383
|
+
end
|
384
|
+
|
385
|
+
def scrum_teams_rules(issue_data, scrum_teams)
|
386
|
+
processed_teams = scrum_teams.to_a.flatten.reject { |t| t =~ /^\s*$/ }
|
387
|
+
if epic?(issue_data)
|
388
|
+
return ['components', processed_teams.map { |team| { FIELD_NAME => team } }]
|
389
|
+
end
|
390
|
+
|
391
|
+
# For ordinary issues we can only set one team.
|
392
|
+
scrum_team_rules(issue_data, processed_teams.first)
|
393
|
+
end
|
394
|
+
|
343
395
|
def set_cloud_jira_fields(issue_data, jira_fields)
|
344
396
|
if issue_data['assignee']
|
345
397
|
assignee = issue_data['assignee']
|
@@ -350,7 +402,7 @@ module Tefoji
|
|
350
402
|
if issue_data['type']
|
351
403
|
jira_fields['issuetype'] = { FIELD_NAME => issue_data['type'] }
|
352
404
|
# If this is an epic, we need to add an epic name
|
353
|
-
if
|
405
|
+
if epic?(issue_data)
|
354
406
|
jira_fields['customfield_10011'] = issue_data['epic_name'] || issue_data['summary']
|
355
407
|
end
|
356
408
|
end
|
@@ -408,7 +460,7 @@ module Tefoji
|
|
408
460
|
if issue_data['type']
|
409
461
|
jira_fields['issuetype'] = { FIELD_NAME => issue_data['type'] }
|
410
462
|
# If this is an epic, we need to add an epic name
|
411
|
-
if
|
463
|
+
if epic?(issue_data)
|
412
464
|
jira_fields['customfield_10007'] = issue_data['epic_name'] || issue_data['summary']
|
413
465
|
end
|
414
466
|
end
|
@@ -416,15 +468,7 @@ module Tefoji
|
|
416
468
|
if issue_data['story_points']
|
417
469
|
jira_fields['customfield_10002'] = issue_data['story_points'].to_i
|
418
470
|
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
|
471
|
+
|
428
472
|
if issue_data['subteam']
|
429
473
|
jira_fields['customfield_11700'] = [issue_data['subteam']]
|
430
474
|
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: 1.
|
4
|
+
version: 2.1.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-
|
11
|
+
date: 2023-07-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry-byebug
|