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 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