synqa 0.1.0 → 0.2.0
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.
- 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
|