sshkit 1.7.1 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/BREAKING_API_WISHLIST.md +14 -0
- data/CHANGELOG.md +74 -0
- data/CONTRIBUTING.md +43 -0
- data/EXAMPLES.md +265 -169
- data/Gemfile +7 -0
- data/README.md +274 -9
- data/RELEASING.md +16 -8
- data/Rakefile +8 -0
- data/lib/sshkit.rb +0 -9
- data/lib/sshkit/all.rb +6 -4
- data/lib/sshkit/backends/abstract.rb +42 -42
- data/lib/sshkit/backends/connection_pool.rb +57 -8
- data/lib/sshkit/backends/local.rb +21 -50
- data/lib/sshkit/backends/netssh.rb +45 -98
- data/lib/sshkit/backends/printer.rb +3 -23
- data/lib/sshkit/backends/skipper.rb +4 -8
- data/lib/sshkit/color.rb +51 -20
- data/lib/sshkit/command.rb +68 -47
- data/lib/sshkit/configuration.rb +38 -5
- data/lib/sshkit/deprecation_logger.rb +17 -0
- data/lib/sshkit/formatters/abstract.rb +28 -4
- data/lib/sshkit/formatters/black_hole.rb +1 -2
- data/lib/sshkit/formatters/dot.rb +3 -10
- data/lib/sshkit/formatters/pretty.rb +31 -56
- data/lib/sshkit/formatters/simple_text.rb +6 -44
- data/lib/sshkit/host.rb +5 -6
- data/lib/sshkit/logger.rb +0 -1
- data/lib/sshkit/mapping_interaction_handler.rb +47 -0
- data/lib/sshkit/runners/parallel.rb +1 -1
- data/lib/sshkit/runners/sequential.rb +1 -1
- data/lib/sshkit/version.rb +1 -1
- data/sshkit.gemspec +0 -1
- data/test/functional/backends/test_local.rb +14 -1
- data/test/functional/backends/test_netssh.rb +58 -50
- data/test/helper.rb +2 -2
- data/test/unit/backends/test_abstract.rb +145 -0
- data/test/unit/backends/test_connection_pool.rb +27 -2
- data/test/unit/backends/test_printer.rb +47 -47
- data/test/unit/formatters/test_custom.rb +65 -0
- data/test/unit/formatters/test_dot.rb +25 -32
- data/test/unit/formatters/test_pretty.rb +114 -22
- data/test/unit/formatters/test_simple_text.rb +83 -0
- data/test/unit/test_color.rb +69 -5
- data/test/unit/test_command.rb +53 -18
- data/test/unit/test_command_map.rb +0 -4
- data/test/unit/test_configuration.rb +47 -7
- data/test/unit/test_coordinator.rb +45 -52
- data/test/unit/test_deprecation_logger.rb +38 -0
- data/test/unit/test_host.rb +3 -4
- data/test/unit/test_logger.rb +0 -1
- data/test/unit/test_mapping_interaction_handler.rb +101 -0
- metadata +37 -41
- data/lib/sshkit/utils/capture_output_methods.rb +0 -13
- data/test/functional/test_coordinator.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a957fee02819d1f897b5b77ee0311b7ad7d4dd9e
|
4
|
+
data.tar.gz: 2b84ecc03637cf6d23664fa8c00f98a165d12db4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a84d2b9ca8f28c5a32eb8a97b2b8f444f8c77885d07321eca21e6e62b2c9bfb2fbb35c59f02e6b9e920cf5ca6b91bb66e6e5965118f4269dfc2d22a5f292e950
|
7
|
+
data.tar.gz: 83a1190b38ca613ec201909b974c7f04ce0c9619f02493b45c29f04cde6e3bca1a98304ce5ec72a2858739e7effd3117ce11d1ea0fcc850855b8954c5d91a539
|
data/.travis.yml
CHANGED
@@ -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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
220
|
+
```ruby
|
221
|
+
# The default format is pretty, which outputs colored text
|
222
|
+
SSHKit.config.format = :pretty
|
169
223
|
|
170
|
-
|
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
|
-
|
183
|
-
|
184
|
-
|
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
|
-
|
189
|
-
|
260
|
+
```ruby
|
261
|
+
host = SSHKit::Host.new('user@example.com')
|
262
|
+
host.password = "hackme"
|
190
263
|
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
198
|
-
|
199
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
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
|
-
|
238
|
-
|
239
|
-
|
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
|
-
|
251
|
-
|
252
|
-
|
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
|
-
|
255
|
-
|
256
|
-
|
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
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
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
|
-
|
368
|
+
```ruby
|
369
|
+
require 'sshkit/dsl'
|
283
370
|
|
284
|
-
|
371
|
+
SSHKit.config.command_map[:rake] = "./bin/rake"
|
285
372
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
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
|
-
|
305
|
-
|
306
|
-
|
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
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
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
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
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
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
443
|
+
```ruby
|
444
|
+
on(:local) do
|
445
|
+
within '/tmp' do
|
446
|
+
execute :whoami
|
447
|
+
end
|
448
|
+
end
|
449
|
+
```
|