tefoji 2.1.0 → 3.0.1

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: 2c2caf0e00b4916388f8bfb1db2b6ad61317558ed7495d2b39573670c3d7ebc1
4
- data.tar.gz: 11f6804530ed3bdcdfa68ef5caeee1f34c070695646b228fa5842493a060732e
3
+ metadata.gz: 751842bf3544f557c135661e9d4b57531fadf07df24b7e3d4ff69784a415d948
4
+ data.tar.gz: c7c957bf732fe96d8719acdcd483ef8778b464aacd33de2ae9db589246cd7c89
5
5
  SHA512:
6
- metadata.gz: ed85494595e4f21db5a8cd26f8b556e29e496481611c6a1322621c495151b94d9ea253450ca2dc0db79493bf8bb74bf0e7c0674ed54ded1974614347e9f523be
7
- data.tar.gz: 4c00b11dcb3ef0179838b5bbcac8fdeb1c808905fcf6f9f3236150999cb7a2f696171ff539ad1d6653252f3eabef01664dc36785855aa1d395684d3504646d80
6
+ metadata.gz: 1bf8f8a192b87c00ffa4856c6c72f5e7d52a490f75eb2e54b6b95707a573cbca3d323540f841cad33b5a61f59c761d31d3170a40bba4f24b0e22b4354ef286f6
7
+ data.tar.gz: bbe6db199d6bafab455ce075f6c34cd63eeb91273a1d6469f3cbbd246ef918848f3d53fc070d8eda1cbf4c745c3cf77a2905880df6dc2ebaabd24d63a2e4c239
@@ -265,7 +265,7 @@ module UserFunctions
265
265
  condition_hash = args[1]
266
266
 
267
267
  unless condition_hash.keys.sort == %w[X Y Z].sort
268
- fatal 'function "release_type" requires a hash with keys ("X", "Y", "Z") as 2nd argument. '\
268
+ fatal 'function "release_type" requires a hash with keys ("X", "Y", "Z") as 2nd argument. ' \
269
269
  "Got: #{condition_hash}"
270
270
  end
271
271
 
@@ -281,7 +281,7 @@ module UserFunctions
281
281
  def split(args)
282
282
  _fail_if_unset(__method__.to_s, args)
283
283
  fatal('function "split" requires exactly two arguments') unless args.size == 2
284
- args[1].split(/#{args[0]}/)
284
+ args[1].split(%r{#{args[0]}})
285
285
  end
286
286
 
287
287
  ## Jira fields helper functions
data/lib/tefoji/cli.rb CHANGED
@@ -5,12 +5,9 @@ require 'yaml'
5
5
 
6
6
  module Tefoji
7
7
  class CLI
8
- DEFAULT_JIRA_URL = 'https://tickets.puppetlabs.com'
9
- JIRA_TEST_URL = 'https://jira-app-test-1.ops.puppetlabs.net'
10
- DEFAULT_JIRA_AUTH_FILE = "#{ENV['HOME']}/.tefoji-auth.yaml"
11
- JIRA_CLOUD_URLS = [
12
- 'https://puppet.atlassian.net'
13
- ]
8
+ DEFAULT_JIRA_URL = 'https://perforce.atlassian.net'
9
+ JIRA_TEST_URL = 'https://perforce-sandbox-903.atlassian.net'
10
+ DEFAULT_JIRA_AUTH_FILE = "#{Dir.home}/.tefoji-auth.yaml"
14
11
 
15
12
  DOCUMENTATION = <<~DOCOPT
16
13
  Generate Jira issues from YAML files.
@@ -24,8 +21,8 @@ module Tefoji
24
21
  Options:
25
22
  -j --jira URL URL of the Jira instance (defaults to #{DEFAULT_JIRA_URL})
26
23
  -J --jira-test Use the jira test environment (#{JIRA_TEST_URL})
27
- -a --jira-auth=FILE Alternate YAML file with Base64-encoded 'username:password' string
28
- for Jira authentication
24
+ -a --jira-auth=FILE Alternate YAML file with Base64-encoded 'username:application-token'
25
+ string for Jira authentication
29
26
  -S --jira-save-auth Save/overwrite Base64-encoded yaml authentication to the jira-auth
30
27
  file (defaults to #{DEFAULT_JIRA_AUTH_FILE}) after succesfully
31
28
  authenticating to the Jira server.
@@ -56,18 +53,11 @@ module Tefoji
56
53
  @user_options['jira-auth-string'] = nil
57
54
  @user_options['jira-auth-file'] = @user_options['--jira-auth'] || DEFAULT_JIRA_AUTH_FILE
58
55
 
59
- @user_options['jira-cloud'] = false
60
- @user_options['jira-cloud'] = true if JIRA_CLOUD_URLS.include?(@user_options['--jira'])
61
-
62
56
  jira_auth_file = @user_options['jira-auth-file']
63
57
  return unless File.file?(jira_auth_file)
64
58
 
65
59
  authentication = YAML.load_file(jira_auth_file)
66
- if @user_options['jira-cloud'] && authentication.key?('jira-cloud-auth')
67
- @user_options['jira-auth-string'] = authentication['jira-cloud-auth']
68
- elsif authentication.key?('jira-auth')
69
- @user_options['jira-auth-string'] = authentication['jira-auth']
70
- end
60
+ @user_options['jira-auth-string'] = authentication['jira-auth']
71
61
  end
72
62
 
73
63
  # Iterate through issue templates, validating each. If that goes well, generate
@@ -37,11 +37,7 @@ module Tefoji
37
37
  end
38
38
 
39
39
  def falsey?
40
- if @value.nil? || @value.empty? || @value.downcase == 'false' || @value == false
41
- true
42
- else
43
- false
44
- end
40
+ @value.nil? || @value.empty? || @value.downcase == 'false' || @value == false
45
41
  end
46
42
 
47
43
  def truthy?
@@ -2,6 +2,8 @@ require 'base64'
2
2
  require 'io/console'
3
3
  require 'json'
4
4
  require 'rest-client'
5
+ require 'uri'
6
+
5
7
  RestClient.log = 'stdout'
6
8
 
7
9
  module Tefoji
@@ -35,8 +37,8 @@ module Tefoji
35
37
 
36
38
  # Depending on how user specified their authorization we want to
37
39
  # derive and store the username and the Base64-encoded
38
- # 'username:password' string.
39
- def authenticate(jira_base_url, requested_user = nil, jira_auth_string = nil)
40
+ # 'username:application-token' string.
41
+ def authenticate(jira_base_url, requested_user = nil, jira_auth = nil)
40
42
  @jira_base_url = jira_base_url
41
43
  @jira_base_rest_url = "#{jira_base_url}/rest/api/2"
42
44
 
@@ -45,12 +47,12 @@ module Tefoji
45
47
  # If we found an auth string, try to use it. Allow the requested_user
46
48
  # to override and send us to prompt
47
49
  decoded_auth = nil
48
- unless jira_auth_string.nil?
49
- auth_string_user, auth_string_password = Base64.decode64(jira_auth_string).split(':')
50
+ unless jira_auth.nil?
51
+ auth_user, auth_application_token = Base64.decode64(jira_auth).split(':')
50
52
  # Only set decoded auth if the user name in the auth string matches the
51
53
  # requested user name
52
- unless requested_user && requested_user != auth_string_user
53
- decoded_auth = [auth_string_user, auth_string_password]
54
+ unless requested_user && requested_user != auth_user
55
+ decoded_auth = [auth_user, auth_application_token]
54
56
  end
55
57
  end
56
58
 
@@ -63,38 +65,35 @@ module Tefoji
63
65
  print 'Enter jira user name: '
64
66
  decoded_auth[0] = $stdin.gets.chomp
65
67
  end
66
- decoded_auth[1] = IO.console.getpass("Enter jira password for #{requested_user}: ")
68
+ decoded_auth[1] = IO.console.getpass(
69
+ "Enter jira application_token for #{decoded_auth[0]}: "
70
+ )
67
71
  end
68
72
 
69
73
  @jira_username = decoded_auth[0]
70
74
 
71
75
  # HTTP doesn't like linefeeds in the auth string, hence #strict_encode64
72
- @jira_auth_string = Base64.strict_encode64(decoded_auth.join(':'))
73
- @authentication_header = { 'Authorization' => "Basic #{@jira_auth_string}" }
76
+ @jira_auth = Base64.strict_encode64(decoded_auth.join(':'))
77
+ @authentication_header = { 'Authorization' => "Basic #{@jira_auth}" }
74
78
  end
75
79
 
76
80
  # Do this so we can inform the user quickly that their credentials didn't work
77
81
  # There may be a better test, but this is the one the original Winston uses
78
- def test_authentication(jira_cloud)
79
- get_username(@jira_username, jira_cloud)
82
+ def test_authentication
83
+ get_username(@jira_username)
80
84
  rescue RestClient::Forbidden
81
85
  fatal 'Forbidden: either the authentication is incorrect or ' \
82
86
  'Jira might be requiring a CAPTCHA response from the web interface.'
83
87
  end
84
88
 
85
89
  # Get information about user in Jira
86
- def get_username(username, jira_cloud, fail_if_not_found = true)
87
- search_parameters = if jira_cloud
88
- "user/search?query=#{username}"
89
- else
90
- "user?username=#{username}"
91
- end
92
-
90
+ def get_username(username, fail_if_not_found = true)
91
+ search_parameters = "user/search?query=#{username}"
93
92
  jira_get(search_parameters, fail_if_not_found)
94
93
  end
95
94
 
96
95
  # Save authentication YAML to the a file for reuse.
97
- def save_authentication(save_file_name, jira_cloud)
96
+ def save_authentication(save_file_name)
98
97
  backup_file_name = "#{save_file_name}.bak"
99
98
 
100
99
  if File.readable?(save_file_name)
@@ -102,12 +101,8 @@ module Tefoji
102
101
  @logger.info "Saved #{save_file_name} to #{backup_file_name}"
103
102
  end
104
103
 
105
- jira_auth_string = if jira_cloud
106
- "jira-auth: #{@jira_auth_string}\n"
107
- else
108
- "jira-cloud-auth: #{@jira_auth_string}\n"
109
- end
110
- File.write(save_file_name, jira_auth_string)
104
+ jira_auth = "jira-auth: #{@jira_auth}\n"
105
+ File.write(save_file_name, jira_auth)
111
106
 
112
107
  File.chmod(0o600, save_file_name)
113
108
 
@@ -115,21 +110,21 @@ module Tefoji
115
110
  end
116
111
 
117
112
  # Create a Jira issue
118
- def create_issue(issue_data, jira_cloud)
113
+ def create_issue(issue_data)
119
114
  request_path = 'issue'
120
- jira_fields = issue_data_to_jira_fields(issue_data, jira_cloud)
115
+ jira_fields = issue_data_to_jira_fields(issue_data)
121
116
  jira_post(request_path, jira_fields)
122
117
  rescue RestClient::Forbidden
123
- fatal "Forbidden: could not send #{request_path} request to Jira. "\
118
+ fatal "Forbidden: could not send #{request_path} request to Jira. " \
124
119
  'Jira may not be accepting requests.'
125
120
  rescue RestClient::BadRequest => e
126
121
  error_json = JSON.parse(e.response.body)
127
122
  errors = error_json['errors'].values.join("\n")
128
- fatal "Bad Request: something went wrong with this #{request_path} request: "\
123
+ fatal "Bad Request: something went wrong with this #{request_path} request: " \
129
124
  "#{request_path}, #{jira_fields}\n" \
130
125
  "Errors were: #{errors}"
131
126
  rescue RestClient::Unauthorized
132
- fatal "Unauthorized: could not send #{request_path} request to Jira. "\
127
+ fatal "Unauthorized: could not send #{request_path} request to Jira. " \
133
128
  'Did you use the correct credentials?'
134
129
  end
135
130
 
@@ -171,23 +166,42 @@ module Tefoji
171
166
  end
172
167
 
173
168
  # https://www.youtube.com/watch?v=JsntlJZ9h1U
174
- def add_watcher(issue_key, watcher, jira_cloud)
169
+ def add_watcher(issue_key, watcher_name)
175
170
  request_path = "issue/#{issue_key}/watchers"
176
- watcher = useremail_to_id(watcher) if jira_cloud
177
- request_data = watcher
178
- jira_post(request_path, request_data)
171
+ watcher_id = user_email_to_account_id(watcher_name)
172
+ jira_post(request_path, watcher_id)
179
173
  end
180
174
 
181
- # jira cloud api calls now require an accountId rather than a username. This method will convert
182
- # emails or display names to accountIds.
183
- def useremail_to_id(email)
184
- response = get_username(email, true, true)[0]
185
- id = response['accountId']
186
- if id.nil? || id.empty?
187
- @logger.error "accountId not found for #{email}."
188
- exit 1
175
+ # Jira on-prem used user names ('eric.griswold', 'tefoji', etc.) to identify users.
176
+ # Jira cloud requires an '@domain.extension' suffix. Handle translating from the onprem
177
+ # form to the cloud form.
178
+ # Nothe the special case of the tefoji service user.
179
+ def user_name_to_account_name(user_name)
180
+ case user_name
181
+ when 'tefoji'
182
+ 'svc-tefoji-puppet-jira@perforce.com'
183
+ when %r{URI::MailTo::EMAIL_REGEXP}
184
+ user_name
185
+ else
186
+ "#{user_name.gsub(%r{@.*}, '')}@perforce.com"
189
187
  end
190
- return id
188
+ end
189
+
190
+ # jira cloud api calls require an accountId rather than a username. This method will convert
191
+ # emails or display names to accountIds.
192
+ def user_email_to_account_id(user_email)
193
+ account_name = user_name_to_account_name(user_email)
194
+ response = get_username(account_name)
195
+
196
+ fatal "Jira account ID not found for \"#{account_name}\"" if response.empty?
197
+ account_id = response[0]['accountId']
198
+
199
+ @logger.debug("user account name: \"#{account_name} \" " \
200
+ "converted to account ID: \"#{account_id}\"")
201
+
202
+ return account_id if account_id
203
+
204
+ fatal "Jira account ID not found for #{account_name}"
191
205
  end
192
206
 
193
207
  private
@@ -221,11 +235,10 @@ module Tefoji
221
235
  rescue RestClient::NotFound,
222
236
  SocketError,
223
237
  Errno::ECONNREFUSED => e
224
- # Return False if not found rather than fail to allow for checking the existence of users.
238
+ # Return false if not found rather than fail to allow for checking the existence of users.
225
239
  fatal "Cannot connect to #{@jira_base_rest_url}: #{e.message}" if fail_if_not_found
226
240
  return false
227
241
  end
228
-
229
242
  return JSON.parse(response.body)
230
243
  end
231
244
 
@@ -243,6 +256,8 @@ module Tefoji
243
256
  end
244
257
 
245
258
  return JSON.parse(response.body) unless response.body.empty?
259
+
260
+ true
246
261
  end
247
262
 
248
263
  # Using JIRA's REST API to update the Epic Link field returns "204 No Content",
@@ -273,7 +288,7 @@ module Tefoji
273
288
  # Provide the needed translation of user-created data to required format
274
289
  # of Jira requests. This is mostly a list of fussing with the Jira field
275
290
  # names.
276
- def issue_data_to_jira_fields(issue_data, jira_cloud)
291
+ def issue_data_to_jira_fields(issue_data)
277
292
  # Check to ensure we have what we need to create a issue
278
293
 
279
294
  unless issue_data['summary']
@@ -293,12 +308,7 @@ module Tefoji
293
308
  }
294
309
 
295
310
  set_common_jira_fields(issue_data, jira_fields)
296
-
297
- if jira_cloud
298
- set_cloud_jira_fields(issue_data, jira_fields)
299
- else
300
- set_onprem_jira_fields(issue_data, jira_fields)
301
- end
311
+ set_cloud_jira_fields(issue_data, jira_fields)
302
312
 
303
313
  return { 'fields' => jira_fields }
304
314
  end
@@ -310,7 +320,7 @@ module Tefoji
310
320
  end
311
321
 
312
322
  if issue_data['labels']
313
- labels = issue_data['labels'].to_a.flatten.reject { |t| t =~ /^\s*$/ }
323
+ labels = issue_data['labels'].to_a.flatten.reject { |t| t =~ %r{^\s*$} }
314
324
  jira_fields['labels'] = labels unless labels.empty?
315
325
  end
316
326
  if issue_data['duedate']
@@ -321,7 +331,7 @@ module Tefoji
321
331
  end
322
332
 
323
333
  if issue_data['components']
324
- components = issue_data[:components].to_a.flatten.reject { |t| t =~ /^\s*$/ }
334
+ components = issue_data[:components].to_a.flatten.reject { |t| t =~ %r{^\s*$} }
325
335
  unless components.empty?
326
336
  jira_fields['components'] = components.map { |component| { FIELD_NAME => component } }
327
337
  end
@@ -339,51 +349,30 @@ module Tefoji
339
349
  scrum_teams = issue_data['scrum_teams'] || issue_data['teams']
340
350
 
341
351
  if scrum_teams
342
- field_name, field_value = scrum_teams_rules(issue_data, scrum_team)
352
+ field_name, field_value = scrum_teams_rules(issue_data, scrum_teams)
343
353
  jira_fields[field_name] = field_value
344
354
  end
345
355
 
346
356
  # Default issue type to ISSUE_TASK if it isn't already set
347
357
  jira_fields['issuetype'] = { FIELD_NAME => ISSUE_TASK }
348
-
349
- security = ENV['SECURITY'] || issue_data['security']
350
- return unless security
351
-
352
- case security.downcase
353
- when 'confidential'
354
- jira_fields['security'] = { FIELD_ID => '10002' }
355
- when 'internal'
356
- jira_fields['security'] = { FIELD_ID => '10001' }
357
- when 'public'
358
- # Nothing to do here - public is default
359
- else
360
- @logger.fatal "Unknown security type: #{security}"
361
- exit 1
362
- end
363
358
  end
364
359
 
365
360
  # There are now some funky rules for setting the 'scrum_team' (previously 'team')
366
361
  # and 'scrum_teams' field in different issues.
367
362
  #
368
363
  # 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
364
+ # For ordinary issues, put the scrum team in customfield_10067
372
365
  def scrum_team_rules(issue_data, scrum_team)
373
366
  if epic?(issue_data)
374
367
  return ['components', [{ FIELD_NAME => scrum_team }]]
375
368
  end
376
369
 
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 }]
370
+ return ['customfield_10067', { FIELD_VALUE => scrum_team }]
383
371
  end
384
372
 
385
373
  def scrum_teams_rules(issue_data, scrum_teams)
386
- processed_teams = scrum_teams.to_a.flatten.reject { |t| t =~ /^\s*$/ }
374
+ # For epics, we can have a list of teams.
375
+ processed_teams = scrum_teams.to_a.flatten.reject { |t| t =~ %r{^\s*$} }
387
376
  if epic?(issue_data)
388
377
  return ['components', processed_teams.map { |team| { FIELD_NAME => team } }]
389
378
  end
@@ -395,7 +384,7 @@ module Tefoji
395
384
  def set_cloud_jira_fields(issue_data, jira_fields)
396
385
  if issue_data['assignee']
397
386
  assignee = issue_data['assignee']
398
- assignee = useremail_to_id(assignee)
387
+ assignee = user_email_to_account_id(assignee.value)
399
388
  jira_fields['assignee'] = { FIELD_ACCOUNT_ID => assignee }
400
389
  end
401
390
 
@@ -408,20 +397,9 @@ module Tefoji
408
397
  end
409
398
 
410
399
  if issue_data['story_points']
411
- jira_fields['customfield_10038'] = issue_data['story_points'].to_i
412
- end
413
- if issue_data['team']
414
- jira_fields['customfield_10052'] = { FIELD_VALUE => issue_data['team'] }
415
- end
416
- if issue_data['teams']
417
- teams = issue_data['teams'].to_a.flatten.reject { |t| t =~ /^\s*$/ }
418
- unless teams.empty?
419
- jira_fields['customfield_10066'] = teams.map { |team| { FIELD_VALUE => team } }
420
- end
421
- end
422
- if issue_data['subteam']
423
- jira_fields['customfield_10045'] = [issue_data['subteam']]
400
+ jira_fields['customfield_10058'] = issue_data['story_points'].to_i
424
401
  end
402
+
425
403
  if issue_data['sprint']
426
404
  jira_fields['customfield_10020'] = issue_data['sprint'].to_i
427
405
  end
@@ -451,56 +429,6 @@ module Tefoji
451
429
  jira_fields['customfield_10018'] = issue_data['epic_parent']
452
430
  end
453
431
  end
454
-
455
- def set_onprem_jira_fields(issue_data, jira_fields)
456
- if issue_data['assignee']
457
- jira_fields['assignee'] = { FIELD_NAME => issue_data['assignee'] }
458
- end
459
-
460
- if issue_data['type']
461
- jira_fields['issuetype'] = { FIELD_NAME => issue_data['type'] }
462
- # If this is an epic, we need to add an epic name
463
- if epic?(issue_data)
464
- jira_fields['customfield_10007'] = issue_data['epic_name'] || issue_data['summary']
465
- end
466
- end
467
-
468
- if issue_data['story_points']
469
- jira_fields['customfield_10002'] = issue_data['story_points'].to_i
470
- end
471
-
472
- if issue_data['subteam']
473
- jira_fields['customfield_11700'] = [issue_data['subteam']]
474
- end
475
- if issue_data['sprint']
476
- jira_fields['customfield_10005'] = issue_data['sprint'].to_i
477
- end
478
- if issue_data['acceptance']
479
- jira_fields['customfield_11501'] = issue_data['acceptance']
480
- end
481
- if issue_data['release_notes']
482
- jira_fields['customfield_11100'] = { FIELD_VALUE => issue_data['release_notes'] }
483
- end
484
-
485
- # If a issue has a specified parent issue, prefer that. The parent issue *should* already
486
- # be linked to the main epic. Otherwise, we need to set it to have an epic_parent. This can
487
- # either be an epic linked to the main epic or the main epic itself.
488
-
489
- if issue_data['parent']
490
- unless issue_data['type'].casecmp?(ISSUE_SUB_TASK) || !issue_data['type']
491
- @logger.fatal "A issue with a parent must be classified as a Sub-issue\n\n#{issue_data}"
492
- exit 1
493
- end
494
- jira_fields['issuetype'] = { FIELD_NAME => ISSUE_SUB_TASK }
495
- jira_fields['parent'] = { FIELD_KEY => issue_data['parent'] }
496
- elsif issue_data['epic_parent']
497
- if issue_data['type'].casecmp?(ISSUE_SUB_TASK)
498
- @logger.fatal "This issue cannot be a subtask of an epic\n\n#{issue_data}"
499
- exit 1
500
- end
501
- jira_fields['customfield_10006'] = issue_data['epic_parent']
502
- end
503
- end
504
432
  end
505
433
 
506
434
  class JiraMockApi < JiraApi
@@ -510,20 +438,20 @@ module Tefoji
510
438
  @summary_count = 0
511
439
  end
512
440
 
513
- def authenticate(jira_base_url, username = nil, jira_auth_string = nil)
441
+ def authenticate(jira_base_url, username = nil, jira_auth = nil)
514
442
  @jira_base_url = jira_base_url
515
443
  @jira_base_rest_url = "#{jira_base_url}/rest/api/2"
516
444
 
517
445
  @logger.info "Using mock of Jira instance: \"#{jira_base_url}\""
518
446
 
519
- if jira_auth_string.nil?
447
+ if jira_auth.nil?
520
448
  if username.nil?
521
449
  print 'Enter jira user name: '
522
450
  username = $stdin.gets.chomp
523
451
  end
524
452
  else
525
- plain_auth_string = Base64.decode64(jira_auth_string)
526
- username = plain_auth_string.split(':')[0]
453
+ plain_auth = Base64.decode64(jira_auth)
454
+ username = plain_auth.split(':')[0]
527
455
  end
528
456
 
529
457
  @jira_username = username
data/lib/tefoji.rb CHANGED
@@ -30,7 +30,6 @@ module Tefoji
30
30
  @jira_auth_string = user_options['jira-auth-string']
31
31
  @jira_auth_file = user_options['jira-auth-file']
32
32
  @jira_mock = user_options['--jira-mock']
33
- @jira_cloud = user_options['jira-cloud']
34
33
 
35
34
  @no_notes = user_options['--no-notes']
36
35
  @template_data = {}
@@ -270,13 +269,13 @@ module Tefoji
270
269
  @jira_api.logger = @logger
271
270
 
272
271
  @jira_api.authenticate(@jira_url, @jira_user, @jira_auth_string)
273
- @jira_api.test_authentication(@jira_cloud) unless @jira_mock
272
+ @jira_api.test_authentication unless @jira_mock
274
273
  end
275
274
 
276
275
  # Save Jira auth data
277
276
  def save_authentication
278
277
  authenticate unless @jira_api
279
- @jira_api.save_authentication(@jira_auth_file, @jira_cloud)
278
+ @jira_api.save_authentication(@jira_auth_file)
280
279
  end
281
280
 
282
281
  def generate_epics_with_issues(epic_key)
@@ -312,31 +311,9 @@ module Tefoji
312
311
  end
313
312
 
314
313
  epic['type'] = JiraApi::ISSUE_EPIC
315
- if private_release?(epic)
316
- @epic_security = 'internal'
317
-
318
- private_description = ''
319
- private_repos = epic['private_release'].value['repos']
320
-
321
- if private_repos
322
- private_description << private_release_note
323
- private_description << private_repos.value.map do |repo|
324
- repo.map { |k, v| "* #{k.upcase}: #{v.value}" }
325
- end.join("\n")
326
- private_description << "\n\n"
327
- end
328
-
329
- private_leads = epic['private_release'].value['leads']
330
- if private_leads
331
- leads = private_leads.value.map { |lead| "[~#{lead}]" }.join(' ')
332
- private_description << "/cc #{leads} \n\n"
333
- end
334
-
335
- epic['description'].prepend(private_description)
336
- epic['security'] = 'internal'
337
- end
314
+ add_private_release(epic) if private_release?(epic)
338
315
 
339
- epic_issue = @jira_api.create_issue(epic, @jira_cloud)
316
+ epic_issue = @jira_api.create_issue(epic)
340
317
  epic_issue['short_name'] = short_name
341
318
  @logger.info 'Epic: %16s [%s]' % [epic_issue['key'], short_name]
342
319
  @deferral_data[short_name.to_s] = {
@@ -359,6 +336,30 @@ module Tefoji
359
336
  "NOTE: THIS IS A PRIVATE RELEASE. WORK WAS DONE IN THESE PRIVATE REPOS:\n"
360
337
  end
361
338
 
339
+ def add_private_release(epic)
340
+ private_description = ''
341
+ private_repos = epic['private_release'].value['repos']
342
+
343
+ if private_repos
344
+ private_description << private_release_note
345
+ private_description << private_repos.value.map do |repo|
346
+ repo.map { |k, v| "* #{k.upcase}: #{v.value}" }
347
+ end.join("\n")
348
+ private_description << "\n\n"
349
+ end
350
+
351
+ private_leads = epic['private_release'].value['leads']
352
+ if private_leads
353
+ leads = private_leads.value.map do |lead|
354
+ "[~accountid:#{@jira_api.user_email_to_account_id(lead)}]"
355
+ end.join(' ')
356
+
357
+ private_description << "/cc #{leads} \n\n"
358
+ end
359
+
360
+ epic['description'].prepend(private_description)
361
+ end
362
+
362
363
  # Iterate through all issues in the template, creating each one in Jira.
363
364
  # Link the issues back to their epic, if required
364
365
  def generate_ordinary_issues
@@ -375,7 +376,7 @@ module Tefoji
375
376
  jira_ready_data, raw_issue_data = prepare_jira_ready_data(issue, issue_defaults)
376
377
  next if jira_ready_data.nil? || raw_issue_data.nil?
377
378
 
378
- response_data = @jira_api.create_issue(jira_ready_data, @jira_cloud)
379
+ response_data = @jira_api.create_issue(jira_ready_data)
379
380
  jira_issue = @jira_api.retrieve_issue(response_data['self'])
380
381
  jira_issue['short_name'] = raw_issue_data['short_name']
381
382
 
@@ -584,7 +585,7 @@ module Tefoji
584
585
  issue_key = jira_issue_data['key']
585
586
  watchers = raw_issue_data[deferred_tag].value
586
587
  watchers.each do |watcher|
587
- @jira_api.add_watcher(issue_key, watcher, @jira_cloud)
588
+ @jira_api.add_watcher(issue_key, watcher)
588
589
  @logger.info '%14s: watched by %s' % [issue_key, watcher]
589
590
  end
590
591
  end
@@ -652,10 +653,10 @@ module Tefoji
652
653
  # cannot.
653
654
  if right_value.unset?
654
655
  # Convert %{FOO} to FOO
655
- environment_variable = value.gsub(/^%{(.+?)}/, '\1')
656
+ environment_variable = value.gsub(%r{^%{(.+?)}}, '\1')
656
657
  if environment_keys.include?(environment_variable)
657
- @logger.info "Setting \"#{key}\" to \"#{ENV[environment_variable]}\" "\
658
- 'from environment variable.'
658
+ @logger.info "Setting \"#{key}\" to \"#{ENV[environment_variable]}\" " \
659
+ 'from environment variable.'
659
660
  @declarations[key.to_sym] = DeclaredValue.new(ENV[environment_variable])
660
661
  next
661
662
  end
@@ -717,7 +718,7 @@ module Tefoji
717
718
  def expand_string(value)
718
719
  # Special-case where 'foo: "%{bar}"'; this allows for passing around hashes and
719
720
  # arrays without interpolation into strings
720
- single_value = /\A%{(.+?)}\Z/.match(value)
721
+ single_value = %r{\A%{(.+?)}\Z}.match(value)
721
722
  return @declarations[single_value[1].to_sym] if single_value
722
723
 
723
724
  return value % @declarations
@@ -741,10 +742,8 @@ module Tefoji
741
742
  def conflicting_conditionals?(template)
742
743
  if template.key?('unless') && (template.key?('conditional') || template.key?('if'))
743
744
  true
744
- elsif template.key?('if') && template.key?('conditional')
745
- true
746
745
  else
747
- false
746
+ template.key?('if') && template.key?('conditional')
748
747
  end
749
748
  end
750
749
 
@@ -789,7 +788,7 @@ module Tefoji
789
788
 
790
789
  def missing_expected_keys?(template_uri, template)
791
790
  expected_keys = %w[epic epics issues]
792
- return false if (template.keys & expected_keys).any?
791
+ return false if template.keys.intersect?(expected_keys)
793
792
 
794
793
  @logger.error "Cannot find any known template keywords in \"#{template_uri}\""
795
794
  return true
@@ -809,7 +808,6 @@ module Tefoji
809
808
 
810
809
  def check_assignees(main_template_data)
811
810
  valid_users = []
812
-
813
811
  invalid_users_to_epics = {}
814
812
  main_template_data['epics']&.each do |epic|
815
813
  next unless epic['assignee']
@@ -906,7 +904,7 @@ module Tefoji
906
904
  def update_user_validity(username, issue_name, valid_users, invalid_users)
907
905
  if invalid_users.key?(username)
908
906
  invalid_users[username] += [issue_name]
909
- elsif @jira_api.get_username(username, @jira_cloud, false)
907
+ elsif @jira_api.get_username(username, false)
910
908
  valid_users << username
911
909
  else
912
910
  invalid_users[username] = invalid_users.fetch(username, []) + [issue_name]
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tefoji
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
- - Puppet Labs
7
+ - Puppet By Perforce
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-10 00:00:00.000000000 Z
11
+ date: 2023-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: pry-byebug
14
+ name: debug
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 1.0.0
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: pry-doc
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
26
+ version: 1.0.0
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: rake
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -168,7 +154,7 @@ description: 'From a YAML specification, create a batch of Jira issues allowing
168
154
  variable substitutions.
169
155
 
170
156
  '
171
- email: info@puppetlabs.com
157
+ email: release@puppet.com
172
158
  executables:
173
159
  - tefoji
174
160
  extensions: []