airblade-fyi 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # FYI
2
+
3
+ Be informed about task execution.
4
+
5
+
6
+ ## Synopsis
7
+
8
+ fyi some_command
9
+
10
+
11
+ ## Examples
12
+
13
+ fyi ls -lt
14
+ fyi "ls -lt | grep total"
15
+ fyi "cd /var/www/apps/current && /opt/ree/bin/rake RAILS_ENV=production thinking_sphinx:index"
16
+
17
+
18
+ ## Description
19
+
20
+ The `fyi` command executes `some_command` and tells you what
21
+ happened. This is useful when `some_command` is executed
22
+ asynchronously, e.g. via cron, and you want to know how it
23
+ went without cluttering up your crontab with pipe redirections.
24
+
25
+ When `fyi` executes `some_command` it captures standard out,
26
+ standard error, and whether `some_command` succeeded or failed.
27
+ These are then reported by any notifiers you have configured.
28
+ Success is defined by a 0 exit code and failure by a non-zero
29
+ exit code.
30
+
31
+
32
+ ## Notifiers
33
+
34
+ The default notifier is the `Log` notifier. This writes to
35
+ `fyi.log` in the process's home directory unless you configure
36
+ it otherwise (see below).
37
+
38
+ One other notifier is currently available: the `Email` notifier.
39
+ Simply configure it (see below) to use it. You can switch
40
+ success notifications on and off and failure notifications
41
+ on and off independently. By default this notifier will only
42
+ email you when `some_command` fails.
43
+
44
+ To provide additional notifiers, e.g. Campfire / HTTP / Jabber,
45
+ add a class in `lib/notifiers/` and configure it (see below).
46
+ `fyi` will automatically instantiate it, configure it and use
47
+ it.
48
+
49
+ A notifier must:
50
+
51
+ * subclass Fyi::Notifier
52
+ * accept an options hash at initialisation (populated from
53
+ configuration).
54
+ * respond to `notify(command, result, output, error = '')`
55
+
56
+
57
+ ## Installation
58
+
59
+ sudo gem install fyi
60
+
61
+
62
+ ## Configuration
63
+
64
+ Configure `fyi` with a YAML file at `<home>/.fyi`, where
65
+ `<home>` is the process's home directory.
66
+
67
+ Each top-level key should be the name of a notifier class.
68
+ The key-value pairs in each notifier section are passed in
69
+ a hash to the notifier class at instantiation.
70
+
71
+
72
+ ## Problems
73
+
74
+ Please use GitHub's [issue tracker](http://github.com/airblade/fyi/issues).
75
+
76
+
77
+ ## Intellectual Property
78
+
79
+ Copyright (c) 2009 Andy Stewart (boss@airbladesoftware.com).
80
+ Released under the MIT licence.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gemspec|
6
+ gemspec.name = 'fyi'
7
+ gemspec.summary = 'Be informed about task execution.'
8
+ gemspec.email = 'boss@airbladesoftware.com'
9
+ gemspec.homepage = 'http://github.com/airblade/fyi'
10
+ gemspec.authors = ['Andy Stewart']
11
+ gemspec.add_dependency('pony')
12
+ end
13
+ rescue LoadError
14
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
15
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.3
data/bin/fyi ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
3
+ require 'fyi'
4
+ Fyi.run ARGV.join(' ')
@@ -0,0 +1,17 @@
1
+ #
2
+ # Move this to ~/.fyi
3
+ #
4
+
5
+ log:
6
+ file: /var/log/fyi.log
7
+ email:
8
+ on_success: yes
9
+ from: fyi@yourdomain.com
10
+ to: you@yourdomain.com
11
+ smtp:
12
+ host: smtp.yourserver.com
13
+ port: 25
14
+ user: username
15
+ password: password
16
+ auth: :login
17
+ domain: yourdomain.com
data/fyi.gemspec ADDED
@@ -0,0 +1,52 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{fyi}
8
+ s.version = "0.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Andy Stewart"]
12
+ s.date = %q{2009-08-08}
13
+ s.default_executable = %q{fyi}
14
+ s.email = %q{boss@airbladesoftware.com}
15
+ s.executables = ["fyi"]
16
+ s.extra_rdoc_files = [
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "README.md",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "bin/fyi",
25
+ "config_example.yml",
26
+ "fyi.gemspec",
27
+ "lib/fyi.rb",
28
+ "lib/fyi/config.rb",
29
+ "lib/fyi/core_ext.rb",
30
+ "lib/fyi/notifiers/email.rb",
31
+ "lib/fyi/notifiers/log.rb"
32
+ ]
33
+ s.has_rdoc = true
34
+ s.homepage = %q{http://github.com/airblade/fyi}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.1}
38
+ s.summary = %q{Be informed about task execution.}
39
+
40
+ if s.respond_to? :specification_version then
41
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
+ s.specification_version = 2
43
+
44
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<pony>, [">= 0"])
46
+ else
47
+ s.add_dependency(%q<pony>, [">= 0"])
48
+ end
49
+ else
50
+ s.add_dependency(%q<pony>, [">= 0"])
51
+ end
52
+ end
data/lib/fyi.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'open4'
3
+ require 'fyi/config'
4
+ require 'fyi/core_ext'
5
+
6
+ #
7
+ # See /bin/fyi for documentation.
8
+ #
9
+ class Fyi
10
+ def self.run(command)
11
+ new(command).run
12
+ end
13
+
14
+ def initialize(command)
15
+ @command = command
16
+ @config = Config.new
17
+ end
18
+
19
+ def run
20
+ # Borrowed from CI Joe.
21
+ out, err, status = '', '', nil
22
+ status = Open4.popen4(@command) do |@pid, stdin, stdout, stderr|
23
+ err, out = stderr.read.strip, stdout.read.strip
24
+ end
25
+ status.exitstatus.to_i == 0 ? run_succeeded(out) : run_failed(out, err)
26
+ rescue Object => e
27
+ run_failed('', e.to_s)
28
+ end
29
+
30
+ private
31
+
32
+ def run_succeeded output
33
+ notify :success, output
34
+ end
35
+
36
+ def run_failed output, error
37
+ notify :failure, output, error
38
+ end
39
+
40
+ def notify result, output, error = ''
41
+ notifiers.each do |notifier|
42
+ notifier.notify @command, result, output, error
43
+ end
44
+ end
45
+
46
+ # Instantiate and configure notifiers as per the config.
47
+ def notifiers
48
+ @config.notifiers.map do |klass_name, options|
49
+ require "fyi/notifiers/#{klass_name}"
50
+ klass = constantize "Fyi::Notifier::#{klass_name.capitalize}"
51
+ klass.send :new, options
52
+ end
53
+ end
54
+
55
+ end
data/lib/fyi/config.rb ADDED
@@ -0,0 +1,29 @@
1
+ class Fyi
2
+ class Config
3
+
4
+ def notifiers
5
+ config
6
+ end
7
+
8
+ private
9
+
10
+ def config
11
+ defaults = { 'log' => {} }
12
+ conf = YAML::load(config_file) rescue {}
13
+ defaults.merge(conf)
14
+ end
15
+
16
+ def config_file
17
+ File.read config_file_path
18
+ end
19
+
20
+ def config_file_path
21
+ File.join home_dir, '.fyi'
22
+ end
23
+
24
+ def home_dir
25
+ ENV['HOME']
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ # Lifted from ActiveSupport.
2
+ #
3
+ # Ruby 1.9 introduces an inherit argument for Module#const_get and
4
+ # #const_defined? and changes their default behavior.
5
+ if Module.method(:const_get).arity == 1
6
+ # Tries to find a constant with the name specified in the argument string:
7
+ #
8
+ # "Module".constantize # => Module
9
+ # "Test::Unit".constantize # => Test::Unit
10
+ #
11
+ # The name is assumed to be the one of a top-level constant, no matter whether
12
+ # it starts with "::" or not. No lexical context is taken into account:
13
+ #
14
+ # C = 'outside'
15
+ # module M
16
+ # C = 'inside'
17
+ # C # => 'inside'
18
+ # "C".constantize # => 'outside', same as ::C
19
+ # end
20
+ #
21
+ # NameError is raised when the name is not in CamelCase or the constant is
22
+ # unknown.
23
+ def constantize(camel_cased_word)
24
+ names = camel_cased_word.split('::')
25
+ names.shift if names.empty? || names.first.empty?
26
+
27
+ constant = Object
28
+ names.each do |name|
29
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
30
+ end
31
+ constant
32
+ end
33
+ else
34
+ def constantize(camel_cased_word) #:nodoc:
35
+ names = camel_cased_word.split('::')
36
+ names.shift if names.empty? || names.first.empty?
37
+
38
+ constant = Object
39
+ names.each do |name|
40
+ constant = constant.const_get(name, false) || constant.const_missing(name)
41
+ end
42
+ constant
43
+ end
44
+ end
@@ -0,0 +1,83 @@
1
+ require 'pony'
2
+
3
+ class Fyi
4
+ class Notifier
5
+ # Emails the results of command execution via SMTP.
6
+ #
7
+ # By default only failures are emailed.
8
+ class Email
9
+
10
+ # Options you may supply:
11
+ #
12
+ # +from+: the from address
13
+ # +to+: the to address
14
+ # +on_success+: whether to notify when the command succeeded.
15
+ # Optional. Defaults to false.
16
+ # +on_failure+: whether to notify when the command failed.
17
+ # Optional. Defaults to true.
18
+ # +smtp+: you should supply SMTP config options under this key.
19
+ #
20
+ # SMTP config options are:
21
+ # +host+
22
+ # +port+
23
+ # +user+
24
+ # +password+
25
+ # +auth+
26
+ # +domain+
27
+ def initialize options
28
+ @from = options['from']
29
+ @to = options['to']
30
+ @smtp = symbolize_keys options['smtp']
31
+ @on_success = options['on_success']
32
+ # Notify of failures by default.
33
+ @on_failure = options.has_key?('on_failure') ? options['on_failure'] : true
34
+ end
35
+
36
+ def notify command, result, output, error = ''
37
+ send_email(command, result, output, error) if should_notify?(result)
38
+ end
39
+
40
+ private
41
+
42
+ def should_notify?(result)
43
+ (result == :success && @on_success) || (result == :failure && @on_failure)
44
+ end
45
+
46
+ def send_email command, result, output, error
47
+ Pony.mail :to => @to,
48
+ :from => @from,
49
+ :subject => subject(command, result),
50
+ :body => body(command, output, error),
51
+ :via => :smtp,
52
+ :smtp => @smtp
53
+ end
54
+
55
+ def subject command, result
56
+ "[#{result.to_s.upcase}] #{truncate command}"
57
+ end
58
+
59
+ def body command, output, error
60
+ <<END
61
+ command: #{command}
62
+
63
+ stdout: #{output}
64
+
65
+ stderr: #{error}
66
+ END
67
+ end
68
+
69
+ def truncate string, length = 30
70
+ if string.length > length
71
+ "#{string[0..(length - 3)]}..."
72
+ else
73
+ string
74
+ end
75
+ end
76
+
77
+ def symbolize_keys hsh
78
+ hsh.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,48 @@
1
+ require 'logger'
2
+
3
+ class Fyi
4
+ class Notifier
5
+ # Logs command execution to a file.
6
+ #
7
+ # The log level depends on the execution result: INFO if the
8
+ # command succeeded, WARN if it failed.
9
+ class Log
10
+ attr_reader :logger
11
+
12
+ # Options you can supply:
13
+ # +file+: full path to a log file. Defaults to +fyi.log+ in
14
+ # the process's home directory.
15
+ def initialize options
16
+ log_file = options['file'] || default_log_file
17
+ @logger = Logger.new log_file
18
+ end
19
+
20
+ def notify command, result, output, error = ''
21
+ logger.log severity(result), message(command, result, output, error)
22
+ end
23
+
24
+ private
25
+
26
+ def severity result
27
+ result == :success ? Logger::INFO : Logger::WARN
28
+ end
29
+
30
+ def message command, result, output, error
31
+ <<END
32
+ command: #{command}
33
+ status: #{result.to_s.upcase}
34
+ stdout: #{output}
35
+ stderr: #{error}
36
+ END
37
+ end
38
+
39
+ def default_log_file
40
+ File.join home_dir, 'fyi.log'
41
+ end
42
+
43
+ def home_dir
44
+ ENV['HOME']
45
+ end
46
+ end
47
+ end
48
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: airblade-fyi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Andy Stewart
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-08 00:00:00 -07:00
13
+ default_executable: fyi
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: pony
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description:
26
+ email: boss@airbladesoftware.com
27
+ executables:
28
+ - fyi
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.md
33
+ files:
34
+ - .gitignore
35
+ - README.md
36
+ - Rakefile
37
+ - VERSION
38
+ - bin/fyi
39
+ - config_example.yml
40
+ - fyi.gemspec
41
+ - lib/fyi.rb
42
+ - lib/fyi/config.rb
43
+ - lib/fyi/core_ext.rb
44
+ - lib/fyi/notifiers/email.rb
45
+ - lib/fyi/notifiers/log.rb
46
+ has_rdoc: true
47
+ homepage: http://github.com/airblade/fyi
48
+ licenses:
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.3.5
70
+ signing_key:
71
+ specification_version: 2
72
+ summary: Be informed about task execution.
73
+ test_files: []
74
+