tefoji 3.4.1 → 3.5.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: d877edf49d4a6326964946cb9550504d33d0d27f2d647ee9d4cbd44989ef35df
4
- data.tar.gz: 24722e16086cd273ec562ac8f5628f98c09e73c2c51e6c31db2e01cbffcb4618
3
+ metadata.gz: fc4b951086f14bcc81ab1aabd55e9598d10178ba5cbfbd61984d89444b2df2b3
4
+ data.tar.gz: a822ad69a634e8e78d763adde8e0606460b290f7b291553216194ddb7c59c23a
5
5
  SHA512:
6
- metadata.gz: b095075f6a039349fa85a748f788091274fc2e0f751f78c399a6fc28d159f18e1827503889cd865a1dd47d871c6b8a30b8bb710c16d65f92605ff63c70ad8a60
7
- data.tar.gz: e9f3a20477f55ddfb188f501451b652bd6f6a2b9a116fdb529b37846adcea282e82132723b2f943e70ae171cd2f14b7fab0407414b6d2da100b5025bdacf78e3
6
+ metadata.gz: 4720d892040f8f1be05329d2de1845c1a2255441c9e71cb7211df1b189c4d0d342f31c6611ad52f210fb442a289406250f53c0a7705bbc7c244bd8fe3de3d750
7
+ data.tar.gz: b2e7f33d4e8cd0ee744a3e7d55b8d6d29b5a48887441f54ba0bfdb55789f31167913a963c368fcee9d4458572bd48e7571257e2c853935ac5ed9d52d500a2d05
@@ -48,6 +48,7 @@ module UserFunctions
48
48
  MODULES: 'MODULES',
49
49
  MODULES_INTERNAL: 'FM',
50
50
  OPERATIONS: 'OPS',
51
+ P4DEVOPS: 'P4DEVOPS',
51
52
  PDK: 'CONT',
52
53
  PE_INTERNAL: 'PE',
53
54
  POOLER: 'POOLER',
@@ -57,7 +58,6 @@ module UserFunctions
57
58
  PUPPETSERVER: 'SERVER',
58
59
  PUPPET_AGENT: 'PA',
59
60
  QUALITY_ENGINEERING: 'QENG',
60
- RELEASE_ENGINEERING: 'RE',
61
61
  SLV: 'SLV',
62
62
  SUPPORT: 'SUP',
63
63
  VANAGON: 'VANAGON',
@@ -80,8 +80,7 @@ module UserFunctions
80
80
  CODE_MANAGEMENT: 'Dumpling',
81
81
  DUMPLING: 'Dumpling',
82
82
  FACTER: 'Phoenix',
83
- INSTALLER: 'Installer and Management',
84
- NETWORKING: 'Network Automation',
83
+ INSTALLER: 'Installer',
85
84
  OPERATIONS: 'Operations',
86
85
  PE: 'Dumpling',
87
86
  PLATFORM_OS: 'Unicorn',
@@ -73,21 +73,28 @@ module Tefoji
73
73
  # HTTP doesn't like linefeeds in the auth string, hence #strict_encode64
74
74
  @jira_auth = Base64.strict_encode64(decoded_auth.join(':'))
75
75
  @authentication_header = { 'Authorization' => "Basic #{@jira_auth}" }
76
- end
77
76
 
78
- # Do this so we can inform the user quickly that their credentials didn't work
79
- # There may be a better test, but this is the one the original Winston uses
80
- def test_authentication
81
- get_username(@jira_username)
82
- rescue RestClient::Forbidden
83
- fatal 'Forbidden: either the authentication is incorrect or ' \
84
- 'Jira might be requiring a CAPTCHA response from the web interface.'
77
+ # Verify authentication
78
+ return if @jira_mock
79
+ return if get_username(@jira_username)
80
+
81
+ @logger.info "Authentication failed, trying again with username: #{@jira_username}@perforce.com"
82
+ # Reset authentication with @perforce.com added to username
83
+ @jira_username = "#{@jira_username}@perforce.com"
84
+ decoded_auth[0] = @jira_username
85
+ @jira_auth = Base64.strict_encode64(decoded_auth.join(':'))
86
+ @authentication_header = { 'Authorization' => "Basic #{@jira_auth}" }
87
+ # Retry authentication
88
+ fatal 'Authentication failed: Credentials are invalid' unless get_username(@jira_username)
85
89
  end
86
90
 
87
91
  # Get information about user in Jira
88
- def get_username(username, fail_if_not_found = true)
92
+ def get_username(username)
89
93
  search_parameters = "user/search?query=#{username}"
90
- jira_get(search_parameters, fail_if_not_found)
94
+ response = jira_get(search_parameters)
95
+ return false if response.empty?
96
+
97
+ response
91
98
  end
92
99
 
93
100
  # Get information about user in Jira
@@ -197,7 +204,7 @@ module Tefoji
197
204
  account_name = user_name_to_account_name(user_email)
198
205
  response = get_username(account_name)
199
206
 
200
- fatal "Jira account ID not found for \"#{account_name}\"" if response.empty?
207
+ fatal "Jira account ID not found for \"#{account_name}\"" unless response
201
208
  account_id = response[0]['accountId']
202
209
 
203
210
  @logger.debug("user account name: \"#{account_name} \" " \
@@ -241,7 +248,7 @@ module Tefoji
241
248
  issue_data.key?('type') && issue_data['type'].casecmp?(ISSUE_EPIC)
242
249
  end
243
250
 
244
- def jira_get(jira_request_path, fail_if_not_found = true)
251
+ def jira_get(jira_request_path)
245
252
  # Jira likes to send complete URLs for responses. Handle
246
253
  # the case where we've received a 'self' query from Jira with
247
254
  # fully formed url
@@ -265,8 +272,7 @@ module Tefoji
265
272
  SocketError,
266
273
  Errno::ECONNREFUSED => e
267
274
  # Return false if not found rather than fail to allow for checking the existence of users.
268
- fatal "Cannot connect to #{@jira_base_rest_url}: #{e.message}" if fail_if_not_found
269
- return false
275
+ fatal "Cannot connect to #{@jira_base_rest_url}: #{e.message}"
270
276
  end
271
277
  return JSON.parse(response.body)
272
278
  end
@@ -433,6 +439,14 @@ module Tefoji
433
439
  jira_fields['customfield_10020'] = issue_data['sprint'].to_i
434
440
  end
435
441
 
442
+ if issue_data['product']
443
+ jira_fields['customfield_10350'] = { FIELD_VALUE => issue_data['product'] }
444
+ end
445
+
446
+ if issue_data['shirt_size']
447
+ jira_fields['customfield_10078'] = { FIELD_VALUE => issue_data['shirt_size'] }
448
+ end
449
+
436
450
  # If a issue has a specified parent issue, prefer that. The parent issue *should* already
437
451
  # be linked to the main epic. Otherwise, we need to set it to have an epic_parent. This can
438
452
  # either be an epic linked to the main epic or the main epic itself.
@@ -1,3 +1,3 @@
1
1
  module Tefoji
2
- VERSION = '3.4.1'
2
+ VERSION = '3.5.0'
3
3
  end
data/lib/tefoji.rb CHANGED
@@ -75,6 +75,8 @@ module Tefoji
75
75
  @declarations[k] = DeclaredValue.new(v)
76
76
  end
77
77
 
78
+ standardize_epics(main_template_data)
79
+
78
80
  resolve_variables(main_template_data['declare'])
79
81
  @logger.debug "Declarations: #{@declarations}"
80
82
 
@@ -85,6 +87,7 @@ module Tefoji
85
87
  main_template.dig(:includes, :before)&.each do |before|
86
88
  default_epic_saved = @default_target_epic
87
89
  @formats = before.dig(:data, 'formats') if before.dig(:data, 'formats')
90
+ standardize_epics(before[:data])
88
91
  process_template(before[:template_name], before[:data], before[:conditional])
89
92
  @default_target_epic = default_epic_saved
90
93
  end
@@ -98,6 +101,7 @@ module Tefoji
98
101
  main_template.dig(:includes, :after)&.each do |after|
99
102
  default_epic_saved = @default_target_epic
100
103
  @formats = after.dig(:data, 'formats') if after.dig(:data, 'formats')
104
+ standardize_epics(after[:data])
101
105
  process_template(after[:template_name], after[:data], after[:conditional])
102
106
  @default_target_epic = default_epic_saved
103
107
  end
@@ -118,9 +122,7 @@ module Tefoji
118
122
  @template_data = template_data
119
123
 
120
124
  if @template_data.key?('epics')
121
- generate_epics_with_issues('epics')
122
- elsif @template_data.key?('epic')
123
- generate_epics_with_issues('epic')
125
+ generate_epics_with_issues
124
126
  elsif @template_data.key?('issues')
125
127
  generate_ordinary_issues
126
128
  else
@@ -272,7 +274,6 @@ module Tefoji
272
274
  @jira_api.logger = @logger
273
275
 
274
276
  @jira_api.authenticate(@jira_url, @jira_user, @jira_auth_string)
275
- @jira_api.test_authentication unless @jira_mock
276
277
  end
277
278
 
278
279
  # Save Jira auth data
@@ -281,8 +282,8 @@ module Tefoji
281
282
  @jira_api.save_authentication(@jira_auth_file)
282
283
  end
283
284
 
284
- def generate_epics_with_issues(epic_key)
285
- epics = generate_epics(epic_key)
285
+ def generate_epics_with_issues
286
+ epics = generate_epics
286
287
  epics.each do |epic|
287
288
  @default_target_epic = epic
288
289
  generate_ordinary_issues
@@ -290,8 +291,8 @@ module Tefoji
290
291
  end
291
292
 
292
293
  # @return [Array] an array of responses from Jira containing a hash of the epic fields.
293
- def generate_epics(epic_key)
294
- epic_to_do_list = [@template_data[epic_key]].flatten
294
+ def generate_epics
295
+ epic_to_do_list = [@template_data['epics']].flatten
295
296
  @logger.debug "Generating data: #{@template_data}"
296
297
 
297
298
  epic_to_do_list.map do |epic|
@@ -427,6 +428,17 @@ module Tefoji
427
428
  # Return the fully prepared Jira hash as well as the 'raw' variable-substituted one;
428
429
  # The latter will be needed when processing deferrals.
429
430
  def prepare_jira_ready_data(issue, issue_defaults)
431
+ # For multi-epic templates, skip this issue if it is not part of this epic
432
+ if issue.key?('epic_link') &&
433
+ issue['epic_link'] != @default_target_epic['short_name'].value
434
+ @logger.debug 'Issue %s "%s" does not have "%s" as epic_link, skipping' % [
435
+ issue['short_name'],
436
+ issue['epic_link'],
437
+ @default_target_epic['short_name'].value
438
+ ]
439
+ return nil
440
+ end
441
+
430
442
  raw_issue_data = variable_substitute(issue_defaults.merge(issue), except: %w[summary_format])
431
443
  error_name = raw_issue_data['short_name']
432
444
 
@@ -441,20 +453,6 @@ module Tefoji
441
453
  )
442
454
  end
443
455
 
444
- # For multi-epic templates, skip this issue if it is not part of this epic
445
- if raw_issue_data.key?('epic_link') &&
446
- raw_issue_data['epic_link'].value != @default_target_epic['short_name'].value
447
- @logger.debug 'Issue %s "%s" does not have "%s" as epic_link, skipping' % [
448
- raw_issue_data['short_name'].value,
449
- raw_issue_data['epic_link'].value,
450
- @default_target_epic['short_name'].value
451
- ]
452
- return nil
453
- end
454
-
455
- return nil if raw_issue_data.key?('epic_link') &&
456
- raw_issue_data['epic_link'].value != @default_target_epic['short_name'].value
457
-
458
456
  if conflicting_conditionals?(raw_issue_data)
459
457
  fatal "Issue \"#{error_name}\" has conflicting conditionals."
460
458
  end
@@ -851,6 +849,7 @@ module Tefoji
851
849
  def check_assignees(main_template_data)
852
850
  valid_users = []
853
851
  invalid_users_to_epics = {}
852
+
854
853
  main_template_data['epics']&.each do |epic|
855
854
  next unless epic['assignee']
856
855
 
@@ -865,18 +864,6 @@ module Tefoji
865
864
  )
866
865
  end
867
866
 
868
- if main_template_data.dig('epic', 'assignee')
869
- assignee_username = get_username_from_assignee(main_template_data['epic']['assignee'])
870
- unless valid_users.include?(assignee_username)
871
- update_user_validity(
872
- assignee_username,
873
- main_template_data['epic']['summary'],
874
- valid_users,
875
- invalid_users_to_epics
876
- )
877
- end
878
- end
879
-
880
867
  invalid_users_to_issue_defaults = {}
881
868
  if main_template_data.dig('issue_defaults', 'assignee')
882
869
  assignee_username = get_username_from_assignee(
@@ -946,7 +933,7 @@ module Tefoji
946
933
  def update_user_validity(username, issue_name, valid_users, invalid_users)
947
934
  if invalid_users.key?(username)
948
935
  invalid_users[username] += [issue_name]
949
- elsif @jira_api.get_username(username, false)
936
+ elsif @jira_api.get_username(username)
950
937
  valid_users << username
951
938
  else
952
939
  invalid_users[username] = invalid_users.fetch(username, []) + [issue_name]
@@ -957,6 +944,7 @@ module Tefoji
957
944
  # in the associated project.
958
945
  def check_components(main_template_data)
959
946
  invalid_components = Set.new
947
+
960
948
  main_template_data['epics']&.each do |epic|
961
949
  next unless epic['components']
962
950
 
@@ -967,14 +955,6 @@ module Tefoji
967
955
  end
968
956
  end
969
957
 
970
- if main_template_data.dig('epic', 'components')
971
- project = expand_right_value(main_template_data['epic']['project']).value
972
- update_valid_components(project)
973
- main_template_data['epic']['components'].each do |component|
974
- invalid_components.add([project, component]) unless @valid_components[project].include?(component)
975
- end
976
- end
977
-
978
958
  main_template_data['issues'].each do |issue|
979
959
  next unless issue['components']
980
960
 
@@ -1016,5 +996,25 @@ module Tefoji
1016
996
 
1017
997
  logger
1018
998
  end
999
+
1000
+ # Takes template data as input and ensures the format for the epics
1001
+ # field is the 'epics' key with an array as the value.
1002
+ def standardize_epics(template_data)
1003
+ # Determine if epic or epics key exists
1004
+ if template_data['epic']
1005
+ epic_value = 'epic'
1006
+ elsif template_data['epics']
1007
+ epic_value = 'epics'
1008
+ else
1009
+ @logger.debug('No epics found in template, skipping standardize_epics')
1010
+ return
1011
+ end
1012
+ # Convert epic data to array if not currently an array
1013
+ epics_data = template_data[epic_value]
1014
+ epics_data = [template_data[epic_value]] unless epics_data.is_a?(Array)
1015
+ # Set epics data and delete epic data if it exists
1016
+ template_data['epics'] = epics_data
1017
+ template_data.delete('epic') if epic_value == 'epic'
1018
+ end
1019
1019
  end
1020
1020
  end
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: 3.4.1
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet By Perforce
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-31 00:00:00.000000000 Z
11
+ date: 2025-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: debug
@@ -174,8 +174,8 @@ licenses:
174
174
  metadata:
175
175
  homepage_uri: https://github.com/puppetlabs/tefoji
176
176
  source_code_uri: https://github.com/puppetlabs/tefoji
177
- changelog_uri: https://github.com/puppetlabs/tefoji/CHANGELOG.md
178
- post_install_message:
177
+ changelog_uri: https://github.com/puppetlabs/tefoji/blob/main/CHANGELOG.md
178
+ post_install_message:
179
179
  rdoc_options: []
180
180
  require_paths:
181
181
  - lib
@@ -191,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
191
191
  version: '0'
192
192
  requirements: []
193
193
  rubygems_version: 3.2.33
194
- signing_key:
194
+ signing_key:
195
195
  specification_version: 4
196
196
  summary: Generate Jira issues from a YAML specification.
197
197
  test_files: []