bfs-sftp 0.9.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 +7 -0
- data/bfs-sftp.gemspec +22 -0
- data/lib/bfs/bucket/sftp.rb +143 -0
- data/lib/bfs/sftp.rb +1 -0
- data/spec/bfs/bucket/sftp_spec.rb +19 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1c4dfdb49d9d05b759223cabb87be952c6dfae65f966eabe4cf0662ad26dea2d
|
4
|
+
data.tar.gz: 950c37378a426f30b038b67cf4e9c48d252801714f2f161e7fa23556a96a8585
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a481be112d9b2873a303675fb33e33dbb993678f889f0ca501fd5cab6cbc7f39c5cc41b1c4818187e5672e652beac6cc07276b19621175eb548f251be45577ac
|
7
|
+
data.tar.gz: 511f81ecb71ff42f0f6a987af1871bc56960e50b187c9e352fe824d3e624534d2a2c91c76d600d24675a114c190c5785955e3fdf91d986afa39beafd128a6985
|
data/bfs-sftp.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'bfs-sftp'
|
3
|
+
s.version = File.read(File.expand_path('../.version', __dir__)).strip
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
|
6
|
+
s.licenses = ['Apache-2.0']
|
7
|
+
s.summary = 'SFTP adapter for bfs'
|
8
|
+
s.description = 'https://github.com/bsm/bfs.rb'
|
9
|
+
|
10
|
+
s.authors = ['Dimitrij Denissenko']
|
11
|
+
s.email = 'dimitrij@blacksquaremedia.com'
|
12
|
+
s.homepage = 'https://github.com/bsm/bfs.rb'
|
13
|
+
|
14
|
+
s.executables = []
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
s.required_ruby_version = '>= 2.6.0'
|
19
|
+
|
20
|
+
s.add_dependency 'bfs', s.version
|
21
|
+
s.add_dependency 'net-sftp'
|
22
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'bfs'
|
2
|
+
require 'net/sftp'
|
3
|
+
|
4
|
+
module BFS
|
5
|
+
module Bucket
|
6
|
+
# SFTP buckets are operating on SFTP connections.
|
7
|
+
class SFTP < Abstract
|
8
|
+
StatusCodes = Net::SFTP::Constants::StatusCodes
|
9
|
+
|
10
|
+
# Initializes a new bucket
|
11
|
+
# @param [String] host the host name
|
12
|
+
# @param [Hash] opts options
|
13
|
+
# @option opts [Integer] :port custom port. Default: 22.
|
14
|
+
# @option opts [String] :user user name for login.
|
15
|
+
# @option opts [String] :password password for login.
|
16
|
+
# @option opts [String] :prefix optional prefix.
|
17
|
+
# @option opts [Boolean] :compression use compression.
|
18
|
+
# @option opts [Boolean] :keepalive use keepalive.
|
19
|
+
# @option opts [Integer] :keepalive_interval interval if keepalive enabled. Default: 300.
|
20
|
+
# @option opts [Array<String>] :keys an array of file names of private keys to use for publickey and hostbased authentication.
|
21
|
+
# @option opts [Boolean|Symbol] :verify_host_key specifying how strict host-key verification should be, either false, true, :very, or :secure.
|
22
|
+
def initialize(host, prefix: nil, **opts)
|
23
|
+
super(**opts)
|
24
|
+
|
25
|
+
@prefix = prefix
|
26
|
+
@session = Net::SSH.start(host, nil, **opts.slice(*Net::SSH::VALID_OPTIONS), non_interactive: true)
|
27
|
+
|
28
|
+
if @prefix # rubocop:disable Style/GuardClause
|
29
|
+
@prefix = "#{norm_path(@prefix)}/"
|
30
|
+
mkdir_p @prefix
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Lists the contents of a bucket using a glob pattern
|
35
|
+
def ls(pattern = '**/*', **_opts)
|
36
|
+
Enumerator.new do |acc|
|
37
|
+
walk(pattern) {|path, _| acc << path }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Iterates over the contents of a bucket using a glob pattern
|
42
|
+
def glob(pattern = '**/*', **_opts)
|
43
|
+
Enumerator.new do |acc|
|
44
|
+
walk(pattern) do |path, attrs|
|
45
|
+
acc << file_info(path, attrs)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Info returns the object info
|
51
|
+
def info(path, **_opts)
|
52
|
+
full = full_path(path)
|
53
|
+
path = norm_path(path)
|
54
|
+
attrs = @session.sftp.stat!(full)
|
55
|
+
raise BFS::FileNotFound, path unless attrs.file?
|
56
|
+
|
57
|
+
file_info path, attrs
|
58
|
+
rescue Net::SFTP::StatusException => e
|
59
|
+
raise BFS::FileNotFound, path if e.code == StatusCodes::FX_NO_SUCH_FILE
|
60
|
+
|
61
|
+
raise
|
62
|
+
end
|
63
|
+
|
64
|
+
# Creates a new file and opens it for writing
|
65
|
+
# @option opts [String|Encoding] :encoding Custom file encoding.
|
66
|
+
# @option opts [Integer] :perm Custom file permission, default: 0600.
|
67
|
+
def create(path, encoding: self.encoding, perm: self.perm, **opts, &block)
|
68
|
+
full = full_path(path)
|
69
|
+
|
70
|
+
opts[:preserve] = true if perm && !opts.key?(:preserve)
|
71
|
+
BFS::Writer.new(path, encoding: encoding, perm: perm) do |temp_path|
|
72
|
+
mkdir_p File.dirname(full)
|
73
|
+
@session.sftp.upload!(temp_path, full, **opts)
|
74
|
+
end.perform(&block)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Opens an existing file for reading
|
78
|
+
def open(path, encoding: self.encoding, tempdir: nil, **_opts, &block)
|
79
|
+
full = full_path(path)
|
80
|
+
temp = Tempfile.new(File.basename(path), tempdir, encoding: encoding)
|
81
|
+
temp.close
|
82
|
+
|
83
|
+
@session.sftp.download!(full, temp.path)
|
84
|
+
File.open(temp.path, encoding: encoding, &block)
|
85
|
+
rescue Net::SFTP::StatusException => e
|
86
|
+
raise BFS::FileNotFound, path if e.code == StatusCodes::FX_NO_SUCH_FILE
|
87
|
+
|
88
|
+
raise
|
89
|
+
end
|
90
|
+
|
91
|
+
# Deletes a file.
|
92
|
+
def rm(path, **_opts)
|
93
|
+
full = full_path(path)
|
94
|
+
@session.sftp.remove!(full)
|
95
|
+
rescue Net::SFTP::StatusException => e
|
96
|
+
raise unless e.code == StatusCodes::FX_NO_SUCH_FILE
|
97
|
+
end
|
98
|
+
|
99
|
+
# Closes the underlying connection
|
100
|
+
def close
|
101
|
+
@session.close unless @session.closed?
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def file_info(path, attrs)
|
107
|
+
BFS::FileInfo.new(path: path, size: attrs.size.to_i, mtime: Time.at(attrs.mtime.to_i), mode: BFS.norm_mode(attrs.permissions))
|
108
|
+
end
|
109
|
+
|
110
|
+
def walk(pattern)
|
111
|
+
@session.sftp.dir.glob(@prefix || '/', pattern) do |ent|
|
112
|
+
next unless ent.file?
|
113
|
+
|
114
|
+
path = norm_path(ent.name)
|
115
|
+
yield(path, ent.attributes)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def mkdir_p(path)
|
120
|
+
parts = path.split('/').reject(&:empty?)
|
121
|
+
cmds = (0...parts.size).map do |i|
|
122
|
+
@session.sftp.mkdir parts[0..i].join('/')
|
123
|
+
end
|
124
|
+
cmds.each do |req|
|
125
|
+
req.wait
|
126
|
+
next if req.response.code <= StatusCodes::FX_FAILURE
|
127
|
+
|
128
|
+
raise Net::SFTP::StatusException, req.response
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
BFS.register('sftp') do |url, opts, block|
|
136
|
+
prefix = BFS.norm_path(opts[:prefix] || url.path)
|
137
|
+
opts[:prefix] = prefix unless prefix.empty?
|
138
|
+
opts[:user] ||= CGI.unescape(url.user) if url.user
|
139
|
+
opts[:password] ||= CGI.unescape(url.password) if url.password
|
140
|
+
opts[:port] ||= url.port if url.port
|
141
|
+
|
142
|
+
BFS::Bucket::SFTP.open(url.host, **opts, &block)
|
143
|
+
end
|
data/lib/bfs/sftp.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bfs/bucket/sftp'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe BFS::Bucket::SFTP, sftp: true do
|
4
|
+
subject do
|
5
|
+
described_class.new '127.0.0.1', port: 7023, user: 'sftp', password: 'DockerPassSFTP', prefix: SecureRandom.uuid
|
6
|
+
end
|
7
|
+
|
8
|
+
it_behaves_like 'a bucket', content_type: false, metadata: false
|
9
|
+
|
10
|
+
it 'resolves from URL' do
|
11
|
+
bucket = BFS.resolve('sftp://sftp:DockerPassSFTP@127.0.0.1:7023')
|
12
|
+
expect(bucket).to be_instance_of(described_class)
|
13
|
+
bucket.close
|
14
|
+
|
15
|
+
bucket = BFS.resolve('sftp://sftp:DockerPassSFTP@127.0.0.1:7023/a/b/')
|
16
|
+
expect(bucket).to be_instance_of(described_class)
|
17
|
+
bucket.close
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bfs-sftp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dimitrij Denissenko
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-06-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bfs
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.9.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.9.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: net-sftp
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: https://github.com/bsm/bfs.rb
|
42
|
+
email: dimitrij@blacksquaremedia.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- bfs-sftp.gemspec
|
48
|
+
- lib/bfs/bucket/sftp.rb
|
49
|
+
- lib/bfs/sftp.rb
|
50
|
+
- spec/bfs/bucket/sftp_spec.rb
|
51
|
+
homepage: https://github.com/bsm/bfs.rb
|
52
|
+
licenses:
|
53
|
+
- Apache-2.0
|
54
|
+
metadata: {}
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 2.6.0
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubygems_version: 3.2.15
|
71
|
+
signing_key:
|
72
|
+
specification_version: 4
|
73
|
+
summary: SFTP adapter for bfs
|
74
|
+
test_files:
|
75
|
+
- spec/bfs/bucket/sftp_spec.rb
|