toolshed 1.0.2 → 1.0.3

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.
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,10 +1,23 @@
1
+ require 'toolshed/git'
2
+
1
3
  module Toolshed
2
4
  module Commands
3
5
  class CheckoutBranch
6
+ def self.cli_options
7
+ {
8
+ banner: 'Usage: checkout_branch [options]',
9
+ options: {
10
+ branch_name: {
11
+ short_on: '-b',
12
+ }
13
+ }
14
+ }
15
+ end
16
+
4
17
  def execute(args, options = {})
5
18
  branch_name = read_user_input("Ticket ID or Branch Name:", options)
6
- branch_name = Toolshed::Git::Base.checkout(branch_name)
7
- puts "Switched to '#{branch_name}'"
19
+ Toolshed::Git::Branch.checkout(branch_name)
20
+ Toolshed.die
8
21
  end
9
22
 
10
23
  def read_user_input(message, options)
@@ -1,39 +1,46 @@
1
+ require 'toolshed/git'
2
+
1
3
  module Toolshed
2
4
  module Commands
5
+ # Create a branch from a given set of parameters
3
6
  class CreateBranch
4
- def execute(args, options = {})
5
- begin
6
- branch_name = read_user_input_branch_name("Branch name:", options)
7
- branch_from = read_user_input_branch_from("Branch from:", options)
8
-
9
- puts "Branch name: #{branch_name}"
10
- puts "Branching from: #{branch_from}"
7
+ def self.cli_options # rubocop:disable Metrics/MethodLength
8
+ {
9
+ banner: 'Usage: create_branch [options]',
10
+ options: {
11
+ branch_name: {
12
+ short_on: '-b'
13
+ },
14
+ branch_from: {
15
+ short_on: '-f'
16
+ }
17
+ }
18
+ }
19
+ end
11
20
 
12
- git = Toolshed::Git::Base.new({
13
- from_remote_branch_name: branch_from,
14
- to_remote_branch_name: branch_name,
15
- })
16
- git.create_branch
21
+ def execute(_args, options = {}) # rubocop:disable Metrics/MethodLength
22
+ branch_name = read_user_input_branch_name('Branch name:', options)
23
+ branch_from = read_user_input_branch_from('Branch from:', options)
17
24
 
18
- puts "Branch #{branch_name} has been created"
19
- return
20
- rescue Veto::InvalidEntity => e
21
- puts "Unable to create branch due to the following errors"
22
- e.message.each do |key, value|
23
- puts "#{key}: #{value}"
24
- end
25
- return
25
+ branch = Toolshed::Git::Branch.new(from_remote_branch_name: branch_from, to_remote_branch_name: branch_name) # rubocop:disable Metrics/LineLength
26
+ branch.create
27
+ Toolshed.die
28
+ rescue Veto::InvalidEntity => e
29
+ Toolshed.logger.fatal 'Unable to create branch due to the following errors' # rubocop:disable Metrics/LineLength
30
+ e.message.each do |key, value|
31
+ Toolshed.logger.fatal "#{key}: #{value}"
26
32
  end
33
+ Toolshed.die
27
34
  end
28
35
 
29
36
  def read_user_input_branch_name(message, options)
30
- return options[:branch_name] if (options.has_key?(:branch_name))
37
+ return options[:branch_name] if options.key?(:branch_name)
31
38
 
32
39
  puts message
33
40
  value = $stdin.gets.chomp
34
41
 
35
- until (!value.empty?)
36
- puts "Branch name cannot be empty"
42
+ while value.empty?
43
+ puts 'Branch name cannot be empty'
37
44
  puts message
38
45
  value = $stdin.gets.chomp
39
46
  end
@@ -42,18 +49,16 @@ module Toolshed
42
49
  end
43
50
 
44
51
  def read_user_input_branch_from(message, options)
45
- return options[:branch_from] if (options.has_key?(:branch_from))
52
+ return options[:branch_from] if options.key?(:branch_from)
46
53
 
47
54
  # if branch-name was supplied then default to master if not supplied
48
- if (options.has_key?(:branch_name))
49
- return Toolshed::Git::DEFAULT_BRANCH_FROM
50
- end
55
+ return Toolshed::Git::DEFAULT_BRANCH_FROM if options.key?(:branch_name)
51
56
 
52
57
  puts message
53
58
  value = $stdin.gets.chomp
54
59
 
55
- until (!value.empty?)
56
- puts "Branch from cannot be empty"
60
+ while value.empty?
61
+ puts 'Branch from cannot be empty'
57
62
  puts message
58
63
  value = $stdin.gets.chomp
59
64
  end
@@ -1,11 +1,17 @@
1
+ require 'toolshed/git'
2
+
1
3
  module Toolshed
2
4
  module Commands
3
5
  class CreatePivotalTrackerNote
6
+ def branch
7
+ @branch ||= Toolshed::Git::Branch.new
8
+ end
9
+
4
10
  def execute(args, options = {})
5
- print "Project ID (Default: #{Toolshed::Client.default_pivotal_tracker_project_id})? "
11
+ print "Project ID (Default: #{Toolshed::Client.instance.default_pivotal_tracker_project_id})? "
6
12
  project_id = $stdin.gets.chomp.strip
7
13
  if (project_id == '')
8
- project_id = Toolshed::Client.default_pivotal_tracker_project_id
14
+ project_id = Toolshed::Client.instance.default_pivotal_tracker_project_id
9
15
  end
10
16
 
11
17
  pivotal_tracker = Toolshed::TicketTracking::PivotalTracker.new({
@@ -14,7 +20,7 @@ module Toolshed
14
20
  password: Toolshed::TicketTracking::PivotalTracker.password,
15
21
  })
16
22
 
17
- default_story_id = Toolshed::TicketTracking::PivotalTracker::story_id_from_branch_name(Toolshed::Git::Base.branch_name)
23
+ default_story_id = Toolshed::TicketTracking::PivotalTracker::story_id_from_branch_name(branch.branch_name)
18
24
  print "Story ID (Default: #{default_story_id})? "
19
25
  story_id = $stdin.gets.chomp.strip
20
26
  if (story_id == '')
@@ -1,36 +1,70 @@
1
1
  require 'toolshed/commands/base'
2
+ require 'toolshed/git'
3
+ require 'toolshed/git/github'
2
4
 
3
5
  module Toolshed
4
6
  module Commands
5
- class CreatePullRequest < Base
7
+ # Allow a user to create pull request based on information in .toolshedrc and prompts
8
+ class CreatePullRequest < Toolshed::Commands::Base # rubocop:disable ClassLength, LineLength
6
9
  attr_accessor :ticket_tracking_url, :ticket_tracking_title,
7
10
  :ticket_id, :ticket_tracker,
8
11
  :ticket_tracker_class, :ticket_tracker_project_id,
9
12
  :pull_request_url, :git_tool,
10
13
  :pull_request_title, :pull_request_body
11
14
 
12
- def initialize(options={})
15
+ def initialize(options = {})
13
16
  super(options)
14
17
  self.ticket_tracker_class = nil
15
18
  self.pull_request_url = ''
16
19
  end
17
20
 
18
- def execute(args, options = {})
21
+ def self.cli_options # rubocop:disable Metrics/MethodLength
22
+ {
23
+ banner: 'Usage: push_branch [options]',
24
+ options: {
25
+ tool: {
26
+ short_on: '-a'
27
+ },
28
+ ticket_system: {
29
+ short_on: '-m'
30
+ },
31
+ use_defaults: {
32
+ short_on: '-d'
33
+ },
34
+ title: {
35
+ short_on: '-t'
36
+ },
37
+ body: {
38
+ short_on: '-b'
39
+ },
40
+ skip_ticket: {
41
+ short_on: '-s',
42
+ default: true
43
+ }
44
+ }
45
+ }
46
+ end
47
+
48
+ def branch
49
+ @branch ||= Toolshed::Git::Branch.new
50
+ end
51
+
52
+ def execute(_args, options = {})
19
53
  output_begining_messages
20
54
  options = execute_ticket_tracking(options)
21
- options = execute_pull_request(options) unless options.nil?
55
+ execute_pull_request(options) unless options.nil?
22
56
  end
23
57
 
24
58
  private
25
59
 
26
60
  def read_user_input_add_note_to_ticket(message)
27
- return true if Toolshed::Client.use_defaults
61
+ return true if Toolshed::Client.instance.use_defaults
28
62
 
29
63
  puts message
30
64
  value = $stdin.gets.chomp
31
65
 
32
66
  until %w(y n).include?(value.downcase)
33
- puts "Value must be Y or N"
67
+ puts 'Value must be Y or N'
34
68
  puts message
35
69
  value = $stdin.gets.chomp
36
70
  end
@@ -43,103 +77,116 @@ module Toolshed
43
77
  end
44
78
 
45
79
  def output_begining_messages
46
- puts "Current Branch: #{Toolshed::Git::Base.branch_name}"
47
- puts "Branched From: #{Toolshed::Git::Base.branched_from}"
48
- puts "Using Defaults: #{(Toolshed::Client.use_defaults.nil?) ? 'No' : 'Yes'}"
80
+ Toolshed.logger.info "Current Branch: #{branch.name}"
81
+ Toolshed.logger.info "Branched From: #{Toolshed::Git::Branch.from}"
82
+ Toolshed.logger.info "Using Defaults: #{(Toolshed::Client.instance.use_defaults.nil?) ? 'No' : 'Yes'}" # rubocop:disable Metrics/LineLength
49
83
  end
50
84
 
51
85
  def get_ticket_id(options)
52
86
  self.ticket_id = read_user_input_ticket_tracker_ticket_id(
53
- "Ticket ID (Default: #{Toolshed::TicketTracking::story_id_from_branch_name(Toolshed::Git::Base.branch_name)}):", {
54
- default: Toolshed::TicketTracking::story_id_from_branch_name(Toolshed::Git::Base.branch_name),
55
- }
87
+ "Ticket ID (Default: #{Toolshed::TicketTracking.story_id_from_branch_name(branch.name)}):", # rubocop:disable Metrics/LineLength
88
+ default: Toolshed::TicketTracking.story_id_from_branch_name(branch.name)
56
89
  )
57
- options.merge!({
58
- ticket_id: self.ticket_id,
59
- })
90
+ options.merge!(ticket_id: ticket_id)
60
91
  options
61
92
  end
62
93
 
63
94
  def output_ticket_information
64
- puts "Ticket Tracking URL: #{ticket_tracking_url}"
65
- puts "Ticket Tracking title: #{ticket_tracking_title}"
66
- puts "Ticket ID: #{ticket_id}"
95
+ Toolshed.logger.info "Ticket Tracking URL: #{ticket_tracking_url}"
96
+ Toolshed.logger.info "Ticket Tracking title: #{ticket_tracking_title}"
97
+ Toolshed.logger.info "Ticket ID: #{ticket_id}"
67
98
  end
68
99
 
69
- def execute_ticket_tracking(options)
70
- unless Toolshed::Client.ticket_tracking_tool.nil? || Toolshed::Client.ticket_tracking_tool.empty?
71
- begin
72
- self.ticket_tracker_class = Object.const_get("Toolshed::TicketTracking::#{Toolshed::Client.ticket_tracking_tool.camel_case}")
73
- options = get_ticket_project_information(options)
74
- initialize_ticket_tracker_properties(options)
75
-
76
- output_ticket_information
77
- rescue Exception => e
78
- puts e.inspect
79
- puts e.backtrace
80
- puts "Ticket tracking tool is not supported at this time"
81
- return
82
- end
83
- options
100
+ def execute_ticket_tracking(options) # rubocop:disable Metrics/AbcSize
101
+ return options if options[:skip_ticket]
102
+ return options if Toolshed::Client.instance.ticket_tracking_tool.nil? || Toolshed::Client.instance.ticket_tracking_tool.empty? # rubocop:disable Metrics/LineLength
103
+
104
+ begin
105
+ self.ticket_tracker_class = Object.const_get("Toolshed::TicketTracking::#{Toolshed::Client.instance.ticket_tracking_tool.camel_case}") # rubocop:disable Metrics/LineLength
106
+ options = get_ticket_project_information(options)
107
+ initialize_ticket_tracker_properties(options)
108
+ output_ticket_information
109
+ rescue StandardError => e
110
+ Toolshed.logger.fatal e.inspect
111
+ Toolshed.logger.fatal e.backtrace
112
+ Toolshed.logger.fatal 'Ticket tracking tool is not supported at this time'
113
+ Toolshed.die
84
114
  end
115
+
85
116
  options
86
117
  end
87
118
 
88
119
  def add_note_to_ticket
89
- add_note_to_ticket_response = read_user_input_add_note_to_ticket(
90
- "Would you like to add a note with the pull request url?"
91
- )
92
- if add_note_to_ticket_response
93
- result = self.ticket_tracker.add_note(pull_request_url)
94
- default_completed_status = Object.const_get("#{ticket_tracker_class}::DEFAULT_COMPLETED_STATUS") rescue false
95
- default_completed_status = Toolshed::Client.ticket_status_for_complete unless default_completed_status
96
- ticket_tracker.update_ticket_status(default_completed_status)
120
+ add_note_to_ticket_response = read_user_input_add_note_to_ticket('Would you like to add a note with the pull request url?') # rubocop:disable Metrics/LineLength
121
+ return unless add_note_to_ticket_response
122
+
123
+ ticket_tracker.add_note(pull_request_url)
124
+ completed_status = Toolshed::Client.instance.ticket_status_for_complete unless default_completed_status # rubocop:disable Metrics/LineLength
125
+ ticket_tracker.update_ticket_status(completed_status)
126
+ end
127
+
128
+ def default_completed_status
129
+ default_completed_status = nil
130
+ begin
131
+ default_completed_status = Object.const_get("#{ticket_tracker_class}::DEFAULT_COMPLETED_STATUS") # rubocop:disable Metrics/LineLength
132
+ rescue
133
+ false
97
134
  end
135
+ default_completed_status
98
136
  end
99
137
 
100
138
  def pull_request_created_message
101
- puts "Created Pull Request: #{pull_request_url}"
139
+ Toolshed.logger.info "Created Pull Request: #{pull_request_url}"
102
140
  end
103
141
 
104
- def execute_pull_request(options)
105
- begin
106
- self.git_tool = Object.const_get("Toolshed::Git::#{Toolshed::Client.git_tool.camel_case}").create_instance
107
-
108
- options = set_pull_request_title(options)
109
- options = set_pull_request_body(options)
110
- send_pull_request
111
- add_note_to_ticket unless ticket_tracker_class.nil?
112
- pull_request_created_message
113
- rescue => e
114
- puts e.message
115
- exit
116
- end
142
+ def execute_pull_request(options) # rubocop:disable Metrics/AbcSize
143
+ self.git_tool = Object.const_get("Toolshed::Git::#{Toolshed::Client.instance.git_tool.camel_case}").create_instance # rubocop:disable Metrics/LineLength
144
+
145
+ options = get_pull_request_title(options)
146
+ get_pull_request_body(options)
147
+ send_pull_request
148
+ add_note_to_ticket unless ticket_tracker_class.nil?
149
+ pull_request_created_message
150
+ self
151
+ rescue => e
152
+ Toolshed.logger.fatal e.message
153
+ Toolshed.die
117
154
  end
118
155
 
119
- def set_pull_request_title(options)
156
+ def get_pull_request_title(options)
120
157
  self.pull_request_title = read_user_input_title(
121
- "Pull request title (Default: #{self.ticket_tracking_title}):",
122
- options.merge!({
123
- default: self.ticket_tracking_title,
124
- })
158
+ "Pull request title (Default: #{ticket_tracking_title}):",
159
+ options.merge!(default: ticket_tracking_title)
125
160
  )
126
161
  options
127
162
  end
128
163
 
129
- def set_pull_request_body(options)
164
+ def get_pull_request_body(options)
130
165
  self.pull_request_body = read_user_input_body(
131
- "Pull request body (Default: #{self.ticket_tracking_url}):",
132
- options.merge!({
133
- default: self.ticket_tracking_url
134
- })
166
+ "Pull request body (Default: #{ticket_tracking_url}):",
167
+ options.merge!(default: ticket_tracking_url)
135
168
  )
136
169
  options
137
170
  end
138
171
 
139
172
  def send_pull_request
140
- puts "Pull request being created"
141
- git_pull_request_result = self.git_tool.create_pull_request(pull_request_title, pull_request_body)
142
- self.pull_request_url = git_pull_request_result["html_url"]
173
+ Toolshed.logger.info 'Your pull request is being created.'
174
+ git_pull_request_result = git_tool.create_pull_request(pull_request_title, pull_request_body) # rubocop:disable Metrics/LineLength
175
+
176
+ self.pull_request_url = git_pull_request_result['html_url']
177
+ return if git_pull_request_result['message'].nil?
178
+
179
+ if git_pull_request_result['message'].downcase == 'bad credentials'
180
+ Toolshed.logger.fatal 'Bad Credentials please try again.'
181
+ Toolshed.die
182
+ end
183
+
184
+ if git_pull_request_result['message'].downcase == 'not found'
185
+ Toolshed.logger.fatal 'Pull request not found please try again.'
186
+ Toolshed.die
187
+ end
188
+
189
+ self.pull_request_url = git_pull_request_result['html_url']
143
190
  end
144
191
 
145
192
  def get_ticket_project_information(options)
@@ -1,4 +1,5 @@
1
1
  require 'toolshed/commands/base'
2
+ require 'toolshed/git'
2
3
 
3
4
  module Toolshed
4
5
  module Commands
@@ -8,13 +9,28 @@ module Toolshed
8
9
  super(options)
9
10
  end
10
11
 
12
+ def self.cli_options
13
+ {
14
+ banner: 'Usage: create_ticket_comment [options]',
15
+ options: {
16
+ use_defaults: {
17
+ short_on: '-d'
18
+ }
19
+ }
20
+ }
21
+ end
22
+
23
+ def branch
24
+ @branch ||= Toolshed::Git::Branch.new
25
+ end
26
+
11
27
  def execute(args, options = {})
12
28
  ticket_tracker_class = Object.const_get("Toolshed::TicketTracking::#{Toolshed::Client.ticket_tracking_tool.camel_case}")
13
29
 
14
30
  options = use_ticket_tracker_project_id(options)
15
31
  options = use_ticket_tracker_project_name(options)
16
32
 
17
- default_ticket_id = Toolshed::TicketTracking::story_id_from_branch_name(Toolshed::Git::Base.branch_name)
33
+ default_ticket_id = Toolshed::TicketTracking::story_id_from_branch_name(branch.name)
18
34
  ticket_id = read_user_input("Ticket ID (Default: #{default_ticket_id}):", options.merge!({ default: default_ticket_id }))
19
35
  options.merge!({ ticket_id: ticket_id })
20
36
 
@@ -1,11 +1,32 @@
1
+ require 'toolshed/git'
2
+
3
+ require 'highline/import'
4
+
1
5
  module Toolshed
2
6
  module Commands
3
7
  class DeleteBranch
8
+ attr_accessor :branch
9
+
10
+ def self.cli_options
11
+ {
12
+ banner: 'Usage: delete_branch [options]',
13
+ options: {
14
+ branch_name: {
15
+ short_on: '-b',
16
+ }
17
+ }
18
+ }
19
+ end
20
+
4
21
  def execute(args, options = {})
5
22
  branch_name = read_user_input("Ticket ID or branch name:", options)
6
- branch_name = Toolshed::Git::Base.delete(branch_name)
7
- puts "#{branch_name} has been deleted"
8
- return
23
+ self.branch = Toolshed::Git::Branch.new(branch_name: branch_name)
24
+ if confirm_delete
25
+ branch.delete(branch_name)
26
+ else
27
+ Toolshed.logger.info "Branch '#{branch.name}' was not deleted."
28
+ end
29
+ Toolshed.die
9
30
  end
10
31
 
11
32
  def read_user_input(message, options)
@@ -22,6 +43,16 @@ module Toolshed
22
43
 
23
44
  value
24
45
  end
46
+
47
+ def confirm_delete
48
+ choices = "yn"
49
+ answer = ask("Are you sure you want to delete #{branch.name} [#{choices}]? ") do |q|
50
+ q.echo = false
51
+ q.character = true
52
+ q.validate = /\A[#{choices}]\Z/
53
+ end
54
+ answer == 'y'
55
+ end
25
56
  end
26
57
  end
27
58
  end