toolshed 1.0.3 → 1.0.4
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/lib/toolshed/cli.rb +10 -29
- data/lib/toolshed/commands/base.rb +14 -4
- data/lib/toolshed/commands/scp/download.rb +43 -0
- data/lib/toolshed/commands/scp/upload.rb +43 -0
- data/lib/toolshed/commands/scp_base.rb +21 -0
- data/lib/toolshed/entry_point.rb +55 -24
- data/lib/toolshed/exceptions.rb +1 -0
- data/lib/toolshed/git/branch.rb +2 -1
- data/lib/toolshed/password.rb +17 -41
- data/lib/toolshed/server_administration/scp.rb +46 -0
- data/lib/toolshed/server_administration/ssh.rb +47 -29
- data/lib/toolshed/version.rb +1 -1
- data/test/commands/scp/download_test.rb +34 -0
- data/test/commands/scp/upload_test.rb +34 -0
- data/test/helper.rb +25 -0
- data/test/password_test.rb +5 -19
- data/test/server_administration/scp_test.rb +37 -0
- data/toolshed.gemspec +2 -0
- metadata +42 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2284363dbacddbe04adfd021331c5a27870e279
|
4
|
+
data.tar.gz: 739d370c284895290ae0a7213a75123c5d23c9e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a50e84f7789f7443e01de39d64708970dbe948e8ed441c9b2a166b7a51ffb4e4c3559d73a9705aa08f1b5ea238afd5b3b019a08cf5dd6180a4ecfedb1317f84
|
7
|
+
data.tar.gz: 595834a2bbf3cdae26a088a28dfa32dceef915a754e0c9432fe0f8c64112acfc3f186be061ee72771331c079712297b14dc903a9516492eff7c1346f325940cd
|
data/lib/toolshed/cli.rb
CHANGED
@@ -7,42 +7,23 @@ module Toolshed
|
|
7
7
|
|
8
8
|
# CLI is responsible for executing the initial command
|
9
9
|
class CLI
|
10
|
-
def execute(
|
11
|
-
load_config(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
rescue RuntimeError => e
|
19
|
-
Toolshed.logger.fatal "An error occurred: #{e.message}"
|
20
|
-
end
|
21
|
-
else
|
22
|
-
fail CommandNotFound, "Unknown command: #{command_name}"
|
10
|
+
def execute(command_class, args, options = {})
|
11
|
+
load_config(command_class)
|
12
|
+
begin
|
13
|
+
command_class.new.execute(args, options)
|
14
|
+
rescue Toolshed::Error => e
|
15
|
+
Toolshed.logger.fatal "An error occurred: #{e.message}"
|
16
|
+
rescue RuntimeError => e
|
17
|
+
Toolshed.logger.fatal "An error occurred: #{e.message}"
|
23
18
|
end
|
24
19
|
end
|
25
20
|
|
26
|
-
def load_config(
|
21
|
+
def load_config(command_class)
|
27
22
|
Toolshed::Client.load_credentials
|
28
|
-
Toolshed.add_file_log_source(
|
23
|
+
Toolshed.add_file_log_source(command_class.class.name)
|
29
24
|
Toolshed.logger.info "Credentials loaded from #{File.absolute_path(Toolshed::Client.instance.credentials_loaded_from)}" # rubocop:disable Metrics/LineLength
|
30
25
|
rescue => e
|
31
26
|
Toolshed.logger.fatal "Error loading your credentials: #{e.message}"
|
32
27
|
end
|
33
|
-
|
34
|
-
def method_missing(method_name)
|
35
|
-
require "toolshed/commands/#{method_name}"
|
36
|
-
"Toolshed::Commands::#{translate_method_name(method_name)}".split('::').inject(Object) { |o, c| o.const_get c } # rubocop:disable Metrics/LineLength
|
37
|
-
rescue NameError => e
|
38
|
-
Toolshed.logger.fatal e.message
|
39
|
-
Toolshed.die
|
40
|
-
end
|
41
|
-
|
42
|
-
def translate_method_name(name)
|
43
|
-
name = name.to_s
|
44
|
-
name.upcase! if %w(ssh).include?(name.downcase)
|
45
|
-
name.camel_case
|
46
|
-
end
|
47
28
|
end
|
48
29
|
end
|
@@ -5,10 +5,10 @@ module Toolshed
|
|
5
5
|
module Commands
|
6
6
|
# Base class for all commands responsible for common methods
|
7
7
|
class Base
|
8
|
-
def initialize(
|
8
|
+
def initialize(options = {})
|
9
9
|
end
|
10
10
|
|
11
|
-
def self.parse(
|
11
|
+
def self.parse(command_class, cli_options = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/LineLength, Metrics/CyclomaticComplexity
|
12
12
|
options = {}
|
13
13
|
options_parser = OptionParser.new do |opts|
|
14
14
|
opts.banner = cli_options[:banner] if cli_options[:banner]
|
@@ -25,7 +25,7 @@ module Toolshed
|
|
25
25
|
options_parser.order! if options_parser
|
26
26
|
begin
|
27
27
|
cli = Toolshed::CLI.new
|
28
|
-
cli.execute(
|
28
|
+
cli.execute(command_class, ARGV, options)
|
29
29
|
rescue Toolshed::CommandNotFound => e
|
30
30
|
Toolshed.logger.fatal e.message
|
31
31
|
Toolshed.die
|
@@ -34,7 +34,8 @@ module Toolshed
|
|
34
34
|
|
35
35
|
def read_user_input(message, options = {})
|
36
36
|
return options[:default] if Toolshed::Client.instance.use_defaults
|
37
|
-
required = options[:required]
|
37
|
+
required = options[:required].is_a?(TrueClass)
|
38
|
+
|
38
39
|
value = ''
|
39
40
|
if required
|
40
41
|
value = prompt_user_input(message, options) while value.empty?
|
@@ -44,6 +45,15 @@ module Toolshed
|
|
44
45
|
value
|
45
46
|
end
|
46
47
|
|
48
|
+
def read_user_input_password(password, prompt_message='Password:')
|
49
|
+
return password unless password.nil? || password.empty?
|
50
|
+
system "stty -echo"
|
51
|
+
puts prompt_message
|
52
|
+
value = $stdin.gets.chomp.strip
|
53
|
+
system "stty echo"
|
54
|
+
value
|
55
|
+
end
|
56
|
+
|
47
57
|
def read_user_input_title(message, options = {})
|
48
58
|
return options[:title] if options.key?(:title)
|
49
59
|
read_user_input(message, options)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'toolshed/commands/scp_base'
|
2
|
+
|
3
|
+
module Toolshed
|
4
|
+
module Commands
|
5
|
+
module SCP
|
6
|
+
# Responsible for handing downloading of files
|
7
|
+
class Download < Toolshed::Commands::SCPBase
|
8
|
+
def self.cli_options # rubocop:disable MethodLength
|
9
|
+
{
|
10
|
+
banner: 'Usage: scp download [options]',
|
11
|
+
options: {
|
12
|
+
remote_host: {
|
13
|
+
short_on: '-r'
|
14
|
+
},
|
15
|
+
remote_path: {
|
16
|
+
short_on: '-d'
|
17
|
+
},
|
18
|
+
local_path: {
|
19
|
+
short_on: '-s'
|
20
|
+
},
|
21
|
+
username: {
|
22
|
+
short_on: '-u'
|
23
|
+
},
|
24
|
+
password: {
|
25
|
+
short_on: '-p'
|
26
|
+
},
|
27
|
+
verbose_output: {
|
28
|
+
short_on: '-v'
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute(_args, options = nil)
|
35
|
+
options ||= nil
|
36
|
+
Toolshed.logger.info ''
|
37
|
+
Toolshed::ServerAdministration::SCP.new(scp_options(options)).download
|
38
|
+
Toolshed.die
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'toolshed/commands/scp_base'
|
2
|
+
|
3
|
+
module Toolshed
|
4
|
+
module Commands
|
5
|
+
module SCP
|
6
|
+
# Responsible for handing uploading of files
|
7
|
+
class Upload < Toolshed::Commands::SCPBase
|
8
|
+
def self.cli_options # rubocop:disable MethodLength
|
9
|
+
{
|
10
|
+
banner: 'Usage: scp upload [options]',
|
11
|
+
options: {
|
12
|
+
remote_host: {
|
13
|
+
short_on: '-r'
|
14
|
+
},
|
15
|
+
remote_path: {
|
16
|
+
short_on: '-d'
|
17
|
+
},
|
18
|
+
local_path: {
|
19
|
+
short_on: '-s'
|
20
|
+
},
|
21
|
+
username: {
|
22
|
+
short_on: '-u'
|
23
|
+
},
|
24
|
+
password: {
|
25
|
+
short_on: '-p'
|
26
|
+
},
|
27
|
+
verbose_output: {
|
28
|
+
short_on: '-v'
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute(_args, options = nil)
|
35
|
+
options ||= nil
|
36
|
+
Toolshed.logger.info ''
|
37
|
+
Toolshed::ServerAdministration::SCP.new(scp_options(options)).upload
|
38
|
+
Toolshed.die
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'toolshed/commands/base'
|
2
|
+
require 'toolshed/server_administration/scp'
|
3
|
+
|
4
|
+
module Toolshed
|
5
|
+
module Commands
|
6
|
+
# Shared code between scp classes
|
7
|
+
class SCPBase < Toolshed::Commands::Base
|
8
|
+
private
|
9
|
+
|
10
|
+
def scp_options(options = nil) # rubocop:disable AbcSize
|
11
|
+
options ||= {}
|
12
|
+
options[:remote_host] = read_user_input('Remote Host?', required: true) if options[:remote_host].nil? # rubocop:disable LineLength
|
13
|
+
options[:remote_path] = read_user_input('Remote Path?', required: true) if options[:remote_path].nil? # rubocop:disable LineLength
|
14
|
+
options[:local_path] = read_user_input('Local Path?', required: true) if options[:local_path].nil? # rubocop:disable LineLength
|
15
|
+
options[:username] = read_user_input('Username?', required: true) if options[:username].nil? # rubocop:disable LineLength
|
16
|
+
options[:password] = read_user_input_password(options[:password])
|
17
|
+
options
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/toolshed/entry_point.rb
CHANGED
@@ -44,46 +44,77 @@ class EntryPoint
|
|
44
44
|
end
|
45
45
|
|
46
46
|
global.order!
|
47
|
-
|
48
|
-
if command.nil?
|
47
|
+
if command_parts.length == 0
|
49
48
|
usage
|
50
|
-
elsif
|
49
|
+
elsif command_parts[0] == 'version'
|
51
50
|
Toolshed::Version.banner
|
52
51
|
Toolshed.die
|
53
52
|
else
|
54
|
-
command_class =
|
55
|
-
|
53
|
+
command_class = default_command_class_string
|
54
|
+
attempts = 0
|
56
55
|
begin
|
57
|
-
require "toolshed/commands/#{
|
58
|
-
command_class =
|
59
|
-
rescue NameError
|
60
|
-
|
56
|
+
require "toolshed/commands/#{command_parts.join('/')}"
|
57
|
+
command_class = command_class.split('::').inject(Object) { |o,c| o.const_get c }
|
58
|
+
rescue NameError => e
|
59
|
+
name_error_name = e.message.sub('wrong constant name ', '')
|
60
|
+
name_error_name = e.message.sub('uninitialized constant ', '')
|
61
|
+
command_class = command_class.sub(name_error_name, name_error_name.upcase)
|
62
|
+
attempts += 1
|
63
|
+
retry unless attempts > command_parts.length
|
61
64
|
end
|
62
|
-
Toolshed::Commands::Base.parse(
|
65
|
+
Toolshed::Commands::Base.parse(command_class, command_class.cli_options)
|
63
66
|
end
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
70
|
+
def command_parts
|
71
|
+
@command_parts ||= begin
|
72
|
+
command_parts = []
|
73
|
+
arguments_left = true
|
74
|
+
until !arguments_left
|
75
|
+
if ARGV.first.nil? || ARGV.first.start_with?('--') || ARGV.first.start_with?('-')
|
76
|
+
arguments_left = false
|
77
|
+
else
|
78
|
+
command_parts << ARGV.shift
|
79
|
+
end
|
80
|
+
end
|
81
|
+
command_parts
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def default_command_class_string
|
86
|
+
command_class = "Toolshed::Commands"
|
87
|
+
command_parts.each do |command_part|
|
88
|
+
if command_part.include?('_')
|
89
|
+
command_class = "#{command_class}::#{command_part.split('_').map(&:capitalize).join('')}"
|
90
|
+
else
|
91
|
+
command_class = "#{command_class}::#{command_part.capitalize}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
command_class
|
95
|
+
end
|
96
|
+
|
67
97
|
def usage
|
68
98
|
$stdout.puts <<EOF
|
69
99
|
Please see toolshedrc.sample to create your toolshedrc settings file.
|
70
100
|
|
71
101
|
Available Commands:
|
72
102
|
|
73
|
-
checkout_branch
|
74
|
-
create_branch
|
75
|
-
create_pivotal_tracker_note
|
76
|
-
create_pull_request
|
77
|
-
create_ticket_comment
|
78
|
-
delete_branch
|
79
|
-
get_daily_time_update
|
80
|
-
list_branches
|
81
|
-
push_branch
|
82
|
-
rename_branch
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
103
|
+
toolshed checkout_branch
|
104
|
+
toolshed create_branch
|
105
|
+
toolshed create_pivotal_tracker_note
|
106
|
+
toolshed create_pull_request
|
107
|
+
toolshed create_ticket_comment
|
108
|
+
toolshed delete_branch
|
109
|
+
toolshed get_daily_time_update
|
110
|
+
toolshed list_branches
|
111
|
+
toolshed push_branch
|
112
|
+
toolshed rename_branch
|
113
|
+
toolshed scp download
|
114
|
+
toolshed ssh
|
115
|
+
toolshed ticket_information
|
116
|
+
toolshed update_pivotal_tracker_story_status
|
117
|
+
toolshed update_ticket_status
|
87
118
|
EOF
|
88
119
|
end
|
89
120
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
class RequiredOptionException < StandardError;
|
data/lib/toolshed/git/branch.rb
CHANGED
@@ -160,7 +160,8 @@ module Toolshed
|
|
160
160
|
results = Toolshed::Base.wait_for_command('git branch -avv')
|
161
161
|
results[:stdout].each do |stdout|
|
162
162
|
next if /remotes.*/.match(stdout) || /HEAD.*/.match(stdout)
|
163
|
-
|
163
|
+
matches = /([^\s]+)/.match(stdout)
|
164
|
+
branch_name = matches[0]
|
164
165
|
branch_name = branch_name.gsub('*', '')
|
165
166
|
branch_info_match = /\[[a-z].*\]/.match(stdout)
|
166
167
|
branch_info = ''
|
data/lib/toolshed/password.rb
CHANGED
@@ -1,47 +1,23 @@
|
|
1
|
-
|
2
|
-
class Password
|
3
|
-
attr_accessor :password, :sudo_password
|
4
|
-
|
5
|
-
def initialize(options={})
|
6
|
-
self.password = options[:password] ||= ''
|
7
|
-
self.sudo_password = options[:sudo_password] ||= ''
|
8
|
-
end
|
9
|
-
|
10
|
-
def read_user_input_password(type, prompt_message='Password:')
|
11
|
-
value = self.send(type)
|
12
|
-
unless value.nil? || value.empty?
|
13
|
-
read_password_from_configuration(type)
|
14
|
-
else
|
15
|
-
prompt_user_to_input_password(prompt_message)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
protected
|
1
|
+
require 'toolshed/client'
|
20
2
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
system "stty echo"
|
26
|
-
|
27
|
-
value
|
28
|
-
end
|
29
|
-
|
30
|
-
def get_password_input
|
31
|
-
$stdin.gets.chomp.strip
|
32
|
-
end
|
3
|
+
module Toolshed
|
4
|
+
module Password
|
5
|
+
def password_from_config(password)
|
6
|
+
return '' if password.nil? || password.empty?
|
33
7
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
self.send(type)
|
41
|
-
end
|
42
|
-
rescue => e
|
43
|
-
puts e.inspect
|
8
|
+
begin
|
9
|
+
credentials = Toolshed::Client.read_credenials
|
10
|
+
password_parts = password.split(':')
|
11
|
+
password_parts.each do |password_part|
|
12
|
+
return password if credentials[password_part].nil?
|
13
|
+
credentials = credentials[password_part]
|
44
14
|
end
|
15
|
+
rescue => e
|
16
|
+
Toolshed::Logger.instance.fatal e.message
|
17
|
+
Toolshed::Logger.instance.fatal e.inspect
|
18
|
+
return password
|
45
19
|
end
|
20
|
+
credentials
|
21
|
+
end
|
46
22
|
end
|
47
23
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'toolshed/server_administration/ssh'
|
2
|
+
require 'toolshed/password'
|
3
|
+
|
4
|
+
require 'net/scp'
|
5
|
+
require 'ruby-progressbar'
|
6
|
+
|
7
|
+
module Toolshed
|
8
|
+
module ServerAdministration
|
9
|
+
# Handles SCP file from one place to another
|
10
|
+
class SCP
|
11
|
+
include Toolshed::Password
|
12
|
+
|
13
|
+
attr_reader :local_path, :password, :remote_host, :remote_path, :username, :verbose_output # rubocop:disable LineLength
|
14
|
+
|
15
|
+
def initialize(options = nil)
|
16
|
+
options ||= {}
|
17
|
+
|
18
|
+
@password = options[:password]
|
19
|
+
@remote_host = options[:remote_host]
|
20
|
+
@remote_path = options[:remote_path]
|
21
|
+
@local_path = options[:local_path]
|
22
|
+
@username = options[:username]
|
23
|
+
@verbose_output = options[:verbose_output]
|
24
|
+
end
|
25
|
+
|
26
|
+
def download
|
27
|
+
Toolshed.logger.info "Attempting to SCP from #{username}@#{remote_host}:#{remote_path} to #{local_path}." # rubocop:disable LineLength
|
28
|
+
Net::SCP.download!(remote_host, username, remote_path, local_path, ssh: { password: password_from_config(password) }, recursive: true) # rubocop:disable LineLength
|
29
|
+
on_complete
|
30
|
+
end
|
31
|
+
|
32
|
+
def upload
|
33
|
+
Toolshed.logger.info "Attempting to SCP from #{local_path} to #{username}@#{remote_host}:#{remote_path}." # rubocop:disable LineLength
|
34
|
+
Net::SCP.upload!(remote_host, username, local_path, remote_path, ssh: { password: password_from_config(password) }, recursive: true) # rubocop:disable LineLength
|
35
|
+
on_complete
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def on_complete
|
41
|
+
Toolshed.logger.info ''
|
42
|
+
Toolshed.logger.info 'SCP file transfer has completed.'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -4,24 +4,31 @@ require 'net/ssh'
|
|
4
4
|
|
5
5
|
module Toolshed
|
6
6
|
module ServerAdministration
|
7
|
+
# SSH class that can ssh to a host and perform commands
|
7
8
|
class SSH
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
include Toolshed::Password
|
10
|
+
|
11
|
+
attr_accessor :channel, :commands, :data, :host, :keys, :password, :ssh_options, :sudo_password, :user # rubocop:disable LineLength
|
12
|
+
attr_reader :silent
|
13
|
+
|
14
|
+
def initialize(options = nil) # rubocop:disable AbcSize, CyclomaticComplexity, PerceivedComplexity, LineLength
|
15
|
+
options ||= {}
|
16
|
+
@password = options[:password] || ''
|
17
|
+
@sudo_password = options[:sudo_password] || ''
|
18
|
+
@keys = options[:keys] || ''
|
19
|
+
@host = options[:host] || ''
|
20
|
+
@user = options[:user] || ''
|
21
|
+
@ssh_options = options[:ssh_options] || {}
|
22
|
+
@commands = options[:commands] || ''
|
23
|
+
@password = options[:password] || ''
|
24
|
+
@data = []
|
25
|
+
@silent = options[:silent] || false
|
19
26
|
|
20
27
|
set_ssh_options
|
21
28
|
end
|
22
29
|
|
23
30
|
def execute
|
24
|
-
Net::SSH.start(
|
31
|
+
Net::SSH.start(host, user, ssh_options) do |ssh|
|
25
32
|
ssh.open_channel do |channel|
|
26
33
|
self.channel = channel
|
27
34
|
request_pty
|
@@ -29,14 +36,16 @@ module Toolshed
|
|
29
36
|
end
|
30
37
|
ssh.loop
|
31
38
|
end
|
39
|
+
data
|
32
40
|
end
|
33
41
|
|
34
42
|
protected
|
35
43
|
|
36
44
|
def run_commands
|
37
|
-
# @TODO fix this so it does not just pass in the commands option
|
38
|
-
|
39
|
-
|
45
|
+
# @TODO fix this so it does not just pass in the commands option
|
46
|
+
# Converts to string like command1;command2
|
47
|
+
channel.exec(commands) do |_ch, success|
|
48
|
+
abort 'Could not execute commands!' unless success
|
40
49
|
|
41
50
|
on_data
|
42
51
|
on_extended_data
|
@@ -45,46 +54,55 @@ module Toolshed
|
|
45
54
|
end
|
46
55
|
|
47
56
|
def request_pty
|
48
|
-
|
49
|
-
|
57
|
+
channel.request_pty do |_ch, success|
|
58
|
+
unless silent
|
59
|
+
message = (success) ? 'Successfully obtained pty' : 'Could not obtain pty'
|
60
|
+
puts message
|
61
|
+
end
|
50
62
|
end
|
51
63
|
end
|
52
64
|
|
53
65
|
def on_close
|
54
|
-
|
55
|
-
puts
|
66
|
+
channel.on_close do |_ch|
|
67
|
+
puts 'Channel is closing!' unless silent
|
56
68
|
end
|
57
69
|
end
|
58
70
|
|
59
71
|
def on_extended_data
|
60
|
-
|
61
|
-
puts "stderr: #{data}"
|
72
|
+
channel.on_extended_data do |_ch, _type, data|
|
73
|
+
puts "stderr: #{data}" unless silent
|
62
74
|
end
|
63
75
|
end
|
64
76
|
|
65
77
|
def on_data
|
66
|
-
|
67
|
-
puts "#{data}"
|
78
|
+
channel.on_data do |_ch, data|
|
79
|
+
puts "#{data}" unless silent
|
80
|
+
self.data << data
|
68
81
|
send_data(data)
|
69
82
|
end
|
70
83
|
end
|
71
84
|
|
72
85
|
def send_data(data)
|
73
86
|
send_password_data if data =~ /password/
|
74
|
-
send_yes_no_data if data =~ /Do you want to continue \[Y\/n\]?/
|
87
|
+
# send_yes_no_data if data =~ /Do you want to continue \[Y\/n\]?/
|
88
|
+
send_yes_no_data if data =~ %r{Do you want to continue [Y/n]?}
|
75
89
|
end
|
76
90
|
|
77
91
|
def send_password_data
|
78
|
-
|
92
|
+
channel.send_data "#{password_from_config(sudo_password)}\n"
|
79
93
|
end
|
80
94
|
|
81
95
|
def send_yes_no_data
|
82
|
-
|
96
|
+
channel.send_data "Y\n"
|
83
97
|
end
|
84
98
|
|
85
|
-
def set_ssh_options
|
86
|
-
|
87
|
-
|
99
|
+
def set_ssh_options # rubocop:disable AbcSize
|
100
|
+
if keys.nil? || keys.empty?
|
101
|
+
final_password = password_from_config(password)
|
102
|
+
ssh_options.merge!(password: final_password)
|
103
|
+
else
|
104
|
+
ssh_options.merge!(keys: [keys]) unless keys.nil? || keys.empty?
|
105
|
+
end
|
88
106
|
end
|
89
107
|
end
|
90
108
|
end
|
data/lib/toolshed/version.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'commands/commands_helper'
|
2
|
+
require 'toolshed/commands/scp/download'
|
3
|
+
require 'net/scp'
|
4
|
+
|
5
|
+
module Test
|
6
|
+
module Commands
|
7
|
+
module SCP
|
8
|
+
class DownloadTest < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
|
11
|
+
Toolshed.expects(:die).at_least(0).returns('Exiting')
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_scp_upload
|
19
|
+
Net::SCP.expects(:download!).returns('downloaded')
|
20
|
+
|
21
|
+
scp_download_command = Toolshed::Commands::SCP::Download.new
|
22
|
+
scp_download_command.execute({}, password: 'test1234', username: 'test', remote_host: 'localhost', remote_path: '/tmp', local_path: '/tmp')
|
23
|
+
|
24
|
+
assert Toolshed::Logger.instance.logs[:info].include?('SCP file transfer has completed.')
|
25
|
+
assert Toolshed::Logger.instance.logs[:info].include?('Attempting to SCP from test@localhost:/tmp to /tmp.')
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_cli_options
|
29
|
+
assert 'Usage: scp download [options]', Toolshed::Commands::SCP::Download.cli_options[:banner]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'commands/commands_helper'
|
2
|
+
require 'toolshed/commands/scp/upload'
|
3
|
+
require 'net/scp'
|
4
|
+
|
5
|
+
module Test
|
6
|
+
module Commands
|
7
|
+
module SCP
|
8
|
+
class UploadTest < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
|
11
|
+
Toolshed.expects(:die).at_least(0).returns('Exiting')
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_scp_upload
|
19
|
+
Net::SCP.expects(:upload!).returns('uploaded')
|
20
|
+
|
21
|
+
scp_upload_command = Toolshed::Commands::SCP::Upload.new
|
22
|
+
scp_upload_command.execute({}, password: 'test1234', username: 'test', remote_host: 'localhost', remote_path: '/tmp', local_path: '/tmp')
|
23
|
+
|
24
|
+
assert Toolshed::Logger.instance.logs[:info].include?('SCP file transfer has completed.')
|
25
|
+
assert Toolshed::Logger.instance.logs[:info].include?('Attempting to SCP from /tmp to test@localhost:/tmp.')
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_cli_options
|
29
|
+
assert 'Usage: scp upload [options]', Toolshed::Commands::SCP::Upload.cli_options[:banner]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/test/helper.rb
CHANGED
@@ -18,6 +18,7 @@ require 'fileutils'
|
|
18
18
|
require 'json'
|
19
19
|
|
20
20
|
require 'toolshed'
|
21
|
+
require 'toolshed/logger'
|
21
22
|
|
22
23
|
Test::Unit.at_start do
|
23
24
|
Toolshed::Client.instance.use_git_submodules = false
|
@@ -100,3 +101,27 @@ def capture_stdout(&block)
|
|
100
101
|
end
|
101
102
|
fake.string
|
102
103
|
end
|
104
|
+
|
105
|
+
class Toolshed::Logger
|
106
|
+
attr_reader :logs
|
107
|
+
|
108
|
+
def initialize
|
109
|
+
@logs = { debug: [], fatal: [], info: [], warn: [] }
|
110
|
+
end
|
111
|
+
|
112
|
+
def debug(message)
|
113
|
+
logs[:debug] << message
|
114
|
+
end
|
115
|
+
|
116
|
+
def fatal(message)
|
117
|
+
logs[:fatal] << message
|
118
|
+
end
|
119
|
+
|
120
|
+
def info(message)
|
121
|
+
logs[:info] << message
|
122
|
+
end
|
123
|
+
|
124
|
+
def warn(message)
|
125
|
+
logs[:warn] << message
|
126
|
+
end
|
127
|
+
end
|
data/test/password_test.rb
CHANGED
@@ -1,24 +1,10 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'toolshed/server_administration/scp'
|
2
3
|
|
3
4
|
class PasswordTest < Test::Unit::TestCase
|
4
|
-
def
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def test_read_user_input_password_sudo
|
10
|
-
password = Toolshed::Password.new({ sudo_password: 'test12345' })
|
11
|
-
assert_equal password.read_user_input_password('sudo_password'), 'test12345'
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_read_user_input_password_prompt
|
15
|
-
password = Toolshed::Password.new({})
|
16
|
-
password.expects(:get_password_input).returns('readin1234')
|
17
|
-
assert_equal password.read_user_input_password('password'), 'readin1234'
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_read_user_input_from_toolshedrc
|
21
|
-
password = Toolshed::Password.new({ password: 'server_password' })
|
22
|
-
assert_equal password.read_user_input_password('password'), 'tester1234'
|
5
|
+
def test_password_from_config
|
6
|
+
expected_password = 'tester1234'
|
7
|
+
actual_password = Toolshed::ServerAdministration::SCP.new({}).password_from_config('server_password')
|
8
|
+
assert_equal expected_password, actual_password
|
23
9
|
end
|
24
10
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'commands/commands_helper'
|
2
|
+
require 'toolshed/commands/scp/download'
|
3
|
+
require 'net/scp'
|
4
|
+
|
5
|
+
module Test
|
6
|
+
module ServerAdministration
|
7
|
+
class SCPTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_scp_download
|
17
|
+
Net::SCP.expects(:download!).returns('downloaded')
|
18
|
+
|
19
|
+
scp = Toolshed::ServerAdministration::SCP.new(password: 'test1234', username: 'test', remote_host: 'localhost', remote_path: '/tmp', local_path: '/tmp')
|
20
|
+
scp.download
|
21
|
+
|
22
|
+
assert Toolshed::Logger.instance.logs[:info].include?('SCP file transfer has completed.')
|
23
|
+
assert Toolshed::Logger.instance.logs[:info].include?('Attempting to SCP from test@localhost:/tmp to /tmp.')
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_scp_upload
|
27
|
+
Net::SCP.expects(:upload!).returns('uploaded')
|
28
|
+
|
29
|
+
scp = Toolshed::ServerAdministration::SCP.new(password: 'test1234', username: 'test', remote_host: 'localhost', remote_path: '/tmp', local_path: '/tmp')
|
30
|
+
scp.upload
|
31
|
+
|
32
|
+
assert Toolshed::Logger.instance.logs[:info].include?('SCP file transfer has completed.')
|
33
|
+
assert Toolshed::Logger.instance.logs[:info].include?('Attempting to SCP from /tmp to test@localhost:/tmp.')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/toolshed.gemspec
CHANGED
@@ -29,6 +29,8 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_dependency 'net-ssh', '2.9.2'
|
30
30
|
spec.add_dependency 'term-ansicolor', '1.3.0'
|
31
31
|
spec.add_dependency 'highline', '1.7.2'
|
32
|
+
spec.add_dependency 'net-scp', '1.2.1'
|
33
|
+
spec.add_dependency 'ruby-progressbar', '1.7.5'
|
32
34
|
|
33
35
|
spec.add_development_dependency 'bundler'
|
34
36
|
spec.add_development_dependency 'rake'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: toolshed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jake Waller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -150,6 +150,34 @@ dependencies:
|
|
150
150
|
- - '='
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: 1.7.2
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: net-scp
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 1.2.1
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 1.2.1
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: ruby-progressbar
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 1.7.5
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: 1.7.5
|
153
181
|
- !ruby/object:Gem::Dependency
|
154
182
|
name: bundler
|
155
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -337,18 +365,23 @@ files:
|
|
337
365
|
- lib/toolshed/commands/list_branches.rb
|
338
366
|
- lib/toolshed/commands/push_branch.rb
|
339
367
|
- lib/toolshed/commands/rename_branch.rb
|
368
|
+
- lib/toolshed/commands/scp/download.rb
|
369
|
+
- lib/toolshed/commands/scp/upload.rb
|
370
|
+
- lib/toolshed/commands/scp_base.rb
|
340
371
|
- lib/toolshed/commands/ssh.rb
|
341
372
|
- lib/toolshed/commands/ticket_information.rb
|
342
373
|
- lib/toolshed/commands/update_pivotal_tracker_story_status.rb
|
343
374
|
- lib/toolshed/commands/update_ticket_status.rb
|
344
375
|
- lib/toolshed/entry_point.rb
|
345
376
|
- lib/toolshed/error.rb
|
377
|
+
- lib/toolshed/exceptions.rb
|
346
378
|
- lib/toolshed/git.rb
|
347
379
|
- lib/toolshed/git/branch.rb
|
348
380
|
- lib/toolshed/git/github.rb
|
349
381
|
- lib/toolshed/git/validator.rb
|
350
382
|
- lib/toolshed/logger.rb
|
351
383
|
- lib/toolshed/password.rb
|
384
|
+
- lib/toolshed/server_administration/scp.rb
|
352
385
|
- lib/toolshed/server_administration/ssh.rb
|
353
386
|
- lib/toolshed/ticket_tracking/jira.rb
|
354
387
|
- lib/toolshed/ticket_tracking/pivotal_tracker.rb
|
@@ -365,12 +398,15 @@ files:
|
|
365
398
|
- test/commands/get_daily_time_update_test.rb
|
366
399
|
- test/commands/push_branch_test.rb
|
367
400
|
- test/commands/rename_branch_test.rb
|
401
|
+
- test/commands/scp/download_test.rb
|
402
|
+
- test/commands/scp/upload_test.rb
|
368
403
|
- test/config.rb
|
369
404
|
- test/git/git_helper.rb
|
370
405
|
- test/git/git_test.rb
|
371
406
|
- test/git/github_test.rb
|
372
407
|
- test/helper.rb
|
373
408
|
- test/password_test.rb
|
409
|
+
- test/server_administration/scp_test.rb
|
374
410
|
- test/server_administration/ssh_test.rb
|
375
411
|
- test/ticket_tracking/jira_test.rb
|
376
412
|
- test/time_tracking/harvest_test.rb
|
@@ -395,7 +431,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
395
431
|
version: '0'
|
396
432
|
requirements: []
|
397
433
|
rubyforge_project:
|
398
|
-
rubygems_version: 2.4.5
|
434
|
+
rubygems_version: 2.4.5.1
|
399
435
|
signing_key:
|
400
436
|
specification_version: 4
|
401
437
|
summary: Create a Github pull request with minimal work. Will automatically read ticket
|
@@ -410,12 +446,15 @@ test_files:
|
|
410
446
|
- test/commands/get_daily_time_update_test.rb
|
411
447
|
- test/commands/push_branch_test.rb
|
412
448
|
- test/commands/rename_branch_test.rb
|
449
|
+
- test/commands/scp/download_test.rb
|
450
|
+
- test/commands/scp/upload_test.rb
|
413
451
|
- test/config.rb
|
414
452
|
- test/git/git_helper.rb
|
415
453
|
- test/git/git_test.rb
|
416
454
|
- test/git/github_test.rb
|
417
455
|
- test/helper.rb
|
418
456
|
- test/password_test.rb
|
457
|
+
- test/server_administration/scp_test.rb
|
419
458
|
- test/server_administration/ssh_test.rb
|
420
459
|
- test/ticket_tracking/jira_test.rb
|
421
460
|
- test/time_tracking/harvest_test.rb
|