tefoji 1.0.8 → 1.0.9

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: 888097f6ae0c3877e00fa2a2fc0318fb094f5e340621b445f510ffab51b01f85
4
- data.tar.gz: 65af396135aa1f22b1385a3c2ed7738db817733429d2243651b3ced884a2a9fe
3
+ metadata.gz: 8279bb8ebda1af1e674da5c4d16932b790e69210a5c163563746a80d44aca92b
4
+ data.tar.gz: f17bbc221ed1ec7c4fde90d2e64caddb45078ee0ee2d5cb59e7226d90df14317
5
5
  SHA512:
6
- metadata.gz: 9d5ce9983a3401f0fe6644fa35725a9c892733d7ad1bdb7ba6051e058fb6aa0a911a2a5cd30aba783c2260c19067fc1112c49c1bf4bf64501b7188096b5ae64e
7
- data.tar.gz: 171807f4bf998fd42dd6296de339934c6a22dcd73722749886abf474a795a2e6dfdb2b6faea95a6a8bd5cf7956bd5f9c6d81d786b1c30c554756c7faba7d18db
6
+ metadata.gz: d790551fe749db5eea41ce6889479c3f23e8278ad94744b8be4561d0cceadb33b609949c612224e49bbc21587b8b3cf50501a6eeef1e2d3059c860420e583c15
7
+ data.tar.gz: a0b4bd8875027f141a87f2bc960c43143813500db372e0764df428896fc04f34fd3f5b91d3a4d4ca305e9949b88dd3329f4363a1981905f6b4b7d7fe6c7b9fe9
@@ -3,7 +3,7 @@ module Logging
3
3
  # Another cheap hack to wrap 'logger' above. Called as 'fatal' rather than 'logger.fatal'
4
4
  # directly, does the exit for us so we can be lazy.
5
5
  def fatal(message)
6
- logger.fatal(message)
6
+ @logger.fatal(message)
7
7
  exit 1
8
8
  end
9
9
  end
@@ -48,7 +48,7 @@ module UserFunctions
48
48
  MODULES: 'MODULES',
49
49
  MODULES_INTERNAL: 'FM',
50
50
  OPERATIONS: 'OPS',
51
- PDK: 'PDK',
51
+ PDK: 'CONT',
52
52
  PE_INTERNAL: 'PE',
53
53
  PROJECT_CENTRAL: 'PC',
54
54
  PUPPETDB: 'PDB',
@@ -89,12 +89,12 @@ module UserFunctions
89
89
  CD4PE: 'CD4PE',
90
90
  CODE_MANAGEMENT: 'Dumpling',
91
91
  DUMPLING: 'Dumpling',
92
- FACTER: "Phoenix",
92
+ FACTER: 'Phoenix',
93
93
  INSTALLER: 'Installer and Management',
94
94
  NETWORKING: 'Network Automation',
95
95
  OPERATIONS: 'Operations',
96
96
  PE: 'Dumpling',
97
- PLATFORM_OS: "Phoenix",
97
+ PLATFORM_OS: 'Phoenix',
98
98
  PUPPETDB: 'Dumpling',
99
99
  PUPPETSERVER: 'Dumpling',
100
100
  QE: 'Quality Engineering',
data/lib/tefoji/cli.rb CHANGED
@@ -43,7 +43,7 @@ module Tefoji
43
43
  @user_options = parse_options(argv)
44
44
 
45
45
  if @user_options['--version']
46
- warn "tefoji version #{Gem.loaded_specs['tefoji'].version.to_s}"
46
+ warn "tefoji version #{Gem.loaded_specs['tefoji'].version}"
47
47
  exit 0
48
48
  end
49
49
 
@@ -75,14 +75,15 @@ module Tefoji
75
75
  # Do this so we can inform the user quickly that their credentials didn't work
76
76
  # There may be a better test, but this is the one the original Winston uses
77
77
  def test_authentication
78
- get_username
78
+ get_username(@jira_username)
79
79
  rescue RestClient::Forbidden
80
80
  fatal 'Forbidden: either the authentication is incorrect or ' \
81
81
  'Jira might be requiring a CAPTCHA response from the web interface.'
82
82
  end
83
83
 
84
- def get_username
85
- jira_get("user?username=#{@jira_username}")
84
+ # Get information about user in Jira
85
+ def get_username(username, fail_if_not_found = true)
86
+ jira_get("user?username=#{username}", fail_if_not_found)
86
87
  end
87
88
 
88
89
  # Save authentication YAML to the a file for reuse.
@@ -167,7 +168,7 @@ module Tefoji
167
168
 
168
169
  private
169
170
 
170
- def jira_get(jira_request_path)
171
+ def jira_get(jira_request_path, fail_if_not_found = true)
171
172
  # Jira likes to send complete URLs for responses. Handle
172
173
  # the case where we've received a 'self' query from Jira with
173
174
  # fully formed url
@@ -191,7 +192,9 @@ module Tefoji
191
192
  rescue RestClient::NotFound,
192
193
  SocketError,
193
194
  Errno::ECONNREFUSED => e
194
- fatal "Cannot connect to #{@jira_base_rest_url}: #{e.message}"
195
+ # Return False if not found rather than fail to allow for checking the existence of users.
196
+ fatal "Cannot connect to #{@jira_base_rest_url}: #{e.message}" if fail_if_not_found
197
+ return false
195
198
  end
196
199
 
197
200
  return JSON.parse(response.body)
data/lib/tefoji.rb CHANGED
@@ -74,6 +74,8 @@ module Tefoji
74
74
  resolve_variables(main_template_data['declare'])
75
75
  @logger.debug "Declarations: #{@declarations}"
76
76
 
77
+ check_assignees(main_template_data)
78
+
77
79
  # Process 'before' templates
78
80
  main_template.dig(:includes, :before)&.each do |before|
79
81
  default_epic_saved = @default_target_epic
@@ -641,12 +643,17 @@ module Tefoji
641
643
  def user_function_call(function_definition)
642
644
  @logger.debug "function_definition is: #{function_definition}"
643
645
  function_name = function_definition['name']
644
- if function_definition.key?('arguments')
645
- function_arguments = function_definition['arguments'].map do |a|
646
+ argument = function_definition['argument']
647
+ arguments = function_definition['arguments']
648
+
649
+ if argument.is_a?(Array) || arguments.is_a?(Array)
650
+ arguments = argument || function_definition['arguments']
651
+ function_arguments = arguments.map do |a|
646
652
  expand_right_value(a).value
647
653
  end
648
- elsif function_definition.key?('argument')
649
- function_arguments = [expand_right_value(function_definition['argument']).value]
654
+ elsif argument.is_a?(String) || arguments.is_a?(String)
655
+ argument = function_definition['argument'] || function_definition['arguments']
656
+ function_arguments = [expand_right_value(argument).value]
650
657
  else
651
658
  fatal("No arguments supplied to function \"#{function_name}\"")
652
659
  end
@@ -814,6 +821,124 @@ module Tefoji
814
821
  return true
815
822
  end
816
823
 
824
+ # Iterates through the assignees in a template and checks if they exist on jira. If any assignee in
825
+ # the template does not exist, it will fail, outputting a message saying which users failed and
826
+ # what epics/issues they are associated with.
827
+ # NOTE: This check does not guarantee a user is assignable, so there may be cases when a user passes
828
+ # the check but still fails when being assigned a ticket/epic. If we find a reasonable way to
829
+ # check assignability prior to the epic/issue being created, we should implement it in place of
830
+ # using the get_username method.
831
+ def check_assignees(main_template_data)
832
+ valid_users = []
833
+
834
+ invalid_users_to_features = {}
835
+ main_template_data['features']&.each do |feature|
836
+ next unless feature['assignee']
837
+
838
+ assignee_username = get_username_from_assignee(feature['assignee'])
839
+ next if valid_users.include?(assignee_username)
840
+
841
+ update_user_validity(assignee_username, feature['summary'], valid_users, invalid_users_to_features)
842
+ end
843
+
844
+ if main_template_data.dig('feature', 'assignee')
845
+ assignee_username = get_username_from_assignee(main_template_data['feature']['assignee'])
846
+ unless valid_users.include?(assignee_username)
847
+ update_user_validity(assignee_username, main_template_data['feature']['summary'], valid_users,
848
+ invalid_users_to_features)
849
+ end
850
+ end
851
+
852
+ invalid_users_to_epics = {}
853
+ main_template_data['epics']&.each do |epic|
854
+ next unless epic['assignee']
855
+
856
+ assignee_username = get_username_from_assignee(epic['assignee'])
857
+ next if valid_users.include?(assignee_username)
858
+
859
+ update_user_validity(assignee_username, epic['summary'], valid_users, invalid_users_to_epics)
860
+ end
861
+
862
+ if main_template_data.dig('epic', 'assignee')
863
+ assignee_username = get_username_from_assignee(main_template_data['epic']['assignee'])
864
+ unless valid_users.include?(assignee_username)
865
+ update_user_validity(assignee_username, main_template_data['epic']['summary'], valid_users,
866
+ invalid_users_to_epics)
867
+ end
868
+ end
869
+
870
+ invalid_users_to_issue_defaults = {}
871
+ if main_template_data.dig('issue_defaults', 'assignee')
872
+ assignee_username = get_username_from_assignee(main_template_data['issue_defaults']['assignee'])
873
+ unless valid_users.include?(assignee_username)
874
+ update_user_validity(assignee_username, 'issue_defaults', valid_users, invalid_users_to_issue_defaults)
875
+ end
876
+ end
877
+
878
+ invalid_users_to_issues = {}
879
+ main_template_data['issues'].each do |issue|
880
+ next unless issue['assignee']
881
+
882
+ assignee_username = get_username_from_assignee(issue['assignee'])
883
+ next if valid_users.include?(assignee_username)
884
+
885
+ update_user_validity(assignee_username, issue['summary'], valid_users, invalid_users_to_issues)
886
+ end
887
+ unless invalid_users_to_features.empty? &&
888
+ invalid_users_to_epics.empty? &&
889
+ invalid_users_to_issue_defaults.empty? &&
890
+ invalid_users_to_issues.empty?
891
+ invalid_user_message = "Invalid assignees:\n"
892
+ end
893
+ unless invalid_users_to_features.empty?
894
+ invalid_users_to_features.each do |assignee, feature|
895
+ invalid_user_message += "Assignee #{assignee} associated with features #{feature}\n"
896
+ end
897
+ end
898
+ unless invalid_users_to_epics.empty?
899
+ invalid_users_to_epics.each do |assignee, epic|
900
+ invalid_user_message += "Assignee #{assignee} associated with epics #{epic}\n"
901
+ end
902
+ end
903
+ unless invalid_users_to_issue_defaults.empty?
904
+ invalid_users_to_issue_defaults.each do |assignee, _|
905
+ invalid_user_message += "Assignee #{assignee} associated with issue_defaults\n"
906
+ end
907
+ end
908
+ unless invalid_users_to_issues.empty?
909
+ invalid_users_to_issues.each do |assignee, issues|
910
+ invalid_user_message += "Assignee #{assignee} associated with issues #{issues}\n"
911
+ end
912
+ end
913
+ fatal invalid_user_message if invalid_user_message
914
+ end
915
+
916
+ def get_username_from_assignee(assignee)
917
+ # Assignee is a variable or direct input
918
+ username = expand_string(assignee) if assignee.is_a?(String)
919
+ # Assignee is a function
920
+ if assignee.is_a?(Hash) && assignee.key?('function')
921
+ username = user_function_call(assignee['function'])
922
+ end
923
+
924
+ unless username
925
+ fatal "invalid assignee format for issue: #{issue['short_name']} failed: must be a function or a string"
926
+ end
927
+
928
+ username = username.value unless username.is_a?(String)
929
+ username
930
+ end
931
+
932
+ def update_user_validity(username, issue_name, valid_users, invalid_users)
933
+ if invalid_users.key?(username)
934
+ invalid_users[username] += [issue_name]
935
+ elsif @jira_api.get_username(username, false)
936
+ valid_users << username
937
+ else
938
+ invalid_users[username] = invalid_users.fetch(username, []) + [issue_name]
939
+ end
940
+ end
941
+
817
942
  def logger_initialize(log_location)
818
943
  logger = Logger.new(log_location, progname: 'tefoji', level: @log_level)
819
944
  logger.formatter = proc do |severity, _, script_name, message|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tefoji
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet Labs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-11 00:00:00.000000000 Z
11
+ date: 2023-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry-byebug