sshkit 1.21.5 → 1.23.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 +4 -4
- data/.docker/Dockerfile +6 -0
- data/.docker/ubuntu_setup.sh +22 -0
- data/.github/release-drafter.yml +10 -2
- data/.github/workflows/ci.yml +19 -34
- data/.github/workflows/push.yml +1 -1
- data/.gitignore +0 -1
- data/.rubocop_todo.yml +0 -7
- data/CONTRIBUTING.md +2 -2
- data/EXAMPLES.md +25 -13
- data/README.md +2 -1
- data/RELEASING.md +1 -1
- data/Rakefile +0 -4
- data/docker-compose.yml +8 -0
- data/lib/sshkit/backends/connection_pool/cache.rb +2 -2
- data/lib/sshkit/backends/netssh/known_hosts.rb +8 -8
- data/lib/sshkit/backends/netssh/scp_transfer.rb +26 -0
- data/lib/sshkit/backends/netssh/sftp_transfer.rb +46 -0
- data/lib/sshkit/backends/netssh.rb +36 -7
- data/lib/sshkit/host.rb +25 -0
- data/lib/sshkit/version.rb +1 -1
- data/sshkit.gemspec +2 -0
- data/test/functional/backends/netssh_transfer_tests.rb +83 -0
- data/test/functional/backends/test_netssh.rb +1 -67
- data/test/functional/backends/test_netssh_scp.rb +23 -0
- data/test/functional/backends/test_netssh_sftp.rb +23 -0
- data/test/helper.rb +4 -42
- data/test/support/docker_wrapper.rb +71 -0
- data/test/unit/backends/test_abstract.rb +1 -1
- data/test/unit/backends/test_netssh.rb +48 -0
- data/test/unit/test_command_map.rb +8 -8
- data/test/unit/test_deprecation_logger.rb +1 -1
- data/test/unit/test_host.rb +39 -0
- metadata +44 -10
- data/Vagrantfile +0 -24
- data/test/boxes.json +0 -17
- data/test/functional/test_ssh_server_comes_up_for_functional_tests.rb +0 -24
- data/test/support/vagrant_wrapper.rb +0 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f40ad7a1382ef707a259094c945692f08c44392e0bba7c37fe4099e748301a5e
|
4
|
+
data.tar.gz: 72f3fea395eaaa0036dd701c04692e75c379867465cffaa4ac6cdf1c4f647813
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab28074eba7cb9bdbfcbca9857f5e74fc89ed06acdf872148fcb7329b45754a714c658dacd879fead076de3bbd2541d84bcfd8ef66369007a1444b3edc2b3ac3
|
7
|
+
data.tar.gz: a6d0deb01db101ba2a41dcfcd861a10c7604b17ebe5e25981eebe6d74a0b5c9a9ea345d2dcbb45a4ba30952b571b6736f3bdcd7721d7d8fe684db772cc723700
|
data/.docker/Dockerfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
export DEBIAN_FRONTEND=noninteractive
|
6
|
+
apt -y update
|
7
|
+
|
8
|
+
# Create `deployer` user that can sudo without a password
|
9
|
+
apt-get -y install sudo
|
10
|
+
adduser --disabled-password deployer < /dev/null
|
11
|
+
echo "deployer:topsecret" | chpasswd
|
12
|
+
echo "deployer ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
13
|
+
|
14
|
+
# Install and configure sshd
|
15
|
+
apt-get -y install openssh-server
|
16
|
+
{
|
17
|
+
echo "Port 22"
|
18
|
+
echo "PasswordAuthentication yes"
|
19
|
+
echo "ChallengeResponseAuthentication no"
|
20
|
+
} >> /etc/ssh/sshd_config
|
21
|
+
mkdir /var/run/sshd
|
22
|
+
chmod 0755 /var/run/sshd
|
data/.github/release-drafter.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
name-template: "$
|
2
|
-
tag-template: "v$
|
1
|
+
name-template: "$RESOLVED_VERSION"
|
2
|
+
tag-template: "v$RESOLVED_VERSION"
|
3
3
|
categories:
|
4
4
|
- title: "⚠️ Breaking Changes"
|
5
5
|
label: "⚠️ Breaking"
|
@@ -11,7 +11,15 @@ categories:
|
|
11
11
|
label: "📚 Docs"
|
12
12
|
- title: "🏠 Housekeeping"
|
13
13
|
label: "🏠 Housekeeping"
|
14
|
+
version-resolver:
|
15
|
+
minor:
|
16
|
+
labels:
|
17
|
+
- "⚠️ Breaking"
|
18
|
+
- "✨ Feature"
|
19
|
+
default: patch
|
14
20
|
change-template: "- $TITLE (#$NUMBER) @$AUTHOR"
|
15
21
|
no-changes-template: "- No changes"
|
16
22
|
template: |
|
17
23
|
$CHANGES
|
24
|
+
|
25
|
+
**Full Changelog:** https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
|
data/.github/workflows/ci.yml
CHANGED
@@ -8,9 +8,21 @@ jobs:
|
|
8
8
|
runs-on: ubuntu-latest
|
9
9
|
strategy:
|
10
10
|
matrix:
|
11
|
-
ruby:
|
11
|
+
ruby:
|
12
|
+
[
|
13
|
+
"2.3",
|
14
|
+
"2.4",
|
15
|
+
"2.5",
|
16
|
+
"2.6",
|
17
|
+
"2.7",
|
18
|
+
"3.0",
|
19
|
+
"3.1",
|
20
|
+
"3.2",
|
21
|
+
"3.3",
|
22
|
+
"head",
|
23
|
+
]
|
12
24
|
steps:
|
13
|
-
- uses: actions/checkout@
|
25
|
+
- uses: actions/checkout@v4
|
14
26
|
- name: Set up Ruby
|
15
27
|
uses: ruby/setup-ruby@v1
|
16
28
|
with:
|
@@ -25,7 +37,7 @@ jobs:
|
|
25
37
|
matrix:
|
26
38
|
ruby: ["2.0", "2.1", "2.2"]
|
27
39
|
steps:
|
28
|
-
- uses: actions/checkout@
|
40
|
+
- uses: actions/checkout@v4
|
29
41
|
- name: Set up Ruby
|
30
42
|
uses: ruby/setup-ruby@v1
|
31
43
|
with:
|
@@ -49,7 +61,7 @@ jobs:
|
|
49
61
|
rubocop:
|
50
62
|
runs-on: ubuntu-latest
|
51
63
|
steps:
|
52
|
-
- uses: actions/checkout@
|
64
|
+
- uses: actions/checkout@v4
|
53
65
|
- name: Set up Ruby
|
54
66
|
uses: ruby/setup-ruby@v1
|
55
67
|
with:
|
@@ -59,44 +71,17 @@ jobs:
|
|
59
71
|
run: bundle exec rake lint
|
60
72
|
|
61
73
|
functional:
|
62
|
-
runs-on:
|
74
|
+
runs-on: ubuntu-latest
|
63
75
|
strategy:
|
64
76
|
matrix:
|
65
|
-
ruby:
|
66
|
-
[
|
67
|
-
"2.0",
|
68
|
-
"2.1",
|
69
|
-
"2.2",
|
70
|
-
"2.3",
|
71
|
-
"2.4",
|
72
|
-
"2.5",
|
73
|
-
"2.6",
|
74
|
-
"2.7",
|
75
|
-
"3.0",
|
76
|
-
"3.1",
|
77
|
-
"3.2",
|
78
|
-
"head",
|
79
|
-
]
|
77
|
+
ruby: ["2.0", "ruby"]
|
80
78
|
steps:
|
81
|
-
- uses: actions/checkout@
|
82
|
-
|
83
|
-
- name: Cache Vagrant boxes
|
84
|
-
uses: actions/cache@v3
|
85
|
-
with:
|
86
|
-
path: ~/.vagrant.d/boxes
|
87
|
-
key: ${{ runner.os }}-vagrant-v2-${{ hashFiles('Vagrantfile') }}
|
88
|
-
restore-keys: |
|
89
|
-
${{ runner.os }}-vagrant-v2-
|
90
|
-
|
91
|
-
- name: Run vagrant up
|
92
|
-
run: vagrant up
|
93
|
-
|
79
|
+
- uses: actions/checkout@v4
|
94
80
|
- name: Set up Ruby
|
95
81
|
uses: ruby/setup-ruby@v1
|
96
82
|
with:
|
97
83
|
ruby-version: ${{ matrix.ruby }}
|
98
84
|
bundler-cache: true
|
99
|
-
|
100
85
|
- name: Run functional tests
|
101
86
|
run: bundle exec rake test:functional
|
102
87
|
|
data/.github/workflows/push.yml
CHANGED
data/.gitignore
CHANGED
data/.rubocop_todo.yml
CHANGED
@@ -102,7 +102,6 @@ Layout/IndentHash:
|
|
102
102
|
Exclude:
|
103
103
|
- 'test/functional/backends/test_local.rb'
|
104
104
|
- 'test/functional/backends/test_netssh.rb'
|
105
|
-
- 'test/support/vagrant_wrapper.rb'
|
106
105
|
- 'test/unit/formatters/test_custom.rb'
|
107
106
|
- 'test/unit/formatters/test_pretty.rb'
|
108
107
|
- 'test/unit/test_mapping_interaction_handler.rb'
|
@@ -445,12 +444,6 @@ Style/MethodName:
|
|
445
444
|
Exclude:
|
446
445
|
- 'test/unit/test_color.rb'
|
447
446
|
|
448
|
-
# Offense count: 1
|
449
|
-
# Cop supports --auto-correct.
|
450
|
-
Style/MutableConstant:
|
451
|
-
Exclude:
|
452
|
-
- 'Vagrantfile'
|
453
|
-
|
454
447
|
# Offense count: 1
|
455
448
|
# Cop supports --auto-correct.
|
456
449
|
# Configuration parameters: Strict.
|
data/CONTRIBUTING.md
CHANGED
@@ -24,8 +24,8 @@ using unsupported features.
|
|
24
24
|
|
25
25
|
## Tests
|
26
26
|
|
27
|
-
SSHKit has a unit test suite and a functional test suite. Some functional tests run
|
28
|
-
[
|
27
|
+
SSHKit has a unit test suite and a functional test suite. Some functional tests run using
|
28
|
+
[Docker](https://docs.docker.com/get-docker/). If possible, you should make sure that the
|
29
29
|
tests pass for each commit by running `rake` in the sshkit directory. This is in case we
|
30
30
|
need to cherry pick commits or rebase. You should ensure the tests pass, (preferably on
|
31
31
|
the minimum and maximum ruby version), before creating a PR.
|
data/EXAMPLES.md
CHANGED
@@ -121,9 +121,6 @@ on hosts do |host|
|
|
121
121
|
end
|
122
122
|
```
|
123
123
|
|
124
|
-
**Note:** The `upload!()` method doesn't honor the values of `as()` etc, this
|
125
|
-
will be improved as the library matures, but we're not there yet.
|
126
|
-
|
127
124
|
## Upload a file from a stream
|
128
125
|
|
129
126
|
```ruby
|
@@ -148,9 +145,6 @@ end
|
|
148
145
|
This spares one from having to figure out the correct escaping sequences for
|
149
146
|
something like "echo(:cat, '...?...', '> /etc/sudoers.d/yolo')".
|
150
147
|
|
151
|
-
**Note:** The `upload!()` method doesn't honor the values of `within()`, `as()`
|
152
|
-
etc, this will be improved as the library matures, but we're not there yet.
|
153
|
-
|
154
148
|
## Upload a directory of files
|
155
149
|
|
156
150
|
```ruby
|
@@ -160,7 +154,25 @@ end
|
|
160
154
|
```
|
161
155
|
|
162
156
|
In this case the `recursive: true` option mirrors the same options which are
|
163
|
-
available to [`Net::
|
157
|
+
available to [`Net::SCP`](https://github.com/net-ssh/net-scp) and
|
158
|
+
[`Net::SFTP`](https://github.com/net-ssh/net-sftp).
|
159
|
+
|
160
|
+
## Set the upload/download method (SCP or SFTP).
|
161
|
+
|
162
|
+
SSHKit can use SCP or SFTP for file transfers. The default is SCP, but this can be changed to SFTP per host:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
host = SSHKit::Host.new('user@example.com')
|
166
|
+
host.transfer_method = :sftp
|
167
|
+
```
|
168
|
+
|
169
|
+
or globally:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
SSHKit::Backend::Netssh.configure do |ssh|
|
173
|
+
ssh.transfer_method = :sftp
|
174
|
+
end
|
175
|
+
```
|
164
176
|
|
165
177
|
## Setting global SSH options
|
166
178
|
|
@@ -235,16 +247,16 @@ end
|
|
235
247
|
|
236
248
|
```ruby
|
237
249
|
# The default format is pretty, which outputs colored text
|
238
|
-
SSHKit.config.
|
250
|
+
SSHKit.config.use_format :pretty
|
239
251
|
|
240
252
|
# Text with no coloring
|
241
|
-
SSHKit.config.
|
253
|
+
SSHKit.config.use_format :simpletext
|
242
254
|
|
243
255
|
# Red / Green dots for each completed step
|
244
|
-
SSHKit.config.
|
256
|
+
SSHKit.config.use_format :dot
|
245
257
|
|
246
258
|
# No output
|
247
|
-
SSHKit.config.
|
259
|
+
SSHKit.config.use_format :blackhole
|
248
260
|
```
|
249
261
|
|
250
262
|
## Implement a dirt-simple formatter class
|
@@ -254,7 +266,7 @@ module SSHKit
|
|
254
266
|
module Formatter
|
255
267
|
class MyFormatter < SSHKit::Formatter::Abstract
|
256
268
|
def write(obj)
|
257
|
-
|
269
|
+
if obj.is_a? SSHKit::Command
|
258
270
|
# Do something here, see the SSHKit::Command documentation
|
259
271
|
end
|
260
272
|
end
|
@@ -263,7 +275,7 @@ module SSHKit
|
|
263
275
|
end
|
264
276
|
|
265
277
|
# If your formatter is defined in the SSHKit::Formatter module configure with the format option:
|
266
|
-
SSHKit.config.
|
278
|
+
SSHKit.config.use_format :myformatter
|
267
279
|
|
268
280
|
# Or configure the output directly
|
269
281
|
SSHKit.config.output = MyFormatter.new($stdout)
|
data/README.md
CHANGED
@@ -68,7 +68,8 @@ you can pass the `strip: false` option: `capture(:ls, '-l', strip: false)`
|
|
68
68
|
### Transferring files
|
69
69
|
|
70
70
|
All backends also support the `upload!` and `download!` methods for transferring files.
|
71
|
-
For the remote backend, the file is transferred with scp
|
71
|
+
For the remote backend, the file is transferred with scp by default, but sftp is also
|
72
|
+
supported. See [EXAMPLES.md](EXAMPLES.md) for details.
|
72
73
|
|
73
74
|
```ruby
|
74
75
|
on '1.example.com' do
|
data/RELEASING.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
## How to release
|
10
10
|
|
11
11
|
1. Run `bundle install` to make sure that you have all the gems necessary for testing and releasing.
|
12
|
-
2. **Ensure the tests are passing by running `rake test`.** If functional tests fail, ensure you have [
|
12
|
+
2. **Ensure the tests are passing by running `rake test`.** If functional tests fail, ensure you have [Docker installed](https://docs.docker.com/get-docker/) and running.
|
13
13
|
3. Determine which would be the correct next version number according to [semver](http://semver.org/).
|
14
14
|
4. Update the version in `./lib/sshkit/version.rb`.
|
15
15
|
5. Commit the `version.rb` change with a message like "Preparing vX.Y.Z"
|
data/Rakefile
CHANGED
@@ -21,10 +21,6 @@ namespace :test do
|
|
21
21
|
|
22
22
|
end
|
23
23
|
|
24
|
-
Rake::Task["test:functional"].enhance do
|
25
|
-
warn "Remember there are still some VMs running, kill them with `vagrant halt` if you are finished using them."
|
26
|
-
end
|
27
|
-
|
28
24
|
desc 'Run RuboCop lint checks'
|
29
25
|
RuboCop::RakeTask.new(:lint) do |task|
|
30
26
|
task.options = ['--lint']
|
data/docker-compose.yml
ADDED
@@ -36,8 +36,8 @@ class SSHKit::Backend::ConnectionPool::Cache
|
|
36
36
|
def evict
|
37
37
|
# Peek at the first connection to see if it is still fresh. If so, we can
|
38
38
|
# return right away without needing to use `synchronize`.
|
39
|
-
first_expires_at,
|
40
|
-
return if (first_expires_at.nil? || fresh?(first_expires_at))
|
39
|
+
first_expires_at, _first_conn = connections.first
|
40
|
+
return if (first_expires_at.nil? || fresh?(first_expires_at))
|
41
41
|
|
42
42
|
connections.synchronize do
|
43
43
|
fresh, stale = connections.partition do |expires_at, conn|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "base64"
|
2
|
+
|
1
3
|
module SSHKit
|
2
4
|
|
3
5
|
module Backend
|
@@ -5,12 +7,11 @@ module SSHKit
|
|
5
7
|
class Netssh < Abstract
|
6
8
|
|
7
9
|
class KnownHostsKeys
|
8
|
-
include Mutex_m
|
9
|
-
|
10
10
|
def initialize(path)
|
11
11
|
super()
|
12
12
|
@path = File.expand_path(path)
|
13
13
|
@hosts_keys = nil
|
14
|
+
@mutex = Mutex.new
|
14
15
|
end
|
15
16
|
|
16
17
|
def keys_for(hostlist)
|
@@ -44,7 +45,7 @@ module SSHKit
|
|
44
45
|
end
|
45
46
|
|
46
47
|
def parse_file
|
47
|
-
synchronize do
|
48
|
+
@mutex.synchronize do
|
48
49
|
return if hosts_keys && hosts_hashes
|
49
50
|
|
50
51
|
unless File.readable?(path)
|
@@ -110,11 +111,10 @@ module SSHKit
|
|
110
111
|
end
|
111
112
|
|
112
113
|
class KnownHosts
|
113
|
-
include Mutex_m
|
114
|
-
|
115
114
|
def initialize
|
116
115
|
super()
|
117
116
|
@files = {}
|
117
|
+
@mutex = Mutex.new
|
118
118
|
end
|
119
119
|
|
120
120
|
def search_for(host, options = {})
|
@@ -126,13 +126,13 @@ module SSHKit
|
|
126
126
|
|
127
127
|
def add(*args)
|
128
128
|
::Net::SSH::KnownHosts.add(*args)
|
129
|
-
synchronize { @files = {} }
|
129
|
+
@mutex.synchronize { @files = {} }
|
130
130
|
end
|
131
131
|
|
132
132
|
private
|
133
133
|
|
134
134
|
def known_hosts_file(path)
|
135
|
-
@files[path] || synchronize { @files[path] ||= KnownHostsKeys.new(path) }
|
135
|
+
@files[path] || @mutex.synchronize { @files[path] ||= KnownHostsKeys.new(path) }
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
@@ -140,4 +140,4 @@ module SSHKit
|
|
140
140
|
|
141
141
|
end
|
142
142
|
|
143
|
-
end
|
143
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "net/scp"
|
2
|
+
|
3
|
+
module SSHKit
|
4
|
+
module Backend
|
5
|
+
class Netssh < Abstract
|
6
|
+
class ScpTransfer
|
7
|
+
def initialize(ssh, summarizer)
|
8
|
+
@ssh = ssh
|
9
|
+
@summarizer = summarizer
|
10
|
+
end
|
11
|
+
|
12
|
+
def upload!(local, remote, options)
|
13
|
+
ssh.scp.upload!(local, remote, options, &summarizer)
|
14
|
+
end
|
15
|
+
|
16
|
+
def download!(remote, local, options)
|
17
|
+
ssh.scp.download!(remote, local, options, &summarizer)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :ssh, :summarizer
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "net/sftp"
|
2
|
+
|
3
|
+
module SSHKit
|
4
|
+
module Backend
|
5
|
+
class Netssh < Abstract
|
6
|
+
class SftpTransfer
|
7
|
+
def initialize(ssh, summarizer)
|
8
|
+
@ssh = ssh
|
9
|
+
@summarizer = summarizer
|
10
|
+
end
|
11
|
+
|
12
|
+
def upload!(local, remote, options)
|
13
|
+
options = { progress: self }.merge(options || {})
|
14
|
+
ssh.sftp.connect!
|
15
|
+
ssh.sftp.upload!(local, remote, options)
|
16
|
+
ensure
|
17
|
+
ssh.sftp.close_channel
|
18
|
+
end
|
19
|
+
|
20
|
+
def download!(remote, local, options)
|
21
|
+
options = { progress: self }.merge(options || {})
|
22
|
+
destination = local ? local : StringIO.new.tap { |io| io.set_encoding('BINARY') }
|
23
|
+
|
24
|
+
ssh.sftp.connect!
|
25
|
+
ssh.sftp.download!(remote, destination, options)
|
26
|
+
local ? true : destination.string
|
27
|
+
ensure
|
28
|
+
ssh.sftp.close_channel
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_get(download, entry, offset, data)
|
32
|
+
entry.size ||= download.sftp.file.open(entry.remote) { |file| file.stat.size }
|
33
|
+
summarizer.call(nil, entry.remote, offset + data.bytesize, entry.size)
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_put(_upload, file, offset, data)
|
37
|
+
summarizer.call(nil, file.local, offset + data.bytesize, file.size)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :ssh, :summarizer
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'English'
|
2
2
|
require 'strscan'
|
3
|
-
require 'mutex_m'
|
4
3
|
require 'net/ssh'
|
5
|
-
require 'net/scp'
|
6
4
|
|
7
5
|
module Net
|
8
6
|
module SSH
|
@@ -23,10 +21,27 @@ module SSHKit
|
|
23
21
|
module Backend
|
24
22
|
|
25
23
|
class Netssh < Abstract
|
24
|
+
def self.assert_valid_transfer_method!(method)
|
25
|
+
return if [:scp, :sftp].include?(method)
|
26
|
+
|
27
|
+
raise ArgumentError, "#{method.inspect} is not a valid transfer method. Supported methods are :scp, :sftp."
|
28
|
+
end
|
29
|
+
|
26
30
|
class Configuration
|
27
31
|
attr_accessor :connection_timeout, :pty
|
32
|
+
attr_reader :transfer_method
|
28
33
|
attr_writer :ssh_options
|
29
34
|
|
35
|
+
def initialize
|
36
|
+
self.transfer_method = :scp
|
37
|
+
end
|
38
|
+
|
39
|
+
def transfer_method=(method)
|
40
|
+
Netssh.assert_valid_transfer_method!(method)
|
41
|
+
|
42
|
+
@transfer_method = method
|
43
|
+
end
|
44
|
+
|
30
45
|
def ssh_options
|
31
46
|
default_options.merge(@ssh_options ||= {})
|
32
47
|
end
|
@@ -64,16 +79,16 @@ module SSHKit
|
|
64
79
|
def upload!(local, remote, options = {})
|
65
80
|
summarizer = transfer_summarizer('Uploading', options)
|
66
81
|
remote = File.join(pwd_path, remote) unless remote.to_s.start_with?("/") || pwd_path.nil?
|
67
|
-
|
68
|
-
|
82
|
+
with_transfer(summarizer) do |transfer|
|
83
|
+
transfer.upload!(local, remote, options)
|
69
84
|
end
|
70
85
|
end
|
71
86
|
|
72
87
|
def download!(remote, local=nil, options = {})
|
73
88
|
summarizer = transfer_summarizer('Downloading', options)
|
74
89
|
remote = File.join(pwd_path, remote) unless remote.to_s.start_with?("/") || pwd_path.nil?
|
75
|
-
|
76
|
-
|
90
|
+
with_transfer(summarizer) do |transfer|
|
91
|
+
transfer.download!(remote, local, options)
|
77
92
|
end
|
78
93
|
end
|
79
94
|
|
@@ -105,7 +120,7 @@ module SSHKit
|
|
105
120
|
last_percentage = nil
|
106
121
|
proc do |_ch, name, transferred, total|
|
107
122
|
percentage = (transferred.to_f * 100 / total.to_f)
|
108
|
-
unless percentage.nan?
|
123
|
+
unless percentage.nan? || percentage.infinite?
|
109
124
|
message = "#{action} #{name} #{percentage.round(2)}%"
|
110
125
|
percentage_r = (percentage / log_percent).truncate * log_percent
|
111
126
|
if percentage_r > 0 && (last_name != name || last_percentage != percentage_r)
|
@@ -183,6 +198,20 @@ module SSHKit
|
|
183
198
|
)
|
184
199
|
end
|
185
200
|
|
201
|
+
def with_transfer(summarizer)
|
202
|
+
transfer_method = host.transfer_method || self.class.config.transfer_method
|
203
|
+
transfer_class = if transfer_method == :sftp
|
204
|
+
require_relative "netssh/sftp_transfer"
|
205
|
+
SftpTransfer
|
206
|
+
else
|
207
|
+
require_relative "netssh/scp_transfer"
|
208
|
+
ScpTransfer
|
209
|
+
end
|
210
|
+
|
211
|
+
with_ssh do |ssh|
|
212
|
+
yield(transfer_class.new(ssh, summarizer))
|
213
|
+
end
|
214
|
+
end
|
186
215
|
end
|
187
216
|
end
|
188
217
|
|
data/lib/sshkit/host.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'ostruct'
|
2
|
+
require 'resolv'
|
2
3
|
|
3
4
|
module SSHKit
|
4
5
|
|
@@ -7,6 +8,7 @@ module SSHKit
|
|
7
8
|
class Host
|
8
9
|
|
9
10
|
attr_accessor :password, :hostname, :port, :user, :ssh_options
|
11
|
+
attr_reader :transfer_method
|
10
12
|
|
11
13
|
def key=(new_key)
|
12
14
|
@keys = [new_key]
|
@@ -41,6 +43,12 @@ module SSHKit
|
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
46
|
+
def transfer_method=(method)
|
47
|
+
Backend::Netssh.assert_valid_transfer_method!(method) unless method.nil?
|
48
|
+
|
49
|
+
@transfer_method = method
|
50
|
+
end
|
51
|
+
|
44
52
|
def local?
|
45
53
|
@local
|
46
54
|
end
|
@@ -115,6 +123,22 @@ module SSHKit
|
|
115
123
|
|
116
124
|
end
|
117
125
|
|
126
|
+
# @private
|
127
|
+
# :nodoc:
|
128
|
+
class IPv6HostParser < SimpleHostParser
|
129
|
+
def self.suitable?(host_string)
|
130
|
+
host_string.match(Resolv::IPv6::Regex)
|
131
|
+
end
|
132
|
+
|
133
|
+
def port
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
def hostname
|
138
|
+
@host_string.match(Resolv::IPv6::Regex)[0]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
118
142
|
class HostWithPortParser < SimpleHostParser
|
119
143
|
|
120
144
|
def self.suitable?(host_string)
|
@@ -185,6 +209,7 @@ module SSHKit
|
|
185
209
|
|
186
210
|
PARSERS = [
|
187
211
|
SimpleHostParser,
|
212
|
+
IPv6HostParser,
|
188
213
|
HostWithPortParser,
|
189
214
|
HostWithUsernameAndPortParser,
|
190
215
|
IPv6HostWithPortParser,
|
data/lib/sshkit/version.rb
CHANGED
data/sshkit.gemspec
CHANGED
@@ -20,8 +20,10 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.require_paths = ["lib"]
|
21
21
|
gem.version = SSHKit::VERSION
|
22
22
|
|
23
|
+
gem.add_runtime_dependency('base64') if RUBY_VERSION >= "2.4"
|
23
24
|
gem.add_runtime_dependency('net-ssh', '>= 2.8.0')
|
24
25
|
gem.add_runtime_dependency('net-scp', '>= 1.1.2')
|
26
|
+
gem.add_runtime_dependency('net-sftp', '>= 2.1.2')
|
25
27
|
|
26
28
|
gem.add_development_dependency('danger')
|
27
29
|
gem.add_development_dependency('minitest', '>= 5.0.0')
|