toolshed 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +11 -0
- data/.toolshedrc.sample +32 -0
- data/README.md +159 -2
- data/Rakefile +3 -3
- data/bin/toolshed +6 -1
- data/lib/toolshed.rb +38 -28
- data/lib/toolshed/base.rb +33 -11
- data/lib/toolshed/cli.rb +30 -38
- data/lib/toolshed/client.rb +87 -293
- data/lib/toolshed/commands/base.rb +65 -23
- data/lib/toolshed/commands/checkout_branch.rb +15 -2
- data/lib/toolshed/commands/create_branch.rb +34 -29
- data/lib/toolshed/commands/create_pivotal_tracker_note.rb +9 -3
- data/lib/toolshed/commands/create_pull_request.rb +115 -68
- data/lib/toolshed/commands/create_ticket_comment.rb +17 -1
- data/lib/toolshed/commands/delete_branch.rb +34 -3
- data/lib/toolshed/commands/get_daily_time_update.rb +20 -3
- data/lib/toolshed/commands/list_branches.rb +16 -5
- data/lib/toolshed/commands/push_branch.rb +28 -9
- data/lib/toolshed/commands/rename_branch.rb +29 -0
- data/lib/toolshed/commands/ssh.rb +44 -3
- data/lib/toolshed/commands/ticket_information.rb +30 -4
- data/lib/toolshed/commands/update_pivotal_tracker_story_status.rb +9 -3
- data/lib/toolshed/commands/update_ticket_status.rb +8 -2
- data/lib/toolshed/entry_point.rb +89 -0
- data/lib/toolshed/git.rb +59 -0
- data/lib/toolshed/git/branch.rb +224 -0
- data/lib/toolshed/git/github.rb +45 -57
- data/lib/toolshed/git/validator.rb +14 -0
- data/lib/toolshed/logger.rb +46 -0
- data/lib/toolshed/password.rb +11 -6
- data/lib/toolshed/server_administration/ssh.rb +4 -2
- data/lib/toolshed/ticket_tracking/jira.rb +8 -6
- data/lib/toolshed/ticket_tracking/pivotal_tracker.rb +8 -6
- data/lib/toolshed/time_tracking/harvest.rb +8 -14
- data/lib/toolshed/version.rb +25 -1
- data/test/commands/checkout_branch_test.rb +11 -7
- data/test/commands/create_branch_test.rb +29 -24
- data/test/commands/create_pull_request_test.rb +39 -31
- data/test/commands/delete_branch_test.rb +35 -25
- data/test/commands/get_daily_time_update_test.rb +8 -8
- data/test/commands/push_branch_test.rb +27 -15
- data/test/commands/rename_branch_test.rb +59 -0
- data/test/git/git_helper.rb +5 -5
- data/test/git/git_test.rb +36 -31
- data/test/git/github_test.rb +9 -46
- data/test/helper.rb +11 -11
- data/test/server_administration/ssh_test.rb +1 -0
- data/test/ticket_tracking/jira_test.rb +18 -16
- data/test/time_tracking/harvest_test.rb +8 -6
- data/toolshed.gemspec +23 -20
- metadata +95 -46
- data/bin/toolshed.rb +0 -261
- 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
|
-
|
7
|
-
|
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
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
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
|
-
|
36
|
-
puts
|
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
|
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
|
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
|
-
|
56
|
-
puts
|
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(
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
47
|
-
|
48
|
-
|
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
|
54
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
139
|
+
Toolshed.logger.info "Created Pull Request: #{pull_request_url}"
|
102
140
|
end
|
103
141
|
|
104
|
-
def execute_pull_request(options)
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
156
|
+
def get_pull_request_title(options)
|
120
157
|
self.pull_request_title = read_user_input_title(
|
121
|
-
"Pull request title (Default: #{
|
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
|
164
|
+
def get_pull_request_body(options)
|
130
165
|
self.pull_request_body = read_user_input_body(
|
131
|
-
"Pull request body (Default: #{
|
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
|
-
|
141
|
-
git_pull_request_result =
|
142
|
-
|
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(
|
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
|
-
|
7
|
-
|
8
|
-
|
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
|