bdsync 0.1.1 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb831167bdaa2f2ee0f225dfcb6b709edc4bd74a7509b63066aed2dda30dd324
4
- data.tar.gz: ad06ed3f22fa86fc4fd43528a83cc52912506ee39d2e7f9a85d5d89a68385b65
3
+ metadata.gz: 692f6cf3fe115aa7894fb65d6549438dede5ec6737290da158a10290c3a5f02a
4
+ data.tar.gz: 5a3ca8c23c7f7e35f18fee6789f1e888d94f1cce86bf9adfafc39ab14fa2e6a8
5
5
  SHA512:
6
- metadata.gz: 15a98b305ade38185b517b1ec460f20b992452643a2cf5e9f1526e198b5ae46878abb89a6a163ab25ad9b3ff6fec6faa4cdca9835a51f120f09975ac1b01708f
7
- data.tar.gz: 9e8214682a8aa435abcf859deea30f5df5f0f5050214ecd8bbf3d5b58530064bffceaf93b59f3b64e226e75a70b5f91667e3b1aae595caec92ecb0b85ea51136
6
+ metadata.gz: a24223fd0e493e73af666a2f6427f4e8f822383ca3b30c25f54406fc8e265a1db8bfc6c7ccb0469e82599bc111a2b6f1b5fed47fc5f549398c2ad9a1aacbfe6a
7
+ data.tar.gz: e4e999f5a136ad3221ea3c4cd096c0adb88fbcbe7bb2fa905cdc634af13abad1c52d0536c5a331921b109cbbee87adaafcb90181905671c77edd375b796d4191
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bdsync (0.1.1)
5
+ colorize (~> 0.8)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ colorize (0.8.1)
11
+ diff-lcs (1.3)
12
+ rake (12.3.3)
13
+ rspec (3.9.0)
14
+ rspec-core (~> 3.9.0)
15
+ rspec-expectations (~> 3.9.0)
16
+ rspec-mocks (~> 3.9.0)
17
+ rspec-core (3.9.0)
18
+ rspec-support (~> 3.9.0)
19
+ rspec-expectations (3.9.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.9.0)
22
+ rspec-mocks (3.9.0)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.9.0)
25
+ rspec-support (3.9.0)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ bdsync!
32
+ rake (~> 12.0)
33
+ rspec (~> 3.0)
34
+
35
+ BUNDLED WITH
36
+ 2.1.2
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Bsync
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/bsync`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Bidirectional Synchronization tool for sftp or local file system
6
4
 
7
5
  ## Installation
8
6
 
data/bsync.gemspec CHANGED
@@ -24,4 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.bindir = "exe"
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
+
28
+ spec.add_runtime_dependency "colorize", "~> 0.8"
27
29
  end
data/lib/bsync.rb CHANGED
@@ -1,6 +1,8 @@
1
+ require "bsync/lfs"
2
+ require "bsync/sftp"
1
3
  require "bsync/version"
2
4
 
3
5
  module Bsync
4
- class Error < StandardError; end
5
- # Your code goes here...
6
+ class Error < StandardError; end
7
+ # Your code goes here...
6
8
  end
data/lib/bsync/core.rb ADDED
@@ -0,0 +1,429 @@
1
+ require "bsync/utils"
2
+ require "colorize"
3
+ require "yaml"
4
+
5
+ module Bsync
6
+ class Core
7
+ attr_reader :data_path
8
+
9
+ def initialize params, sync_type
10
+ raise "local path not specified" if !params["local_root_path"]
11
+ raise "remote path not specified" if !params["remote_root_path"]
12
+
13
+ @local_root_path = params["local_root_path"]
14
+ @remote_root_path = params["remote_root_path"]
15
+ @infinite_loop = params["infinite_loop"]
16
+ @data_path = "#{Dir.home}/.bsync/#{sync_type}_#{Utils.md5 params.to_s}.yaml"
17
+ end
18
+
19
+ def self.options
20
+ ["local_root_path:", "remote_root_path:", "infinite_loop"]
21
+ end
22
+
23
+ def synchronize
24
+ # Run only one instance of a Ruby program at the same time - self locking
25
+ # SEE: https://code-maven.com/run-only-one-instance-of-a-script
26
+ exit if !Utils.try_lock
27
+
28
+ loop {
29
+ @old_data = load_data
30
+ @data = {}
31
+
32
+ start_session {
33
+ remote_ensure_dir @remote_root_path
34
+ local_ensure_dir @local_root_path
35
+
36
+ puts "\n==== traverse_remote_path ===="
37
+ traverse_remote_path @remote_root_path
38
+
39
+ # merge @data to @old_data, and clear @data
40
+ @old_data.merge! @data
41
+ @data = {}
42
+
43
+ puts "\n==== traverse_local_path ===="
44
+ traverse_local_path @local_root_path
45
+ }
46
+
47
+ save_data @data
48
+
49
+ break if !@infinite_loop
50
+
51
+ sleep 1
52
+ }
53
+ end
54
+
55
+ def start_session &block
56
+ fail NotImplementedError, "A subclass class must be able to #{__method__}!"
57
+ end
58
+
59
+ def load_data
60
+ puts "\nload #{@data_path}"
61
+ YAML.load_file @data_path
62
+ rescue Errno::ENOENT
63
+ {}
64
+ end
65
+
66
+ def save_data data
67
+ local_ensure_parent @data_path
68
+ File.write @data_path, data.to_yaml
69
+ puts "\nsaved to #{@data_path}"
70
+ end
71
+
72
+ def traverse_remote_path remote_path
73
+ next_level_dirs = []
74
+
75
+ remote_dir_foreach(remote_path) { |entry|
76
+ next if [".", "..", ".conflict"].include? entry.name
77
+
78
+ path = "#{remote_path}/#{entry.name}"
79
+ remote = entry.attributes
80
+
81
+ if remote.directory?
82
+ next_level_dirs << [path, remote]
83
+ else
84
+ handle_remote_entry remote, path, :file
85
+ end
86
+ }
87
+
88
+ next_level_dirs.sort.each { |path, remote|
89
+ handle_remote_entry remote, path, :directory
90
+ traverse_remote_path path
91
+ }
92
+ end
93
+
94
+ def traverse_local_path local_path
95
+ next_level_dirs = []
96
+
97
+ Dir.foreach(local_path) { |entry_name|
98
+ next if [".", "..", ".conflict"].include? entry_name
99
+
100
+ path = "#{local_path}/#{entry_name}"
101
+
102
+ if File.directory? path
103
+ next_level_dirs << path
104
+ else
105
+ handle_local_entry path, :file
106
+ end
107
+ }
108
+
109
+ next_level_dirs.sort.each { |path|
110
+ handle_local_entry path, :directory
111
+ traverse_local_path path
112
+ }
113
+ end
114
+
115
+ def handle_remote_entry remote, path, type
116
+ puts "#{'%9s' % type}: #{path}".green
117
+
118
+ # NOTE: force_encoding to output readable japanese text
119
+ relative_path = path.sub(@remote_root_path + "/", "").force_encoding("UTF-8")
120
+
121
+ local_path = "#{@local_root_path}/#{relative_path}"
122
+ remote_path = "#{@remote_root_path}/#{relative_path}"
123
+
124
+ old = @old_data[relative_path]
125
+
126
+ if !old # no old sync record
127
+ do_first_time_sync_from_remote relative_path, local_path, remote_path, remote, type
128
+ else # old sync record found
129
+ do_sync_from_remote relative_path, local_path, remote_path, remote, type, old
130
+ end
131
+ end
132
+
133
+ def handle_local_entry path, type
134
+ puts "#{'%9s' % type}: #{path}".green
135
+
136
+ # NOTE: force_encoding to output readable japanese text
137
+ relative_path = path.sub(@local_root_path + "/", "").force_encoding("UTF-8")
138
+
139
+ local_path = "#{@local_root_path}/#{relative_path}"
140
+ remote_path = "#{@remote_root_path}/#{relative_path}"
141
+
142
+ remote = remote_get_object remote_path
143
+
144
+ old = @old_data[relative_path]
145
+
146
+ if !old # no old sync record
147
+ do_first_time_sync_from_local relative_path, local_path, remote_path, remote, type
148
+ else # old sync record found
149
+ do_sync_from_local relative_path, local_path, remote_path, remote, type, old
150
+ end
151
+ end
152
+
153
+ def do_first_time_sync_from_remote relative_path, local_path, remote_path, remote, type
154
+ case type
155
+ when :file
156
+ if !File.exist? local_path
157
+ download_file local_path, remote_path
158
+ update_file_data relative_path, local_path, remote.mtime
159
+ elsif File.directory? local_path
160
+ handle_local_conflict local_path
161
+ download_file local_path, remote_path
162
+ update_file_data relative_path, local_path, remote.mtime
163
+ else
164
+ handle_local_conflict local_path
165
+ download_file local_path, remote_path
166
+ update_file_data relative_path, local_path, remote.mtime
167
+ end
168
+ when :directory
169
+ if !File.exist? local_path
170
+ local_mkdir local_path
171
+ update_directory_data relative_path, local_path, remote.mtime
172
+ elsif File.directory? local_path
173
+ update_directory_data relative_path, local_path, remote.mtime
174
+ else
175
+ handle_local_conflict local_path
176
+ local_mkdir local_path
177
+ update_directory_data relative_path, local_path, remote.mtime
178
+ end
179
+ end
180
+ end
181
+
182
+ def do_first_time_sync_from_local relative_path, local_path, remote_path, remote, type
183
+ case type
184
+ when :file
185
+ if !remote
186
+ remote = upload_file local_path, remote_path
187
+ update_file_data relative_path, local_path, remote.mtime
188
+ elsif remote.directory?
189
+ handle_remote_conflict remote_path
190
+ remote = upload_file local_path, remote_path
191
+ update_file_data relative_path, local_path, remote.mtime
192
+ else
193
+ handle_remote_conflict remote_path
194
+ remote = upload_file local_path, remote_path
195
+ update_file_data relative_path, local_path, remote.mtime
196
+ end
197
+ when :directory
198
+ if !remote
199
+ remote = remote_mkdir remote_path
200
+ update_directory_data relative_path, local_path, remote.mtime
201
+ elsif remote.directory?
202
+ update_directory_data relative_path, local_path, remote.mtime
203
+ else
204
+ handle_remote_conflict remote_path
205
+ remote = remote_mkdir remote_path
206
+ update_directory_data relative_path, local_path, remote.mtime
207
+ end
208
+ end
209
+ end
210
+
211
+ def do_sync_from_remote relative_path, local_path, remote_path, remote, type, old
212
+ remote_changed = (remote.mtime - old[:remote_mtime]) != 0
213
+
214
+ case type
215
+ when :file
216
+ if !File.exist? local_path
217
+ if !remote_changed
218
+ remote_remove_file remote_path
219
+ else
220
+ handle_remote_conflict remote_path
221
+ end
222
+ elsif File.directory? local_path
223
+ if !remote_changed
224
+ remote_remove_file remote_path
225
+ remote = remote_mkdir remote_path
226
+ update_directory_data relative_path, local_path, remote.mtime
227
+ else
228
+ if File.mtime(local_path).to_i > remote.mtime
229
+ handle_remote_conflict remote_path
230
+ remote = remote_mkdir remote_path
231
+ update_directory_data relative_path, local_path, remote.mtime
232
+ else
233
+ handle_local_conflict local_path
234
+ download_file local_path, remote_path
235
+ update_file_data relative_path, local_path, remote.mtime
236
+ end
237
+ end
238
+ else
239
+ local_changed = (File.mtime(local_path).to_i - old[:local_mtime]) != 0
240
+
241
+ if !local_changed && !remote_changed
242
+ @data[relative_path] = old
243
+ elsif local_changed && !remote_changed
244
+ remote = upload_file local_path, remote_path
245
+ update_file_data relative_path, local_path, remote.mtime
246
+ elsif !local_changed && remote_changed
247
+ download_file local_path, remote_path
248
+ update_file_data relative_path, local_path, remote.mtime
249
+ else
250
+ if File.mtime(local_path).to_i > remote.mtime
251
+ handle_remote_conflict remote_path
252
+ remote = upload_file local_path, remote_path
253
+ update_file_data relative_path, local_path, remote.mtime
254
+ else
255
+ handle_local_conflict local_path
256
+ download_file local_path, remote_path
257
+ update_file_data relative_path, local_path, remote.mtime
258
+ end
259
+ end
260
+ end
261
+ when :directory
262
+ if !File.exist? local_path
263
+ if !remote_changed
264
+ remote_remove_dir remote_path
265
+ else
266
+ handle_remote_conflict remote_path
267
+ end
268
+ elsif File.directory? local_path
269
+ update_directory_data relative_path, local_path, remote.mtime
270
+ else
271
+ if !remote_changed
272
+ remote_remove_dir remote_path
273
+ remote = upload_file local_path, remote_path
274
+ update_file_data relative_path, local_path, remote.mtime
275
+ else
276
+ if File.mtime(local_path).to_i > remote.mtime
277
+ handle_remote_conflict remote_path
278
+ remote = upload_file local_path, remote_path
279
+ update_file_data relative_path, local_path, remote.mtime
280
+ else
281
+ handle_local_conflict local_path
282
+ local_mkdir local_path
283
+ update_directory_data relative_path, local_path, remote.mtime
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+
290
+ def do_sync_from_local relative_path, local_path, remote_path, remote, type, old
291
+ local_changed = (File.mtime(local_path).to_i - old[:local_mtime]) != 0
292
+
293
+ case type
294
+ when :file
295
+ if !remote
296
+ if !local_changed
297
+ local_remove_file local_path
298
+ else
299
+ handle_local_conflict local_path
300
+ end
301
+ elsif remote.directory?
302
+ if !local_changed
303
+ local_remove_file local_path
304
+ local_mkdir local_path
305
+ update_directory_data relative_path, local_path, remote.mtime
306
+ else
307
+ if File.mtime(local_path).to_i > remote.mtime
308
+ handle_remote_conflict remote_path
309
+ remote = upload_file local_path, remote_path
310
+ update_file_data relative_path, local_path, remote.mtime
311
+ else
312
+ handle_local_conflict local_path
313
+ local_mkdir local_path
314
+ update_directory_data relative_path, local_path, remote.mtime
315
+ end
316
+ end
317
+ else
318
+ remote_changed = (remote.mtime - old[:remote_mtime]) != 0
319
+
320
+ if !local_changed && !remote_changed
321
+ @data[relative_path] = old
322
+ elsif local_changed && !remote_changed
323
+ remote = upload_file local_path, remote_path
324
+ update_file_data relative_path, local_path, remote.mtime
325
+ elsif !local_changed && remote_changed
326
+ download_file local_path, remote_path
327
+ update_file_data relative_path, local_path, remote.mtime
328
+ else
329
+ if File.mtime(local_path).to_i > remote.mtime
330
+ handle_remote_conflict remote_path
331
+ remote = upload_file local_path, remote_path
332
+ update_file_data relative_path, local_path, remote.mtime
333
+ else
334
+ handle_local_conflict local_path
335
+ download_file local_path, remote_path
336
+ update_file_data relative_path, local_path, remote.mtime
337
+ end
338
+ end
339
+ end
340
+ when :directory
341
+ if !remote
342
+ if !local_changed
343
+ local_remove_directory local_path
344
+ else
345
+ handle_local_conflict local_path
346
+ end
347
+ elsif remote.directory?
348
+ update_directory_data relative_path, local_path, remote.mtime
349
+ else
350
+ if !local_changed
351
+ local_remove_directory local_path
352
+ download_file local_path, remote_path
353
+ update_file_data relative_path, local_path, remote.mtime
354
+ else
355
+ if File.mtime(local_path).to_i > remote.mtime
356
+ handle_remote_conflict remote_path
357
+ remote = remote_mkdir remote_path
358
+ update_directory_data relative_path, local_path, remote.mtime
359
+ else
360
+ handle_local_conflict local_path
361
+ download_file local_path, remote_path
362
+ update_file_data relative_path, local_path, remote.mtime
363
+ end
364
+ end
365
+ end
366
+ end
367
+ end
368
+
369
+ def update_file_data relative_path, local_path, remote_mtime
370
+ @data[relative_path] = {
371
+ type: :file,
372
+ remote_mtime: remote_mtime,
373
+ local_mtime: File.mtime(local_path).to_i
374
+ }
375
+ end
376
+
377
+ def update_directory_data relative_path, local_path, remote_mtime
378
+ @data[relative_path] = {
379
+ type: :directory,
380
+ remote_mtime: remote_mtime,
381
+ local_mtime: File.mtime(local_path).to_i
382
+ }
383
+ end
384
+
385
+ def handle_local_conflict local_path
386
+ ts = Utils.timestamp
387
+ local_conflict_path = local_path.sub(@local_root_path, "#{@local_root_path}/.conflict") + "." + ts
388
+
389
+ local_ensure_parent local_conflict_path
390
+ local_rename local_path, local_conflict_path
391
+ end
392
+
393
+ def handle_remote_conflict remote_path
394
+ ts = Utils.timestamp
395
+ remote_conflict_path = remote_path.sub(@remote_root_path, "#{@remote_root_path}/.conflict") + "." + ts
396
+
397
+ remote_ensure_parent remote_conflict_path
398
+ remote_rename remote_path, remote_conflict_path
399
+ end
400
+
401
+ def local_mkdir local_path
402
+ puts "#{Utils.caller_info 1} mkdir_p #{local_path}".white
403
+ FileUtils.mkdir_p local_path
404
+ end
405
+
406
+ def local_remove_file local_path
407
+ puts "#{Utils.caller_info 1} rm #{local_path}".white
408
+ FileUtils.rm local_path
409
+ end
410
+
411
+ def local_remove_directory local_path
412
+ puts "#{Utils.caller_info 1} rm_rf #{local_path}".white
413
+ FileUtils.rm_rf local_path
414
+ end
415
+
416
+ def local_rename local_path, new_local_path
417
+ puts "#{Utils.caller_info 1} mv #{local_path} #{new_local_path}".yellow
418
+ FileUtils.mv local_path, new_local_path
419
+ end
420
+
421
+ def local_ensure_dir path
422
+ FileUtils.mkdir_p path if !File.directory? path
423
+ end
424
+
425
+ def local_ensure_parent path
426
+ local_ensure_dir File.dirname path
427
+ end
428
+ end
429
+ end
data/lib/bsync/lfs.rb ADDED
@@ -0,0 +1,93 @@
1
+ require "bsync/core"
2
+ require "ostruct"
3
+
4
+ module Bsync
5
+ class Lfs < Core
6
+ def initialize params
7
+ super params, "lfs"
8
+ end
9
+
10
+ def self.options
11
+ Core.options
12
+ end
13
+
14
+ def start_session &block
15
+ yield
16
+ end
17
+
18
+ # yield object like this
19
+ # {
20
+ # name:
21
+ # attributes: {
22
+ # directory?:
23
+ # mtime:
24
+ # }
25
+ # }
26
+ def remote_dir_foreach remote_path
27
+ Dir.foreach(remote_path) { |filename|
28
+ file_path = "#{remote_path}/#{filename}"
29
+
30
+ yield OpenStruct.new name: filename, attributes: OpenStruct.new(
31
+ directory?: File.directory?(file_path),
32
+ mtime: File.mtime(file_path).to_i
33
+ )
34
+ }
35
+ end
36
+
37
+ def remote_get_object remote_path
38
+ stat = File.lstat remote_path
39
+
40
+ OpenStruct.new(
41
+ directory?: stat.directory?,
42
+ mtime: stat.mtime.to_i
43
+ )
44
+ rescue Errno::ENOENT
45
+ nil
46
+ end
47
+
48
+ def download_file local_path, remote_path
49
+ local_ensure_parent local_path
50
+
51
+ puts "#{Utils.caller_info 1} cp #{remote_path}, #{local_path}".white
52
+ FileUtils.cp remote_path, local_path
53
+ end
54
+
55
+ def upload_file local_path, remote_path
56
+ remote_ensure_parent remote_path
57
+
58
+ puts "#{Utils.caller_info 1} cp #{local_path}, #{remote_path}".white
59
+ FileUtils.cp local_path, remote_path
60
+
61
+ remote_get_object remote_path
62
+ end
63
+
64
+ def remote_mkdir remote_path
65
+ puts "#{Utils.caller_info 1} mkdir #{remote_path}".white
66
+ FileUtils.mkdir remote_path
67
+ remote_get_object remote_path
68
+ end
69
+
70
+ def remote_remove_file remote_path
71
+ puts "#{Utils.caller_info 1} rm #{remote_path}".white
72
+ FileUtils.rm remote_path
73
+ end
74
+
75
+ def remote_remove_dir remote_path
76
+ puts "#{Utils.caller_info 1} rm_rf #{remote_path}".white
77
+ FileUtils.rm_rf remote_path
78
+ end
79
+
80
+ def remote_rename remote_path, new_remote_path
81
+ puts "#{Utils.caller_info 1} mv #{remote_path} #{new_remote_path}".yellow
82
+ FileUtils.mv remote_path, new_remote_path
83
+ end
84
+
85
+ def remote_ensure_dir path
86
+ local_ensure_dir path
87
+ end
88
+
89
+ def remote_ensure_parent path
90
+ local_ensure_parent path
91
+ end
92
+ end
93
+ end
data/lib/bsync/sftp.rb ADDED
@@ -0,0 +1,85 @@
1
+ require "bsync/core"
2
+
3
+ module Bsync
4
+ class Sftp < Core
5
+ def initialize params
6
+ @site = params["site"]
7
+ @user = params["user"]
8
+
9
+ super params, "sftp"
10
+ end
11
+
12
+ def self.options
13
+ Core.options + ["site:", "user:"]
14
+ end
15
+
16
+ def start_session &block
17
+ Net::SFTP.start(@site, @user) { |sftp|
18
+ @sftp = sftp
19
+ yield
20
+ }
21
+ end
22
+
23
+ def remote_dir_foreach remote_path
24
+ @sftp.dir.foreach(remote_path) { |entry|
25
+ yield entry
26
+ }
27
+ end
28
+
29
+ def remote_get_object remote_path
30
+ @sftp.lstat! remote_path
31
+ rescue Net::SFTP::StatusException
32
+ nil
33
+ end
34
+
35
+ def download_file local_path, remote_path
36
+ local_ensure_parent local_path
37
+
38
+ puts "#{Utils.caller_info 1} sftp.download! #{remote_path}, #{local_path}".white
39
+ @sftp.download! remote_path, local_path
40
+ end
41
+
42
+ def upload_file local_path, remote_path
43
+ remote_ensure_parent remote_path
44
+
45
+ puts "#{Utils.caller_info 1} sftp.upload! #{local_path}, #{remote_path}".white
46
+ @sftp.upload! local_path, remote_path
47
+
48
+ remote_get_object remote_path
49
+ end
50
+
51
+ def remote_mkdir remote_path
52
+ puts "#{Utils.caller_info 1} sftp.mkdir! #{remote_path}".white
53
+ @sftp.mkdir! remote_path
54
+ remote_get_object remote_path
55
+ end
56
+
57
+ def remote_remove_file remote_path
58
+ puts "#{Utils.caller_info 1} sftp.remove! #{remote_path}".white
59
+ @sftp.remove! remote_path
60
+ end
61
+
62
+ def remote_remove_dir remote_path
63
+ puts "#{Utils.caller_info 1} sftp.session.exec! rm -rf #{remote_path}".white
64
+ @sftp.session.exec! "rm -rf #{remote_path}"
65
+ end
66
+
67
+ def remote_rename remote_path, new_remote_path
68
+ puts "#{Utils.caller_info 1} sftp.rename! #{remote_path} #{new_remote_path}".yellow
69
+ @sftp.rename! remote_path, new_remote_path
70
+ end
71
+
72
+ def remote_ensure_dir path
73
+ begin
74
+ @sftp.lstat! path
75
+ rescue Net::SFTP::StatusException
76
+ remote_ensure_parent path
77
+ @sftp.mkdir! path
78
+ end
79
+ end
80
+
81
+ def remote_ensure_parent path
82
+ remote_ensure_dir File.dirname path
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,34 @@
1
+ require "digest"
2
+
3
+ module Bsync
4
+ module Utils
5
+ # Examples:
6
+ #
7
+ # puts Utils.timestamp
8
+ # > 2019-12-11.16-15-57
9
+ #
10
+ # puts Utils.timestamp[0...10].delete("-")
11
+ # > 20191211
12
+ #
13
+ def self.timestamp
14
+ ts = Time.now.to_s[0..18]
15
+ ts[10] = "."
16
+ ts[13] = ts[16] = "-"
17
+
18
+ ts
19
+ end
20
+
21
+ def self.md5(s)
22
+ Digest::MD5.hexdigest(s)
23
+ end
24
+
25
+ def self.caller_info level
26
+ info = caller[level].match(%r{([^/]+):(\d+):in `(.+)'})
27
+ "#{info.captures[0]}:#{info.captures[1]} - #{info.captures[2]}"
28
+ end
29
+
30
+ def self.try_lock
31
+ File.open(__FILE__, 'r').flock(File::LOCK_EX | File::LOCK_NB)
32
+ end
33
+ end
34
+ end
data/lib/bsync/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Bsync
2
- VERSION = "0.1.1"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bdsync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xia Xiongjun
@@ -9,7 +9,21 @@ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
11
  date: 2019-12-26 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.8'
13
27
  description: Bidirectional Synchronization tool for sftp or local file system
14
28
  email:
15
29
  - xxjapp@gmail.com
@@ -22,6 +36,7 @@ files:
22
36
  - ".travis.yml"
23
37
  - CODE_OF_CONDUCT.md
24
38
  - Gemfile
39
+ - Gemfile.lock
25
40
  - LICENSE.txt
26
41
  - README.md
27
42
  - Rakefile
@@ -29,6 +44,10 @@ files:
29
44
  - bin/setup
30
45
  - bsync.gemspec
31
46
  - lib/bsync.rb
47
+ - lib/bsync/core.rb
48
+ - lib/bsync/lfs.rb
49
+ - lib/bsync/sftp.rb
50
+ - lib/bsync/utils.rb
32
51
  - lib/bsync/version.rb
33
52
  homepage: https://github.com/xxjapp/bsync
34
53
  licenses: