parallel_sftp 0.3.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/.envrc +1 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +45 -0
- data/CLAUDE.md +178 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +186 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/parallel_sftp/client.rb +108 -0
- data/lib/parallel_sftp/configuration.rb +41 -0
- data/lib/parallel_sftp/download.rb +164 -0
- data/lib/parallel_sftp/errors.rb +58 -0
- data/lib/parallel_sftp/lftp_command.rb +64 -0
- data/lib/parallel_sftp/progress_parser.rb +82 -0
- data/lib/parallel_sftp/segment_progress_parser.rb +153 -0
- data/lib/parallel_sftp/time_estimator.rb +122 -0
- data/lib/parallel_sftp/version.rb +3 -0
- data/lib/parallel_sftp.rb +128 -0
- data/parallel_sftp.gemspec +35 -0
- data/shell.nix +29 -0
- metadata +109 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "parallel_sftp/version"
|
|
4
|
+
require "parallel_sftp/errors"
|
|
5
|
+
require "parallel_sftp/configuration"
|
|
6
|
+
require "parallel_sftp/lftp_command"
|
|
7
|
+
require "parallel_sftp/progress_parser"
|
|
8
|
+
require "parallel_sftp/segment_progress_parser"
|
|
9
|
+
require "parallel_sftp/time_estimator"
|
|
10
|
+
require "parallel_sftp/download"
|
|
11
|
+
require "parallel_sftp/client"
|
|
12
|
+
|
|
13
|
+
module ParallelSftp
|
|
14
|
+
class << self
|
|
15
|
+
attr_writer :configuration
|
|
16
|
+
|
|
17
|
+
def configuration
|
|
18
|
+
@configuration ||= Configuration.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Configure ParallelSftp globally
|
|
22
|
+
#
|
|
23
|
+
# @example
|
|
24
|
+
# ParallelSftp.configure do |config|
|
|
25
|
+
# config.default_segments = 8
|
|
26
|
+
# config.timeout = 60
|
|
27
|
+
# config.max_retries = 15
|
|
28
|
+
# end
|
|
29
|
+
def configure
|
|
30
|
+
yield(configuration)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Reset configuration to defaults
|
|
34
|
+
def reset_configuration!
|
|
35
|
+
@configuration = Configuration.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Check if lftp is available on the system
|
|
39
|
+
#
|
|
40
|
+
# @return [Boolean] true if lftp is installed and accessible
|
|
41
|
+
def lftp_available?
|
|
42
|
+
system("which lftp > /dev/null 2>&1")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get the installed lftp version
|
|
46
|
+
#
|
|
47
|
+
# @return [String, nil] Version string or nil if not installed
|
|
48
|
+
def lftp_version
|
|
49
|
+
return nil unless lftp_available?
|
|
50
|
+
`lftp --version`.lines.first&.strip
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Raise an error if lftp is not available
|
|
54
|
+
#
|
|
55
|
+
# @raise [LftpNotFoundError] if lftp is not installed
|
|
56
|
+
def ensure_lftp_available!
|
|
57
|
+
raise LftpNotFoundError unless lftp_available?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Simple one-liner for downloading a file
|
|
61
|
+
#
|
|
62
|
+
# @param options [Hash] Connection and download options
|
|
63
|
+
# @option options [String] :host SFTP host
|
|
64
|
+
# @option options [String] :user Username
|
|
65
|
+
# @option options [String] :password Password
|
|
66
|
+
# @option options [Integer] :port SFTP port (default: 22)
|
|
67
|
+
# @option options [String] :remote_path Path to file on server
|
|
68
|
+
# @option options [String] :local_path Local destination path
|
|
69
|
+
# @option options [Integer] :segments Parallel connections (default: 4)
|
|
70
|
+
# @option options [Boolean] :resume Continue interrupted downloads (default: true)
|
|
71
|
+
# @option options [Integer] :timeout Connection timeout in seconds
|
|
72
|
+
# @option options [Integer] :max_retries Maximum retry attempts
|
|
73
|
+
# @option options [Proc] :on_progress Progress callback
|
|
74
|
+
# @option options [Proc] :on_segment_progress Per-segment progress callback
|
|
75
|
+
#
|
|
76
|
+
# @return [String] Local path to downloaded file
|
|
77
|
+
# @raise [DownloadError] if download fails
|
|
78
|
+
#
|
|
79
|
+
# @example Basic progress
|
|
80
|
+
# ParallelSftp.download(
|
|
81
|
+
# host: 'ftp.example.com',
|
|
82
|
+
# user: 'username',
|
|
83
|
+
# password: 'secret',
|
|
84
|
+
# remote_path: '/path/to/large_file.zip',
|
|
85
|
+
# local_path: '/tmp/large_file.zip',
|
|
86
|
+
# segments: 8,
|
|
87
|
+
# on_progress: ->(info) { puts "#{info[:percent]}%" }
|
|
88
|
+
# )
|
|
89
|
+
#
|
|
90
|
+
# @example With per-segment progress
|
|
91
|
+
# ParallelSftp.download(
|
|
92
|
+
# host: 'ftp.example.com',
|
|
93
|
+
# user: 'username',
|
|
94
|
+
# password: 'secret',
|
|
95
|
+
# remote_path: '/path/to/large_file.zip',
|
|
96
|
+
# local_path: '/tmp/large_file.zip',
|
|
97
|
+
# segments: 8,
|
|
98
|
+
# on_segment_progress: lambda do |info|
|
|
99
|
+
# puts "Overall: #{info[:overall_percent]}% (#{info[:eta]} remaining)"
|
|
100
|
+
# puts "Speed: #{info[:speed] / 1_000_000.0} MB/s" if info[:speed]
|
|
101
|
+
# info[:segments].each do |seg|
|
|
102
|
+
# puts " Segment #{seg[:index]}: #{seg[:percent]}%"
|
|
103
|
+
# end
|
|
104
|
+
# end
|
|
105
|
+
# )
|
|
106
|
+
def download(options = {})
|
|
107
|
+
client = Client.new(
|
|
108
|
+
host: options.fetch(:host),
|
|
109
|
+
user: options.fetch(:user),
|
|
110
|
+
password: options.fetch(:password),
|
|
111
|
+
port: options.fetch(:port, configuration.default_port)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
client.download(
|
|
115
|
+
options.fetch(:remote_path),
|
|
116
|
+
options.fetch(:local_path),
|
|
117
|
+
segments: options[:segments],
|
|
118
|
+
resume: options.fetch(:resume, true),
|
|
119
|
+
timeout: options[:timeout],
|
|
120
|
+
max_retries: options[:max_retries],
|
|
121
|
+
reconnect_interval: options[:reconnect_interval],
|
|
122
|
+
sftp_connect_program: options[:sftp_connect_program],
|
|
123
|
+
on_progress: options[:on_progress],
|
|
124
|
+
on_segment_progress: options[:on_segment_progress]
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require "parallel_sftp/version"
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = "parallel_sftp"
|
|
9
|
+
spec.version = ParallelSftp::VERSION
|
|
10
|
+
spec.authors = ["Nestor G Pestelos Jr"]
|
|
11
|
+
spec.email = ["ngpestelos@gmail.com"]
|
|
12
|
+
|
|
13
|
+
spec.summary = "Fast parallel SFTP downloads using lftp's segmented transfer"
|
|
14
|
+
spec.description = "A Ruby gem that wraps lftp for parallel/segmented SFTP downloads of large files. " \
|
|
15
|
+
"Supports resume, progress callbacks, and configurable retry settings."
|
|
16
|
+
spec.homepage = "https://github.com/ngpestelos/parallel_sftp"
|
|
17
|
+
spec.license = "MIT"
|
|
18
|
+
|
|
19
|
+
spec.required_ruby_version = ">= 2.5.0"
|
|
20
|
+
|
|
21
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
22
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
23
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
|
24
|
+
|
|
25
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
26
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
27
|
+
end
|
|
28
|
+
spec.bindir = "exe"
|
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
30
|
+
spec.require_paths = ["lib"]
|
|
31
|
+
|
|
32
|
+
spec.add_development_dependency "bundler", ">= 1.17"
|
|
33
|
+
spec.add_development_dependency "rake", ">= 12.0"
|
|
34
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
35
|
+
end
|
data/shell.nix
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{ pkgs ? import <nixpkgs> {} }:
|
|
2
|
+
|
|
3
|
+
pkgs.mkShell {
|
|
4
|
+
buildInputs = with pkgs; [
|
|
5
|
+
ruby_3_3
|
|
6
|
+
bundler
|
|
7
|
+
lftp
|
|
8
|
+
curl
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
shellHook = ''
|
|
12
|
+
export GEM_HOME="$PWD/.gems"
|
|
13
|
+
export PATH="$GEM_HOME/bin:$PATH"
|
|
14
|
+
mkdir -p "$GEM_HOME"
|
|
15
|
+
# Check if Claude Code is installed natively
|
|
16
|
+
if [ ! -f "$HOME/.local/bin/claude" ]; then
|
|
17
|
+
echo "Installing Claude Code (native)..."
|
|
18
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Add Claude to PATH if not already there
|
|
22
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
23
|
+
|
|
24
|
+
# Indicate we're in a nix-shell with Claude Code
|
|
25
|
+
export PS1="\[\033[1;32m\][nix-shell:claude]\[\033[0m\] $PS1"
|
|
26
|
+
|
|
27
|
+
echo "Claude Code environment activated. Run 'claude' to use the CLI."
|
|
28
|
+
'';
|
|
29
|
+
}
|
metadata
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: parallel_sftp
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nestor G Pestelos Jr
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-01 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: bundler
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.17'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.17'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '12.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '12.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rspec
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.0'
|
|
54
|
+
description: A Ruby gem that wraps lftp for parallel/segmented SFTP downloads of large
|
|
55
|
+
files. Supports resume, progress callbacks, and configurable retry settings.
|
|
56
|
+
email:
|
|
57
|
+
- ngpestelos@gmail.com
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- ".envrc"
|
|
63
|
+
- ".gitignore"
|
|
64
|
+
- ".rspec"
|
|
65
|
+
- CHANGELOG.md
|
|
66
|
+
- CLAUDE.md
|
|
67
|
+
- Gemfile
|
|
68
|
+
- LICENSE
|
|
69
|
+
- README.md
|
|
70
|
+
- Rakefile
|
|
71
|
+
- bin/console
|
|
72
|
+
- bin/setup
|
|
73
|
+
- lib/parallel_sftp.rb
|
|
74
|
+
- lib/parallel_sftp/client.rb
|
|
75
|
+
- lib/parallel_sftp/configuration.rb
|
|
76
|
+
- lib/parallel_sftp/download.rb
|
|
77
|
+
- lib/parallel_sftp/errors.rb
|
|
78
|
+
- lib/parallel_sftp/lftp_command.rb
|
|
79
|
+
- lib/parallel_sftp/progress_parser.rb
|
|
80
|
+
- lib/parallel_sftp/segment_progress_parser.rb
|
|
81
|
+
- lib/parallel_sftp/time_estimator.rb
|
|
82
|
+
- lib/parallel_sftp/version.rb
|
|
83
|
+
- parallel_sftp.gemspec
|
|
84
|
+
- shell.nix
|
|
85
|
+
homepage: https://github.com/ngpestelos/parallel_sftp
|
|
86
|
+
licenses:
|
|
87
|
+
- MIT
|
|
88
|
+
metadata:
|
|
89
|
+
homepage_uri: https://github.com/ngpestelos/parallel_sftp
|
|
90
|
+
source_code_uri: https://github.com/ngpestelos/parallel_sftp
|
|
91
|
+
changelog_uri: https://github.com/ngpestelos/parallel_sftp/blob/master/CHANGELOG.md
|
|
92
|
+
rdoc_options: []
|
|
93
|
+
require_paths:
|
|
94
|
+
- lib
|
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
96
|
+
requirements:
|
|
97
|
+
- - ">="
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: 2.5.0
|
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - ">="
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '0'
|
|
105
|
+
requirements: []
|
|
106
|
+
rubygems_version: 3.7.2
|
|
107
|
+
specification_version: 4
|
|
108
|
+
summary: Fast parallel SFTP downloads using lftp's segmented transfer
|
|
109
|
+
test_files: []
|