bdsync 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: