airbrussh 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|