tefoji 1.2.0 → 2.1.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 +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
|