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,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