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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e49b6c7756e1907e6a3cf024e9f90b811c3fae43
4
- data.tar.gz: 0d721e07a866d4cb7c65e283ee172d760eadc1fa
3
+ metadata.gz: a8636d9e6c1f9b44d7a760d970edd17cdfe08de8
4
+ data.tar.gz: 94139691e2b02c1b24b42c4df7f6a2aa9924ca45
5
5
  SHA512:
6
- metadata.gz: fc1aa66f2be55a796d7adbb03a4d142fcdc53cb22bd2c1800880ffec261997bad86f2a4029c87918ad481e09166235369e80a31246d5de51d7f6a8e60751ac9b
7
- data.tar.gz: 974909bb9964735a526f8c19b423d95ab83136c367e25e503e5dcee436aa68cd8c12faceb951e42cac5cab12a258693c8af3cfc07f35451ab953396363a75ce2
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.8'
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.5.1...HEAD
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
- After checking out the repo, run `bin/setup` to install dependencies.
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
- * `guard` monitors the filesystem and automatically runs tests as you work
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
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.0")
13
+ # rubocop:disable Lint/HandleExceptions
14
+ begin
14
15
  require "chandler/tasks"
15
- task "release:rubygem_push" => "chandler:push"
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.8"
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
@@ -9,6 +9,8 @@ YAML.load_file(".travis.yml")["env"].each do |sshkit_version|
9
9
  system("#{sshkit_version} bundle exec rake test")
10
10
  end
11
11
 
12
+ system("bundle exec rake rubocop")
13
+
12
14
  at_exit do
13
15
  puts "\e[0;34;49m== Resetting sshkit ==\e[0m"
14
16
  system("bundle update")
@@ -1,34 +1,17 @@
1
- require "airbrussh"
2
- require "airbrussh/colors"
3
- require "sshkit/formatter/airbrussh"
1
+ require "airbrussh/capistrano/tasks"
4
2
 
5
- # airbrush/capistrano uses a different default configuration
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
- set :format, :airbrussh
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
- output = env.backend.config.output
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
@@ -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::Command to add string output helpers.
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 command_output_stdout?
16
- command_output_include?(:stdout)
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
- def command_output_stderr?
20
- command_output_include?(:stderr)
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
- private
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
@@ -1,182 +1,17 @@
1
- require "airbrussh/colors"
2
- require "airbrussh/command_formatter"
3
- require "airbrussh/command_output"
4
- require "airbrussh/console"
5
- require "airbrussh/rake/command"
6
- require "airbrussh/rake/context"
7
- require "sshkit"
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 < SSHKit::Formatter::Abstract
11
- include Airbrussh::Colors
12
- extend Forwardable
13
-
14
- attr_reader :config, :context
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
- # Decorate an SSHKit Command with Rake::Command to provide additional
27
- # context-sensitive information.
28
- def decorate_command(command)
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
- Airbrussh::Rake::Command.new(
36
- command,
37
- first_execution,
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
@@ -1,3 +1,3 @@
1
1
  module Airbrussh
2
- VERSION = "0.5.1"
2
+ VERSION = "0.6.0"
3
3
  end
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.5.1
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-06-24 00:00:00.000000000 Z
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.8'
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.8'
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: rb-fsevent
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: minitest-reporters
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/rake/command.rb
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