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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 427d2dac516388b9bfe1db4ce55a164621c0530d30a9a79ce322fe098372d3a4
4
- data.tar.gz: 756adc133f20f25d43f422149df86e17c685a741c10801150092ffd633265bf6
3
+ metadata.gz: 2c2caf0e00b4916388f8bfb1db2b6ad61317558ed7495d2b39573670c3d7ebc1
4
+ data.tar.gz: 11f6804530ed3bdcdfa68ef5caeee1f34c070695646b228fa5842493a060732e
5
5
  SHA512:
6
- metadata.gz: 42c08df5f8727ad1b052b780478d1f66022a35eb2148001f5576eb41874a6334028a91552f02729d526ff96b9c1774606c8cbdc8666e2f83289ae9663ff8ab01
7
- data.tar.gz: 1eb096e1869ae3bcedfebb77aacd69b4241d9f0062625850cbd8b1f3ecc4b04325c8e12eaa114ad6083f084b65e59029e46e26cfe0628f31543f049a01730483
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
- RE_KANBAN: 3150,
71
- DUMPLING_READY_TO_WORK: 5515
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
- READY_FOR_ENGINEERING: 111,
78
- ACCEPTED: 271,
79
- DEVELOPING_FEATURE: 61,
80
- DEVELOPING_EPIC: 41
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)
@@ -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 issue_data['type'].casecmp?(ISSUE_EPIC)
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 issue_data['type'].casecmp?(ISSUE_EPIC)
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
- if issue_data['team']
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?('feature')
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 feature issues]
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 exist on jira. If any assignee in
833
- # the template does not exist, it will fail, outputting a message saying which users failed and
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
- invalid_users_to_features = {}
843
- main_template_data['features']&.each do |feature|
844
- next unless feature['assignee']
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
- assignee_username = get_username_from_assignee(feature['assignee'])
847
- next if valid_users.include?(assignee_username)
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(assignee_username, epic['summary'], valid_users, invalid_users_to_epics)
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(assignee_username, main_template_data['epic']['summary'], valid_users,
874
- invalid_users_to_epics)
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(main_template_data['issue_defaults']['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(assignee_username, 'issue_defaults', valid_users, invalid_users_to_issue_defaults)
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(assignee_username, issue['summary'], valid_users, invalid_users_to_issues)
859
+ update_user_validity(
860
+ assignee_username,
861
+ issue['summary'],
862
+ valid_users,
863
+ invalid_users_to_issues
864
+ )
894
865
  end
895
- unless invalid_users_to_features.empty? &&
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: must be a function or a string"
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.2.0
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-06-01 00:00:00.000000000 Z
11
+ date: 2023-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry-byebug