sshkit 1.12.0 → 1.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2075a49a2e5d52e26409761fffbb3da8dd6bc46d
4
- data.tar.gz: d9579584daf0ac0f3d2170fa4dc0c2bc57d3ac55
3
+ metadata.gz: 63716bfbb3150fe84235f1161bc7ecc9d303155c
4
+ data.tar.gz: 91103b89d73d2c9f56ade1ebd21ae80f85f6fb24
5
5
  SHA512:
6
- metadata.gz: 4a84ef1bf0b996cf4a3ecb0d855f8d7b872c1d0bc31f46804908b3f51f31cb1f2af0ff7d39e7bc498f8a7aecf05576116bff008b8955b853b6e39c3fcb6f71bf
7
- data.tar.gz: b2e42bf3216b4fdfbdd531329a09e9534775b71eb15dd2853319dca4ac358623b494474f179f136034beee435fd424f03b13d64fe6eb64dd283da39fcd6ae3f8
6
+ metadata.gz: 0cd4182399d780bc1a760be58589ee74bcbd1a8428d6ba1007c1f5a47ff587d3ce5f4a93258cac4be3d38f3e9a5f0bbc6e546ce5b4b72469b31195bdf2060dee
7
+ data.tar.gz: b022fccb0e7c55965b5a58420878fd6340eea1fd3a0ac5a47a50573e1149719fc784970c1f1956cfd3ea7ccb5b11ae413082c306806030059b5e0d2e0b0ada64
data/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ appear at the top.
7
7
 
8
8
  * Your contribution here!
9
9
 
10
+ ## [1.13.0][] (2017-03-24)
11
+
12
+ ### Breaking changes
13
+
14
+ * None
15
+
16
+ ### New features
17
+
18
+ * [#372](https://github.com/capistrano/sshkit/pull/372): Use cp_r in local backend with recursive option - [@okuramasafumi](https://github.com/okuramasafumi)
19
+
20
+ ### Bug fixes
21
+
22
+ * [#390](https://github.com/capistrano/sshkit/pull/390): Properly wrap Ruby StandardError w/ add'l context - [@mattbrictson](https://github.com/mattbrictson)
23
+ * [#392](https://github.com/capistrano/sshkit/pull/392): Fix open two connections with changed cache key - [@shirosaki](https://github.com/shirosaki)
24
+
10
25
  ## [1.12.0][] (2017-02-10)
11
26
 
12
27
  ### Breaking changes
data/Dangerfile CHANGED
@@ -1,54 +1 @@
1
- # Adapted from https://github.com/ruby-grape/danger/blob/master/Dangerfile
2
- # Q: What is a Dangerfile, anyway? A: See http://danger.systems/
3
-
4
- # ------------------------------------------------------------------------------
5
- # Additional pull request data
6
- # ------------------------------------------------------------------------------
7
- project_name = github.pr_json["base"]["repo"]["name"]
8
- pr_number = github.pr_json["number"]
9
- pr_url = github.pr_json["_links"]["html"]["href"]
10
-
11
- # ------------------------------------------------------------------------------
12
- # What changed?
13
- # ------------------------------------------------------------------------------
14
- has_lib_changes = !git.modified_files.grep(/^lib/).empty?
15
- has_test_changes = !git.modified_files.grep(/^test/).empty?
16
- has_changelog_changes = git.modified_files.include?("CHANGELOG.md")
17
-
18
- # ------------------------------------------------------------------------------
19
- # You've made changes to lib, but didn't write any tests?
20
- # ------------------------------------------------------------------------------
21
- if has_lib_changes && !has_test_changes
22
- warn("There are code changes, but no corresponding tests. "\
23
- "Please include tests if this PR introduces any modifications in "\
24
- "#{project_name}'s behavior.",
25
- :sticky => false)
26
- end
27
-
28
- # ------------------------------------------------------------------------------
29
- # Have you updated CHANGELOG.md?
30
- # ------------------------------------------------------------------------------
31
- if !has_changelog_changes && has_lib_changes
32
- markdown <<-MARKDOWN
33
- Here's an example of a CHANGELOG.md entry (place it immediately under the `* Your contribution here!` line):
34
-
35
- ```markdown
36
- * [##{pr_number}](#{pr_url}): #{github.pr_title} - [@#{github.pr_author}](https://github.com/#{github.pr_author}).
37
- ```
38
- MARKDOWN
39
- warn("Please update CHANGELOG.md with a description of your changes. "\
40
- "If this PR is not a user-facing change (e.g. just refactoring), "\
41
- "you can disregard this.", :sticky => false)
42
- end
43
-
44
- # ------------------------------------------------------------------------------
45
- # Did you remove the CHANGELOG's "Your contribution here!" line?
46
- # ------------------------------------------------------------------------------
47
- if has_changelog_changes
48
- unless IO.read("CHANGELOG.md") =~ /^\s*\* Your contribution here/i
49
- fail(
50
- "Please put the `* Your contribution here!` line back into CHANGELOG.md.",
51
- :sticky => false
52
- )
53
- end
54
- end
1
+ danger.import_dangerfile(github: "capistrano/danger")
@@ -1,7 +1,10 @@
1
1
  # A Cache holds connections for a given key. Each connection is stored along
2
2
  # with an expiration time so that its idle duration can be measured.
3
3
  class SSHKit::Backend::ConnectionPool::Cache
4
- def initialize(idle_timeout, closer)
4
+ attr_accessor :key
5
+
6
+ def initialize(key, idle_timeout, closer)
7
+ @key = key
5
8
  @connections = []
6
9
  @connections.extend(MonitorMixin)
7
10
  @idle_timeout = idle_timeout
@@ -53,6 +56,10 @@ class SSHKit::Backend::ConnectionPool::Cache
53
56
  end
54
57
  end
55
58
 
59
+ def same_key?(other_key)
60
+ key == other_key
61
+ end
62
+
56
63
  protected
57
64
 
58
65
  attr_reader :connections, :idle_timeout, :closer
@@ -8,4 +8,8 @@ SSHKit::Backend::ConnectionPool::NilCache = Struct.new(:closer) do
8
8
  def push(conn)
9
9
  closer.call(conn)
10
10
  end
11
+
12
+ def same_key?(_key)
13
+ true
14
+ end
11
15
  end
@@ -61,6 +61,9 @@ class SSHKit::Backend::ConnectionPool
61
61
  yield(conn)
62
62
  ensure
63
63
  cache.push(conn) unless conn.nil?
64
+ # Sometimes the args mutate as a result of opening a connection. In this
65
+ # case we need to update the cache key to match the new args.
66
+ update_key_if_args_changed(cache, args)
64
67
  end
65
68
 
66
69
  # Immediately remove all cached connections, without closing them. This only
@@ -84,6 +87,10 @@ class SSHKit::Backend::ConnectionPool
84
87
 
85
88
  private
86
89
 
90
+ def cache_key_for_connection_args(args)
91
+ args.to_s
92
+ end
93
+
87
94
  def cache_enabled?
88
95
  idle_timeout && idle_timeout > 0
89
96
  end
@@ -91,7 +98,7 @@ class SSHKit::Backend::ConnectionPool
91
98
  # Look up a Cache that matches the given connection arguments.
92
99
  def find_cache(args)
93
100
  if cache_enabled?
94
- key = args.to_s
101
+ key = cache_key_for_connection_args(args)
95
102
  caches[key] || thread_safe_find_or_create_cache(key)
96
103
  else
97
104
  NilCache.new(method(:silently_close_connection))
@@ -103,11 +110,22 @@ class SSHKit::Backend::ConnectionPool
103
110
  def thread_safe_find_or_create_cache(key)
104
111
  caches.synchronize do
105
112
  caches[key] ||= begin
106
- Cache.new(idle_timeout, method(:silently_close_connection_later))
113
+ Cache.new(key, idle_timeout, method(:silently_close_connection_later))
107
114
  end
108
115
  end
109
116
  end
110
117
 
118
+ # Update cache key with changed args to prevent cache miss
119
+ def update_key_if_args_changed(cache, args)
120
+ new_key = cache_key_for_connection_args(args)
121
+ return if cache.same_key?(new_key)
122
+
123
+ caches.synchronize do
124
+ caches[new_key] = caches.delete(cache.key)
125
+ cache.key = new_key
126
+ end
127
+ end
128
+
111
129
  # Loops indefinitely to close connections and to find abandoned connections
112
130
  # that need to be closed.
113
131
  def run_eviction_loop
@@ -10,9 +10,13 @@ module SSHKit
10
10
  super(Host.new(:local), &block)
11
11
  end
12
12
 
13
- def upload!(local, remote, _options = {})
13
+ def upload!(local, remote, options = {})
14
14
  if local.is_a?(String)
15
- FileUtils.cp(local, remote)
15
+ if options[:recursive]
16
+ FileUtils.cp_r(local, remote)
17
+ else
18
+ FileUtils.cp(local, remote)
19
+ end
16
20
  else
17
21
  File.open(remote, "wb") do |f|
18
22
  IO.copy_stream(local, f)
@@ -36,12 +36,20 @@ module SSHKit
36
36
  if Net::SSH::VALID_OPTIONS.include?(:known_hosts)
37
37
  def default_options
38
38
  @default_options ||= {known_hosts: SSHKit::Backend::Netssh::KnownHosts.new}
39
+ assign_defaults
39
40
  end
40
41
  else
41
42
  def default_options
42
43
  @default_options ||= {}
44
+ assign_defaults
43
45
  end
44
46
  end
47
+
48
+ # Set default options early for ConnectionPool cache key
49
+ def assign_defaults
50
+ Net::SSH.assign_defaults(@default_options)
51
+ @default_options
52
+ end
45
53
  end
46
54
 
47
55
  def upload!(local, remote, options = {})
@@ -10,7 +10,7 @@ module SSHKit
10
10
  Thread.new(host) do |h|
11
11
  begin
12
12
  backend(h, &block).run
13
- rescue StandardError => e
13
+ rescue ::StandardError => e
14
14
  e2 = ExecuteError.new e
15
15
  raise e2, "Exception while executing #{host.user ? "as #{host.user}@" : "on host "}#{host}: #{e.message}"
16
16
  end
@@ -26,7 +26,7 @@ module SSHKit
26
26
  private
27
27
  def run_backend(host, &block)
28
28
  backend(host, &block).run
29
- rescue StandardError => e
29
+ rescue ::StandardError => e
30
30
  e2 = ExecuteError.new e
31
31
  raise e2, "Exception while executing #{host.user ? "as #{host.user}@" : "on host "}#{host}: #{e.message}"
32
32
  end
@@ -1,3 +1,3 @@
1
1
  module SSHKit
2
- VERSION = "1.12.0".freeze
2
+ VERSION = "1.13.0".freeze
3
3
  end
@@ -10,6 +10,30 @@ module SSHKit
10
10
  SSHKit.config.output = SSHKit::Formatter::BlackHole.new($stdout)
11
11
  end
12
12
 
13
+ def test_upload
14
+ Dir.mktmpdir do |dir|
15
+ File.new("#{dir}/local", 'w')
16
+ Local.new do
17
+ upload!("#{dir}/local", "#{dir}/remote")
18
+ end.run
19
+ assert File.exist?("#{dir}/remote")
20
+ end
21
+ end
22
+
23
+ def test_upload_recursive
24
+ Dir.mktmpdir do |dir|
25
+ Dir.mkdir("#{dir}/local")
26
+ File.new("#{dir}/local/file1", 'w')
27
+ File.new("#{dir}/local/file2", 'w')
28
+ Local.new do
29
+ upload!("#{dir}/local", "#{dir}/remote", recursive: true)
30
+ end.run
31
+ assert File.directory?("#{dir}/remote")
32
+ assert File.exist?("#{dir}/remote/file1")
33
+ assert File.exist?("#{dir}/remote/file2")
34
+ end
35
+ end
36
+
13
37
  def test_capture
14
38
  captured_command_result = ''
15
39
  Local.new do
@@ -59,7 +59,7 @@ module SSHKit
59
59
  capture(:uname)
60
60
  host_ssh_options = host.ssh_options
61
61
  end.run
62
- assert_equal [:forward_agent, :paranoid, :known_hosts].sort, host_ssh_options.keys.sort
62
+ assert_equal [:forward_agent, :paranoid, :known_hosts, :logger, :password_prompt].sort, host_ssh_options.keys.sort
63
63
  assert_equal false, host_ssh_options[:forward_agent]
64
64
  assert_equal true, host_ssh_options[:paranoid]
65
65
  assert_instance_of SSHKit::Backend::Netssh::KnownHosts, host_ssh_options[:known_hosts]
@@ -134,6 +134,15 @@ module SSHKit
134
134
  pool.close_connections
135
135
  end
136
136
  end
137
+
138
+ def test_connections_with_changed_args_is_reused
139
+ options = { known_hosts: "foo" }
140
+ connect_change_options = ->(*args) { args.last[:known_hosts] = "bar"; Object.new }
141
+ conn1 = pool.with(connect_change_options, "arg", options) { |c| c }
142
+ conn2 = pool.with(connect_change_options, "arg", options) { |c| c }
143
+
144
+ assert_equal conn1, conn2
145
+ end
137
146
  end
138
147
  end
139
148
  end
@@ -0,0 +1,17 @@
1
+ require "helper"
2
+ require "sshkit"
3
+
4
+ module SSHKit
5
+ module Runner
6
+ class TestGroup < UnitTest
7
+ def test_wraps_ruby_standard_error_in_execute_error
8
+ localhost = Host.new(:local)
9
+ runner = Group.new([localhost]) { raise "oh no!" }
10
+ error = assert_raises(SSHKit::Runner::ExecuteError) do
11
+ runner.execute
12
+ end
13
+ assert_match(/while executing.*localhost/, error.message)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ require "helper"
2
+ require "sshkit"
3
+
4
+ module SSHKit
5
+ module Runner
6
+ class TestParallel < UnitTest
7
+ def test_wraps_ruby_standard_error_in_execute_error
8
+ host = Host.new("deployer@example")
9
+ runner = Parallel.new([host]) { raise "oh no!" }
10
+ error = assert_raises(SSHKit::Runner::ExecuteError) do
11
+ runner.execute
12
+ end
13
+ assert_match(/deployer@example/, error.message)
14
+ assert_match(/oh no!/, error.message)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require "helper"
2
+ require "sshkit"
3
+
4
+ module SSHKit
5
+ module Runner
6
+ class TestSequential < UnitTest
7
+ def test_wraps_ruby_standard_error_in_execute_error
8
+ host = Host.new("deployer@example")
9
+ runner = Sequential.new([host]) { raise "oh no!" }
10
+ error = assert_raises(SSHKit::Runner::ExecuteError) do
11
+ runner.execute
12
+ end
13
+ assert_match(/deployer@example/, error.message)
14
+ assert_match(/oh no!/, error.message)
15
+ end
16
+ end
17
+ end
18
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sshkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Hambley
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-02-10 00:00:00.000000000 Z
12
+ date: 2017-03-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: net-ssh
@@ -218,6 +218,9 @@ files:
218
218
  - test/unit/formatters/test_dot.rb
219
219
  - test/unit/formatters/test_pretty.rb
220
220
  - test/unit/formatters/test_simple_text.rb
221
+ - test/unit/runners/test_group.rb
222
+ - test/unit/runners/test_parallel.rb
223
+ - test/unit/runners/test_sequential.rb
221
224
  - test/unit/test_color.rb
222
225
  - test/unit/test_command.rb
223
226
  - test/unit/test_command_map.rb
@@ -248,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
248
251
  version: '0'
249
252
  requirements: []
250
253
  rubyforge_project:
251
- rubygems_version: 2.6.10
254
+ rubygems_version: 2.6.11
252
255
  signing_key:
253
256
  specification_version: 4
254
257
  summary: SSHKit makes it easy to write structured, testable SSH commands in Ruby
@@ -271,6 +274,9 @@ test_files:
271
274
  - test/unit/formatters/test_dot.rb
272
275
  - test/unit/formatters/test_pretty.rb
273
276
  - test/unit/formatters/test_simple_text.rb
277
+ - test/unit/runners/test_group.rb
278
+ - test/unit/runners/test_parallel.rb
279
+ - test/unit/runners/test_sequential.rb
274
280
  - test/unit/test_color.rb
275
281
  - test/unit/test_command.rb
276
282
  - test/unit/test_command_map.rb