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