deep_test_pre 2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +47 -0
- data/README.rdoc +199 -0
- data/Rakefile +137 -0
- data/lib/deep_test.rb +78 -0
- data/lib/deep_test/agent.rb +108 -0
- data/lib/deep_test/central_command.rb +165 -0
- data/lib/deep_test/cpu_info.rb +22 -0
- data/lib/deep_test/database/mysql_setup_listener.rb +112 -0
- data/lib/deep_test/database/setup_listener.rb +116 -0
- data/lib/deep_test/deadlock_detector.rb +7 -0
- data/lib/deep_test/demon.rb +25 -0
- data/lib/deep_test/distributed/beachhead.rb +104 -0
- data/lib/deep_test/distributed/dispatch_controller.rb +60 -0
- data/lib/deep_test/distributed/establish_beachhead.rb +19 -0
- data/lib/deep_test/distributed/filename_resolver.rb +40 -0
- data/lib/deep_test/distributed/landing_fleet.rb +30 -0
- data/lib/deep_test/distributed/landing_ship.rb +60 -0
- data/lib/deep_test/distributed/remote_deployment.rb +56 -0
- data/lib/deep_test/distributed/rsync.rb +50 -0
- data/lib/deep_test/distributed/shell_environment.rb +50 -0
- data/lib/deep_test/distributed/ssh_client_connection_info.rb +14 -0
- data/lib/deep_test/extensions/object_extension.rb +40 -0
- data/lib/deep_test/failure_message.rb +19 -0
- data/lib/deep_test/lib_root.rb +4 -0
- data/lib/deep_test/listener_list.rb +17 -0
- data/lib/deep_test/local_deployment.rb +46 -0
- data/lib/deep_test/logger.rb +32 -0
- data/lib/deep_test/main.rb +41 -0
- data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
- data/lib/deep_test/metrics/data.rb +34 -0
- data/lib/deep_test/metrics/measurement.rb +39 -0
- data/lib/deep_test/null_listener.rb +62 -0
- data/lib/deep_test/options.rb +113 -0
- data/lib/deep_test/proxy_io.rb +77 -0
- data/lib/deep_test/rake_tasks.rb +13 -0
- data/lib/deep_test/result_reader.rb +40 -0
- data/lib/deep_test/rspec_detector.rb +21 -0
- data/lib/deep_test/spec.rb +17 -0
- data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
- data/lib/deep_test/spec/extensions/example_methods.rb +52 -0
- data/lib/deep_test/spec/extensions/options.rb +43 -0
- data/lib/deep_test/spec/extensions/spec_task.rb +21 -0
- data/lib/deep_test/spec/runner.rb +72 -0
- data/lib/deep_test/spec/work_result.rb +35 -0
- data/lib/deep_test/spec/work_unit.rb +59 -0
- data/lib/deep_test/test.rb +10 -0
- data/lib/deep_test/test/extensions/error.rb +14 -0
- data/lib/deep_test/test/run_test_suite.rb +5 -0
- data/lib/deep_test/test/runner.rb +24 -0
- data/lib/deep_test/test/supervised_test_suite.rb +48 -0
- data/lib/deep_test/test/work_result.rb +35 -0
- data/lib/deep_test/test/work_unit.rb +40 -0
- data/lib/deep_test/test_task.rb +47 -0
- data/lib/deep_test/ui/console.rb +74 -0
- data/lib/deep_test/ui/null.rb +17 -0
- data/lib/deep_test/warlock.rb +146 -0
- data/lib/telegraph.rb +29 -0
- data/lib/telegraph/ack_sequence.rb +14 -0
- data/lib/telegraph/logging.rb +20 -0
- data/lib/telegraph/message.rb +39 -0
- data/lib/telegraph/operator.rb +47 -0
- data/lib/telegraph/switchboard.rb +57 -0
- data/lib/telegraph/wire.rb +73 -0
- data/test/deep_test/agent_test.rb +175 -0
- data/test/deep_test/central_command_test.rb +147 -0
- data/test/deep_test/cpu_info_test.rb +33 -0
- data/test/deep_test/database/mysql_setup_listener_test.rb +18 -0
- data/test/deep_test/demon_test.rb +23 -0
- data/test/deep_test/distributed/beachhead_test.rb +67 -0
- data/test/deep_test/distributed/dispatch_controller_test.rb +162 -0
- data/test/deep_test/distributed/filename_resolver_test.rb +56 -0
- data/test/deep_test/distributed/landing_fleet_test.rb +55 -0
- data/test/deep_test/distributed/landing_ship_test.rb +48 -0
- data/test/deep_test/distributed/remote_deployment_test.rb +134 -0
- data/test/deep_test/distributed/rsync_test.rb +47 -0
- data/test/deep_test/distributed/shell_environment_test.rb +108 -0
- data/test/deep_test/distributed/ssh_client_connection_info_test.rb +34 -0
- data/test/deep_test/extensions/object_extension_test.rb +37 -0
- data/test/deep_test/listener_list_test.rb +22 -0
- data/test/deep_test/local_deployment_test.rb +19 -0
- data/test/deep_test/logger_test.rb +38 -0
- data/test/deep_test/main_test.rb +12 -0
- data/test/deep_test/marshallable_exception_wrapper_test.rb +46 -0
- data/test/deep_test/metrics/data_test.rb +22 -0
- data/test/deep_test/metrics/measurement_test.rb +18 -0
- data/test/deep_test/proxy_io_test.rb +104 -0
- data/test/deep_test/result_reader_test.rb +128 -0
- data/test/deep_test/test/extensions/error_test.rb +42 -0
- data/test/deep_test/test/runner_test.rb +11 -0
- data/test/deep_test/test/supervised_test_suite_test.rb +107 -0
- data/test/deep_test/test/work_result_test.rb +85 -0
- data/test/deep_test/test/work_unit_test.rb +63 -0
- data/test/deep_test/test_task_test.rb +15 -0
- data/test/deep_test/ui/console_test.rb +13 -0
- data/test/deep_test/warlock_test.rb +40 -0
- data/test/test_helper.rb +30 -0
- data/test/test_task_test.rb +75 -0
- metadata +156 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module DeepTest
|
2
|
+
module Demon
|
3
|
+
def forked(name, options, demon_args)
|
4
|
+
options.connect_to_central_command do |wire|
|
5
|
+
ProxyIO.replace_stdout_stderr!(wire) do
|
6
|
+
begin
|
7
|
+
catch(:exit_demon) do
|
8
|
+
Signal.trap("TERM") { throw :exit_demon }
|
9
|
+
execute *demon_args
|
10
|
+
end
|
11
|
+
rescue SystemExit => e
|
12
|
+
raise
|
13
|
+
rescue Exception => e
|
14
|
+
FailureMessage.show self.class.name, "Process #{Process.pid} exiting with excetion: #{e.class}: #{e.message}"
|
15
|
+
raise
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute(*args)
|
22
|
+
raise "#{self.class} must implement the execute method to be a Demon"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module DeepTest
|
2
|
+
module Distributed
|
3
|
+
class Beachhead < LocalDeployment
|
4
|
+
include Demon
|
5
|
+
|
6
|
+
MERCY_KILLING_GRACE_PERIOD = 10 * 60 unless defined?(MERCY_KILLING_GRACE_PERIOD)
|
7
|
+
|
8
|
+
def initialize(base_path, options)
|
9
|
+
super options
|
10
|
+
@base_path = base_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def launch_mercy_killer(grace_period)
|
14
|
+
Thread.new do
|
15
|
+
sleep grace_period
|
16
|
+
exit(0) unless agents_deployed?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_files(files)
|
21
|
+
spec_support_path = File.expand_path(File.dirname(__FILE__) + "/../spec")
|
22
|
+
Dir.chdir @base_path
|
23
|
+
resolver = FilenameResolver.new(@base_path)
|
24
|
+
files.each do |file|
|
25
|
+
load resolver.resolve(file)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Load rspec support if rspec is available now that we've loaded the host project files
|
29
|
+
#
|
30
|
+
DeepTest::RSpecDetector.if_rspec_available do
|
31
|
+
require spec_support_path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def deploy_agents
|
36
|
+
@agents_deployed = true
|
37
|
+
super
|
38
|
+
warlock.exit_when_none_running
|
39
|
+
end
|
40
|
+
|
41
|
+
def agents_deployed?
|
42
|
+
@agents_deployed
|
43
|
+
end
|
44
|
+
|
45
|
+
def forked(*args)
|
46
|
+
$stdout.reopen("/dev/null")
|
47
|
+
$stderr.reopen("/dev/null")
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
def execute(innie, outie, grace_period)
|
52
|
+
innie.close
|
53
|
+
|
54
|
+
switchboard = Telegraph::Switchboard.new
|
55
|
+
operator = Telegraph::Operator.listen "0.0.0.0", 0, switchboard
|
56
|
+
|
57
|
+
DeepTest.logger.debug { "Beachhead started on port #{operator.port}" }
|
58
|
+
|
59
|
+
outie.write operator.port
|
60
|
+
outie.close
|
61
|
+
|
62
|
+
launch_mercy_killer grace_period
|
63
|
+
|
64
|
+
loop do
|
65
|
+
begin
|
66
|
+
switchboard.process_messages :timeout => 1 do |message, wire|
|
67
|
+
case message.body
|
68
|
+
when LoadFiles
|
69
|
+
load_files message.body.files
|
70
|
+
when DeployAgents
|
71
|
+
deploy_agents
|
72
|
+
wire.send_message Done
|
73
|
+
operator.shutdown
|
74
|
+
break
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def daemonize(grace_period = MERCY_KILLING_GRACE_PERIOD)
|
82
|
+
innie, outie = IO.pipe
|
83
|
+
|
84
|
+
warlock.start "Beachhead", self, innie, outie, grace_period
|
85
|
+
|
86
|
+
outie.close
|
87
|
+
port = innie.gets
|
88
|
+
innie.close
|
89
|
+
port.to_i
|
90
|
+
end
|
91
|
+
|
92
|
+
unless defined? DeployAgents
|
93
|
+
DeployAgents = "DeployAgents"
|
94
|
+
Done = "Done"
|
95
|
+
class LoadFiles
|
96
|
+
attr_reader :files
|
97
|
+
def initialize(files)
|
98
|
+
@files = files
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module DeepTest
|
2
|
+
module Distributed
|
3
|
+
class DispatchController
|
4
|
+
def initialize(options, receivers)
|
5
|
+
@options = options
|
6
|
+
@receivers = receivers
|
7
|
+
end
|
8
|
+
|
9
|
+
def dispatch(method_name, *args)
|
10
|
+
dispatch_with_options(method_name, {}, *args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def dispatch_with_options(method_name, options, *args)
|
14
|
+
raise NoDispatchReceiversError if @receivers.empty?
|
15
|
+
|
16
|
+
@options.ui_instance.dispatch_starting(method_name)
|
17
|
+
|
18
|
+
threads = @receivers.map do |r|
|
19
|
+
Thread.new do
|
20
|
+
begin
|
21
|
+
Thread.current[:receiver] = r
|
22
|
+
r.send method_name, *args
|
23
|
+
rescue Exception => e
|
24
|
+
Thread.current[:original_exception] = e
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
results = []
|
31
|
+
threads.each do |t|
|
32
|
+
begin
|
33
|
+
results << t.value
|
34
|
+
rescue Timeout::Error
|
35
|
+
@receivers.delete t[:receiver]
|
36
|
+
DeepTest.logger.error { "Timeout dispatching #{method_name} to #{description t[:receiver]}" }
|
37
|
+
rescue Exception => this_exception
|
38
|
+
@receivers.delete t[:receiver]
|
39
|
+
DeepTest.logger.error { "Exception while dispatching #{method_name} to #{description t[:receiver]}:" }
|
40
|
+
|
41
|
+
e = t[:original_exception] || this_exception
|
42
|
+
DeepTest.logger.error { "#{e.class}: #{e.message}" }
|
43
|
+
e.backtrace.each {|l| DeepTest.logger.error { l } }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
results
|
48
|
+
ensure
|
49
|
+
@options.ui_instance.dispatch_finished(method_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def description(receiver)
|
53
|
+
receiver.inspect
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
class NoDispatchReceiversError < StandardError; end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../deep_test"
|
2
|
+
options = DeepTest::Options.from_command_line(ENV['OPTIONS'])
|
3
|
+
ENV['DEEP_TEST_LOG_LEVEL'] = options.environment_log_level
|
4
|
+
options.ssh_client_connection_info = DeepTest::Distributed::SshClientConnectionInfo.new
|
5
|
+
|
6
|
+
DeepTest.logger.debug { "mirror establish_beachhead for #{options.origin_hostname}" }
|
7
|
+
|
8
|
+
STDIN.close
|
9
|
+
|
10
|
+
beachhead_port = DeepTest::Distributed::Beachhead.new(
|
11
|
+
File.join(options.mirror_path, File.basename(options.sync_options[:source])),
|
12
|
+
options
|
13
|
+
).daemonize
|
14
|
+
|
15
|
+
puts "Beachhead port: #{beachhead_port}"
|
16
|
+
$stdout.flush
|
17
|
+
|
18
|
+
exit!(0)
|
19
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module DeepTest
|
2
|
+
module Distributed
|
3
|
+
class FilenameResolver
|
4
|
+
def initialize(base_path)
|
5
|
+
@base_path = base_path
|
6
|
+
end
|
7
|
+
|
8
|
+
def resolve(filename)
|
9
|
+
return resolve("/" + filename) unless filename[0] == ?/
|
10
|
+
|
11
|
+
return filename.sub(@cached_replaced_path, @base_path) if @cached_replaced_path
|
12
|
+
|
13
|
+
each_potential_filename(filename) do |potential_filename|
|
14
|
+
if File.exist?(potential_filename)
|
15
|
+
cache_resolution(filename, potential_filename)
|
16
|
+
return potential_filename
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
raise "Filename resolution failed. Cannot resolve #{filename} within #{@base_path}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def cache_resolution(original_filename, resolved_filename)
|
24
|
+
@cached_replaced_path = original_filename.sub(
|
25
|
+
resolved_filename.sub(@base_path, ""), ""
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def each_potential_filename(filename)
|
30
|
+
basename = File.basename(filename)
|
31
|
+
dirs = File.dirname(filename).split('/')
|
32
|
+
|
33
|
+
begin
|
34
|
+
dirs.shift
|
35
|
+
yield [@base_path, dirs, basename].flatten.join('/')
|
36
|
+
end until dirs.empty?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module DeepTest
|
2
|
+
module Distributed
|
3
|
+
class LandingFleet
|
4
|
+
def initialize(options, slaves)
|
5
|
+
DeepTest.logger.debug { "LandingFleet#initialize #{slaves.length} slaves" }
|
6
|
+
@slave_controller = DispatchController.new(options, slaves)
|
7
|
+
end
|
8
|
+
|
9
|
+
def establish_beachhead(options)
|
10
|
+
DeepTest.logger.debug { "dispatch establish_beachhead for #{options.origin_hostname}" }
|
11
|
+
@slave_controller.dispatch :establish_beachhead, options
|
12
|
+
end
|
13
|
+
|
14
|
+
def push_code(options)
|
15
|
+
DeepTest.logger.debug { "dispatch push_code for #{options.origin_hostname}" }
|
16
|
+
@slave_controller.dispatch :push_code, options
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_files(files)
|
20
|
+
DeepTest.logger.debug { "dispatch load_files" }
|
21
|
+
@slave_controller.dispatch :load_files, files
|
22
|
+
end
|
23
|
+
|
24
|
+
def deploy_agents
|
25
|
+
DeepTest.logger.debug { "dispatch deploy_agents" }
|
26
|
+
@slave_controller.dispatch :deploy_agents
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module DeepTest
|
2
|
+
module Distributed
|
3
|
+
class LandingShip
|
4
|
+
def initialize(config)
|
5
|
+
@config = config
|
6
|
+
end
|
7
|
+
|
8
|
+
def push_code(options)
|
9
|
+
RSync.push(@config[:address], options.sync_options, options.mirror_path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def establish_beachhead(options)
|
13
|
+
command = "#{ssh_command(options)} '#{spawn_command(options)}' 2>&1"
|
14
|
+
DeepTest.logger.debug { "Establishing Beachhead: #{command}" }
|
15
|
+
|
16
|
+
output = `#{command}`
|
17
|
+
output.each do |line|
|
18
|
+
if DeepTest.logger.level == Logger::DEBUG
|
19
|
+
puts output
|
20
|
+
end
|
21
|
+
if line =~ /Beachhead port: (.+)/
|
22
|
+
@wire = Telegraph::Wire.connect(@config[:address], $1.to_i)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
raise "LandingShip unable to establish Beachhead. Output from #{@config[:address]} was:\n#{output}" unless @wire
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_files(files)
|
29
|
+
@wire.send_message Beachhead::LoadFiles.new(files)
|
30
|
+
end
|
31
|
+
|
32
|
+
def deploy_agents
|
33
|
+
@wire.send_message Beachhead::DeployAgents
|
34
|
+
begin
|
35
|
+
message = @wire.next_message :timeout => 1
|
36
|
+
raise "Unexpected message from Beachhead: #{message.inspect}" unless message.body == Beachhead::Done
|
37
|
+
rescue Telegraph::NoMessageAvailable
|
38
|
+
retry
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def ssh_command(options)
|
43
|
+
username_option = if options.sync_options[:username]
|
44
|
+
" -l #{options.sync_options[:username]}"
|
45
|
+
else
|
46
|
+
""
|
47
|
+
end
|
48
|
+
|
49
|
+
"ssh -4 #{@config[:address]}#{username_option}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def spawn_command(options)
|
53
|
+
"#{ShellEnvironment.like_login} && " +
|
54
|
+
"cd #{options.mirror_path} && " +
|
55
|
+
"OPTIONS=#{options.to_command_line} " +
|
56
|
+
"ruby lib/deep_test/distributed/establish_beachhead.rb"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module DeepTest
|
2
|
+
module Distributed
|
3
|
+
class RemoteDeployment
|
4
|
+
def initialize(options, landing_fleet, failover_deployment)
|
5
|
+
@failover_deployment = failover_deployment
|
6
|
+
@options = options
|
7
|
+
@landing_fleet = landing_fleet
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_files(filelist)
|
11
|
+
# load one file before calling listeners to make sure environment has
|
12
|
+
# been initialized as expected
|
13
|
+
#
|
14
|
+
load filelist.first
|
15
|
+
@options.new_listener_list.before_sync
|
16
|
+
|
17
|
+
t = Thread.new do
|
18
|
+
@landing_fleet.push_code(@options)
|
19
|
+
@landing_fleet.establish_beachhead(@options)
|
20
|
+
@landing_fleet.load_files filelist
|
21
|
+
end
|
22
|
+
|
23
|
+
filelist[1..-1].each {|f| load f}
|
24
|
+
|
25
|
+
begin
|
26
|
+
t.join
|
27
|
+
rescue => e
|
28
|
+
# The failover here doesn't invoke load_files on the failover_deployment
|
29
|
+
# because it will be a LocalDeployment, which forks from the current
|
30
|
+
# process. The fact that we depend in this here is damp...
|
31
|
+
#
|
32
|
+
fail_over("load_files", e)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def deploy_agents
|
37
|
+
DeepTest.logger.debug { "RemoteDeployment deploying agents with #{@landing_fleet}" }
|
38
|
+
@landing_fleet.deploy_agents
|
39
|
+
rescue => e
|
40
|
+
raise if failed_over?
|
41
|
+
fail_over("deploy_agents", e)
|
42
|
+
retry
|
43
|
+
end
|
44
|
+
|
45
|
+
def fail_over(method, exception)
|
46
|
+
DeepTest.logger.debug { "RemoteDeployment failing over on #{method}." }
|
47
|
+
@options.ui_instance.distributed_failover_to_local(method, exception)
|
48
|
+
@landing_fleet = @failover_deployment
|
49
|
+
end
|
50
|
+
|
51
|
+
def failed_over?
|
52
|
+
@landing_fleet == @failover_deployment
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module DeepTest
|
2
|
+
module Distributed
|
3
|
+
class RSync
|
4
|
+
def self.push(address, sync_options, destination)
|
5
|
+
sync(:push, address, sync_options, destination)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.sync(operation, address, sync_options, destination)
|
9
|
+
command = Args.new(address, sync_options).send("#{operation}_command",
|
10
|
+
destination)
|
11
|
+
|
12
|
+
DeepTest.logger.debug { "rsycing: #{command}" }
|
13
|
+
successful = system command
|
14
|
+
raise "RSync Failed!!" unless successful
|
15
|
+
end
|
16
|
+
|
17
|
+
class Args
|
18
|
+
def initialize(address, sync_options)
|
19
|
+
@address = address
|
20
|
+
@sync_options = sync_options
|
21
|
+
end
|
22
|
+
|
23
|
+
def pull_command(destination)
|
24
|
+
command remote_location(@sync_options[:source]), destination
|
25
|
+
end
|
26
|
+
|
27
|
+
def push_command(destination)
|
28
|
+
command @sync_options[:source], remote_location(destination)
|
29
|
+
end
|
30
|
+
|
31
|
+
def command(source, destination)
|
32
|
+
# The '/' after source tells rsync to copy the contents
|
33
|
+
# of source to destination, rather than the source directory
|
34
|
+
# itself
|
35
|
+
"rsync -az --delete #{@sync_options[:rsync_options]} #{DeepTest::LIB_ROOT} #{source} #{destination}".strip.squeeze(" ")
|
36
|
+
end
|
37
|
+
|
38
|
+
def remote_location(path)
|
39
|
+
source = ""
|
40
|
+
unless @sync_options[:local]
|
41
|
+
source << @sync_options[:username] << '@' if @sync_options[:username]
|
42
|
+
source << @address
|
43
|
+
source << (@sync_options[:daemon] ? '::' : ':')
|
44
|
+
end
|
45
|
+
source << path
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|