sshkit 1.7.1 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ ```