tefoji 2.0.0 → 3.0.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: c181a5680af5eebeab13d8a78d1e5430df0f3e7535cd9083771af8fbff6d3d06
4
- data.tar.gz: 9e0e8b9dcbc36cdbc093a038bd67d4b38f56197bf25695329d78f15c273e5ace
3
+ metadata.gz: 438ee8e0448c8a7805362ad3821f68f9c1ce6c50f06f4ad93aa1928d2a60516b
4
+ data.tar.gz: a6dd2a6d4f52ba6145d68889671ba6b0a95d36d34d3cf2c0a2fa9f11b426972f
5
5
  SHA512:
6
- metadata.gz: 40b84da52997e8df28012872982e3e50c2f3b18da3a2f80650d08c0f45525a3fa6803e1c9fdca9a82e6461c67882357ddc77d85125b39000176930bb6ccf3464
7
- data.tar.gz: 6396ee3ff93fd5214500ebe2913d5eddce18968741532e8c7a95b4547a4ffef7251fd9cf0667a30236503b76d1468f44e650f2816ca8815e0f8d6dbaf7891593
6
+ metadata.gz: 885c76aa6b283c24412fe5114397dae1375c68acce44186d421aa3be5d9e87e59e934d6d6cdb863d0bc17434bdabd3eee50c281de0bee50297ea4e1e936d0f2b
7
+ data.tar.gz: 434262e7a761ab55076bd4e53d679bd7a3f45154803d21409600456998bad4e49dd68742bf753ccb92e0b2556a7740e26c6cfea724393b3396fed4647e3aede1
@@ -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
@@ -329,49 +339,58 @@ module Tefoji
329
339
 
330
340
  # Backward compatiblity: translate 'team' to 'scrum_team'
331
341
  scrum_team = issue_data['scrum_team'] || issue_data['team']
342
+
332
343
  if scrum_team
333
- if epic?(issue_data)
334
- jira_fields['components'] = [{ FIELD_NAME => scrum_team }]
335
- else
336
- jira_fields['customfield_11500'] = { FIELD_VALUE => scrum_team }
337
- end
344
+ field_name, field_value = scrum_team_rules(issue_data, scrum_team)
345
+ jira_fields[field_name] = field_value
338
346
  end
339
347
 
340
- # We're only allowed multiple teams (as 'components') in epics. For ordinary
341
- # issues take only the first of the list
348
+ # Backward compatiblity: translate 'teams' to 'scrum_teams'
342
349
  scrum_teams = issue_data['scrum_teams'] || issue_data['teams']
350
+
343
351
  if scrum_teams
344
- processed_teams = scrum_teams.to_a.flatten.reject { |t| t =~ /^\s*$/ }
345
- if epic?(issue_data)
346
- jira_fields['components'] = processed_teams.map { |team| { FIELD_NAME => team } }
347
- else
348
- jira_fields['customfield_11500'] = { FIELD_VALUE => processed_teams.first }
349
- end
352
+ field_name, field_value = scrum_teams_rules(issue_data, scrum_team)
353
+ jira_fields[field_name] = field_value
350
354
  end
351
355
 
352
356
  # Default issue type to ISSUE_TASK if it isn't already set
353
357
  jira_fields['issuetype'] = { FIELD_NAME => ISSUE_TASK }
358
+ end
354
359
 
355
- security = ENV['SECURITY'] || issue_data['security']
356
- return unless security
360
+ # There are now some funky rules for setting the 'scrum_team' (previously 'team')
361
+ # and 'scrum_teams' field in different issues.
362
+ #
363
+ # For epics, put the scrum team in the 'components' field.
364
+ # Except for a couple of projects, put the scrum team in customfield_11500
365
+ # For Puppet Agent (PA) and Puppet Server (SERVER) projects, put the scrum team in
366
+ # customfield_14200
367
+ def scrum_team_rules(issue_data, scrum_team)
368
+ if epic?(issue_data)
369
+ return ['components', [{ FIELD_NAME => scrum_team }]]
370
+ end
357
371
 
358
- case security.downcase
359
- when 'confidential'
360
- jira_fields['security'] = { FIELD_ID => '10002' }
361
- when 'internal'
362
- jira_fields['security'] = { FIELD_ID => '10001' }
363
- when 'public'
364
- # Nothing to do here - public is default
365
- else
366
- @logger.fatal "Unknown security type: #{security}"
367
- exit 1
372
+ customfield = 'customfield_11500'
373
+ if %w[PA SERVER].include?(issue_data['project'].value)
374
+ customfield = 'customfield_14200'
368
375
  end
376
+
377
+ return [customfield, { FIELD_VALUE => scrum_team }]
378
+ end
379
+
380
+ def scrum_teams_rules(issue_data, scrum_teams)
381
+ processed_teams = scrum_teams.to_a.flatten.reject { |t| t =~ %r{^\s*$} }
382
+ if epic?(issue_data)
383
+ return ['components', processed_teams.map { |team| { FIELD_NAME => team } }]
384
+ end
385
+
386
+ # For ordinary issues we can only set one team.
387
+ scrum_team_rules(issue_data, processed_teams.first)
369
388
  end
370
389
 
371
390
  def set_cloud_jira_fields(issue_data, jira_fields)
372
391
  if issue_data['assignee']
373
392
  assignee = issue_data['assignee']
374
- assignee = useremail_to_id(assignee)
393
+ assignee = user_email_to_account_id(assignee.value)
375
394
  jira_fields['assignee'] = { FIELD_ACCOUNT_ID => assignee }
376
395
  end
377
396
 
@@ -390,7 +409,7 @@ module Tefoji
390
409
  jira_fields['customfield_10052'] = { FIELD_VALUE => issue_data['team'] }
391
410
  end
392
411
  if issue_data['teams']
393
- teams = issue_data['teams'].to_a.flatten.reject { |t| t =~ /^\s*$/ }
412
+ teams = issue_data['teams'].to_a.flatten.reject { |t| t =~ %r{^\s*$} }
394
413
  unless teams.empty?
395
414
  jira_fields['customfield_10066'] = teams.map { |team| { FIELD_VALUE => team } }
396
415
  end
@@ -427,56 +446,6 @@ module Tefoji
427
446
  jira_fields['customfield_10018'] = issue_data['epic_parent']
428
447
  end
429
448
  end
430
-
431
- def set_onprem_jira_fields(issue_data, jira_fields)
432
- if issue_data['assignee']
433
- jira_fields['assignee'] = { FIELD_NAME => issue_data['assignee'] }
434
- end
435
-
436
- if issue_data['type']
437
- jira_fields['issuetype'] = { FIELD_NAME => issue_data['type'] }
438
- # If this is an epic, we need to add an epic name
439
- if epic?(issue_data)
440
- jira_fields['customfield_10007'] = issue_data['epic_name'] || issue_data['summary']
441
- end
442
- end
443
-
444
- if issue_data['story_points']
445
- jira_fields['customfield_10002'] = issue_data['story_points'].to_i
446
- end
447
-
448
- if issue_data['subteam']
449
- jira_fields['customfield_11700'] = [issue_data['subteam']]
450
- end
451
- if issue_data['sprint']
452
- jira_fields['customfield_10005'] = issue_data['sprint'].to_i
453
- end
454
- if issue_data['acceptance']
455
- jira_fields['customfield_11501'] = issue_data['acceptance']
456
- end
457
- if issue_data['release_notes']
458
- jira_fields['customfield_11100'] = { FIELD_VALUE => issue_data['release_notes'] }
459
- end
460
-
461
- # If a issue has a specified parent issue, prefer that. The parent issue *should* already
462
- # be linked to the main epic. Otherwise, we need to set it to have an epic_parent. This can
463
- # either be an epic linked to the main epic or the main epic itself.
464
-
465
- if issue_data['parent']
466
- unless issue_data['type'].casecmp?(ISSUE_SUB_TASK) || !issue_data['type']
467
- @logger.fatal "A issue with a parent must be classified as a Sub-issue\n\n#{issue_data}"
468
- exit 1
469
- end
470
- jira_fields['issuetype'] = { FIELD_NAME => ISSUE_SUB_TASK }
471
- jira_fields['parent'] = { FIELD_KEY => issue_data['parent'] }
472
- elsif issue_data['epic_parent']
473
- if issue_data['type'].casecmp?(ISSUE_SUB_TASK)
474
- @logger.fatal "This issue cannot be a subtask of an epic\n\n#{issue_data}"
475
- exit 1
476
- end
477
- jira_fields['customfield_10006'] = issue_data['epic_parent']
478
- end
479
- end
480
449
  end
481
450
 
482
451
  class JiraMockApi < JiraApi
@@ -486,20 +455,20 @@ module Tefoji
486
455
  @summary_count = 0
487
456
  end
488
457
 
489
- def authenticate(jira_base_url, username = nil, jira_auth_string = nil)
458
+ def authenticate(jira_base_url, username = nil, jira_auth = nil)
490
459
  @jira_base_url = jira_base_url
491
460
  @jira_base_rest_url = "#{jira_base_url}/rest/api/2"
492
461
 
493
462
  @logger.info "Using mock of Jira instance: \"#{jira_base_url}\""
494
463
 
495
- if jira_auth_string.nil?
464
+ if jira_auth.nil?
496
465
  if username.nil?
497
466
  print 'Enter jira user name: '
498
467
  username = $stdin.gets.chomp
499
468
  end
500
469
  else
501
- plain_auth_string = Base64.decode64(jira_auth_string)
502
- username = plain_auth_string.split(':')[0]
470
+ plain_auth = Base64.decode64(jira_auth)
471
+ username = plain_auth.split(':')[0]
503
472
  end
504
473
 
505
474
  @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
@@ -906,7 +905,7 @@ module Tefoji
906
905
  def update_user_validity(username, issue_name, valid_users, invalid_users)
907
906
  if invalid_users.key?(username)
908
907
  invalid_users[username] += [issue_name]
909
- elsif @jira_api.get_username(username, @jira_cloud, false)
908
+ elsif @jira_api.get_username(username, false)
910
909
  valid_users << username
911
910
  else
912
911
  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.0.0
4
+ version: 3.0.0
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-06-29 00:00:00.000000000 Z
11
+ date: 2023-08-17 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: []