toolshed 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +11 -0
  4. data/.toolshedrc.sample +32 -0
  5. data/README.md +159 -2
  6. data/Rakefile +3 -3
  7. data/bin/toolshed +6 -1
  8. data/lib/toolshed.rb +38 -28
  9. data/lib/toolshed/base.rb +33 -11
  10. data/lib/toolshed/cli.rb +30 -38
  11. data/lib/toolshed/client.rb +87 -293
  12. data/lib/toolshed/commands/base.rb +65 -23
  13. data/lib/toolshed/commands/checkout_branch.rb +15 -2
  14. data/lib/toolshed/commands/create_branch.rb +34 -29
  15. data/lib/toolshed/commands/create_pivotal_tracker_note.rb +9 -3
  16. data/lib/toolshed/commands/create_pull_request.rb +115 -68
  17. data/lib/toolshed/commands/create_ticket_comment.rb +17 -1
  18. data/lib/toolshed/commands/delete_branch.rb +34 -3
  19. data/lib/toolshed/commands/get_daily_time_update.rb +20 -3
  20. data/lib/toolshed/commands/list_branches.rb +16 -5
  21. data/lib/toolshed/commands/push_branch.rb +28 -9
  22. data/lib/toolshed/commands/rename_branch.rb +29 -0
  23. data/lib/toolshed/commands/ssh.rb +44 -3
  24. data/lib/toolshed/commands/ticket_information.rb +30 -4
  25. data/lib/toolshed/commands/update_pivotal_tracker_story_status.rb +9 -3
  26. data/lib/toolshed/commands/update_ticket_status.rb +8 -2
  27. data/lib/toolshed/entry_point.rb +89 -0
  28. data/lib/toolshed/git.rb +59 -0
  29. data/lib/toolshed/git/branch.rb +224 -0
  30. data/lib/toolshed/git/github.rb +45 -57
  31. data/lib/toolshed/git/validator.rb +14 -0
  32. data/lib/toolshed/logger.rb +46 -0
  33. data/lib/toolshed/password.rb +11 -6
  34. data/lib/toolshed/server_administration/ssh.rb +4 -2
  35. data/lib/toolshed/ticket_tracking/jira.rb +8 -6
  36. data/lib/toolshed/ticket_tracking/pivotal_tracker.rb +8 -6
  37. data/lib/toolshed/time_tracking/harvest.rb +8 -14
  38. data/lib/toolshed/version.rb +25 -1
  39. data/test/commands/checkout_branch_test.rb +11 -7
  40. data/test/commands/create_branch_test.rb +29 -24
  41. data/test/commands/create_pull_request_test.rb +39 -31
  42. data/test/commands/delete_branch_test.rb +35 -25
  43. data/test/commands/get_daily_time_update_test.rb +8 -8
  44. data/test/commands/push_branch_test.rb +27 -15
  45. data/test/commands/rename_branch_test.rb +59 -0
  46. data/test/git/git_helper.rb +5 -5
  47. data/test/git/git_test.rb +36 -31
  48. data/test/git/github_test.rb +9 -46
  49. data/test/helper.rb +11 -11
  50. data/test/server_administration/ssh_test.rb +1 -0
  51. data/test/ticket_tracking/jira_test.rb +18 -16
  52. data/test/time_tracking/harvest_test.rb +8 -6
  53. data/toolshed.gemspec +23 -20
  54. metadata +95 -46
  55. data/bin/toolshed.rb +0 -261
  56. data/lib/toolshed/git/git.rb +0 -119
@@ -1,308 +1,102 @@
1
+ require 'singleton'
1
2
  require 'toolshed/version'
2
3
  require 'yaml'
3
4
 
4
5
  module Toolshed
6
+ # This is responsible for loading .toolshedrc file
5
7
  class Client
6
- GITHUB_BASE_API_URL = "https://api.github.com/"
7
- PIVOTAL_TRACKER_BASE_API_URL = "https://www.pivotaltracker.com/services/v5/"
8
-
9
- # debugging options
10
- def self.debug?
11
- @debug
12
- end
13
-
14
- def self.debug=(debug)
15
- @debug = debug
16
- end
17
-
18
- def self.git_quiet
19
- @git_quiet
20
- end
21
-
22
- def self.git_quiet=(git_quiet)
23
- @git_quiet = git_quiet
24
- end
25
-
26
- def self.use_defaults
27
- @use_defaults
28
- end
29
-
30
- def self.use_defaults=(use_defaults)
31
- @use_defaults = use_defaults
32
- end
33
-
34
-
35
- # github config settings
36
- def self.github_username
37
- @github_username
38
- end
39
-
40
- def self.github_username=(username)
41
- @github_username = username
42
- end
43
-
44
- def self.github_password
45
- @github_password
46
- end
47
-
48
- def self.github_password=(password)
49
- @github_password = password
50
- end
51
-
52
- def self.github_token
53
- @github_token
54
- end
55
-
56
- def self.github_token=(token)
57
- @github_token = token
58
- end
59
-
60
- def self.pull_from_remote_name
61
- @pull_from_remote_name
62
- end
63
-
64
- def self.pull_from_remote_name=(pull_from_remote_name)
65
- @pull_from_remote_name = pull_from_remote_name
66
- end
67
-
68
- def self.pull_from_repository_user
69
- @pull_from_repository_user
70
- end
71
-
72
- def self.pull_from_repository_user=(pull_from_repository_user)
73
- @pull_from_repository_user = pull_from_repository_user
74
- end
75
-
76
- def self.pull_from_repository_name
77
- @pull_from_repository_name
78
- end
79
-
80
- def self.pull_from_repository_name=(pull_from_repository_name)
81
- @pull_from_repository_name = pull_from_repository_name
82
- end
83
-
84
- def self.push_to_repository_user
85
- @push_to_repository_user
86
- end
87
-
88
- def self.push_to_repository_user=(push_to_repository_user)
89
- @push_to_repository_user = push_to_repository_user
90
- end
91
-
92
- def self.push_to_remote_name
93
- @push_to_remote_name
94
- end
95
-
96
- def self.push_to_remote_name=(push_to_remote_name)
97
- @push_to_remote_name = push_to_remote_name
98
- end
99
-
100
- def self.use_git_submodules
101
- @use_git_submodules
102
- end
103
-
104
- def self.use_git_submodules=(use_git_submodules)
105
- @use_git_submodules = use_git_submodules
106
- end
107
-
108
- def self.git_tool
109
- (@git_tool.nil?) ? Toolshed::Git::DEFAULT_GIT_TOOL : @git_tool
110
- end
111
-
112
- def self.git_tool=(git_tool)
113
- @git_tool = git_tool
114
- end
115
-
116
-
117
- # ticket tracking information
118
-
119
- def self.pivotal_tracker_username
120
- @pivotal_tracker_username
121
- end
122
-
123
- def self.pivotal_tracker_username=(username)
124
- @pivotal_tracker_username = username
125
- end
126
-
127
- def self.pivotal_tracker_password
128
- @pivotal_tracker_password
129
- end
130
-
131
- def self.pivotal_tracker_password=(password)
132
- @pivotal_tracker_password = password
133
- end
134
-
135
- def self.default_pivotal_tracker_project_id
136
- @default_pivotal_tracker_project_id
137
- end
138
-
139
- def self.default_pivotal_tracker_project_id=(default_pivotal_tracker_project_id)
140
- @default_pivotal_tracker_project_id = default_pivotal_tracker_project_id
141
- end
142
-
143
- def self.ticket_tracker_username
144
- @ticket_tracker_username
145
- end
146
-
147
- def self.ticket_tracker_username=(username)
148
- @ticket_tracker_username = username
149
- end
150
-
151
- def self.ticket_tracker_password
152
- @ticket_tracker_password
153
- end
154
-
155
- def self.ticket_tracker_password=(password)
156
- @ticket_tracker_password = password
157
- end
158
-
159
- def self.default_ticket_tracker_project
160
- @default_ticket_tracker_project
161
- end
162
-
163
- def self.default_ticket_tracker_project=(default_ticket_tracker_project)
164
- @default_ticket_tracker_project = default_ticket_tracker_project
165
- end
166
-
167
- def self.ticket_tracking_tool
168
- @ticket_tracking_tool
169
- end
170
-
171
- def self.ticket_tracking_tool=(ticket_tracking_tool)
172
- @ticket_tracking_tool = ticket_tracking_tool
173
- end
174
-
175
- def self.ticket_tracker_owner
176
- @ticket_tracker_owner
177
- end
178
-
179
- def self.ticket_tracker_owner=(owner)
180
- @ticket_tracker_owner = owner
181
- end
182
-
183
- def self.ticket_status_for_complete
184
- @ticket_status_for_complete
185
- end
186
-
187
- def self.ticket_status_for_complete=(status)
188
- @ticket_status_for_complete = status
189
- end
190
-
191
- def self.default_pull_request_title_format
192
- @default_pull_request_title_format
193
- end
194
-
195
- def self.default_pull_request_title_format=(default_pull_request_title_format)
196
- @default_pull_request_title_format = default_pull_request_title_format
197
- end
198
-
199
- # time tracking configuration
200
-
201
- def self.time_tracking_tool
202
- @time_tracking_tool
203
- end
204
-
205
- def self.time_tracking_tool=(time_tracking_tool)
206
- @time_tracking_tool = time_tracking_tool
207
- end
208
-
209
- def self.time_tracking_username
210
- @time_tracking_username
211
- end
212
-
213
- def self.time_tracking_username=(time_tracking_username)
214
- @time_tracking_username = time_tracking_username
215
- end
216
-
217
- def self.time_tracking_password
218
- @time_tracking_password
219
- end
220
-
221
- def self.time_tracking_password=(time_tracking_password)
222
- @time_tracking_password = time_tracking_password
223
- end
224
-
225
- def self.time_tracking_owner
226
- @time_tracking_owner
227
- end
228
-
229
- def self.time_tracking_owner=(time_tracking_owner)
230
- @time_tracking_owner = time_tracking_owner
231
- end
232
-
233
- def self.time_tracking_default_project_id
234
- @time_tracking_default_project_id
235
- end
236
-
237
- def self.time_tracking_default_project_id=(time_tracking_default_project_id)
238
- @time_tracking_default_project_id = time_tracking_default_project_id
239
- end
240
-
241
- def self.load_credentials_if_necessary
242
- load_credentials unless credentials_loaded?
243
- end
244
-
245
- def self.config_path
246
- ENV['TOOLSHED_CONFIG'] || '~/.toolshedrc'
247
- end
8
+ include Singleton
9
+
10
+ attr_accessor :debug,
11
+ :git_quiet,
12
+ :use_defaults,
13
+ :github_username,
14
+ :github_password,
15
+ :github_token,
16
+ :use_defaults,
17
+ :pull_from_repository_user,
18
+ :pull_from_repository_name,
19
+ :pull_from_remote_name,
20
+ :push_to_repository_user,
21
+ :push_to_remote_name,
22
+ :use_git_submodules,
23
+ :git_tool,
24
+ :pivotal_tracker_username,
25
+ :pivotal_tracker_password,
26
+ :default_pivotal_tracker_project_id,
27
+ :ticket_tracker_username,
28
+ :ticket_tracker_password,
29
+ :default_ticket_tracker_project,
30
+ :ticket_tracking_tool,
31
+ :ticket_tracker_owner,
32
+ :ticket_status_for_complete,
33
+ :default_pull_request_title_format,
34
+ :time_tracking_tool,
35
+ :time_tracking_username,
36
+ :time_tracking_password,
37
+ :time_tracking_owner,
38
+ :time_tracking_default_project_id,
39
+ :load_credentials_if_necessary,
40
+ :log_path,
41
+ :credentials_loaded_from
42
+
43
+ GITHUB_BASE_API_URL = 'https://api.github.com/'
44
+ PIVOTAL_TRACKER_BASE_API_URL = 'https://www.pivotaltracker.com/services/v5/'
45
+
46
+ class << self
47
+ def config_path
48
+ ENV['TOOLSHED_CONFIG'] || '~/.toolshedrc'
49
+ end
248
50
 
249
- def self.load_credentials(path = config_path)
250
- credentials = Client.read_credenials(config_path)
251
- self.github_username ||= credentials['github_username']
252
- self.github_password ||= credentials['github_password']
253
- self.github_token ||= credentials['github_token']
254
- self.pivotal_tracker_username ||= credentials['pivotal_tracker_username']
255
- self.pivotal_tracker_password ||= credentials['pivotal_tracker_password']
256
- self.default_pivotal_tracker_project_id ||= credentials['default_pivotal_tracker_project_id']
257
- self.ticket_tracker_username ||= credentials['ticket_tracker_username']
258
- self.ticket_tracker_password ||= credentials['ticket_tracker_password']
259
- self.ticket_tracker_owner ||= credentials['ticket_tracker_owner']
260
- self.ticket_status_for_complete ||= credentials['ticket_status_for_complete']
261
- self.default_ticket_tracker_project ||= credentials['default_ticket_tracker_project']
262
- self.pull_from_remote_name ||= credentials['pull_from_remote_name']
263
- self.pull_from_repository_user ||= credentials['pull_from_repository_user']
264
- self.pull_from_repository_name ||= credentials['pull_from_repository_name']
265
- self.push_to_repository_user ||= credentials['push_to_repository_user']
266
- self.push_to_remote_name ||= credentials['push_to_remote_name']
267
- self.ticket_tracking_tool ||= credentials['ticket_tracking_tool']
268
- self.use_git_submodules ||= credentials['use_git_submodules']
269
- self.git_tool ||= credentials['git_tool']
270
- self.time_tracking_username ||= credentials['time_tracking_username']
271
- self.time_tracking_password ||= credentials['time_tracking_password']
272
- self.time_tracking_owner ||= credentials['time_tracking_owner']
273
- self.time_tracking_default_project_id ||= credentials['time_tracking_default_project_id']
274
- self.time_tracking_tool ||= credentials['time_tracking_tool']
275
- self.git_quiet ||= (credentials['git_quiet']) ? '&> /dev/null' : ''
276
- self.use_defaults ||= credentials['use_defaults']
277
- self.default_pull_request_title_format ||= credentials['default_pull_request_title_format']
278
- @credentials_loaded = true
279
- end
51
+ def load_credentials # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
52
+ credentials = Client.read_credenials
53
+ instance.github_username ||= credentials['github_username']
54
+ instance.github_password ||= credentials['github_password']
55
+ instance.github_token ||= credentials['github_token']
56
+ instance.pivotal_tracker_username ||= credentials['pivotal_tracker_username']
57
+ instance.pivotal_tracker_password ||= credentials['pivotal_tracker_password']
58
+ instance.default_pivotal_tracker_project_id ||= credentials['default_pivotal_tracker_project_id'] # rubocop:disable Metrics/LineLength
59
+ instance.ticket_tracker_username ||= credentials['ticket_tracker_username']
60
+ instance.ticket_tracker_password ||= credentials['ticket_tracker_password']
61
+ instance.ticket_tracker_owner ||= credentials['ticket_tracker_owner']
62
+ instance.ticket_status_for_complete ||= credentials['ticket_status_for_complete']
63
+ instance.default_ticket_tracker_project ||= credentials['default_ticket_tracker_project'] # rubocop:disable Metrics/LineLength
64
+ instance.pull_from_remote_name ||= credentials['pull_from_remote_name']
65
+ instance.pull_from_repository_user ||= credentials['pull_from_repository_user']
66
+ instance.pull_from_repository_name ||= credentials['pull_from_repository_name']
67
+ instance.push_to_repository_user ||= credentials['push_to_repository_user']
68
+ instance.push_to_remote_name ||= credentials['push_to_remote_name']
69
+ instance.ticket_tracking_tool ||= credentials['ticket_tracking_tool'] # rubocop:disable Metrics/LineLength
70
+ instance.use_git_submodules ||= credentials['use_git_submodules']
71
+ instance.git_tool ||= credentials['git_tool']
72
+ instance.time_tracking_username ||= credentials['time_tracking_username']
73
+ instance.time_tracking_password ||= credentials['time_tracking_password']
74
+ instance.time_tracking_owner ||= credentials['time_tracking_owner']
75
+ instance.time_tracking_default_project_id ||= credentials['time_tracking_default_project_id'] # rubocop:disable Metrics/LineLength
76
+ instance.time_tracking_tool ||= credentials['time_tracking_tool']
77
+ instance.git_quiet ||= (credentials['git_quiet']) ? '&> /dev/null' : ''
78
+ instance.use_defaults ||= credentials['use_defaults']
79
+ instance.default_pull_request_title_format ||= credentials['default_pull_request_title_format'] # rubocop:disable Metrics/LineLength
80
+ instance.log_path ||= credentials['log_path']
81
+ instance.credentials_loaded_from ||= load_credentials_from
82
+ end
280
83
 
281
- def self.credentials_loaded?
282
- (@credentials_loaded ||= false)
283
- end
84
+ def read_credenials
85
+ YAML.load_file(File.expand_path(load_credentials_from))
86
+ end
284
87
 
285
- def self.read_credenials(path = Client.config_path)
286
- begin
88
+ def load_credentials_from
287
89
  dir = Dir.pwd
288
- while File.expand_path(dir) != '/' do
289
- if (File.exists? "#{dir}/.toolshedrc")
290
- loaded_from_path = "#{dir}/.toolshedrc"
291
- break
292
- elsif (File.exists? "#{dir}/.toolshed")
293
- loaded_from_path = "#{dir}/.toolshed"
294
- warn "[DEPRECATION] `.toolshed` file is being deprecated. Please use a `.toolshedrc` file instead."
295
- break
90
+ while File.expand_path(dir) != '/'
91
+ unless File.exist?("#{dir}/.toolshedrc")
92
+ dir = File.join dir, '..'
93
+ next
296
94
  end
297
-
298
- dir = File.join dir, '..'
95
+ credentials_loaded_from = "#{dir}/.toolshedrc"
96
+ break
299
97
  end
300
98
 
301
- puts "Credentials loaded from #{File.absolute_path(loaded_from_path)}"
302
- credentials = YAML.load_file(File.expand_path(loaded_from_path))
303
- rescue => error
304
- puts "Error loading your credentials: #{error.message}"
305
- exit 1
99
+ credentials_loaded_from
306
100
  end
307
101
  end
308
102
  end
@@ -1,66 +1,108 @@
1
+ require 'toolshed/cli'
2
+ require 'optparse'
3
+
1
4
  module Toolshed
2
5
  module Commands
6
+ # Base class for all commands responsible for common methods
3
7
  class Base
4
- def initialize(options={})
8
+ def initialize(_options = {})
9
+ end
10
+
11
+ def self.parse(command, cli_options = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/LineLength, Metrics/CyclomaticComplexity
12
+ options = {}
13
+ options_parser = OptionParser.new do |opts|
14
+ opts.banner = cli_options[:banner] if cli_options[:banner]
15
+ cli_options[:options].each do |option_name, option_variables|
16
+ letter_map = ('a'..'z').map { |letter| letter }
17
+ short_on = (option_variables[:short_on]) ? option_variables[:short_on] : letter_map[rand(letter_map.length)] # rubocop:disable Metrics/LineLength
18
+ on = (option_variables[:on]) ? option_variables[:on] : "--#{option_name.to_s.split('_').join('-')} [ARG]" # rubocop:disable Metrics/LineLength
19
+ opts.on(short_on, on) do |opt|
20
+ value = (option_variables[:default].nil?) ? opt : option_variables[:default]
21
+ options.merge!(option_name => value)
22
+ end
23
+ end
24
+ end
25
+ options_parser.order! if options_parser
26
+ begin
27
+ cli = Toolshed::CLI.new
28
+ cli.execute(command, ARGV, options)
29
+ rescue Toolshed::CommandNotFound => e
30
+ Toolshed.logger.fatal e.message
31
+ Toolshed.die
32
+ end
5
33
  end
6
34
 
7
- def read_user_input(message, options={})
8
- return options[:default] if Toolshed::Client.use_defaults
9
- prompt_user_input(message, options)
35
+ def read_user_input(message, options = {})
36
+ return options[:default] if Toolshed::Client.instance.use_defaults
37
+ required = options[:required] || false
38
+ value = ''
39
+ if required
40
+ value = prompt_user_input(message, options) while value.empty?
41
+ else
42
+ value = prompt_user_input(message, options)
43
+ end
44
+ value
10
45
  end
11
46
 
12
- def read_user_input_title(message, options={})
13
- return options[:title] if options.has_key?(:title)
47
+ def read_user_input_title(message, options = {})
48
+ return options[:title] if options.key?(:title)
14
49
  read_user_input(message, options)
15
50
  end
16
51
 
17
- def read_user_input_body(message, options={})
18
- return options[:body] if options.has_key?(:body)
52
+ def read_user_input_body(message, options = {})
53
+ return options[:body] if options.key?(:body)
19
54
  read_user_input(message, options)
20
55
  end
21
56
 
22
57
  def use_ticket_tracker_project_id(options)
23
- options
24
- options.merge!({
58
+ options.merge!(
25
59
  ticket_tracker_const: 'USE_PROJECT_ID',
26
60
  type: :project_id,
27
61
  default_method: 'default_pivotal_tracker_project_id',
28
- default_message: "Project ID (Default: #{Toolshed::Client.default_pivotal_tracker_project_id}):",
29
- })
30
- options = use_ticket_tracker_by_type(options)
62
+ default_message: "Project ID (Default: #{Toolshed::Client.instance.default_pivotal_tracker_project_id}):" # rubocop:disable Metrics/LineLength
63
+ )
64
+ use_ticket_tracker_by_type(options)
31
65
  end
32
66
 
33
67
  def use_ticket_tracker_project_name(options)
34
- options.merge!({
68
+ options.merge!(
35
69
  ticket_tracker_const: 'USE_PROJECT_NAME',
36
70
  type: :project,
37
71
  default_method: 'default_ticket_tracker_project',
38
- default_message: "Project Name (Default: #{Toolshed::Client.default_ticket_tracker_project}):",
39
- })
72
+ default_message: "Project Name (Default: #{Toolshed::Client.instance.default_ticket_tracker_project}):" # rubocop:disable Metrics/LineLength
73
+ )
40
74
  use_ticket_tracker_by_type(options)
41
75
  end
42
76
 
43
77
  def use_ticket_tracker_by_type(options)
44
- use_field = Object.const_get("#{ticket_tracker_class}::#{options[:ticket_tracker_const]}") rescue false
78
+ use_field = Object.const_get("#{ticket_tracker_class}::#{options[:ticket_tracker_const]}") rescue false # rubocop:disable Metrics/LineLength, Style/RescueModifier
45
79
  if use_field
46
- ticket_tracker_response = read_user_input(options[:default_message],
47
- options.merge!({ default: Toolshed::Client.send(options[:default_method]) })
80
+ ticket_tracker_response = read_user_input(
81
+ options[:default_message],
82
+ options.merge!(
83
+ default: Toolshed::Client.instance.send(options[:default_method])
84
+ )
48
85
  )
49
- options.merge!({ options[:type] => ticket_tracker_response })
86
+ options.merge!(options[:type] => ticket_tracker_response)
50
87
  end
51
88
  options
52
89
  end
53
90
 
54
91
  def use_project_id
55
- Object.const_get("#{ticket_tracker_class}::USE_PROJECT_ID") rescue false
92
+ Object.const_get("#{ticket_tracker_class}::USE_PROJECT_ID") rescue false # rubocop:disable Style/RescueModifier, Metrics/LineLength
56
93
  end
57
94
 
58
95
  private
59
96
 
60
- def prompt_user_input(message, options)
97
+ def prompt_user_input(message, options) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
98
+ required = options[:required] || false
61
99
  puts message
62
100
  value = $stdin.gets.chomp
63
- value.empty? ? options[:default] : value
101
+ if required && value.nil? || value.empty?
102
+ puts 'Value is required please try again.'
103
+ return value
104
+ end
105
+ (value.nil? || value.empty?) ? options[:default] : value
64
106
  end
65
107
  end
66
108
  end