sshkit 1.7.1 → 1.8.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -2
  3. data/BREAKING_API_WISHLIST.md +14 -0
  4. data/CHANGELOG.md +74 -0
  5. data/CONTRIBUTING.md +43 -0
  6. data/EXAMPLES.md +265 -169
  7. data/Gemfile +7 -0
  8. data/README.md +274 -9
  9. data/RELEASING.md +16 -8
  10. data/Rakefile +8 -0
  11. data/lib/sshkit.rb +0 -9
  12. data/lib/sshkit/all.rb +6 -4
  13. data/lib/sshkit/backends/abstract.rb +42 -42
  14. data/lib/sshkit/backends/connection_pool.rb +57 -8
  15. data/lib/sshkit/backends/local.rb +21 -50
  16. data/lib/sshkit/backends/netssh.rb +45 -98
  17. data/lib/sshkit/backends/printer.rb +3 -23
  18. data/lib/sshkit/backends/skipper.rb +4 -8
  19. data/lib/sshkit/color.rb +51 -20
  20. data/lib/sshkit/command.rb +68 -47
  21. data/lib/sshkit/configuration.rb +38 -5
  22. data/lib/sshkit/deprecation_logger.rb +17 -0
  23. data/lib/sshkit/formatters/abstract.rb +28 -4
  24. data/lib/sshkit/formatters/black_hole.rb +1 -2
  25. data/lib/sshkit/formatters/dot.rb +3 -10
  26. data/lib/sshkit/formatters/pretty.rb +31 -56
  27. data/lib/sshkit/formatters/simple_text.rb +6 -44
  28. data/lib/sshkit/host.rb +5 -6
  29. data/lib/sshkit/logger.rb +0 -1
  30. data/lib/sshkit/mapping_interaction_handler.rb +47 -0
  31. data/lib/sshkit/runners/parallel.rb +1 -1
  32. data/lib/sshkit/runners/sequential.rb +1 -1
  33. data/lib/sshkit/version.rb +1 -1
  34. data/sshkit.gemspec +0 -1
  35. data/test/functional/backends/test_local.rb +14 -1
  36. data/test/functional/backends/test_netssh.rb +58 -50
  37. data/test/helper.rb +2 -2
  38. data/test/unit/backends/test_abstract.rb +145 -0
  39. data/test/unit/backends/test_connection_pool.rb +27 -2
  40. data/test/unit/backends/test_printer.rb +47 -47
  41. data/test/unit/formatters/test_custom.rb +65 -0
  42. data/test/unit/formatters/test_dot.rb +25 -32
  43. data/test/unit/formatters/test_pretty.rb +114 -22
  44. data/test/unit/formatters/test_simple_text.rb +83 -0
  45. data/test/unit/test_color.rb +69 -5
  46. data/test/unit/test_command.rb +53 -18
  47. data/test/unit/test_command_map.rb +0 -4
  48. data/test/unit/test_configuration.rb +47 -7
  49. data/test/unit/test_coordinator.rb +45 -52
  50. data/test/unit/test_deprecation_logger.rb +38 -0
  51. data/test/unit/test_host.rb +3 -4
  52. data/test/unit/test_logger.rb +0 -1
  53. data/test/unit/test_mapping_interaction_handler.rb +101 -0
  54. metadata +37 -41
  55. data/lib/sshkit/utils/capture_output_methods.rb +0 -13
  56. data/test/functional/test_coordinator.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c6c3862edc2b96903f2457b6c7cb31befdb32c7b
4
- data.tar.gz: 4174731b56ae910ebd531b92e00d01c1b02f47f4
3
+ metadata.gz: a957fee02819d1f897b5b77ee0311b7ad7d4dd9e
4
+ data.tar.gz: 2b84ecc03637cf6d23664fa8c00f98a165d12db4
5
5
  SHA512:
6
- metadata.gz: a9d4f9d3a3ac85805a4eaa2e810ae8c000308439a25bf1fa5046748f0a2a309a335d1cce420456fd321261ffc6547ff9377d773adab315bc7f266cd5edff0a86
7
- data.tar.gz: d2c81db2fd65cccb1a901096973c60d02dd26b60f49743435e03c0bc93fc58d7a16f2f90349b8593735e991ad1308007768793ccfa9d7c5cd09e48d45b38a1df
6
+ metadata.gz: a84d2b9ca8f28c5a32eb8a97b2b8f444f8c77885d07321eca21e6e62b2c9bfb2fbb35c59f02e6b9e920cf5ca6b91bb66e6e5965118f4269dfc2d22a5f292e950
7
+ data.tar.gz: 83a1190b38ca613ec201909b974c7f04ce0c9619f02493b45c29f04cde6e3bca1a98304ce5ec72a2858739e7effd3117ce11d1ea0fcc850855b8954c5d91a539
data/.travis.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.2.2
4
+ - 2.1.6
3
5
  - 2.0.0
4
- - 1.9.3
5
- - 1.9.2
6
6
  script: "rake test:units"
@@ -0,0 +1,14 @@
1
+ # Breaking API Wishlist
2
+
3
+ SSHKit respects semantic versioning. This file is a place to record breaking API improvements
4
+ which could be considered at the next major release.
5
+
6
+ * Consider no longer stripping by default on `capture` [#249](https://github.com/capistrano/sshkit/pull/249)
7
+
8
+ ## Deprecated code which could be deleted in a future major release
9
+
10
+ * [Abstract.background method](lib/sshkit/backends/abstract.rb#L43)
11
+ * [`@stderr`, `@stdout` attrs on `Command`](lib/sshkit/command.rb#L28)
12
+
13
+ ## Cleanup when Ruby 1.9 support is dropped
14
+ * `to_a` can probably be removed from `"str".lines.to_a`, since `"str".lines` returns an `Array` under Ruby 2
data/CHANGELOG.md CHANGED
@@ -8,6 +8,80 @@ appear at the top.
8
8
  * Add your entries below here, remember to credit yourself however you want
9
9
  to be credited!
10
10
 
11
+ ## 1.8.0
12
+
13
+ * add SSHKit::Backend::ConnectionPool#close_connections
14
+ [PR #285](https://github.com/capistrano/sshkit/pull/285)
15
+ @akm
16
+ * Clean up rubocop lint warnings
17
+ [PR #275](https://github.com/capistrano/sshkit/pull/275)
18
+ @cshaffer
19
+ * Prepend unused parameter names with an underscore
20
+ * Prefer “safe assignment in condition”
21
+ * Disambiguate regexp literals with parens
22
+ * Prefer `sprintf` over `String#%`
23
+ * No longer shadow `caller_line` variable in `DeprecationLogger`
24
+ * Rescue `StandardError` instead of `Exception`
25
+ * Remove useless `private` access modifier in `TestAbstract`
26
+ * Disambiguate block operator with parens
27
+ * Disambiguate between grouped expression and method params
28
+ * Remove assertion in `TestHost#test_assert_hosts_compare_equal` that compares something with itself
29
+ * Export environment variables and execute command in a subshell.
30
+ [PR #273](https://github.com/capistrano/sshkit/pull/273)
31
+ @kuon
32
+ * Introduce `log_command_start`, `log_command_data`, `log_command_exit` methods on `Formatter`
33
+ [PR #257](https://github.com/capistrano/sshkit/pull/257)
34
+ @robd
35
+ * Deprecate `@stdout` and `@stderr` accessors on `Command`
36
+ * Add support for deprecation logging options.
37
+ [README](README.md#deprecation-warnings),
38
+ [PR #258](https://github.com/capistrano/sshkit/pull/258)
39
+ @robd
40
+ * Quote environment variable values.
41
+ [PR #250](https://github.com/capistrano/sshkit/pull/250)
42
+ @Sinjo - Chris Sinjakli
43
+ * Simplified formatter hierarchy.
44
+ [PR #248](https://github.com/capistrano/sshkit/pull/248)
45
+ @robd
46
+ * `SimpleText` formatter now extends `Pretty`, rather than duplicating.
47
+ * Hide ANSI color escape sequences when outputting to a file.
48
+ [README](README.md#output-colors),
49
+ [Issue #245](https://github.com/capistrano/sshkit/issues/245),
50
+ [PR #246](https://github.com/capistrano/sshkit/pull/246)
51
+ @robd
52
+ * Now only color the output if it is associated with a tty,
53
+ or the `SSHKIT_COLOR` environment variable is set.
54
+ * Removed broken support for assigning an `IO` to the `output` config option.
55
+ [Issue #243](https://github.com/capistrano/sshkit/issues/243),
56
+ [PR #244](https://github.com/capistrano/sshkit/pull/244)
57
+ @robd
58
+ * Use `SSHKit.config.output = SSHKit::Formatter::SimpleText.new($stdin)` instead
59
+ * Added support for `:interaction_handler` option on commands.
60
+ [PR #234](https://github.com/capistrano/sshkit/pull/234),
61
+ [PR #242](https://github.com/capistrano/sshkit/pull/242)
62
+ @robd
63
+ * Removed partially supported `TRACE` log level.
64
+ [2aa7890](https://github.com/capistrano/sshkit/commit/2aa78905f0c521ad9f697e7a4ed04ba438d5ee78)
65
+ @robd
66
+ * Add support for the `:strip` option to the `capture` method and strip by default on the `Local` backend.
67
+ [PR #239](https://github.com/capistrano/sshkit/pull/239),
68
+ [PR #249](https://github.com/capistrano/sshkit/pull/249)
69
+ @robd
70
+ * The `Local` backend now strips by default to be consistent with the `Netssh` one.
71
+ * This reverses change [7d15a9a](https://github.com/capistrano/sshkit/commit/7d15a9aebfcc43807c8151bf6f3a4bc038ce6218) to the `Local` capture API to remove stripping by default.
72
+ * If you require the raw, unstripped output, pass the `strip: false` option: `capture(:ls, strip: false)`
73
+ * Simplified backend hierarchy.
74
+ [PR #235](https://github.com/capistrano/sshkit/pull/235),
75
+ [PR #237](https://github.com/capistrano/sshkit/pull/237)
76
+ @robd
77
+ * Moved duplicate implementations of `make`, `rake`, `test`, `capture`, `background` on to `Abstract` backend.
78
+ * Backend implementations now only need to implement `execute_command`, `upload!` and `download!`
79
+ * Removed `Printer` from backend hierarchy for `Local` and `Netssh` backends (they now just extend `Abstract`)
80
+ * Removed unused `Net::SSH:LogLevelShim`
81
+ * Removed dependency on the `colorize` gem. SSHKit now implements its own ANSI color logic, with no external dependencies. Note that SSHKit now only supports the `:bold` or plain modes. Other modes will be gracefully ignored. [#263](https://github.com/capistrano/sshkit/issues/263)
82
+ * New API for setting the formatter: `use_format`. This differs from `format=` in that it accepts options or arguments that will be passed to the formatter's constructor. The `format=` syntax will be deprecated in a future release. [#295](https://github.com/capistrano/sshkit/issues/295)
83
+ * SSHKit now immediately raises a `NameError` if you try to set a formatter that does not exist. [#295](https://github.com/capistrano/sshkit/issues/295)
84
+
11
85
  ## 1.7.1
12
86
 
13
87
  * Fix a regression in 1.7.0 that caused command completion messages to be removed from log output. @mattbrictson
data/CONTRIBUTING.md CHANGED
@@ -2,3 +2,46 @@
2
2
 
3
3
  * [**Don't** push your pull request](http://www.igvita.com/2011/12/19/dont-push-your-pull-requests/)
4
4
  * [**Do** write a good commit message](http://365git.tumblr.com/post/3308646748/writing-git-commit-messages)
5
+
6
+ ## Ruby versions
7
+
8
+ You can see the current ruby versions we support in [.travis.yml](.travis.yml).
9
+ Obviously, any language features you use must be available in the oldest version we support.
10
+ Therefore, it's often helpful to develop / test against the oldest version to avoid accidentally
11
+ using unsupported features.
12
+
13
+ ## Tests
14
+
15
+ SSHKit has a unit test suite and a functional test suite. Some functional tests run against
16
+ [Vagrant](https://www.vagrantup.com/) VMs. If possible, you should make sure that the
17
+ tests pass for each commit by running `rake` in the sshkit directory. This is in case we
18
+ need to cherry pick commits or rebase. You should ensure the tests pass, (preferably on
19
+ the minimum and maximum ruby version), before creating a PR.
20
+
21
+ Pull requests are much more likely to be accepted if you write a tests for the new functionality
22
+ you are adding. If you are fixing a bug, it would be great if you could add a test to
23
+ expose the bug you are fixing to show that the behaviour is fixed by your changes.
24
+
25
+ We use [Travis CI](https://travis-ci.org/capistrano/sshkit) to run the tests on different
26
+ ruby versions.
27
+
28
+ **The Travis build does not run the functional tests,
29
+ so make sure all the tests pass locally before creating your PR.**
30
+
31
+ ## Changelog
32
+
33
+ Most changes should have an accompanying entry in the [CHANGELOG](CHANGELOG.md), unless they
34
+ are minor documentation / config changes. This is incredibly important so that our users can
35
+ see the affect of new versions without having to trawl through the commits.
36
+
37
+ ## Breaking changes
38
+
39
+ We adhere to [semver](http://semver.org/) so breaking changes will require a major release.
40
+ For breaking changes, it would normally be helpful to discuss them by raising a 'Proposal' issue
41
+ or PR with examples of the new API you're proposing
42
+ [before doing a lot of work](https://www.igvita.com/2011/12/19/dont-push-your-pull-requests/).
43
+ Bear in mind that breaking changes may require many hundreds / thousands of users to update their
44
+ code.
45
+
46
+ You can use the [BREAKING_API_WISHLIST](BREAKING_API_WISHLIST.md) to record issues / PRs where
47
+ API changes have been discussed, and not implemented.
data/EXAMPLES.md CHANGED
@@ -2,59 +2,71 @@
2
2
 
3
3
  ## Run a command as a different user
4
4
 
5
- on hosts do |host|
6
- as 'www-data' do
7
- puts capture(:whoami)
8
- end
9
- end
5
+ ```ruby
6
+ on hosts do |host|
7
+ as 'www-data' do
8
+ puts capture(:whoami)
9
+ end
10
+ end
11
+ ```
10
12
 
11
13
  ## Run with default environmental variables
12
14
 
13
- SSHKit.config.default_env = { path: '/usr/local/libexec/bin:$PATH' }
14
- on hosts do |host|
15
- puts capture(:env)
16
- end
15
+ ```ruby
16
+ SSHKit.config.default_env = { path: '/usr/local/libexec/bin:$PATH' }
17
+ on hosts do |host|
18
+ puts capture(:env)
19
+ end
20
+ ```
17
21
 
18
22
  ## Run a command in a different directory
19
23
 
20
- on hosts do |host|
21
- within '/var/log' do
22
- puts capture(:head, '-n5', 'messages')
23
- end
24
- end
24
+ ```ruby
25
+ on hosts do |host|
26
+ within '/var/log' do
27
+ puts capture(:head, '-n5', 'messages')
28
+ end
29
+ end
30
+ ```
25
31
 
26
32
  ## Run a command with specific environmental variables
27
33
 
28
- on hosts do |host|
29
- with rack_env: :test do
30
- puts capture("env | grep RACK_ENV")
31
- end
32
- end
34
+ ```ruby
35
+ on hosts do |host|
36
+ with rack_env: :test do
37
+ puts capture("env | grep RACK_ENV")
38
+ end
39
+ end
40
+ ```
33
41
 
34
42
  ## Print some arbitrary output with the logging methods
35
43
 
36
- on hosts do |host|
37
- f = '/some/file'
38
- if test("[ -d #{f} ]")
39
- execute :touch, f
40
- else
41
- info "#{f} already exists on #{host}!"
42
- end
43
- end
44
+ ```ruby
45
+ on hosts do |host|
46
+ f = '/some/file'
47
+ if test("[ -d #{f} ]")
48
+ execute :touch, f
49
+ else
50
+ info "#{f} already exists on #{host}!"
51
+ end
52
+ end
53
+ ```
44
54
 
45
55
  The `debug()`, `info()`, `warn()`, `error()` and `fatal()` honor the current
46
56
  log level of `SSHKit.config.output_verbosity`
47
57
 
48
58
  ## Run a command in a different directory as a different user
49
59
 
50
- on hosts do |host|
51
- as 'www-data' do
52
- within '/var/log' do
53
- puts capture(:whoami)
54
- puts capture(:pwd)
55
- end
56
- end
60
+ ```ruby
61
+ on hosts do |host|
62
+ as 'www-data' do
63
+ within '/var/log' do
64
+ puts capture(:whoami)
65
+ puts capture(:pwd)
57
66
  end
67
+ end
68
+ end
69
+ ```
58
70
 
59
71
  This will output:
60
72
 
@@ -64,31 +76,59 @@ This will output:
64
76
  **Note:** This example is a bit misleading, as the `www-data` user doesn't
65
77
  have a shell defined, one cannot switch to that user.
66
78
 
79
+ ## Run a command which requires interaction between the client and the server
80
+
81
+ ```ruby
82
+ on hosts do |host|
83
+ execute(:passwd, interaction_handler: {
84
+ '(current) UNIX password: ' => "old_pw\n",
85
+ 'Enter new UNIX password: ' => "new_pw\n",
86
+ 'Retype new UNIX password: ' => "new_pw\n",
87
+ 'passwd: password updated successfully' => nil # For stdout/stderr which can be ignored, map a nil input
88
+ })
89
+ end
90
+ ```
91
+
92
+ ## Download a file from disk
93
+ ```ruby
94
+ on roles(:all) do
95
+ puts 'Downloading DB Backup File'
96
+ date_path = Date.today.strftime("%Y/%m/%d")
97
+ download! "/var/mysql-backup/#{date_path}/my-awesome-db.sql.gz", "my-awesome-db.sql.gz"
98
+ end
99
+ ```
100
+
67
101
  ## Upload a file from disk
68
102
 
69
- on hosts do |host|
70
- upload! '/config/database.yml', '/opt/my_project/shared/database.yml'
71
- end
103
+ ```ruby
104
+ on hosts do |host|
105
+ upload! '/config/database.yml', '/opt/my_project/shared/database.yml'
106
+ end
107
+ ```
72
108
 
73
109
  **Note:** The `upload!()` method doesn't honor the values of `within()`, `as()`
74
110
  etc, this will be improved as the library matures, but we're not there yet.
75
111
 
76
112
  ## Upload a file from a stream
77
113
 
78
- on hosts do |host|
79
- file = File.open('/config/database.yml')
80
- io = StringIO.new(....)
81
- upload! file, '/opt/my_project/shared/database.yml'
82
- upload! io, '/opt/my_project/shared/io.io.io'
83
- end
114
+ ```ruby
115
+ on hosts do |host|
116
+ file = File.open('/config/database.yml')
117
+ io = StringIO.new(....)
118
+ upload! file, '/opt/my_project/shared/database.yml'
119
+ upload! io, '/opt/my_project/shared/io.io.io'
120
+ end
121
+ ```
84
122
 
85
123
  The IO streaming is useful for uploading something rather than "cat"ing it,
86
124
  for example
87
125
 
88
- on hosts do |host|
89
- contents = StringIO.new('ALL ALL = (ALL) NOPASSWD: ALL')
90
- upload! contents, '/etc/sudoers.d/yolo'
91
- end
126
+ ```ruby
127
+ on hosts do |host|
128
+ contents = StringIO.new('ALL ALL = (ALL) NOPASSWD: ALL')
129
+ upload! contents, '/etc/sudoers.d/yolo'
130
+ end
131
+ ```
92
132
 
93
133
  This spares one from having to figure out the correct escaping sequences for
94
134
  something like "echo(:cat, '...?...', '> /etc/sudoers.d/yolo')".
@@ -98,9 +138,11 @@ etc, this will be improved as the library matures, but we're not there yet.
98
138
 
99
139
  ## Upload a directory of files
100
140
 
101
- on hosts do |host|
102
- upload! '.', '/tmp/mypwd', recursive: true
103
- end
141
+ ```ruby
142
+ on hosts do |host|
143
+ upload! '.', '/tmp/mypwd', recursive: true
144
+ end
145
+ ```
104
146
 
105
147
  In this case the `recursive: true` option mirrors the same options which are
106
148
  available to [`Net::{SCP,SFTP}`](http://net-ssh.github.io/net-scp/).
@@ -110,25 +152,29 @@ available to [`Net::{SCP,SFTP}`](http://net-ssh.github.io/net-scp/).
110
152
  Setting global SSH options, these will be overwritten by options set on the
111
153
  individual hosts:
112
154
 
113
- SSHKit::Backend::Netssh.configure do |ssh|
114
- ssh.connection_timeout = 30
115
- ssh.ssh_options = {
116
- keys: %w(/home/user/.ssh/id_rsa),
117
- forward_agent: false,
118
- auth_methods: %w(publickey password)
119
- }
120
- end
155
+ ```ruby
156
+ SSHKit::Backend::Netssh.configure do |ssh|
157
+ ssh.connection_timeout = 30
158
+ ssh.ssh_options = {
159
+ keys: %w(/home/user/.ssh/id_rsa),
160
+ forward_agent: false,
161
+ auth_methods: %w(publickey password)
162
+ }
163
+ end
164
+ ```
121
165
 
122
166
  ## Run a command with a different effective group ID
123
167
 
124
- on hosts do |host|
125
- as user: 'www-data', group: 'project-group' do
126
- within '/var/log' do
127
- execute :touch, 'somefile'
128
- execute :ls, '-l'
129
- end
130
- end
168
+ ```ruby
169
+ on hosts do |host|
170
+ as user: 'www-data', group: 'project-group' do
171
+ within '/var/log' do
172
+ execute :touch, 'somefile'
173
+ execute :ls, '-l'
131
174
  end
175
+ end
176
+ end
177
+ ```
132
178
 
133
179
  One will see that the created file is owned by the user `www-data` and the
134
180
  group `project-group`.
@@ -138,14 +184,16 @@ scripts for deployment between team members without sharing logins.
138
184
 
139
185
  ## Stack directory nestings
140
186
 
141
- on hosts do
142
- within "/var" do
143
- puts capture(:pwd)
144
- within :log do
145
- puts capture(:pwd)
146
- end
147
- end
187
+ ```ruby
188
+ on hosts do
189
+ within "/var" do
190
+ puts capture(:pwd)
191
+ within :log do
192
+ puts capture(:pwd)
148
193
  end
194
+ end
195
+ end
196
+ ```
149
197
 
150
198
  This will output:
151
199
 
@@ -160,17 +208,34 @@ joined according to the expectations of the machine receiving the commands.
160
208
 
161
209
  ## Do not care about the host block
162
210
 
163
- on hosts do
164
- # The |host| argument is optional, it will
165
- # be nil in the block if not passed
166
- end
211
+ ```ruby
212
+ on hosts do
213
+ # The |host| argument is optional, it will
214
+ # be nil in the block if not passed
215
+ end
216
+ ```
217
+
218
+ ## Change the output formatter
167
219
 
168
- ## Redirect all output to `/dev/null`
220
+ ```ruby
221
+ # The default format is pretty, which outputs colored text
222
+ SSHKit.config.format = :pretty
169
223
 
170
- SSHKit.config.output = File.open('/dev/null')
224
+ # Text with no coloring
225
+ SSHKit.config.format = :simpletext
226
+
227
+ # Red / Green dots for each completed step
228
+ SSHKit.config.format = :dot
229
+
230
+ # No output
231
+ SSHKit.config.format = :blackhole
232
+ ```
171
233
 
172
234
  ## Implement a dirt-simple formatter class
173
235
 
236
+ ```ruby
237
+ module SSHKit
238
+ module Formatter
174
239
  class MyFormatter < SSHKit::Formatter::Abstract
175
240
  def write(obj)
176
241
  case obj.is_a? SSHKit::Command
@@ -178,40 +243,53 @@ joined according to the expectations of the machine receiving the commands.
178
243
  end
179
244
  end
180
245
  end
246
+ end
247
+ end
248
+
249
+ # If your formatter is defined in the SSHKit::Formatter module configure with the format option:
250
+ SSHKit.config.format = :myformatter
181
251
 
182
- SSHKit.config.output = MyFormatter.new($stdout)
183
- SSHKit.config.output = MyFormatter.new(SSHKit.config.output)
184
- SSHKit.config.output = MyFormatter.new(File.open('log/deploy.log', 'wb'))
252
+ # Or configure the output directly
253
+ SSHKit.config.output = MyFormatter.new($stdout)
254
+ SSHKit.config.output = MyFormatter.new(SSHKit.config.output)
255
+ SSHKit.config.output = MyFormatter.new(File.open('log/deploy.log', 'wb'))
256
+ ```
185
257
 
186
258
  ## Set a password for a host.
187
259
 
188
- host = SSHKit::Host.new('user@example.com')
189
- host.password = "hackme"
260
+ ```ruby
261
+ host = SSHKit::Host.new('user@example.com')
262
+ host.password = "hackme"
190
263
 
191
- on host do |host|
192
- puts capture(:echo, "I don't care about security!")
193
- end
264
+ on host do |host|
265
+ puts capture(:echo, "I don't care about security!")
266
+ end
267
+ ```
194
268
 
195
269
  ## Execute and raise an error if something goes wrong
196
270
 
197
- on hosts do |host|
198
- execute!(:echo, '"Example Message!" 1>&2; false')
199
- end
271
+ ```ruby
272
+ on hosts do |host|
273
+ execute(:echo, '"Example Message!" 1>&2; false')
274
+ end
275
+ ```
200
276
 
201
277
  This will raise `SSHKit::Command::Failed` with the `#message` "Example Message!"
202
278
  which will cause the command to abort.
203
279
 
204
280
  ## Make a test, or run a command which may fail without raising an error:
205
281
 
206
- on hosts do |host|
207
- if test "[ -d /opt/sites ]"
208
- within "/opt/sites" do
209
- execute :git, :pull
210
- end
211
- else
212
- execute :git, :clone, 'some-repository', '/opt/sites'
213
- end
282
+ ```ruby
283
+ on hosts do |host|
284
+ if test "[ -d /opt/sites ]"
285
+ within "/opt/sites" do
286
+ execute :git, :pull
214
287
  end
288
+ else
289
+ execute :git, :clone, 'some-repository', '/opt/sites'
290
+ end
291
+ end
292
+ ```
215
293
 
216
294
  The `test()` command behaves exactly the same as execute however will return
217
295
  false if the command exits with a non-zero exit (as `man 1 test` does). As it
@@ -219,24 +297,28 @@ returns boolean it can be used to direct the control flow within the block.
219
297
 
220
298
  ## Do something different on one host, or another depending on a host property
221
299
 
222
- host1 = SSHKit::Host.new 'user@example.com'
223
- host2 = SSHKit::Host.new 'user@example.org'
224
-
225
- on hosts do |host|
226
- target = "/var/www/sites/"
227
- if host.hostname =~ /org/
228
- target += "dotorg"
229
- else
230
- target += "dotcom"
231
- end
232
- execute! :git, :clone, "git@git.#{host.hostname}", target
233
- end
300
+ ```ruby
301
+ host1 = SSHKit::Host.new 'user@example.com'
302
+ host2 = SSHKit::Host.new 'user@example.org'
303
+
304
+ on hosts do |host|
305
+ target = "/var/www/sites/"
306
+ if host.hostname =~ /org/
307
+ target += "dotorg"
308
+ else
309
+ target += "dotcom"
310
+ end
311
+ execute! :git, :clone, "git@git.#{host.hostname}", target
312
+ end
313
+ ```
234
314
 
235
315
  ## Connect to a host in the easiest possible way
236
316
 
237
- on 'example.com' do |host|
238
- execute :uptime
239
- end
317
+ ```ruby
318
+ on 'example.com' do |host|
319
+ execute :uptime
320
+ end
321
+ ```
240
322
 
241
323
  This will resolve the `example.com` hostname into a `SSHKit::Host` object, and
242
324
  try to pull up the correct configuration for it.
@@ -247,13 +329,15 @@ try to pull up the correct configuration for it.
247
329
  If the command you attempt to call contains a space character it won't be
248
330
  mapped:
249
331
 
250
- Command.new(:git, :push, :origin, :master).to_s
251
- # => /usr/bin/env git push origin master
252
- # (also: execute(:git, :push, :origin, :master)
332
+ ```ruby
333
+ Command.new(:git, :push, :origin, :master).to_s
334
+ # => /usr/bin/env git push origin master
335
+ # (also: execute(:git, :push, :origin, :master)
253
336
 
254
- Command.new("git push origin master").to_s
255
- # => git push origin master
256
- # (also: execute("git push origin master"))
337
+ Command.new("git push origin master").to_s
338
+ # => git push origin master
339
+ # (also: execute("git push origin master"))
340
+ ```
257
341
 
258
342
  This can be used to access shell builtins (such as `if` and `test`)
259
343
 
@@ -262,14 +346,16 @@ This can be used to access shell builtins (such as `if` and `test`)
262
346
 
263
347
  An extension of the behaviour above, if you write a command like this:
264
348
 
265
- c = Command.new <<-EOCOMMAND
266
- if test -d /var/log
267
- then echo "Directory Exists"
268
- fi
269
- EOCOMMAND
270
- c.to_s
271
- # => if test -d /var/log; then echo "Directory Exists; fi
272
- # (also: execute <<- EOCOMMAND........))
349
+ ```ruby
350
+ c = Command.new <<-EOCOMMAND
351
+ if test -d /var/log
352
+ then echo "Directory Exists"
353
+ fi
354
+ EOCOMMAND
355
+ c.to_s
356
+ # => if test -d /var/log; then echo "Directory Exists; fi
357
+ # (also: execute <<- EOCOMMAND........))
358
+ ```
273
359
 
274
360
  **Note:** The logic which reformats the script into a oneliner may be naïve, but in all
275
361
  known test cases, it works. The key thing is that `if` is not mapped to
@@ -279,31 +365,35 @@ known test cases, it works. The key thing is that `if` is not mapped to
279
365
 
280
366
  Into the `Rakefile` simply put something like:
281
367
 
282
- require 'sshkit/dsl'
368
+ ```ruby
369
+ require 'sshkit/dsl'
283
370
 
284
- SSHKit.config.command_map[:rake] = "./bin/rake"
371
+ SSHKit.config.command_map[:rake] = "./bin/rake"
285
372
 
286
- desc "Deploy the site, pulls from Git, migrate the db and precompile assets, then restart Passenger."
287
- task :deploy do
288
- on "example.com" do |host|
289
- within "/opt/sites/example.com" do
290
- execute :git, :pull
291
- execute :bundle, :install, '--deployment'
292
- execute :rake, 'db:migrate'
293
- execute :rake, 'assets:precompile'
294
- execute :touch, 'tmp/restart.txt'
295
- end
296
- end
373
+ desc "Deploy the site, pulls from Git, migrate the db and precompile assets, then restart Passenger."
374
+ task :deploy do
375
+ on "example.com" do |host|
376
+ within "/opt/sites/example.com" do
377
+ execute :git, :pull
378
+ execute :bundle, :install, '--deployment'
379
+ execute :rake, 'db:migrate'
380
+ execute :rake, 'assets:precompile'
381
+ execute :touch, 'tmp/restart.txt'
297
382
  end
383
+ end
384
+ end
385
+ ```
298
386
 
299
387
  ## Using without the DSL
300
388
 
301
389
  The *Coordinator* will resolve all hosts into *Host* objects, you can mix and
302
390
  match.
303
391
 
304
- Coordinator.new("one.example.com", SSHKit::Host.new('two.example.com')).each in: :sequence do
305
- puts capture :uptime
306
- end
392
+ ```ruby
393
+ Coordinator.new("one.example.com", SSHKit::Host.new('two.example.com')).each in: :sequence do
394
+ puts capture :uptime
395
+ end
396
+ ```
307
397
 
308
398
  You might also look at `./lib/sshkit/dsl.rb` where you can see almost the
309
399
  exact code as above, which implements the `on()` method.
@@ -312,23 +402,25 @@ exact code as above, which implements the `on()` method.
312
402
 
313
403
  Implemented since `v0.0.6`
314
404
 
315
- servers = %w{one.example.com two.example.com
316
- three.example.com four.example.com}.collect do |s|
317
- h = SSHKit::Host.new(s)
318
- if s.match /(one|two)/
319
- h.properties.roles = [:web]
320
- else
321
- h.properties.roles = [:app]
322
- end
323
- end
324
-
325
- on servers do |host|
326
- if host.properties.roles.include?(:web)
327
- # Do something pertinent to web servers
328
- elsif host.properties.roles.include?(:app)
329
- # Do something pertinent to application servers
330
- end
331
- end
405
+ ```ruby
406
+ servers = %w{one.example.com two.example.com
407
+ three.example.com four.example.com}.collect do |s|
408
+ h = SSHKit::Host.new(s)
409
+ if s.match /(one|two)/
410
+ h.properties.roles = [:web]
411
+ else
412
+ h.properties.roles = [:app]
413
+ end
414
+ end
415
+
416
+ on servers do |host|
417
+ if host.properties.roles.include?(:web)
418
+ # Do something pertinent to web servers
419
+ elsif host.properties.roles.include?(:app)
420
+ # Do something pertinent to application servers
421
+ end
422
+ end
423
+ ```
332
424
 
333
425
  The `SSHKit::Host#properties` is an [`OpenStruct`](http://ruby-doc.org/stdlib-1.9.3/libdoc/ostruct/rdoc/OpenStruct.html)
334
426
  which is not verified or validated in any way, it is up to you, or your
@@ -338,16 +430,20 @@ library to attach meanings or conventions to this mechanism.
338
430
 
339
431
  Replace `on` with `run_locally`
340
432
 
341
- run_locally do
342
- within '/tmp' do
343
- execute :whoami
344
- end
345
- end
433
+ ```ruby
434
+ run_locally do
435
+ within '/tmp' do
436
+ execute :whoami
437
+ end
438
+ end
439
+ ```
346
440
 
347
441
  You can achieve the same thing with `on(:local)`
348
442
 
349
- on(:local) do
350
- within '/tmp' do
351
- execute :whoami
352
- end
353
- end
443
+ ```ruby
444
+ on(:local) do
445
+ within '/tmp' do
446
+ execute :whoami
447
+ end
448
+ end
449
+ ```