airbrussh 0.5.1 → 0.6.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 +1 -3
- data/CHANGELOG.md +12 -1
- data/CONTRIBUTING.md +8 -2
- data/Gemfile +13 -0
- data/Rakefile +4 -2
- data/airbrussh.gemspec +2 -9
- data/bin/test_all.rb +2 -0
- data/lib/airbrussh/capistrano.rb +4 -21
- data/lib/airbrussh/capistrano/tasks.rb +80 -0
- data/lib/airbrussh/colors.rb +1 -2
- data/lib/airbrussh/command_formatter.rb +12 -2
- data/lib/airbrussh/configuration.rb +21 -7
- data/lib/airbrussh/console_formatter.rb +120 -0
- data/lib/airbrussh/delegating_formatter.rb +44 -0
- data/lib/airbrussh/formatter.rb +13 -178
- data/lib/airbrussh/log_file_formatter.rb +42 -0
- data/lib/airbrussh/rake/context.rb +12 -9
- data/lib/airbrussh/version.rb +1 -1
- metadata +11 -65
- data/lib/airbrussh/command_output.rb +0 -42
- data/lib/airbrussh/rake/command.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a8636d9e6c1f9b44d7a760d970edd17cdfe08de8
|
4
|
+
data.tar.gz: 94139691e2b02c1b24b42c4df7f6a2aa9924ca45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc9a2939655844b55ad9c006e5e2cd3866bf93ef8c095107a461e77ad692d705ceca5ccec52d2857c83128c5371f98f75d96a2fc0f2f8cb37827614963ac186f
|
7
|
+
data.tar.gz: bb6aba0f61291ce8050273d0e3631227b703d7ac8498341ab830ebd7aa6eb19139589a2171c1c624a44f17a06899396fb01396a9bd297b2f245896b177d8d8d2
|
data/.travis.yml
CHANGED
@@ -6,8 +6,6 @@ rvm:
|
|
6
6
|
- 2.2
|
7
7
|
env:
|
8
8
|
- sshkit="master"
|
9
|
-
# This is the old master before https://github.com/capistrano/sshkit/pull/257 was merged
|
10
|
-
- sshkit="ea211bcbbb71dab0152a18c2774922b7017e4edc"
|
11
9
|
- sshkit="= 1.7.1"
|
12
10
|
- sshkit="= 1.6.1"
|
13
|
-
before_install: gem install bundler --conservative --version '~> 1.
|
11
|
+
before_install: gem install bundler --conservative --version '~> 1.10'
|
data/CHANGELOG.md
CHANGED
@@ -8,6 +8,16 @@ Airbrussh is in a pre-1.0 state. This means that its APIs and behavior are subje
|
|
8
8
|
|
9
9
|
* Your contribution here!
|
10
10
|
|
11
|
+
## [0.6.0][] (2015-07-10)
|
12
|
+
|
13
|
+
This is another release with mostly behind-the-scenes changes. If you notice any differences in Airbrussh's behavior in this version, [please report an issue](https://github.com/mattbrictson/airbrussh/issues).
|
14
|
+
|
15
|
+
Other changes:
|
16
|
+
|
17
|
+
* Bundler 1.10 is now required to build and test airbrussh (this doesn't affect users of airbrussh at all).
|
18
|
+
* If the directory containing the log file doesn't exist, Airbrussh will now attempt to create it using `FileUtils.mkdir_p` ([#30](https://github.com/mattbrictson/airbrussh/issues/30))
|
19
|
+
* By default Airbrussh now always prints `Using airbrussh format.` when it starts up. In previous versions, a bug caused this message to sometimes not be shown. To change or disable this message, set the `banner` configuration option as explained in the README.
|
20
|
+
|
11
21
|
## [0.5.1][] (2015-06-24)
|
12
22
|
|
13
23
|
* Fix `NameError: uninitialized constant Airbrussh::SimpleDelegator`
|
@@ -52,7 +62,8 @@ There are, however, many behind-the-scenes changes and improvements to overall c
|
|
52
62
|
* Initial release
|
53
63
|
|
54
64
|
[Semver]: http://semver.org
|
55
|
-
[Unreleased]: https://github.com/mattbrictson/airbrussh/compare/v0.
|
65
|
+
[Unreleased]: https://github.com/mattbrictson/airbrussh/compare/v0.6.0...HEAD
|
66
|
+
[0.6.0]: https://github.com/mattbrictson/airbrussh/compare/v0.5.1...v0.6.0
|
56
67
|
[0.5.1]: https://github.com/mattbrictson/airbrussh/compare/v0.5.0...v0.5.1
|
57
68
|
[0.5.0]: https://github.com/mattbrictson/airbrussh/compare/v0.4.1...v0.5.0
|
58
69
|
[0.4.1]: https://github.com/mattbrictson/airbrussh/compare/v0.4.0...v0.4.1
|
data/CONTRIBUTING.md
CHANGED
@@ -16,11 +16,17 @@ Have a feature idea, bug fix, or refactoring suggestion? Contributions are welco
|
|
16
16
|
|
17
17
|
## Getting started
|
18
18
|
|
19
|
-
|
19
|
+
Note that Bundler 1.10 is required for development. Run `gem update bundler` to get the latest version.
|
20
|
+
|
21
|
+
After checking out the airbrussh repo, run `bin/setup` to install dependencies.
|
20
22
|
|
21
23
|
* `rake` executes airbrussh's tests and RuboCop checks
|
22
24
|
* `bin/test_all.rb` executes the tests against all versions of SSHKit that airbrussh supports
|
23
|
-
|
25
|
+
|
26
|
+
A Guardfile is also present, so if you'd like to use Guard to do a TDD workflow, then:
|
27
|
+
|
28
|
+
1. Run `bundle install --with extras` to get the optional guard dependencies
|
29
|
+
2. Run `guard` to monitor the filesystem and automatically run tests as you work
|
24
30
|
|
25
31
|
[Issues]: https://github.com/mattbrictson/airbrussh/issues
|
26
32
|
[RuboCop]: https://github.com/bbatsov/rubocop
|
data/Gemfile
CHANGED
@@ -3,6 +3,19 @@ source "https://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in airbrussh.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
+
# Optional development dependencies; requires bundler >= 1.10.
|
7
|
+
# Note that these gems assume a Ruby 2.2 environment. Install them using:
|
8
|
+
#
|
9
|
+
# bundle install --with extras
|
10
|
+
#
|
11
|
+
group :extras, :optional => true do
|
12
|
+
gem "chandler"
|
13
|
+
gem "guard", ">= 2.2.2"
|
14
|
+
gem "guard-minitest"
|
15
|
+
gem "rb-fsevent"
|
16
|
+
gem "terminal-notifier-guard"
|
17
|
+
end
|
18
|
+
|
6
19
|
if (sshkit_version = ENV["sshkit"])
|
7
20
|
requirement = begin
|
8
21
|
Gem::Dependency.new("sshkit", sshkit_version).requirement
|
data/Rakefile
CHANGED
@@ -10,9 +10,11 @@ end
|
|
10
10
|
require "rubocop/rake_task"
|
11
11
|
RuboCop::RakeTask.new
|
12
12
|
|
13
|
-
|
13
|
+
# rubocop:disable Lint/HandleExceptions
|
14
|
+
begin
|
14
15
|
require "chandler/tasks"
|
15
|
-
|
16
|
+
rescue LoadError
|
16
17
|
end
|
18
|
+
task "release:rubygem_push" => "chandler:push" if defined?(Chandler)
|
17
19
|
|
18
20
|
task :default => [:test, :rubocop]
|
data/airbrussh.gemspec
CHANGED
@@ -23,18 +23,11 @@ Gem::Specification.new do |spec|
|
|
23
23
|
|
24
24
|
spec.add_dependency "sshkit", [">= 1.6.1", "!= 1.7.0"]
|
25
25
|
|
26
|
-
spec.add_development_dependency "bundler", "~> 1.
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
27
27
|
spec.add_development_dependency "coveralls"
|
28
|
-
spec.add_development_dependency "guard", ">= 2.2.2"
|
29
|
-
spec.add_development_dependency "guard-minitest"
|
30
28
|
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
-
spec.add_development_dependency "rb-fsevent"
|
32
29
|
spec.add_development_dependency "minitest"
|
33
30
|
spec.add_development_dependency "minitest-reporters"
|
31
|
+
spec.add_development_dependency "mocha"
|
34
32
|
spec.add_development_dependency "rubocop", ">= 0.31.0"
|
35
|
-
spec.add_development_dependency "terminal-notifier-guard"
|
36
|
-
|
37
|
-
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.0")
|
38
|
-
spec.add_development_dependency "chandler"
|
39
|
-
end
|
40
33
|
end
|
data/bin/test_all.rb
CHANGED
data/lib/airbrussh/capistrano.rb
CHANGED
@@ -1,34 +1,17 @@
|
|
1
|
-
require "airbrussh"
|
2
|
-
require "airbrussh/colors"
|
3
|
-
require "sshkit/formatter/airbrussh"
|
1
|
+
require "airbrussh/capistrano/tasks"
|
4
2
|
|
5
|
-
|
6
|
-
Airbrussh.configure do |config|
|
7
|
-
config.log_file = "log/capistrano.log"
|
8
|
-
config.monkey_patch_rake = true
|
9
|
-
end
|
10
|
-
|
11
|
-
# Sanity check!
|
12
|
-
unless defined?(Capistrano) && defined?(:namespace)
|
13
|
-
$stderr.puts(
|
14
|
-
Airbrussh::Colors.red(
|
15
|
-
"WARNING: airbrussh/capistrano must be loaded by Capistrano in order "\
|
16
|
-
"to work.\nRequire this gem within your application's Capfile, as "\
|
17
|
-
"described here:\nhttps://github.com/mattbrictson/airbrussh#installation"
|
18
|
-
))
|
19
|
-
end
|
3
|
+
tasks = Airbrussh::Capistrano::Tasks.new(self)
|
20
4
|
|
21
5
|
# Hook into Capistrano's init process to set the formatter
|
22
6
|
namespace :load do
|
23
7
|
task :defaults do
|
24
|
-
|
8
|
+
tasks.load_defaults
|
25
9
|
end
|
26
10
|
end
|
27
11
|
|
28
12
|
# Capistrano failure hook
|
29
13
|
namespace :deploy do
|
30
14
|
task :failed do
|
31
|
-
|
32
|
-
output.on_deploy_failure if output.respond_to?(:on_deploy_failure)
|
15
|
+
tasks.deploy_failed
|
33
16
|
end
|
34
17
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "airbrussh"
|
2
|
+
require "airbrussh/colors"
|
3
|
+
require "airbrussh/console"
|
4
|
+
require "forwardable"
|
5
|
+
require "shellwords"
|
6
|
+
|
7
|
+
module Airbrussh
|
8
|
+
module Capistrano
|
9
|
+
# Encapsulates the rake behavior that integrates Airbrussh into Capistrano.
|
10
|
+
# This class allows us to easily test the behavior using a mock to stand in
|
11
|
+
# for the Capistrano DSL.
|
12
|
+
#
|
13
|
+
# See airbrussh/capistrano.rb to see how this class is used.
|
14
|
+
#
|
15
|
+
class Tasks
|
16
|
+
extend Forwardable
|
17
|
+
def_delegators :dsl, :set
|
18
|
+
def_delegators :config, :log_file
|
19
|
+
|
20
|
+
include Airbrussh::Colors
|
21
|
+
|
22
|
+
def initialize(dsl, stderr=$stderr, config=Airbrussh.configuration)
|
23
|
+
@dsl = dsl
|
24
|
+
@stderr = stderr
|
25
|
+
@config = config
|
26
|
+
|
27
|
+
configure
|
28
|
+
warn_if_missing_dsl
|
29
|
+
end
|
30
|
+
|
31
|
+
# Behavior for the rake load:defaults task.
|
32
|
+
def load_defaults
|
33
|
+
require "sshkit/formatter/airbrussh"
|
34
|
+
set :format, :airbrussh
|
35
|
+
end
|
36
|
+
|
37
|
+
# Behavior for the rake deploy:failed task.
|
38
|
+
def deploy_failed
|
39
|
+
return if log_file.nil?
|
40
|
+
|
41
|
+
error_line
|
42
|
+
error_line(red("** DEPLOY FAILED"))
|
43
|
+
error_line(yellow("** Refer to #{log_file} for details. "\
|
44
|
+
"Here are the last 20 lines:"))
|
45
|
+
error_line
|
46
|
+
error_line(`tail -n 20 #{log_file.shellescape} 2>&1`)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_reader :dsl, :stderr, :config
|
52
|
+
|
53
|
+
# Change airbrussh's default configuration to be more appropriate for
|
54
|
+
# capistrano.
|
55
|
+
def configure
|
56
|
+
config.log_file = "log/capistrano.log"
|
57
|
+
config.monkey_patch_rake = true
|
58
|
+
end
|
59
|
+
|
60
|
+
# Verify that capistrano and rake DSLs are present
|
61
|
+
def warn_if_missing_dsl
|
62
|
+
return if %w(set namespace task).all? { |m| dsl.respond_to?(m, true) }
|
63
|
+
|
64
|
+
error_line(
|
65
|
+
red("WARNING: airbrussh/capistrano must be loaded by Capistrano in "\
|
66
|
+
"order to work.\nRequire this gem within your application's "\
|
67
|
+
"Capfile, as described here:\n"\
|
68
|
+
"https://github.com/mattbrictson/airbrussh#installation"))
|
69
|
+
end
|
70
|
+
|
71
|
+
def err_console
|
72
|
+
@err_console ||= Airbrussh::Console.new(stderr)
|
73
|
+
end
|
74
|
+
|
75
|
+
def error_line(line="\n")
|
76
|
+
line.each_line(&err_console.method(:print_line))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/airbrussh/colors.rb
CHANGED
@@ -10,8 +10,6 @@ module Airbrussh
|
|
10
10
|
:gray => 90
|
11
11
|
}.freeze
|
12
12
|
|
13
|
-
module_function
|
14
|
-
|
15
13
|
# Define red, green, blue, etc. methods that return a copy of the
|
16
14
|
# String that is wrapped in the corresponding ANSI color escape
|
17
15
|
# sequence.
|
@@ -19,6 +17,7 @@ module Airbrussh
|
|
19
17
|
define_method(name) do |string|
|
20
18
|
"\e[0;#{code};49m#{string}\e[0m"
|
21
19
|
end
|
20
|
+
module_function(name)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end
|
@@ -4,10 +4,20 @@ require "delegate"
|
|
4
4
|
# rubocop:disable Style/AsciiComments
|
5
5
|
|
6
6
|
module Airbrussh
|
7
|
-
# Decorates an SSHKit
|
7
|
+
# Decorates an SSHKit Command to add string output helpers and the
|
8
|
+
# command's position within currently executing rake task:
|
9
|
+
#
|
10
|
+
# * position - zero-based position of this command in the list of
|
11
|
+
# all commands that have been run in the current rake task
|
12
|
+
# class CommandFormatter < SimpleDelegator
|
8
13
|
class CommandFormatter < SimpleDelegator
|
9
14
|
include Airbrussh::Colors
|
10
15
|
|
16
|
+
def initialize(command, position)
|
17
|
+
super(command)
|
18
|
+
@position = position
|
19
|
+
end
|
20
|
+
|
11
21
|
# Prefixes the line with the command number and removes the newline.
|
12
22
|
#
|
13
23
|
# format_output("hello\n") # => "01 hello"
|
@@ -62,7 +72,7 @@ module Airbrussh
|
|
62
72
|
end
|
63
73
|
|
64
74
|
def number
|
65
|
-
format("%02d", position + 1)
|
75
|
+
format("%02d", @position + 1)
|
66
76
|
end
|
67
77
|
|
68
78
|
def success_message
|
@@ -1,3 +1,7 @@
|
|
1
|
+
require "airbrussh/colors"
|
2
|
+
require "airbrussh/console_formatter"
|
3
|
+
require "airbrussh/log_file_formatter"
|
4
|
+
|
1
5
|
module Airbrussh
|
2
6
|
class Configuration
|
3
7
|
attr_accessor :log_file, :monkey_patch_rake, :color, :truncate, :banner,
|
@@ -12,17 +16,27 @@ module Airbrussh
|
|
12
16
|
self.command_output = false
|
13
17
|
end
|
14
18
|
|
15
|
-
def
|
16
|
-
|
19
|
+
def banner_message
|
20
|
+
return nil unless banner
|
21
|
+
return banner unless banner == :auto
|
22
|
+
msg = "Using airbrussh format."
|
23
|
+
if log_file
|
24
|
+
msg << "\n"
|
25
|
+
msg << "Verbose output is being written to #{Colors.blue(log_file)}."
|
26
|
+
end
|
27
|
+
msg
|
17
28
|
end
|
18
29
|
|
19
|
-
|
20
|
-
|
30
|
+
# This returns an array of formatters appropriate for the configuration.
|
31
|
+
# Depending on whether a log file is configured, this could be just the
|
32
|
+
# Airbrussh:ConsoleFormatter, or that plus the LogFileFormatter.
|
33
|
+
def formatters(io)
|
34
|
+
fmts = [Airbrussh::ConsoleFormatter.new(io, self)]
|
35
|
+
fmts.unshift(Airbrussh::LogFileFormatter.new(log_file)) if log_file
|
36
|
+
fmts
|
21
37
|
end
|
22
38
|
|
23
|
-
|
24
|
-
|
25
|
-
def command_output_include?(sym)
|
39
|
+
def show_command_output?(sym)
|
26
40
|
command_output == true || Array(command_output).include?(sym)
|
27
41
|
end
|
28
42
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require "airbrussh/colors"
|
2
|
+
require "airbrussh/command_formatter"
|
3
|
+
require "airbrussh/console"
|
4
|
+
require "airbrussh/rake/context"
|
5
|
+
require "sshkit"
|
6
|
+
|
7
|
+
module Airbrussh
|
8
|
+
class ConsoleFormatter < SSHKit::Formatter::Abstract
|
9
|
+
include Airbrussh::Colors
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
attr_reader :config, :context
|
13
|
+
def_delegators :context, :current_task_name, :register_new_command
|
14
|
+
|
15
|
+
def initialize(io, config=Airbrussh.configuration)
|
16
|
+
super(io)
|
17
|
+
|
18
|
+
@config = config
|
19
|
+
@context = Airbrussh::Rake::Context.new(config)
|
20
|
+
@console = Airbrussh::Console.new(original_output, config)
|
21
|
+
|
22
|
+
write_banner
|
23
|
+
end
|
24
|
+
|
25
|
+
def write_banner
|
26
|
+
print_line(config.banner_message) if config.banner_message
|
27
|
+
end
|
28
|
+
|
29
|
+
def log_command_start(command)
|
30
|
+
return if debug?(command)
|
31
|
+
first_execution = register_new_command(command)
|
32
|
+
command = decorate(command)
|
33
|
+
print_task_if_changed
|
34
|
+
print_indented_line(command.start_message) if first_execution
|
35
|
+
end
|
36
|
+
|
37
|
+
def log_command_data(command, stream_type, string)
|
38
|
+
return if debug?(command)
|
39
|
+
return unless config.show_command_output?(stream_type)
|
40
|
+
command = decorate(command)
|
41
|
+
string.each_line do |line|
|
42
|
+
print_indented_line(command.format_output(line))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def log_command_exit(command)
|
47
|
+
return if debug?(command)
|
48
|
+
command = decorate(command)
|
49
|
+
print_indented_line(command.exit_message(@log_file), -2)
|
50
|
+
end
|
51
|
+
|
52
|
+
def write(obj)
|
53
|
+
case obj
|
54
|
+
when SSHKit::Command
|
55
|
+
log_command_start(obj)
|
56
|
+
log_and_clear_command_output(obj, :stderr)
|
57
|
+
log_and_clear_command_output(obj, :stdout)
|
58
|
+
log_command_exit(obj) if obj.finished?
|
59
|
+
when SSHKit::LogMessage
|
60
|
+
write_log_message(obj)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
alias_method :<<, :write
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
attr_accessor :last_printed_task
|
68
|
+
|
69
|
+
def write_log_message(log_message)
|
70
|
+
return if debug?(log_message)
|
71
|
+
print_task_if_changed
|
72
|
+
print_indented_line(gray(log_message.to_s))
|
73
|
+
end
|
74
|
+
|
75
|
+
# For SSHKit versions up to and including 1.7.1, the stdout and stderr
|
76
|
+
# output was available as attributes on the Command. Print the data for
|
77
|
+
# the specified command and stream if enabled and clear the stream.
|
78
|
+
# (see Airbrussh::Configuration#command_output).
|
79
|
+
def log_and_clear_command_output(command, stream)
|
80
|
+
output = command.public_send(stream)
|
81
|
+
log_command_data(command, stream, output)
|
82
|
+
command.public_send("#{stream}=", "")
|
83
|
+
end
|
84
|
+
|
85
|
+
def print_task_if_changed
|
86
|
+
return if current_task_name.nil?
|
87
|
+
return if current_task_name == last_printed_task
|
88
|
+
|
89
|
+
self.last_printed_task = current_task_name
|
90
|
+
print_line("#{clock} #{blue(current_task_name)}")
|
91
|
+
end
|
92
|
+
|
93
|
+
def clock
|
94
|
+
@start_at ||= Time.now
|
95
|
+
duration = Time.now - @start_at
|
96
|
+
|
97
|
+
minutes = (duration / 60).to_i
|
98
|
+
seconds = (duration - minutes * 60).to_i
|
99
|
+
|
100
|
+
format("%02d:%02d", minutes, seconds)
|
101
|
+
end
|
102
|
+
|
103
|
+
def debug?(obj)
|
104
|
+
obj.verbosity <= SSHKit::Logger::DEBUG
|
105
|
+
end
|
106
|
+
|
107
|
+
def decorate(command)
|
108
|
+
Airbrussh::CommandFormatter.new(command, @context.position(command))
|
109
|
+
end
|
110
|
+
|
111
|
+
def print_line(string)
|
112
|
+
@console.print_line(string)
|
113
|
+
end
|
114
|
+
|
115
|
+
def print_indented_line(string, offset=0)
|
116
|
+
indent = " " * (6 + offset)
|
117
|
+
print_line([indent, string].join)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "sshkit"
|
2
|
+
|
3
|
+
module Airbrussh
|
4
|
+
# This class quacks like an SSHKit::Formatter, but when any formatting
|
5
|
+
# methods are called, it simply forwards them to one more more concrete
|
6
|
+
# formatters. This allows us to split out the responsibilities of
|
7
|
+
# ConsoleFormatter and LogFileFormatter into two separate classes, with
|
8
|
+
# DelegatingFormatter forwarding the logging messages to both at once.
|
9
|
+
#
|
10
|
+
class DelegatingFormatter
|
11
|
+
FORWARD_METHODS = %w(
|
12
|
+
fatal error warn info debug log
|
13
|
+
log_command_start log_command_data log_command_exit
|
14
|
+
)
|
15
|
+
DUP_AND_FORWARD_METHODS = %w(<< write)
|
16
|
+
|
17
|
+
attr_reader :formatters
|
18
|
+
|
19
|
+
def initialize(formatters)
|
20
|
+
@formatters = formatters
|
21
|
+
end
|
22
|
+
|
23
|
+
FORWARD_METHODS.each do |method|
|
24
|
+
define_method(method) do |*args|
|
25
|
+
formatters.map { |f| f.public_send(method, *args) }.last
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# For versions of SSHKit up to and including 1.7.1, the LogfileFormatter
|
30
|
+
# and ConsoleFormatter (and all of SSHKit's built in formatters) clear
|
31
|
+
# the stdout and stderr data in the command obj. Therefore, ensure only
|
32
|
+
# one of the formatters (the last one) gets the original command. This is
|
33
|
+
# also the formatter whose return value is passed to the caller.
|
34
|
+
#
|
35
|
+
DUP_AND_FORWARD_METHODS.each do |method|
|
36
|
+
define_method(method) do |command_or_log_message|
|
37
|
+
formatters[0...-1].each do |f|
|
38
|
+
f.public_send(method, command_or_log_message.dup)
|
39
|
+
end
|
40
|
+
formatters.last.public_send(method, command_or_log_message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/airbrussh/formatter.rb
CHANGED
@@ -1,182 +1,17 @@
|
|
1
|
-
require "airbrussh
|
2
|
-
require "airbrussh/
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
require "airbrussh"
|
2
|
+
require "airbrussh/delegating_formatter"
|
3
|
+
|
4
|
+
# This is the formatter class that conforms to the SSHKit Formatter API and
|
5
|
+
# provides the airbrussh functionality to SSHKit. Note however that this class
|
6
|
+
# doesn't do much by itself; instead, it delegates to the ConsoleFormatter
|
7
|
+
# and (optionally) the LogFileFormatter, which handle the bulk of the logic.
|
8
|
+
#
|
9
9
|
module Airbrussh
|
10
|
-
class Formatter <
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def_delegator :context, :current_task_name
|
16
|
-
|
17
|
-
def initialize(io, config=Airbrussh.configuration)
|
18
|
-
super(io)
|
19
|
-
|
20
|
-
@config = config
|
21
|
-
@context = Airbrussh::Rake::Context.new(config)
|
22
|
-
|
23
|
-
@log_file = config.log_file
|
24
|
-
@log_file_formatter = create_log_file_formatter
|
25
|
-
|
26
|
-
@console = Airbrussh::Console.new(original_output, config)
|
27
|
-
write_log_file_delimiter
|
28
|
-
write_banner
|
29
|
-
end
|
30
|
-
|
31
|
-
def create_log_file_formatter
|
32
|
-
return SSHKit::Formatter::BlackHole.new(nil) if @log_file.nil?
|
33
|
-
SSHKit::Formatter::Pretty.new(
|
34
|
-
::Logger.new(@log_file, 1, 20_971_520)
|
35
|
-
)
|
36
|
-
end
|
37
|
-
|
38
|
-
def write_banner
|
39
|
-
return unless config.banner
|
40
|
-
if config.banner == :auto
|
41
|
-
return if @log_file.nil?
|
42
|
-
print_line "Using airbrussh format."
|
43
|
-
print_line "Verbose output is being written to #{blue(@log_file)}."
|
44
|
-
else
|
45
|
-
print_line config.banner
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def write_log_file_delimiter
|
50
|
-
delimiter = []
|
51
|
-
delimiter << "-" * 75
|
52
|
-
delimiter << "START #{Time.now} cap #{ARGV.join(' ')}"
|
53
|
-
delimiter << "-" * 75
|
54
|
-
delimiter.each do |line|
|
55
|
-
@log_file_formatter << SSHKit::LogMessage.new(
|
56
|
-
SSHKit::Logger::INFO,
|
57
|
-
line
|
58
|
-
)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def log_command_start(command)
|
63
|
-
command = decorate(command)
|
64
|
-
@log_file_formatter.log_command_start(command)
|
65
|
-
write_command_start(command)
|
66
|
-
end
|
67
|
-
|
68
|
-
def log_command_data(command, stream_type, line)
|
69
|
-
command = decorate(command)
|
70
|
-
@log_file_formatter.log_command_data(command, stream_type, line)
|
71
|
-
write_command_output_line(command, stream_type, line)
|
72
|
-
end
|
73
|
-
|
74
|
-
def log_command_exit(command)
|
75
|
-
command = decorate(command)
|
76
|
-
@log_file_formatter.log_command_exit(command)
|
77
|
-
write_command_exit(command)
|
78
|
-
end
|
79
|
-
|
80
|
-
def write(obj)
|
81
|
-
# SSHKit's :pretty formatter mutates the stdout and stderr data in the
|
82
|
-
# command obj. So we need to dup it to ensure our copy is unscathed.
|
83
|
-
@log_file_formatter << obj.dup
|
84
|
-
|
85
|
-
case obj
|
86
|
-
when SSHKit::Command
|
87
|
-
command = decorate(obj)
|
88
|
-
write_command_start(command)
|
89
|
-
write_command_output(command)
|
90
|
-
write_command_exit(command) if command.finished?
|
91
|
-
when SSHKit::LogMessage
|
92
|
-
write_log_message(obj)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
alias_method :<<, :write
|
96
|
-
|
97
|
-
def on_deploy_failure
|
98
|
-
return if @log_file.nil?
|
99
|
-
err = Airbrussh::Console.new($stderr)
|
100
|
-
err.print_line
|
101
|
-
err.print_line(red("** DEPLOY FAILED"))
|
102
|
-
err.print_line(yellow("** Refer to #{@log_file} for details. "\
|
103
|
-
"Here are the last 20 lines:"))
|
104
|
-
err.print_line
|
105
|
-
system("tail -n 20 #{@log_file.shellescape} 1>&2")
|
106
|
-
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
attr_accessor :last_printed_task
|
111
|
-
|
112
|
-
def write_log_message(log_message)
|
113
|
-
return if debug?(log_message)
|
114
|
-
print_task_if_changed
|
115
|
-
print_indented_line(gray(log_message.to_s))
|
116
|
-
end
|
117
|
-
|
118
|
-
def write_command_start(command)
|
119
|
-
return if debug?(command)
|
120
|
-
print_task_if_changed
|
121
|
-
print_indented_line(command.start_message) if command.first_execution?
|
122
|
-
end
|
123
|
-
|
124
|
-
# Prints the data from the stdout and stderr streams of the given command,
|
125
|
-
# but only if enabled (see Airbrussh::Configuration#command_output).
|
126
|
-
def write_command_output(command)
|
127
|
-
# Use a bit of meta-programming here, since stderr and stdout logic
|
128
|
-
# are identical except for different method names.
|
129
|
-
%w(stderr stdout).each do |stream|
|
130
|
-
CommandOutput.for(command).each_line(stream) do |line|
|
131
|
-
write_command_output_line(command, stream, line)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def write_command_output_line(command, stream, line)
|
137
|
-
hide_command_output = !config.public_send("command_output_#{stream}?")
|
138
|
-
return if hide_command_output || debug?(command)
|
139
|
-
print_indented_line(command.format_output(line))
|
140
|
-
end
|
141
|
-
|
142
|
-
def print_task_if_changed
|
143
|
-
return if current_task_name.nil?
|
144
|
-
return if current_task_name == last_printed_task
|
145
|
-
|
146
|
-
self.last_printed_task = current_task_name
|
147
|
-
print_line("#{clock} #{blue(current_task_name)}")
|
148
|
-
end
|
149
|
-
|
150
|
-
def write_command_exit(command)
|
151
|
-
return if debug?(command)
|
152
|
-
print_indented_line(command.exit_message(@log_file), -2)
|
153
|
-
end
|
154
|
-
|
155
|
-
def clock
|
156
|
-
@start_at ||= Time.now
|
157
|
-
duration = Time.now - @start_at
|
158
|
-
|
159
|
-
minutes = (duration / 60).to_i
|
160
|
-
seconds = (duration - minutes * 60).to_i
|
161
|
-
|
162
|
-
format("%02d:%02d", minutes, seconds)
|
163
|
-
end
|
164
|
-
|
165
|
-
def debug?(obj)
|
166
|
-
obj.verbosity <= SSHKit::Logger::DEBUG
|
167
|
-
end
|
168
|
-
|
169
|
-
def decorate(command)
|
170
|
-
Airbrussh::CommandFormatter.new(@context.decorate_command(command))
|
171
|
-
end
|
172
|
-
|
173
|
-
def print_line(string)
|
174
|
-
@console.print_line(string)
|
175
|
-
end
|
176
|
-
|
177
|
-
def print_indented_line(string, offset=0)
|
178
|
-
indent = " " * (6 + offset)
|
179
|
-
print_line([indent, string].join)
|
10
|
+
class Formatter < Airbrussh::DelegatingFormatter
|
11
|
+
def initialize(io, config=::Airbrussh.configuration)
|
12
|
+
# Delegate to ConsoleFormatter and (optionally) LogFileFormatter,
|
13
|
+
# based on the configuration.
|
14
|
+
super(config.formatters(io))
|
180
15
|
end
|
181
16
|
end
|
182
17
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "delegate"
|
2
|
+
require "fileutils"
|
3
|
+
require "logger"
|
4
|
+
require "sshkit"
|
5
|
+
|
6
|
+
module Airbrussh
|
7
|
+
# A Pretty formatter that sends its output to a specified log file path.
|
8
|
+
# LogFileFormatter takes care of creating the file (and its parent
|
9
|
+
# directory) if it does not already exist, opens it for appending, and writes
|
10
|
+
# a delimiter message. The file is automatically rotated if it reaches 20 MB.
|
11
|
+
#
|
12
|
+
class LogFileFormatter < SimpleDelegator
|
13
|
+
attr_reader :path
|
14
|
+
|
15
|
+
def initialize(path, formatter_class=SSHKit::Formatter::Pretty)
|
16
|
+
@path = path
|
17
|
+
ensure_directory_exists if path.is_a?(String)
|
18
|
+
super(formatter_class.new(log_file_io))
|
19
|
+
write_delimiter
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def write_delimiter
|
25
|
+
delimiter = []
|
26
|
+
delimiter << "-" * 75
|
27
|
+
delimiter << "START #{Time.now} cap #{ARGV.join(' ')}"
|
28
|
+
delimiter << "-" * 75
|
29
|
+
delimiter.each do |line|
|
30
|
+
write(SSHKit::LogMessage.new(SSHKit::Logger::INFO, line))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def ensure_directory_exists
|
35
|
+
FileUtils.mkdir_p(File.dirname(path))
|
36
|
+
end
|
37
|
+
|
38
|
+
def log_file_io
|
39
|
+
@io ||= ::Logger.new(path, 1, 20_971_520)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require "rake"
|
2
|
-
require "airbrussh/rake/command"
|
3
2
|
|
4
3
|
module Airbrussh
|
5
4
|
module Rake
|
@@ -23,20 +22,24 @@ module Airbrussh
|
|
23
22
|
self.class.current_task_name
|
24
23
|
end
|
25
24
|
|
26
|
-
#
|
27
|
-
#
|
28
|
-
|
25
|
+
# Update the context when a new command starts by:
|
26
|
+
# * Clearing the command history if the rake task has changed
|
27
|
+
# * Recording the command in the history
|
28
|
+
#
|
29
|
+
# Returns whether or not this command was the first execution
|
30
|
+
# of this command in the current rake task
|
31
|
+
def register_new_command(command)
|
29
32
|
reset_history_if_task_changed
|
30
33
|
|
31
34
|
first_execution = !history.include?(command.to_s)
|
32
35
|
history << command.to_s
|
33
36
|
history.uniq!
|
37
|
+
first_execution
|
38
|
+
end
|
34
39
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
history.index(command.to_s)
|
39
|
-
)
|
40
|
+
# The position of the specified command in the current rake task
|
41
|
+
def position(command)
|
42
|
+
history.index(command.to_s)
|
40
43
|
end
|
41
44
|
|
42
45
|
class << self
|
data/lib/airbrussh/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: airbrussh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Brictson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sshkit
|
@@ -36,14 +36,14 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '1.
|
39
|
+
version: '1.10'
|
40
40
|
type: :development
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '1.
|
46
|
+
version: '1.10'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: coveralls
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,34 +58,6 @@ dependencies:
|
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '0'
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
|
-
name: guard
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
64
|
-
requirements:
|
65
|
-
- - ">="
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: 2.2.2
|
68
|
-
type: :development
|
69
|
-
prerelease: false
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - ">="
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: 2.2.2
|
75
|
-
- !ruby/object:Gem::Dependency
|
76
|
-
name: guard-minitest
|
77
|
-
requirement: !ruby/object:Gem::Requirement
|
78
|
-
requirements:
|
79
|
-
- - ">="
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
version: '0'
|
82
|
-
type: :development
|
83
|
-
prerelease: false
|
84
|
-
version_requirements: !ruby/object:Gem::Requirement
|
85
|
-
requirements:
|
86
|
-
- - ">="
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '0'
|
89
61
|
- !ruby/object:Gem::Dependency
|
90
62
|
name: rake
|
91
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,7 +73,7 @@ dependencies:
|
|
101
73
|
- !ruby/object:Gem::Version
|
102
74
|
version: '10.0'
|
103
75
|
- !ruby/object:Gem::Dependency
|
104
|
-
name:
|
76
|
+
name: minitest
|
105
77
|
requirement: !ruby/object:Gem::Requirement
|
106
78
|
requirements:
|
107
79
|
- - ">="
|
@@ -115,7 +87,7 @@ dependencies:
|
|
115
87
|
- !ruby/object:Gem::Version
|
116
88
|
version: '0'
|
117
89
|
- !ruby/object:Gem::Dependency
|
118
|
-
name: minitest
|
90
|
+
name: minitest-reporters
|
119
91
|
requirement: !ruby/object:Gem::Requirement
|
120
92
|
requirements:
|
121
93
|
- - ">="
|
@@ -129,7 +101,7 @@ dependencies:
|
|
129
101
|
- !ruby/object:Gem::Version
|
130
102
|
version: '0'
|
131
103
|
- !ruby/object:Gem::Dependency
|
132
|
-
name:
|
104
|
+
name: mocha
|
133
105
|
requirement: !ruby/object:Gem::Requirement
|
134
106
|
requirements:
|
135
107
|
- - ">="
|
@@ -156,34 +128,6 @@ dependencies:
|
|
156
128
|
- - ">="
|
157
129
|
- !ruby/object:Gem::Version
|
158
130
|
version: 0.31.0
|
159
|
-
- !ruby/object:Gem::Dependency
|
160
|
-
name: terminal-notifier-guard
|
161
|
-
requirement: !ruby/object:Gem::Requirement
|
162
|
-
requirements:
|
163
|
-
- - ">="
|
164
|
-
- !ruby/object:Gem::Version
|
165
|
-
version: '0'
|
166
|
-
type: :development
|
167
|
-
prerelease: false
|
168
|
-
version_requirements: !ruby/object:Gem::Requirement
|
169
|
-
requirements:
|
170
|
-
- - ">="
|
171
|
-
- !ruby/object:Gem::Version
|
172
|
-
version: '0'
|
173
|
-
- !ruby/object:Gem::Dependency
|
174
|
-
name: chandler
|
175
|
-
requirement: !ruby/object:Gem::Requirement
|
176
|
-
requirements:
|
177
|
-
- - ">="
|
178
|
-
- !ruby/object:Gem::Version
|
179
|
-
version: '0'
|
180
|
-
type: :development
|
181
|
-
prerelease: false
|
182
|
-
version_requirements: !ruby/object:Gem::Requirement
|
183
|
-
requirements:
|
184
|
-
- - ">="
|
185
|
-
- !ruby/object:Gem::Version
|
186
|
-
version: '0'
|
187
131
|
description: A replacement log formatter for SSHKit that makes Capistrano output much
|
188
132
|
easier on the eyes. Just add Airbrussh to your Capfile and enjoy concise, useful
|
189
133
|
log output that is easy to read.
|
@@ -211,13 +155,15 @@ files:
|
|
211
155
|
- demo.gif
|
212
156
|
- lib/airbrussh.rb
|
213
157
|
- lib/airbrussh/capistrano.rb
|
158
|
+
- lib/airbrussh/capistrano/tasks.rb
|
214
159
|
- lib/airbrussh/colors.rb
|
215
160
|
- lib/airbrussh/command_formatter.rb
|
216
|
-
- lib/airbrussh/command_output.rb
|
217
161
|
- lib/airbrussh/configuration.rb
|
218
162
|
- lib/airbrussh/console.rb
|
163
|
+
- lib/airbrussh/console_formatter.rb
|
164
|
+
- lib/airbrussh/delegating_formatter.rb
|
219
165
|
- lib/airbrussh/formatter.rb
|
220
|
-
- lib/airbrussh/
|
166
|
+
- lib/airbrussh/log_file_formatter.rb
|
221
167
|
- lib/airbrussh/rake/context.rb
|
222
168
|
- lib/airbrussh/version.rb
|
223
169
|
- lib/sshkit/formatter/airbrussh.rb
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module Airbrussh
|
2
|
-
# A facade that provides access to stdout and stderr command output of
|
3
|
-
# sshkit commands. This is needed to normalize the API differences in
|
4
|
-
# various sshkit versions.
|
5
|
-
class CommandOutput
|
6
|
-
def self.for(command)
|
7
|
-
if command.respond_to?(:clear_stdout_lines)
|
8
|
-
Modern.new(command)
|
9
|
-
else
|
10
|
-
Legacy.new(command)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
attr_reader :command
|
15
|
-
|
16
|
-
def initialize(command)
|
17
|
-
@command = command
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
class Legacy < CommandOutput
|
22
|
-
# The stderr/stdout methods provided by the command object have the current
|
23
|
-
# "chunk" as received over the wire. Since there may be more chunks
|
24
|
-
# appended and we don't want to print duplicates, clear the current data.
|
25
|
-
def each_line(stream, &block)
|
26
|
-
output = command.public_send(stream)
|
27
|
-
return if output.empty?
|
28
|
-
output.lines.to_a.each(&block)
|
29
|
-
command.public_send("#{stream}=", "")
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class Modern < CommandOutput
|
34
|
-
# Newer versions of sshkit take care of clearing the output with the
|
35
|
-
# clear_stdout_lines/clear_stderr_lines methods.
|
36
|
-
def each_line(stream, &block)
|
37
|
-
lines = command.public_send("clear_#{stream}_lines")
|
38
|
-
return if lines.join.empty?
|
39
|
-
lines.each(&block)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require "delegate"
|
2
|
-
|
3
|
-
module Airbrussh
|
4
|
-
module Rake
|
5
|
-
# Decorates an SSHKit Command to add Rake-specific contextual information:
|
6
|
-
#
|
7
|
-
# * first_execution? - is this the first time this command has been run in
|
8
|
-
# the context of the current rake task?
|
9
|
-
# * position - zero-based position of this command in the list of
|
10
|
-
# all commands that have been run in the current rake task
|
11
|
-
#
|
12
|
-
class Command < SimpleDelegator
|
13
|
-
attr_reader :first_execution, :position
|
14
|
-
|
15
|
-
def initialize(command, first_execution, position)
|
16
|
-
super(command)
|
17
|
-
@first_execution = first_execution
|
18
|
-
@position = position
|
19
|
-
end
|
20
|
-
|
21
|
-
def first_execution?
|
22
|
-
first_execution
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|