tefoji 2.0.0 → 3.0.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: 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: []