bfs-scp 0.8.0 → 0.9.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: 29d510784470981aeebbf54fdde1c56cafbe6e7a53bb3aa7cc1ac6f043315c3f
4
- data.tar.gz: d80b9721dbb7934abddbb5ea09adf1fb0de83421cf9deeb298d0bf0ae450a50e
3
+ metadata.gz: 878d1632ac424efe2225eb33581d0725c07c6a90151b3cca5e5cd72ca2efe4f5
4
+ data.tar.gz: a86995b8873ddb45841358a0b6675b4a2987314812ca3fdca4ff46b04a08cc8d
5
5
  SHA512:
6
- metadata.gz: 1d9bf7a73342ae2090dbbb487d87fc28aefd8680b6f6145c82f790905467e2e662152e7c5710f73147a163f4adc43687dbb63afece37b8867e20c437169b9af6
7
- data.tar.gz: e0458841db2fa130caa4a64d4b954f4ef3fda113a905ce9ae3b9e556d3a468f3747cfdb83d9f5f629dda505a27ed35b60b445a10f35fdc7aeec1db70462f5495
6
+ metadata.gz: 461dc093c7849db7ac94c71596d9fda2ff55bcba1d5bd47002e35ceed2b81a9333221f1a741a0e8a6e04e5d0b81ed4edfd0cce1c471965014e632cfe07cbcded
7
+ data.tar.gz: 868529f0d5e1a9d0d21a7fa37ec6a04ace9b62bad34f92f7004374207a8970209bbe755671eea334af29a3b5e1a54a9931f9196f3db8b481be84d9c9f3f0c46e
@@ -1,6 +1,5 @@
1
1
  require 'bfs'
2
2
  require 'net/scp'
3
- require 'net/ssh'
4
3
  require 'shellwords'
5
4
 
6
5
  module BFS
@@ -32,7 +31,7 @@ module BFS
32
31
  super(**opts)
33
32
 
34
33
  @prefix = prefix
35
- @client = Net::SCP.start(host, nil, **opts.slice(*Net::SSH::VALID_OPTIONS))
34
+ @client = Net::SCP.start(host, nil, **opts.slice(*Net::SSH::VALID_OPTIONS), non_interactive: true)
36
35
 
37
36
  if @prefix # rubocop:disable Style/GuardClause
38
37
  @prefix = "#{norm_path(@prefix)}/"
@@ -42,14 +41,15 @@ module BFS
42
41
 
43
42
  # Lists the contents of a bucket using a glob pattern
44
43
  def ls(pattern = '**/*', **_opts)
45
- prefix = @prefix ? abs_path(@prefix) : '/'
46
- Enumerator.new do |y|
47
- sh! 'find', prefix, '-type', 'f' do |out|
48
- out.each_line do |line|
49
- path = trim_prefix(norm_path(line.strip))
50
- y << path if File.fnmatch?(pattern, path, File::FNM_PATHNAME)
51
- end
52
- end
44
+ Enumerator.new do |acc|
45
+ walk(pattern) {|path| acc << path }
46
+ end
47
+ end
48
+
49
+ # Iterates over the contents of a bucket using a glob pattern
50
+ def glob(pattern = '**/*', **_opts)
51
+ Enumerator.new do |acc|
52
+ walk(pattern, with_stat: true) {|info| acc << info }
53
53
  end
54
54
  end
55
55
 
@@ -57,9 +57,11 @@ module BFS
57
57
  def info(path, **_opts)
58
58
  full = full_path(path)
59
59
  path = norm_path(path)
60
- out = sh! 'stat', '-c', '%s;%Z;%a', full
60
+ out = sh! %(stat -c '%F;%s;%Z;%a' #{Shellwords.escape full})
61
+
62
+ type, size, epoch, mode = out.strip.split(';', 4)
63
+ raise BFS::FileNotFound, path unless type.include?('file')
61
64
 
62
- size, epoch, mode = out.strip.split(';', 3)
63
65
  BFS::FileInfo.new(path: path, size: size.to_i, mtime: Time.at(epoch.to_i), mode: BFS.norm_mode(mode))
64
66
  rescue CommandError => e
65
67
  e.status == 1 ? raise(BFS::FileNotFound, path) : raise
@@ -93,7 +95,7 @@ module BFS
93
95
  # Deletes a file.
94
96
  def rm(path, **_opts)
95
97
  path = full_path(path)
96
- sh! 'rm', '-f', path
98
+ sh! %(rm -f #{Shellwords.escape(path)})
97
99
  end
98
100
 
99
101
  # Copies src to dst
@@ -105,7 +107,7 @@ module BFS
105
107
  full_dst = full_path(dst)
106
108
 
107
109
  mkdir_p File.dirname(full_dst)
108
- sh! 'cp', '-a', '-f', full_src, full_dst
110
+ sh! %(cp -a -f #{Shellwords.escape(full_src)} #{Shellwords.escape(full_dst)})
109
111
  rescue CommandError => e
110
112
  e.status == 1 ? raise(BFS::FileNotFound, src) : raise
111
113
  end
@@ -119,7 +121,7 @@ module BFS
119
121
  full_dst = full_path(dst)
120
122
 
121
123
  mkdir_p File.dirname(full_dst)
122
- sh! 'mv', '-f', full_src, full_dst
124
+ sh! %(mv -f #{Shellwords.escape(full_src)} #{Shellwords.escape(full_dst)})
123
125
  rescue CommandError => e
124
126
  e.status == 1 ? raise(BFS::FileNotFound, src) : raise
125
127
  end
@@ -140,23 +142,47 @@ module BFS
140
142
  abs_path(super)
141
143
  end
142
144
 
145
+ def walk(pattern, with_stat: false)
146
+ prefix = @prefix ? abs_path(@prefix) : '/'
147
+ command = %(find #{Shellwords.escape(prefix)} -type f)
148
+ command << %( -exec stat -c '%s;%Z;%a;%n' {} \\;) if with_stat
149
+
150
+ sh!(command) do |out|
151
+ out.each_line do |line|
152
+ line.strip!
153
+
154
+ if with_stat
155
+ size, epoch, mode, path = out.strip.split(';', 4)
156
+ path = trim_prefix(norm_path(path))
157
+ next unless File.fnmatch?(pattern, path, File::FNM_PATHNAME)
158
+
159
+ info = BFS::FileInfo.new(path: path, size: size.to_i, mtime: Time.at(epoch.to_i), mode: BFS.norm_mode(mode))
160
+ yield info
161
+ else
162
+ path = trim_prefix(norm_path(line))
163
+ yield path if File.fnmatch?(pattern, path, File::FNM_PATHNAME)
164
+ end
165
+ end
166
+ end
167
+ end
168
+
143
169
  def mkdir_p(path)
144
- sh! 'mkdir', '-p', path
170
+ sh! %(mkdir -p #{Shellwords.escape(path)})
145
171
  end
146
172
 
147
- def sh!(*cmd) # rubocop:disable Metrics/MethodLength
173
+ def sh!(command) # rubocop:disable Metrics/MethodLength
148
174
  stdout = ''
149
175
  stderr = nil
150
176
  status = 0
151
- cmdstr = cmd.map {|x| Shellwords.escape(x) }.join(' ')
152
177
 
153
178
  @client.session.open_channel do |ch|
154
- ch.exec(cmdstr) do |_, _success|
179
+ ch.exec(command) do |_, _success|
155
180
  ch.on_data do |_, data|
181
+ stdout << data
182
+
156
183
  if block_given?
157
- yield data
158
- else
159
- stdout += data
184
+ pos = stdout.rindex("\n")
185
+ yield stdout.slice!(0..pos) if pos
160
186
  end
161
187
  end
162
188
  ch.on_extended_data do |_, _, data|
@@ -167,8 +193,14 @@ module BFS
167
193
  end
168
194
  end
169
195
  end
196
+
197
+ if block_given? && stdout.length.positive?
198
+ yield stdout
199
+ stdout.clear
200
+ end
201
+
170
202
  @client.session.loop
171
- raise CommandError.new(cmdstr, status, stderr) unless status.zero?
203
+ raise CommandError.new(command, status, stderr) unless status.zero?
172
204
 
173
205
  stdout
174
206
  end
@@ -1,24 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe BFS::Bucket::SCP, scp: true do
4
+ subject { described_class.new hostname, **conn_opts }
5
+
4
6
  let(:hostname) { '127.0.0.1' }
5
7
  let(:conn_opts) { { port: 7022, user: 'root', password: 'root', prefix: prefix } }
6
8
  let(:prefix) { SecureRandom.uuid }
7
9
 
8
- subject { described_class.new hostname, **conn_opts }
9
10
  after { subject.close }
10
11
 
11
- context 'absolute' do
12
+ context 'with absolute path' do
12
13
  it_behaves_like 'a bucket', content_type: false, metadata: false
13
14
  end
14
15
 
15
- context 'relative' do
16
+ context 'with relative path' do
16
17
  let(:prefix) { "~/#{SecureRandom.uuid}" }
17
18
 
18
19
  it_behaves_like 'a bucket', content_type: false, metadata: false
19
20
  end
20
21
 
21
- it 'should resolve from URL' do
22
+ it 'resolves from URL' do
22
23
  bucket = BFS.resolve('scp://root:root@127.0.0.1:7022')
23
24
  expect(bucket).to be_instance_of(described_class)
24
25
  expect(bucket.instance_variable_get(:@prefix)).to be_nil
@@ -30,7 +31,7 @@ RSpec.describe BFS::Bucket::SCP, scp: true do
30
31
  bucket.close
31
32
  end
32
33
 
33
- it 'should handle absolute and relative paths' do
34
+ it 'handles absolute and relative paths' do
34
35
  abs = BFS::Blob.new("scp://root:root@127.0.0.1:7022/#{SecureRandom.uuid}/file.txt")
35
36
  abs.create {|w| w.write 'absolute' }
36
37
 
@@ -44,7 +45,7 @@ RSpec.describe BFS::Bucket::SCP, scp: true do
44
45
  rel.close
45
46
  end
46
47
 
47
- it 'should support custom perms' do
48
+ it 'supports custom perms' do
48
49
  blob = BFS::Blob.new("scp://root:root@127.0.0.1:7022/#{SecureRandom.uuid}/file.txt")
49
50
  blob.create(perm: 0o666) {|w| w.write 'foo' }
50
51
  expect(blob.info.mode).to eq(0o666)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bfs-scp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitrij Denissenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-01 00:00:00.000000000 Z
11
+ date: 2021-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bfs
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.8.0
19
+ version: 0.9.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.8.0
26
+ version: 0.9.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: net-scp
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -67,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  requirements: []
70
- rubygems_version: 3.1.4
70
+ rubygems_version: 3.2.15
71
71
  signing_key:
72
72
  specification_version: 4
73
73
  summary: SCP/SSH adapter for bfs