synqa 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/Gemfile.lock +5 -0
- data/README.rdoc +9 -3
- data/VERSION +1 -1
- data/examples/sample-rakefile +8 -3
- data/examples/synqa-useage.rb +9 -3
- data/lib/{based-directory.rb → based.rb} +0 -0
- data/lib/synqa.rb +170 -79
- data/test/{test_based_directory.rb → test_based.rb} +1 -1
- metadata +36 -14
data/Gemfile
CHANGED
@@ -3,6 +3,9 @@ source "http://rubygems.org"
|
|
3
3
|
# Example:
|
4
4
|
# gem "activesupport", ">= 2.3.5"
|
5
5
|
|
6
|
+
gem "net-ssh", ">= 2.0"
|
7
|
+
gem "net-scp", ">= 1.0"
|
8
|
+
|
6
9
|
# Add dependencies to develop your gem here.
|
7
10
|
# Include everything needed to run rake, tests, features, etc.
|
8
11
|
group :development do
|
data/Gemfile.lock
CHANGED
@@ -6,6 +6,9 @@ GEM
|
|
6
6
|
bundler (~> 1.0.0)
|
7
7
|
git (>= 1.2.5)
|
8
8
|
rake
|
9
|
+
net-scp (1.0.4)
|
10
|
+
net-ssh (>= 1.99.1)
|
11
|
+
net-ssh (2.1.4)
|
9
12
|
rake (0.8.7)
|
10
13
|
rcov (0.9.9)
|
11
14
|
shoulda (2.11.3)
|
@@ -16,5 +19,7 @@ PLATFORMS
|
|
16
19
|
DEPENDENCIES
|
17
20
|
bundler (~> 1.0.0)
|
18
21
|
jeweler (~> 1.5.2)
|
22
|
+
net-scp (>= 1.0)
|
23
|
+
net-ssh (>= 2.0)
|
19
24
|
rcov
|
20
25
|
shoulda
|
data/README.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
= synqa
|
2
2
|
|
3
3
|
*Synqa* is a simple file syncing tool that works over SSH, and is designed
|
4
|
-
primarily for maintaining static websites. It uses a hash function to
|
4
|
+
primarily for maintaining static websites. It uses a secure hash function to
|
5
5
|
determine which files don't need to be copied because the destination copy
|
6
6
|
is already identical to the source copy.
|
7
7
|
|
@@ -20,8 +20,11 @@ I wrote *synqa* for two main reasons:
|
|
20
20
|
== Dependencies of *synqa* are:
|
21
21
|
|
22
22
|
* Ruby 1.9.2
|
23
|
-
*
|
24
|
-
|
23
|
+
* Ruby gems *net-ssh* and *net-scp*
|
24
|
+
|
25
|
+
Optionally:
|
26
|
+
* An external SSH client. I use *plink*.
|
27
|
+
* An external SCP client. I use *pscp*.
|
25
28
|
|
26
29
|
For some sample code, see <b>examples/synga-useage.rb</b> and <b>examples/sample-rakefile</b>.
|
27
30
|
|
@@ -35,3 +38,6 @@ Synqa is licensed under the GNU General Public License version 3.
|
|
35
38
|
containing whitespace or non-ASCII characters. Typically this doesn't matter for
|
36
39
|
many static websites, but it will reduce the tool's usefulness as a general purpose
|
37
40
|
backup tool.
|
41
|
+
|
42
|
+
* Currently *Synqa* does not provide authentication options, on the assumption that you
|
43
|
+
will use Pageant (which automagically provides "presented" keys for specified user/host combinations).
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/examples/sample-rakefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'based
|
1
|
+
require 'based'
|
2
2
|
require 'synqa'
|
3
3
|
require 'digest/sha2'
|
4
4
|
|
@@ -19,7 +19,7 @@ task :default => [:uploaddry] do |t|
|
|
19
19
|
end
|
20
20
|
|
21
21
|
REMOTE_HOST = SshContentHost.new("yourusername@yourhostname.example.com",
|
22
|
-
Sha256Command.new()
|
22
|
+
Sha256Command.new())
|
23
23
|
|
24
24
|
REMOTE_SITE = RemoteContentLocation.new(REMOTE_HOST,
|
25
25
|
"/home/username/public",
|
@@ -29,22 +29,27 @@ LOCAL_SITE = LocalContentLocation.new(UPLOAD_DIR,
|
|
29
29
|
Digest::SHA256,
|
30
30
|
File.join(SYNQA_DIR, "localContent.txt"))
|
31
31
|
|
32
|
+
# Ensure that directory for cached content files exists
|
32
33
|
task :init do |t|
|
33
34
|
ensureDirectoryExists(SYNQA_DIR)
|
34
35
|
end
|
35
36
|
|
37
|
+
# Delete the cached content files
|
36
38
|
task :clean => [:init] do |t|
|
37
39
|
SyncOperation.new(LOCAL_SITE, REMOTE_SITE).clearCachedContentFiles()
|
38
40
|
end
|
39
41
|
|
42
|
+
# List the files and directories in the remote directory
|
40
43
|
task :list do |t|
|
41
44
|
REMOTE_SITE.listFiles()
|
42
45
|
end
|
43
|
-
|
46
|
+
|
47
|
+
# Dry run for uploading (i.e. syncing) files to remote site
|
44
48
|
task :uploaddry => [:init] do |t|
|
45
49
|
SyncOperation.new(LOCAL_SITE, REMOTE_SITE).doSync(:dryRun => true)
|
46
50
|
end
|
47
51
|
|
52
|
+
# Upload (i.e. sync) files to remote site
|
48
53
|
task :upload => [:init] do |t|
|
49
54
|
SyncOperation.new(LOCAL_SITE, REMOTE_SITE).doSync()
|
50
55
|
end
|
data/examples/synqa-useage.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Sample code for synqa useage -- you will need to fill in your own details
|
2
2
|
|
3
3
|
require 'synqa.rb'
|
4
|
-
require 'based
|
4
|
+
require 'based'
|
5
5
|
require 'digest/sha2'
|
6
6
|
|
7
7
|
STDOUT.sync = true
|
@@ -10,12 +10,18 @@ include Synqa
|
|
10
10
|
sha256Sum = Sha256SumCommand.new() # sha256sum (with 2 characters between hash and file name)
|
11
11
|
sha256 = Sha256Command.new() # sha256 -r (with 1 space between hash and file name)
|
12
12
|
|
13
|
+
# Default is use Ruby net-ssh & net-scp gems, the following line defines external SSH/SCP applications
|
14
|
+
# plinkAndPscp = ExternalSshScp.new("plink", "pscp")
|
15
|
+
|
13
16
|
localContentLocation = LocalContentLocation.new(Based::BaseDirectory.new("c:/dev/src/project"),
|
14
17
|
Digest::SHA256,
|
15
18
|
"c:/temp/synqa/local.project.content.cache.txt")
|
19
|
+
# Default uses Ruby net-ssh & net-scp gems
|
20
|
+
remoteHost = SshContentHost.new("username@host.example.com", sha256)
|
21
|
+
|
22
|
+
# Alternative uses plink & pscp
|
23
|
+
#remoteHost = SshContentHost.new("username@host.example.com", sha256, ExternalSshScp)
|
16
24
|
|
17
|
-
remoteHost = SshContentHost.new("username@host.example.com",
|
18
|
-
sha256, "plink", "pscp")
|
19
25
|
|
20
26
|
# Note: the specification of plink & pscp assumes that keys are managed with Pageant, and therefore
|
21
27
|
# do not need to be specified on the command line.
|
File without changes
|
data/lib/synqa.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'time'
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'net/scp'
|
2
4
|
|
3
5
|
module Synqa
|
4
6
|
|
@@ -13,6 +15,12 @@ module Synqa
|
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
18
|
+
# Return the enumerated lines of the command's output
|
19
|
+
def getCommandOutput(command)
|
20
|
+
puts "#{command.inspect} ..."
|
21
|
+
return IO.popen(command)
|
22
|
+
end
|
23
|
+
|
16
24
|
# Check if the last executed process exited with status 0, if not, raise an exception
|
17
25
|
def checkProcessStatus(description)
|
18
26
|
processStatus = $?
|
@@ -161,12 +169,6 @@ module Synqa
|
|
161
169
|
return fileHashes
|
162
170
|
end
|
163
171
|
|
164
|
-
# Return the enumerated lines of the command's output
|
165
|
-
def getCommandOutput(command)
|
166
|
-
puts "#{command.inspect} ..."
|
167
|
-
return IO.popen(command)
|
168
|
-
end
|
169
|
-
|
170
172
|
# Construct the ContentTree for the given base directory
|
171
173
|
def getContentTree(baseDir)
|
172
174
|
contentTree = ContentTree.new()
|
@@ -181,64 +183,167 @@ module Synqa
|
|
181
183
|
end
|
182
184
|
end
|
183
185
|
|
184
|
-
#
|
185
|
-
|
186
|
+
# Execute a (local) command, or, if dryRun, just pretend to execute it.
|
187
|
+
# Raise an exception if the process exit status is not 0.
|
188
|
+
def executeCommand(command, dryRun)
|
189
|
+
puts "EXECUTE: #{command}"
|
190
|
+
if not dryRun
|
191
|
+
system(command)
|
192
|
+
checkProcessStatus(command)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Base SSH/SCP implementation
|
197
|
+
class BaseSshScp
|
186
198
|
|
199
|
+
# delete remote directory (if dryRun is false) using "rm -r"
|
200
|
+
def deleteDirectory(userAtHost, dirPath, dryRun)
|
201
|
+
ssh(userAtHost, "rm -r #{dirPath}", dryRun)
|
202
|
+
end
|
203
|
+
|
204
|
+
# delete remote file (if dryRun is false) using "rm"
|
205
|
+
def deleteFile(userAtHost, filePath, dryRun)
|
206
|
+
ssh(userAtHost, "rm #{filePath}", dryRun)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# SSH/SCP using Ruby Net::SSH & Net::SCP
|
211
|
+
class InternalSshScp<BaseSshScp
|
212
|
+
|
213
|
+
# execute command on remote host (if dryRun is false), yielding lines of output
|
214
|
+
def ssh(userAtHost, commandString, dryRun)
|
215
|
+
user, host = userAtHost.split("@")
|
216
|
+
description = "SSH #{user}@#{host}: executing #{commandString}"
|
217
|
+
puts description
|
218
|
+
if not dryRun
|
219
|
+
Net::SSH.start(host, user) do |ssh|
|
220
|
+
outputText = ssh.exec!(commandString)
|
221
|
+
if outputText != nil then
|
222
|
+
for line in outputText.split("\n") do
|
223
|
+
yield line
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# copy a local directory to a remote directory (if dryRun is false)
|
231
|
+
def copyLocalToRemoteDirectory(userAtHost, sourcePath, destinationPath, dryRun)
|
232
|
+
user, host = userAtHost.split("@")
|
233
|
+
description = "SCP: copy directory #{sourcePath} to #{user}@#{host}:#{destinationPath}"
|
234
|
+
puts description
|
235
|
+
if not dryRun
|
236
|
+
Net::SCP.upload!(host, user, sourcePath, destinationPath, :recursive => true)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# copy a local file to a remote directory (if dryRun is false)
|
241
|
+
def copyLocalFileToRemoteDirectory(userAtHost, sourcePath, destinationPath, dryRun)
|
242
|
+
user, host = userAtHost.split("@")
|
243
|
+
description = "SCP: copy file #{sourcePath} to #{user}@#{host}:#{destinationPath}"
|
244
|
+
puts description
|
245
|
+
if not dryRun
|
246
|
+
Net::SCP.upload!(host, user, sourcePath, destinationPath)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
# SSH/SCP using external commands, such as "plink" and "pscp"
|
253
|
+
class ExternalSshScp<BaseSshScp
|
187
254
|
# The SSH client, e.g. ["ssh"] or ["plink","-pw","mysecretpassword"] (i.e. command + args as an array)
|
188
255
|
attr_reader :shell
|
189
256
|
|
190
257
|
# The SCP client, e.g. ["scp"] or ["pscp","-pw","mysecretpassword"] (i.e. command + args as an array)
|
191
258
|
attr_reader :scpProgram
|
192
|
-
|
193
|
-
# The remote host, e.g. "username@host.example.com"
|
194
|
-
attr_reader :host
|
195
|
-
|
259
|
+
|
196
260
|
# The SCP command as a string
|
197
261
|
attr_reader :scpCommandString
|
198
|
-
|
199
|
-
def initialize(
|
200
|
-
super(hashCommand)
|
201
|
-
@host = host
|
262
|
+
|
263
|
+
def initialize(shell, scpProgram)
|
202
264
|
@shell = shell.is_a?(String) ? [shell] : shell
|
203
265
|
@scpProgram = scpProgram.is_a?(String) ? [scpProgram] : scpProgram
|
204
266
|
@scpCommandString = @scpProgram.join(" ")
|
205
267
|
end
|
206
268
|
|
207
|
-
#
|
208
|
-
def
|
209
|
-
|
210
|
-
return "#{host}:#{baseDir} (connect = #{shell}/#{scpProgram}, hashCommand = #{hashCommand})"
|
211
|
-
end
|
212
|
-
|
213
|
-
# execute an SSH command on the remote system, yielding lines of output
|
214
|
-
# (or don't actually execute, if dryRun is true)
|
215
|
-
def executeRemoteCommand(commandString, dryRun = false)
|
216
|
-
puts "SSH #{host} (#{shell.join(" ")}): executing #{commandString}"
|
269
|
+
# execute command on remote host (if dryRun is false), yielding lines of output
|
270
|
+
def ssh(userAtHost, commandString, dryRun)
|
271
|
+
puts "SSH #{userAtHost} (#{shell.join(" ")}): executing #{commandString}"
|
217
272
|
if not dryRun
|
218
|
-
output = getCommandOutput(shell + [
|
273
|
+
output = getCommandOutput(shell + [userAtHost, commandString])
|
219
274
|
while (line = output.gets)
|
220
275
|
yield line.chomp
|
221
276
|
end
|
222
277
|
output.close()
|
223
|
-
checkProcessStatus("SSH #{
|
278
|
+
checkProcessStatus("SSH #{userAtHost} #{commandString}")
|
224
279
|
end
|
225
280
|
end
|
226
281
|
|
227
|
-
#
|
228
|
-
|
282
|
+
# copy a local directory to a remote directory (if dryRun is false)
|
283
|
+
def copyLocalToRemoteDirectory(userAtHost, sourcePath, destinationPath, dryRun)
|
284
|
+
executeCommand("#{@scpCommandString} -r #{sourcePath} #{userAtHost}:#{destinationPath}", dryRun)
|
285
|
+
end
|
286
|
+
|
287
|
+
# copy a local file to a remote directory (if dryRun is false)
|
288
|
+
def copyLocalFileToRemoteDirectory(userAtHost, sourcePath, destinationPath, dryRun)
|
289
|
+
executeCommand("#{@scpCommandString} #{sourcePath} #{userAtHost}:#{destinationPath}", dryRun)
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
293
|
+
|
294
|
+
# Representation of a remote system accessible via SSH
|
295
|
+
class SshContentHost<DirContentHost
|
296
|
+
|
297
|
+
# The remote userAtHost, e.g. "username@host.example.com"
|
298
|
+
attr_reader :userAtHost, :sshAndScp
|
299
|
+
|
300
|
+
def initialize(userAtHost, hashCommand, sshAndScp = nil)
|
301
|
+
super(hashCommand)
|
302
|
+
@sshAndScp = sshAndScp != nil ? sshAndScp : InternalSshScp.new()
|
303
|
+
@userAtHost = userAtHost
|
304
|
+
end
|
305
|
+
|
306
|
+
# Return readable description of base directory on remote system
|
307
|
+
def locationDescriptor(baseDir)
|
308
|
+
baseDir = normalisedDir(baseDir)
|
309
|
+
return "#{userAtHost}:#{baseDir} (connect = #{shell}/#{scpProgram}, hashCommand = #{hashCommand})"
|
310
|
+
end
|
311
|
+
|
312
|
+
# execute an SSH command on the remote system, yielding lines of output
|
313
|
+
# (or don't actually execute, if dryRun is false)
|
229
314
|
def ssh(commandString, dryRun = false)
|
230
|
-
|
231
|
-
|
315
|
+
sshAndScp.ssh(userAtHost, commandString, dryRun) do |line|
|
316
|
+
yield line
|
232
317
|
end
|
233
318
|
end
|
234
319
|
|
320
|
+
# delete a remote directory, if dryRun is false
|
321
|
+
def deleteDirectory(dirPath, dryRun)
|
322
|
+
sshAndScp.deleteDirectory(userAtHost, dirPath, dryRun)
|
323
|
+
end
|
324
|
+
|
325
|
+
# delete a remote file, if dryRun is false
|
326
|
+
def deleteFile(filePath, dryRun)
|
327
|
+
sshAndScp.deleteFile(userAtHost, filePath, dryRun)
|
328
|
+
end
|
329
|
+
|
330
|
+
# copy a local directory to a remote directory, if dryRun is false
|
331
|
+
def copyLocalToRemoteDirectory(sourcePath, destinationPath, dryRun)
|
332
|
+
sshAndScp.copyLocalToRemoteDirectory(userAtHost, sourcePath, destinationPath, dryRun)
|
333
|
+
end
|
334
|
+
|
335
|
+
# copy a local file to a remote directory, if dryRun is false
|
336
|
+
def copyLocalFileToRemoteDirectory(sourcePath, destinationPath, dryRun)
|
337
|
+
sshAndScp.copyLocalFileToRemoteDirectory(userAtHost, sourcePath, destinationPath, dryRun)
|
338
|
+
end
|
339
|
+
|
235
340
|
# Return a list of all subdirectories of the base directory (as paths relative to the base directory)
|
236
341
|
def listDirectories(baseDir)
|
237
342
|
baseDir = normalisedDir(baseDir)
|
238
343
|
puts "Listing directories ..."
|
239
344
|
directories = []
|
240
345
|
baseDirLen = baseDir.length
|
241
|
-
|
346
|
+
ssh(findDirectoriesCommand(baseDir).join(" ")) do |line|
|
242
347
|
puts " #{line}"
|
243
348
|
if line.start_with?(baseDir)
|
244
349
|
directories << line[baseDirLen..-1]
|
@@ -254,7 +359,7 @@ module Synqa
|
|
254
359
|
def listFileHashLines(baseDir)
|
255
360
|
baseDir = normalisedDir(baseDir)
|
256
361
|
remoteFileHashLinesCommand = findFilesCommand(baseDir) + ["|", "xargs", "-r"] + @hashCommand.command
|
257
|
-
|
362
|
+
ssh(remoteFileHashLinesCommand.join(" ")) do |line|
|
258
363
|
puts " #{line}"
|
259
364
|
yield line
|
260
365
|
end
|
@@ -263,15 +368,11 @@ module Synqa
|
|
263
368
|
# List all files within the base directory to stdout
|
264
369
|
def listFiles(baseDir)
|
265
370
|
baseDir = normalisedDir(baseDir)
|
266
|
-
|
371
|
+
ssh(findFilesCommand(baseDir).join(" ")) do |line|
|
267
372
|
puts " #{line}"
|
268
373
|
end
|
269
374
|
end
|
270
375
|
|
271
|
-
# Get the remote path of the directory or file on the host, in the format required by SCP
|
272
|
-
def getScpPath(path)
|
273
|
-
return host + ":" + path
|
274
|
-
end
|
275
376
|
end
|
276
377
|
|
277
378
|
# An object representing the content of a file within a ContentTree.
|
@@ -315,8 +416,8 @@ module Synqa
|
|
315
416
|
return "#{name} (#{hash})"
|
316
417
|
end
|
317
418
|
|
318
|
-
# The
|
319
|
-
def
|
419
|
+
# The relative name of this file in the content tree (relative to the base dir)
|
420
|
+
def relativePath
|
320
421
|
return (parentPathElements + [name]).join("/")
|
321
422
|
end
|
322
423
|
end
|
@@ -376,8 +477,8 @@ module Synqa
|
|
376
477
|
@toBeDeleted = true
|
377
478
|
end
|
378
479
|
|
379
|
-
# the
|
380
|
-
def
|
480
|
+
# the path of the directory that this content tree represents, relative to the base directory
|
481
|
+
def relativePath
|
381
482
|
return @pathElements.join("/")
|
382
483
|
end
|
383
484
|
|
@@ -446,7 +547,7 @@ module Synqa
|
|
446
547
|
puts "#{currentIndent}#{name}"
|
447
548
|
end
|
448
549
|
if copyDestination != nil
|
449
|
-
puts "#{currentIndent} [COPY to #{copyDestination.
|
550
|
+
puts "#{currentIndent} [COPY to #{copyDestination.relativePath}]"
|
450
551
|
end
|
451
552
|
if toBeDeleted
|
452
553
|
puts "#{currentIndent} [DELETE]"
|
@@ -458,7 +559,7 @@ module Synqa
|
|
458
559
|
for file in files
|
459
560
|
puts "#{nextIndent}#{file.name} - #{file.hash}"
|
460
561
|
if file.copyDestination != nil
|
461
|
-
puts "#{nextIndent} [COPY to #{file.copyDestination.
|
562
|
+
puts "#{nextIndent} [COPY to #{file.copyDestination.relativePath}]"
|
462
563
|
end
|
463
564
|
if file.toBeDeleted
|
464
565
|
puts "#{nextIndent} [DELETE]"
|
@@ -677,11 +778,6 @@ module Synqa
|
|
677
778
|
@hashClass = hashClass
|
678
779
|
end
|
679
780
|
|
680
|
-
# get the path as required for an SCP command
|
681
|
-
def getScpPath(relativePath)
|
682
|
-
return getFullPath(relativePath)
|
683
|
-
end
|
684
|
-
|
685
781
|
# get the full path of a relative path (i.e. of a file/directory within the base directory)
|
686
782
|
def getFullPath(relativePath)
|
687
783
|
return @baseDirectory.fullPath + relativePath
|
@@ -722,25 +818,25 @@ module Synqa
|
|
722
818
|
# A directory of files on a remote system
|
723
819
|
class RemoteContentLocation<ContentLocation
|
724
820
|
# the remote username@host value
|
725
|
-
attr_reader :
|
821
|
+
attr_reader :userAtHost
|
726
822
|
|
727
823
|
# the base directory on the remote system
|
728
824
|
attr_reader :baseDir
|
729
825
|
|
730
|
-
def initialize(
|
826
|
+
def initialize(userAtHost, baseDir, cachedContentFile = nil)
|
731
827
|
super(cachedContentFile)
|
732
|
-
@
|
828
|
+
@userAtHost = userAtHost
|
733
829
|
@baseDir = normalisedDir(baseDir)
|
734
830
|
end
|
735
831
|
|
736
|
-
# list files within the base directory on the remote
|
832
|
+
# list files within the base directory on the remote userAtHost
|
737
833
|
def listFiles()
|
738
|
-
|
834
|
+
userAtHost.listFiles(baseDir)
|
739
835
|
end
|
740
836
|
|
741
|
-
#
|
742
|
-
def
|
743
|
-
return
|
837
|
+
# object required to execute SCP (e.g. "scp" or "pscp", possibly with extra args)
|
838
|
+
def sshAndScp
|
839
|
+
return userAtHost.sshAndScp
|
744
840
|
end
|
745
841
|
|
746
842
|
# get the full path of a relative path
|
@@ -748,28 +844,23 @@ module Synqa
|
|
748
844
|
return baseDir + relativePath
|
749
845
|
end
|
750
846
|
|
751
|
-
# get the full path of a file as required in an SCP command (i.e. with username@host prepended)
|
752
|
-
def getScpPath(relativePath)
|
753
|
-
return host.getScpPath(getFullPath(relativePath))
|
754
|
-
end
|
755
|
-
|
756
847
|
# execute an SSH command on the remote host (or just pretend, if dryRun is true)
|
757
848
|
def ssh(commandString, dryRun = false)
|
758
|
-
|
849
|
+
userAtHost.sshAndScp.ssh(commandString, dryRun)
|
759
850
|
end
|
760
851
|
|
761
852
|
# list all sub-directories of the base directory on the remote host
|
762
853
|
def listDirectories
|
763
|
-
return
|
854
|
+
return userAtHost.listDirectories(baseDir)
|
764
855
|
end
|
765
856
|
|
766
857
|
# list all the file hashes of the files within the base directory
|
767
858
|
def listFileHashes
|
768
|
-
return
|
859
|
+
return userAtHost.listFileHashes(baseDir)
|
769
860
|
end
|
770
861
|
|
771
862
|
def to_s
|
772
|
-
return
|
863
|
+
return userAtHost.locationDescriptor(baseDir)
|
773
864
|
end
|
774
865
|
|
775
866
|
# Get the content tree, from the cached content file if it exists,
|
@@ -780,7 +871,7 @@ module Synqa
|
|
780
871
|
if cachedContentFile and File.exists?(cachedContentFile)
|
781
872
|
return ContentTree.readFromFile(cachedContentFile)
|
782
873
|
else
|
783
|
-
contentTree =
|
874
|
+
contentTree = userAtHost.getContentTree(baseDir)
|
784
875
|
contentTree.sort!
|
785
876
|
if cachedContentFile != nil
|
786
877
|
contentTree.writeToFile(cachedContentFile)
|
@@ -845,7 +936,7 @@ module Synqa
|
|
845
936
|
end
|
846
937
|
doAllCopyOperations(dryRun)
|
847
938
|
doAllDeleteOperations(dryRun)
|
848
|
-
if (@destinationLocation.cachedContentFile and @sourceLocation.cachedContentFile and
|
939
|
+
if (not dryRun and @destinationLocation.cachedContentFile and @sourceLocation.cachedContentFile and
|
849
940
|
File.exists?(@sourceLocation.cachedContentFile))
|
850
941
|
FileUtils::Verbose.cp(@sourceLocation.cachedContentFile, @destinationLocation.cachedContentFile)
|
851
942
|
end
|
@@ -876,18 +967,18 @@ module Synqa
|
|
876
967
|
def doCopyOperations(sourceContent, destinationContent, dryRun)
|
877
968
|
for dir in sourceContent.dirs
|
878
969
|
if dir.copyDestination != nil
|
879
|
-
sourcePath = sourceLocation.
|
880
|
-
destinationPath = destinationLocation.
|
881
|
-
|
970
|
+
sourcePath = sourceLocation.getFullPath(dir.relativePath)
|
971
|
+
destinationPath = destinationLocation.getFullPath(dir.copyDestination.relativePath)
|
972
|
+
destinationLocation.userAtHost.copyLocalToRemoteDirectory(sourcePath, destinationPath, dryRun)
|
882
973
|
else
|
883
974
|
doCopyOperations(dir, destinationContent.getDir(dir.name), dryRun)
|
884
975
|
end
|
885
976
|
end
|
886
977
|
for file in sourceContent.files
|
887
978
|
if file.copyDestination != nil
|
888
|
-
sourcePath = sourceLocation.
|
889
|
-
destinationPath = destinationLocation.
|
890
|
-
|
979
|
+
sourcePath = sourceLocation.getFullPath(file.relativePath)
|
980
|
+
destinationPath = destinationLocation.getFullPath(file.copyDestination.relativePath)
|
981
|
+
destinationLocation.userAtHost.copyLocalFileToRemoteDirectory(sourcePath, destinationPath, dryRun)
|
891
982
|
end
|
892
983
|
end
|
893
984
|
end
|
@@ -897,16 +988,16 @@ module Synqa
|
|
897
988
|
def doDeleteOperations(destinationContent, dryRun)
|
898
989
|
for dir in destinationContent.dirs
|
899
990
|
if dir.toBeDeleted
|
900
|
-
dirPath = destinationLocation.getFullPath(dir.
|
901
|
-
destinationLocation.
|
991
|
+
dirPath = destinationLocation.getFullPath(dir.relativePath)
|
992
|
+
destinationLocation.userAtHost.deleteDirectory(dirPath, dryRun)
|
902
993
|
else
|
903
994
|
doDeleteOperations(dir, dryRun)
|
904
995
|
end
|
905
996
|
end
|
906
997
|
for file in destinationContent.files
|
907
998
|
if file.toBeDeleted
|
908
|
-
filePath = destinationLocation.getFullPath(file.
|
909
|
-
destinationLocation.
|
999
|
+
filePath = destinationLocation.getFullPath(file.relativePath)
|
1000
|
+
destinationLocation.userAtHost.deleteFile(filePath, dryRun)
|
910
1001
|
end
|
911
1002
|
end
|
912
1003
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synqa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,12 +9,34 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-04-19 00:00:00.000000000 +12:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: net-ssh
|
17
|
+
requirement: &25189860 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *25189860
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: net-scp
|
28
|
+
requirement: &25189176 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *25189176
|
15
37
|
- !ruby/object:Gem::Dependency
|
16
38
|
name: shoulda
|
17
|
-
requirement: &
|
39
|
+
requirement: &25188444 !ruby/object:Gem::Requirement
|
18
40
|
none: false
|
19
41
|
requirements:
|
20
42
|
- - ! '>='
|
@@ -22,10 +44,10 @@ dependencies:
|
|
22
44
|
version: '0'
|
23
45
|
type: :development
|
24
46
|
prerelease: false
|
25
|
-
version_requirements: *
|
47
|
+
version_requirements: *25188444
|
26
48
|
- !ruby/object:Gem::Dependency
|
27
49
|
name: bundler
|
28
|
-
requirement: &
|
50
|
+
requirement: &25187796 !ruby/object:Gem::Requirement
|
29
51
|
none: false
|
30
52
|
requirements:
|
31
53
|
- - ~>
|
@@ -33,10 +55,10 @@ dependencies:
|
|
33
55
|
version: 1.0.0
|
34
56
|
type: :development
|
35
57
|
prerelease: false
|
36
|
-
version_requirements: *
|
58
|
+
version_requirements: *25187796
|
37
59
|
- !ruby/object:Gem::Dependency
|
38
60
|
name: jeweler
|
39
|
-
requirement: &
|
61
|
+
requirement: &25180356 !ruby/object:Gem::Requirement
|
40
62
|
none: false
|
41
63
|
requirements:
|
42
64
|
- - ~>
|
@@ -44,10 +66,10 @@ dependencies:
|
|
44
66
|
version: 1.5.2
|
45
67
|
type: :development
|
46
68
|
prerelease: false
|
47
|
-
version_requirements: *
|
69
|
+
version_requirements: *25180356
|
48
70
|
- !ruby/object:Gem::Dependency
|
49
71
|
name: rcov
|
50
|
-
requirement: &
|
72
|
+
requirement: &25179864 !ruby/object:Gem::Requirement
|
51
73
|
none: false
|
52
74
|
requirements:
|
53
75
|
- - ! '>='
|
@@ -55,7 +77,7 @@ dependencies:
|
|
55
77
|
version: '0'
|
56
78
|
type: :development
|
57
79
|
prerelease: false
|
58
|
-
version_requirements: *
|
80
|
+
version_requirements: *25179864
|
59
81
|
description: Sync files from a local directory to a remote directory via SSH/SCP
|
60
82
|
email: http://www.1729.com/email.html
|
61
83
|
executables: []
|
@@ -74,7 +96,7 @@ files:
|
|
74
96
|
- _project.el
|
75
97
|
- examples/sample-rakefile
|
76
98
|
- examples/synqa-useage.rb
|
77
|
-
- lib/based
|
99
|
+
- lib/based.rb
|
78
100
|
- lib/synqa.rb
|
79
101
|
- test/data/dir1/dir2/dir4/dir5/file6.text
|
80
102
|
- test/data/dir1/dir2/dir4/file5.text
|
@@ -83,7 +105,7 @@ files:
|
|
83
105
|
- test/data/dir1/file1.txt
|
84
106
|
- test/data/dir1/file2.txt
|
85
107
|
- test/helper.rb
|
86
|
-
- test/
|
108
|
+
- test/test_based.rb
|
87
109
|
- test/test_synqa.rb
|
88
110
|
has_rdoc: true
|
89
111
|
homepage: http://www.1729.com/software/synqa/
|
@@ -101,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
101
123
|
version: '0'
|
102
124
|
segments:
|
103
125
|
- 0
|
104
|
-
hash:
|
126
|
+
hash: 874582639
|
105
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
128
|
none: false
|
107
129
|
requirements:
|
@@ -117,5 +139,5 @@ summary: Sync files from a local directory to a remote directory via SSH/SCP
|
|
117
139
|
test_files:
|
118
140
|
- examples/synqa-useage.rb
|
119
141
|
- test/helper.rb
|
120
|
-
- test/
|
142
|
+
- test/test_based.rb
|
121
143
|
- test/test_synqa.rb
|