warningshot 0.9.4
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.
- data/CHANGELOG +0 -0
- data/CONTRIBUTORS +2 -0
- data/LICENSE +22 -0
- data/README +101 -0
- data/Rakefile +28 -0
- data/TODO +148 -0
- data/bin/warningshot +19 -0
- data/bin/ws-stage.bat +3 -0
- data/bin/ws-stage.sh +3 -0
- data/images/warning_shot.png +0 -0
- data/images/warning_shot_sml.png +0 -0
- data/images/warningshot_green.png +0 -0
- data/images/warningshot_red.png +0 -0
- data/lib/core_ext/hash.rb +14 -0
- data/lib/core_ext/kernel.rb +8 -0
- data/lib/core_ext/object.rb +7 -0
- data/lib/core_ext/string.rb +5 -0
- data/lib/resolvers/core_lib_resolver.rb +76 -0
- data/lib/resolvers/directory_resolver.rb +31 -0
- data/lib/resolvers/file_resolver.rb +87 -0
- data/lib/resolvers/gem_resolver.rb +94 -0
- data/lib/resolvers/integrity_resolver.rb +69 -0
- data/lib/resolvers/manual_resolver.rb +19 -0
- data/lib/resolvers/permission_resolver.rb +72 -0
- data/lib/resolvers/symlink_resolver.rb +43 -0
- data/lib/resolvers/url_resolver.rb +76 -0
- data/lib/warning_shot/config.rb +132 -0
- data/lib/warning_shot/dependency_resolver.rb +155 -0
- data/lib/warning_shot/growl.rb +14 -0
- data/lib/warning_shot/logger.rb +27 -0
- data/lib/warning_shot/resolver.rb +542 -0
- data/lib/warning_shot/template_generator.rb +28 -0
- data/lib/warning_shot/version.rb +7 -0
- data/lib/warning_shot/warning_shot.rb +130 -0
- data/lib/warningshot.rb +16 -0
- data/tasks/gemspec.rb +48 -0
- data/tasks/rpsec.rb +58 -0
- data/tasks/yard.rb +4 -0
- data/templates/binaries.yml +26 -0
- data/templates/core_libs.yml +18 -0
- data/templates/directories.yml +23 -0
- data/templates/files.yml +18 -0
- data/templates/gems.yml +26 -0
- data/templates/manual.yml +29 -0
- data/templates/mounts.yml +8 -0
- data/templates/permissions.yml +31 -0
- data/templates/symlinks.yml +30 -0
- data/templates/urls.yml +21 -0
- data/test/data/mock.yaml +16 -0
- data/test/data/mock.yml +10 -0
- data/test/data/mock_resolver.rb +16 -0
- data/test/data/permission_test.txt +1 -0
- data/test/data/resolvers/file/src/that.txt +1 -0
- data/test/log/warningshot.log +643 -0
- data/test/spec/spec.opts.zoiks +0 -0
- data/test/spec/spec_helper.rb +11 -0
- data/test/spec/unit/resolvers/core_lib_resolver_spec.rb +39 -0
- data/test/spec/unit/resolvers/directory_resolver_spec.rb +39 -0
- data/test/spec/unit/resolvers/file_resolver_spec.rb +134 -0
- data/test/spec/unit/resolvers/gem_resolver_spec.rb +74 -0
- data/test/spec/unit/resolvers/integrity_resolver_spec.rb +103 -0
- data/test/spec/unit/resolvers/manual_resolver_spec.rb +17 -0
- data/test/spec/unit/resolvers/permission_resolver_spec.rb +56 -0
- data/test/spec/unit/resolvers/symlink_resolver_spec.rb +44 -0
- data/test/spec/unit/resolvers/url_resolver_spec.rb +64 -0
- data/test/spec/unit/warning_shot/config_spec.rb +35 -0
- data/test/spec/unit/warning_shot/dependency_resolver_spec.rb +43 -0
- data/test/spec/unit/warning_shot/resolver_spec.rb +347 -0
- data/test/spec/unit/warning_shot/template_generator_spec.rb +21 -0
- data/test/spec/unit/warning_shot/version_spec.rb +7 -0
- data/test/spec/unit/warning_shot/warning_shot_spec.rb +3 -0
- metadata +143 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
class WarningShot::IntegrityResolver
|
5
|
+
include WarningShot::Resolver
|
6
|
+
order 600
|
7
|
+
#disable!
|
8
|
+
|
9
|
+
# Uses the same config files as file resolver, just add md5 field to config YML
|
10
|
+
branch :file
|
11
|
+
description 'Check file integrity via md5 or sha1 digest'
|
12
|
+
|
13
|
+
# Define FileResource struct
|
14
|
+
FileResource = Struct.new(:source,:target,:digest,:digest_method) do
|
15
|
+
def exists?;File.exists?(target.path);end;
|
16
|
+
end
|
17
|
+
|
18
|
+
cast String do |file|
|
19
|
+
FileResource.new URI.parse(''), URI.parse(file), nil, nil
|
20
|
+
end
|
21
|
+
|
22
|
+
cast Hash do |file|
|
23
|
+
file[:source].sub!(/file:\/\//i,'') unless file[:source].nil?
|
24
|
+
|
25
|
+
if file[:sha1] && file[:md5]
|
26
|
+
digest, digest_method = nil, nil
|
27
|
+
elsif file[:sha1]
|
28
|
+
digest, digest_method = file[:sha1], :sha1
|
29
|
+
elsif file[:md5]
|
30
|
+
digest, digest_method = file[:md5], :md5
|
31
|
+
end
|
32
|
+
|
33
|
+
FileResource.new URI.parse(file[:source] || ''), URI.parse(file[:target]), digest, digest_method
|
34
|
+
end
|
35
|
+
|
36
|
+
register(:test,{:name=>:sha1_digest,
|
37
|
+
:if => lambda{|dep| dep.digest_method == :sha1}
|
38
|
+
})do |dep|
|
39
|
+
dep_ok = (dep.exists? ? Digest::SHA1.hexdigest(File.read(dep.target.path)) == dep.digest : false)
|
40
|
+
|
41
|
+
if dep_ok
|
42
|
+
logger.debug " ~ [PASSED] checksum #{dep.target}"
|
43
|
+
else
|
44
|
+
logger.warn " ~ [FAILED] checksum #{dep.target}"
|
45
|
+
end
|
46
|
+
|
47
|
+
dep_ok
|
48
|
+
end
|
49
|
+
|
50
|
+
register(:test,{:name=>:md5_digest,
|
51
|
+
:if => lambda{|dep| dep.digest_method == :md5}
|
52
|
+
})do |dep|
|
53
|
+
dep_ok = (dep.exists? ? Digest::MD5.hexdigest(File.read(dep.target.path)) == dep.digest : false)
|
54
|
+
|
55
|
+
if dep_ok
|
56
|
+
logger.debug " ~ [PASSED] checksum #{dep.target}"
|
57
|
+
else
|
58
|
+
logger.warn " ~ [FAILED] checksum #{dep.target}"
|
59
|
+
end
|
60
|
+
|
61
|
+
dep_ok
|
62
|
+
end
|
63
|
+
|
64
|
+
register :test,{:name=>:no_digest,
|
65
|
+
:if => lambda{|dep| dep.digest_method == nil}
|
66
|
+
} do |dep|
|
67
|
+
true
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class WarningShot::ManualResolver
|
2
|
+
include WarningShot::Resolver
|
3
|
+
order 10000
|
4
|
+
#disable!
|
5
|
+
|
6
|
+
branch :manual
|
7
|
+
description 'A glorified todo list of things that need to be resolved manually'
|
8
|
+
|
9
|
+
#Encapsulated in a struct so Resolver doesn't freak out when we instance_eval #met & #resolved
|
10
|
+
NoteResource = Struct.new(:msg)
|
11
|
+
|
12
|
+
cast do |note|
|
13
|
+
NoteResource.new(note)
|
14
|
+
end
|
15
|
+
|
16
|
+
register :test do |dep|
|
17
|
+
logger.info " ~ #{dep.msg}"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# TODO It would be really sweet if there was a way for FileResolver,
|
2
|
+
# DirectoryResolver, and SymlinkResolver inherited this functionality if
|
3
|
+
# mode, user, or group is set in its config file.
|
4
|
+
class WarningShot::PermissionResolver
|
5
|
+
include WarningShot::Resolver
|
6
|
+
order 100
|
7
|
+
branch :permission
|
8
|
+
description 'Validates mode, user, and group permission on files and directories'
|
9
|
+
|
10
|
+
module UnixPermissionsInterface;end;
|
11
|
+
module WindowsPermissionsInterface;end;
|
12
|
+
|
13
|
+
if WarningShot.platform != :windows
|
14
|
+
include WarningShot::PermissionResolver::UnixPermissionsInterface
|
15
|
+
#http://www.ruby-doc.org/core/classes/File/Stat.html
|
16
|
+
#http://www.ruby-doc.org/stdlib/libdoc/etc/rdoc/index.html
|
17
|
+
#http://www.ruby-doc.org/stdlib/libdoc/pathname/rdoc/index.html
|
18
|
+
#http://www.ruby-doc.org/stdlib/libdoc/fileutils/rdoc/index.html
|
19
|
+
#http://www.ruby-doc.org/core/classes/File.html#M002574
|
20
|
+
else
|
21
|
+
include WarningShot::PermissionResolver::WindowsPermissionsInterface
|
22
|
+
end
|
23
|
+
|
24
|
+
PermissionResource = Struct.new(:path,:target_mode,:target_user,:target_group,:recursive) do
|
25
|
+
def exists?
|
26
|
+
File.exist? self.path
|
27
|
+
end
|
28
|
+
|
29
|
+
def correct_owner?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def correct_group?
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
def correct_mode?
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
cast Hash do |yaml|
|
43
|
+
_path = yaml.delete(:path)
|
44
|
+
_mode = yaml.delete(:mode) || '0755'
|
45
|
+
_user = yaml.delete(:user) || 'nobody'
|
46
|
+
_group = yaml.delete(:group) || 'nobody'
|
47
|
+
_recursive = yaml.delete(:recursive) || 'none'
|
48
|
+
|
49
|
+
PermissionResource.new _path, _mode, _user, _group, _recursive
|
50
|
+
end
|
51
|
+
|
52
|
+
register :test do |resource|
|
53
|
+
_valid = resource.exists?
|
54
|
+
|
55
|
+
if _valid
|
56
|
+
_valid &= resource.correct_owner?
|
57
|
+
_valid &= resource.correct_group?
|
58
|
+
_valid &= resource.correct_mode?
|
59
|
+
end
|
60
|
+
|
61
|
+
if _valid
|
62
|
+
logger.debug " ~ [PASSED] permission: #{resource.path}"
|
63
|
+
else
|
64
|
+
logger.warn " ~ [FAILED] permission: #{resource.path}"
|
65
|
+
end
|
66
|
+
|
67
|
+
_valid
|
68
|
+
end
|
69
|
+
|
70
|
+
register :resolution do |resource|
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class WarningShot::SymlinkResolver
|
2
|
+
include WarningShot::Resolver
|
3
|
+
|
4
|
+
order 50
|
5
|
+
branch :symlink
|
6
|
+
description 'Validates symlinks exist'
|
7
|
+
|
8
|
+
SymlinkResource = Struct.new(:source,:target,:force) do
|
9
|
+
def link!;FileUtils.ln_s(self.source,self.target,:force=>self.force);end;
|
10
|
+
|
11
|
+
def exists?
|
12
|
+
self.target ? File.symlink?(self.target) : false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
cast String do |yaml|
|
17
|
+
SymlinkResource.new yaml, nil, false
|
18
|
+
end
|
19
|
+
|
20
|
+
cast Hash do |yaml|
|
21
|
+
use_force = yaml[:force].nil? ? true : yaml[:force]
|
22
|
+
SymlinkResource.new yaml[:source],yaml[:target], use_force
|
23
|
+
end
|
24
|
+
|
25
|
+
# If the target wasn't specified, it doesn't exist
|
26
|
+
register :test do |dep|
|
27
|
+
if symlink_found = dep.exists?
|
28
|
+
logger.debug " ~ [PASSED] symlink #{dep.target}"
|
29
|
+
else
|
30
|
+
logger.warn " ~ [FAILED] symlink #{dep.target}"
|
31
|
+
end
|
32
|
+
symlink_found
|
33
|
+
end
|
34
|
+
|
35
|
+
register :resolution do |dep|
|
36
|
+
begin
|
37
|
+
dep.link! if dep.target
|
38
|
+
rescue Errno::EEXIST, Errno::ENOTDIR => ex
|
39
|
+
logger.error " ~ Could not create symlink #{dep.source} => #{dep.target}"
|
40
|
+
end
|
41
|
+
dep.exists?
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
|
5
|
+
module WarningShot
|
6
|
+
class UrlResolver
|
7
|
+
include WarningShot::Resolver
|
8
|
+
order 900
|
9
|
+
#disable!
|
10
|
+
|
11
|
+
branch :url
|
12
|
+
description 'Validates that URLs are reachable.'
|
13
|
+
|
14
|
+
cli(
|
15
|
+
:long => "--strict",
|
16
|
+
:description => "Success is only for 200 instead of 2xx|3xx",
|
17
|
+
:name => "url_strict",
|
18
|
+
:default => false
|
19
|
+
)
|
20
|
+
|
21
|
+
cli(
|
22
|
+
:long => "--rootcert",
|
23
|
+
:description => "Path to root ca certificate",
|
24
|
+
:name => "root_ca",
|
25
|
+
:default => nil
|
26
|
+
)
|
27
|
+
|
28
|
+
cli(
|
29
|
+
:long => "--vdepth",
|
30
|
+
:description => "SSL Verify Peer Depth",
|
31
|
+
:name => "ssl_verify_depth",
|
32
|
+
:default => 5
|
33
|
+
)
|
34
|
+
|
35
|
+
UrlResource = Struct.new(:uri)
|
36
|
+
cast do |dep|
|
37
|
+
UrlResource.new URI.parse(dep)
|
38
|
+
end
|
39
|
+
|
40
|
+
register :test do |dep|
|
41
|
+
begin
|
42
|
+
http = Net::HTTP.new(dep.uri.host,dep.uri.port)
|
43
|
+
|
44
|
+
if dep.uri.scheme == 'https'
|
45
|
+
http.use_ssl = true
|
46
|
+
|
47
|
+
if WarningShot::Config.configuration[:root_ca] && File.exist?(WarningShot::Config.configuration[:root_ca])
|
48
|
+
http.ca_file = WarningShot::Config.configuration[:root_ca]
|
49
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
50
|
+
http.verify_depth = WarningShot::Config.configuration[:ssl_verify_depth]
|
51
|
+
else
|
52
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
dep.uri.path = '/' if dep.uri.path.empty?
|
57
|
+
resp = http.head(dep.uri.path)
|
58
|
+
|
59
|
+
valid_codes = WarningShot::Config.configuration[:url_strict] ? /200/ : /^[23][0-9][0-9]$/
|
60
|
+
|
61
|
+
page_found = (resp.code =~ valid_codes)
|
62
|
+
|
63
|
+
if page_found
|
64
|
+
logger.debug " ~ [PASSED] url #{dep.uri.to_s}"
|
65
|
+
else
|
66
|
+
logger.warn " ~ [FAILED] url #{dep.uri.to_s}"
|
67
|
+
end
|
68
|
+
|
69
|
+
page_found
|
70
|
+
rescue Exception
|
71
|
+
logger.error "Could not reach #{dep.uri.to_s}"
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# I pretty much goinked this from merb, you love merb, merb loves you.
|
2
|
+
# http://merbivore.com
|
3
|
+
|
4
|
+
module WarningShot
|
5
|
+
class Config
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def defaults
|
9
|
+
@defaults ||= {
|
10
|
+
:environment => 'development',
|
11
|
+
:resolve => false,
|
12
|
+
:config_paths => ['.' / 'config' / 'warningshot', '~' / '.warningshot'],
|
13
|
+
:application => '.',
|
14
|
+
:log_path => '.' / 'log' / 'warningshot.log',
|
15
|
+
:log_level => :info,
|
16
|
+
:growl => false,
|
17
|
+
:verbose => false,
|
18
|
+
:colorize => true,
|
19
|
+
:resolvers => ['~' / '.warningshot' / '*.rb']
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def use
|
24
|
+
@configuration ||= {}
|
25
|
+
yield @configuration
|
26
|
+
@configuration = defaults.merge(@configuration)
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](key)
|
30
|
+
(@configuration||={})[key]
|
31
|
+
end
|
32
|
+
|
33
|
+
def []=(key,val)
|
34
|
+
(@configuration||={})[key] = val
|
35
|
+
#@configuration[key] = val
|
36
|
+
end
|
37
|
+
|
38
|
+
def setup(settings = {})
|
39
|
+
@configuration = defaults.merge(settings)
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_accessor :configuration
|
43
|
+
|
44
|
+
def parse_args(argv = ARGV)
|
45
|
+
@configuration ||= {}
|
46
|
+
options = {}
|
47
|
+
options[:environment] = ENV["WARNING_SHOT_ENV"] if ENV["WARNING_SHOT_ENV"]
|
48
|
+
|
49
|
+
WarningShot.parser.banner = <<-BANNER
|
50
|
+
WarningShot v. #{WarningShot::VERSION}
|
51
|
+
Dependency Resolution Framework
|
52
|
+
|
53
|
+
Usage: warningshot [options]
|
54
|
+
BANNER
|
55
|
+
WarningShot.parser.separator '*'*80
|
56
|
+
|
57
|
+
WarningShot.parser.on("-e", "--environment=STRING", String, "Environment to test in","Default: #{defaults[:environment]}") do |env|
|
58
|
+
options[:environment] = env
|
59
|
+
end
|
60
|
+
WarningShot.parser.on("--resolve","Resolve missing dependencies (run as sudo)") do |resolve|
|
61
|
+
options[:resolve] = resolve
|
62
|
+
end
|
63
|
+
WarningShot.parser.on("-a","--app=PATH", String, "Path to application", "Default: #{defaults[:application]}") do |app|
|
64
|
+
options[:application] = app
|
65
|
+
end
|
66
|
+
WarningShot.parser.on("-c","--configs=PATH", String,"Path to config directories (':' seperated)","Default: #{defaults[:config_paths].join(':')}") do |config|
|
67
|
+
options[:config_paths] = config.split(':')
|
68
|
+
end
|
69
|
+
WarningShot.parser.on("-r","--resolvers=PATH", String,"Path to add'l resolvers (':' seperated)","Default: #{defaults[:resolvers].join(':')}") do |config|
|
70
|
+
options[:resolvers] = config.split(':')
|
71
|
+
end
|
72
|
+
WarningShot.parser.on("-t","--templates=PATH", String, "Generate template files", "Default: False") do |template_path|
|
73
|
+
template_path = options[:config_paths].first if template_path.nil? || template_path.empty?
|
74
|
+
WarningShot::TemplateGenerator.create(template_path)
|
75
|
+
exit
|
76
|
+
end
|
77
|
+
WarningShot.parser.on("-l","--log LOG", String, "Path to log file", "Default: #{defaults[:log_path]}") do |log_path|
|
78
|
+
options[:log_path] = log_path
|
79
|
+
end
|
80
|
+
WarningShot.parser.on("--loglevel [LEVEL]",[:debug, :info, :warn, :error, :fatal], "Default: #{defaults[:log_level]}") do |log_level|
|
81
|
+
options[:log_level] = log_level
|
82
|
+
end
|
83
|
+
WarningShot.parser.on("-g", "--growl", "Output results via growl (Requires growlnotify)") do |growl|
|
84
|
+
options[:growl] = growl
|
85
|
+
end
|
86
|
+
WarningShot.parser.on("-v", "--[no-]verbose", "Output verbose information") do |verbose|
|
87
|
+
options[:verbose] = verbose
|
88
|
+
end
|
89
|
+
WarningShot.parser.on("-p", "--[no-]prettycolors", "Colorize output") do |colorize|
|
90
|
+
options[:colorize] = colorize
|
91
|
+
end
|
92
|
+
WarningShot.parser.on_tail("--version", "Show version"){
|
93
|
+
WarningShot.parser.parse!(argv)
|
94
|
+
WarningShot::Config.setup(options)
|
95
|
+
|
96
|
+
WarningShot.load_app
|
97
|
+
WarningShot.load_addl_resolvers
|
98
|
+
|
99
|
+
puts "WarningShot v. #{WarningShot::VERSION}"
|
100
|
+
puts "Installed resolvers:"
|
101
|
+
Resolver.descendents.each { |klass|
|
102
|
+
puts "\n"
|
103
|
+
puts klass
|
104
|
+
puts " Tests: #{klass.tests.length}, Resolutions: #{klass.resolutions.length} [#{klass.resolutions.empty? ? 'irresolvable' : 'resolvable'}]"
|
105
|
+
puts " #{klass.description}"
|
106
|
+
puts " Command Line Options: #{klass.raw_cli_ext.inject([]){|m,c| m << c[:long]}.join(',')}" if klass.raw_cli_ext
|
107
|
+
}
|
108
|
+
exit
|
109
|
+
}
|
110
|
+
WarningShot.parser.on_tail("-h", "--help","Show this help message") { puts WarningShot.parser; exit }
|
111
|
+
WarningShot.parser.on_tail("--debugger","Enable debugging") do
|
112
|
+
begin
|
113
|
+
require "ruby-debug"
|
114
|
+
Debugger.start
|
115
|
+
Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
|
116
|
+
puts "Debugger enabled"
|
117
|
+
rescue LoadError => ex
|
118
|
+
puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
|
119
|
+
exit
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
WarningShot.parser.parse!(argv)
|
124
|
+
WarningShot::Config.setup(options)
|
125
|
+
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument => op
|
126
|
+
puts op
|
127
|
+
puts WarningShot.parser #; exit;
|
128
|
+
end
|
129
|
+
|
130
|
+
end #End self
|
131
|
+
end #End Config
|
132
|
+
end#End WarningShot
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module WarningShot
|
2
|
+
class DependencyResolver
|
3
|
+
|
4
|
+
attr_reader :environment, :dependency_tree, :resolvers
|
5
|
+
def initialize(config={})
|
6
|
+
@config = config
|
7
|
+
@environment = @config[:environment].to_sym
|
8
|
+
@dependency_tree = {}
|
9
|
+
@resolvers = []
|
10
|
+
|
11
|
+
init_logger
|
12
|
+
|
13
|
+
# Parsed yml files
|
14
|
+
self.load_configs
|
15
|
+
@dependency_tree.symbolize_keys!
|
16
|
+
end
|
17
|
+
|
18
|
+
# gets stats of all resolvers
|
19
|
+
# @return [Hash]
|
20
|
+
# :passed, :failed, :resolved, :unresolved
|
21
|
+
#
|
22
|
+
# @api public
|
23
|
+
def stats
|
24
|
+
_stats = {
|
25
|
+
:passed => 0, :failed => 0, :resolved => 0, :unresolved => 0
|
26
|
+
}
|
27
|
+
|
28
|
+
resolvers.each do |resolver|
|
29
|
+
_stats[:passed] += resolver.passed.size
|
30
|
+
_stats[:failed] += resolver.failed.size
|
31
|
+
_stats[:resolved] += resolver.resolved.size
|
32
|
+
_stats[:unresolved] += resolver.unresolved.size
|
33
|
+
end
|
34
|
+
|
35
|
+
_stats
|
36
|
+
end
|
37
|
+
|
38
|
+
# initializes the logger
|
39
|
+
#
|
40
|
+
# @api private
|
41
|
+
def init_logger
|
42
|
+
FileUtils.mkdir_p(File.dirname(File.expand_path(@config[:log_path]))) unless @config[:verbose]
|
43
|
+
|
44
|
+
@logger = Logger.new(
|
45
|
+
@config[:verbose] ? STDOUT : @config[:log_path], 10, 1024000
|
46
|
+
)
|
47
|
+
_log_level = (@config[:log_level] || :debug).to_s.upcase
|
48
|
+
|
49
|
+
_formatter = WarningShot::LoggerFormatter.new
|
50
|
+
_formatter.colorize = @config[:colorize]
|
51
|
+
|
52
|
+
@logger.formatter = _formatter
|
53
|
+
@logger.level = Object.class_eval("Logger::#{_log_level}")
|
54
|
+
end
|
55
|
+
|
56
|
+
# runs all loaded resolvers
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
def run
|
60
|
+
@logger.info "WarningShot v. #{WarningShot::VERSION}"
|
61
|
+
@logger.info "Environment: #{WarningShot.environment}; Application: #{WarningShot.framework}"
|
62
|
+
@logger.info "On host: #{WarningShot.hostname}"
|
63
|
+
|
64
|
+
WarningShot::Resolver.descendents.each do |klass|
|
65
|
+
@logger.info "\n#{'-'*60}"
|
66
|
+
|
67
|
+
branch = @dependency_tree[klass.branch.to_sym]
|
68
|
+
|
69
|
+
if branch.nil?
|
70
|
+
@logger.info "No config file was found for branch #{klass.branch}"
|
71
|
+
next
|
72
|
+
end
|
73
|
+
|
74
|
+
klass.logger = @logger
|
75
|
+
resolver = klass.new(*branch)
|
76
|
+
|
77
|
+
@resolvers << resolver
|
78
|
+
|
79
|
+
@logger.info "#{resolver.class}; branch: #{klass.branch} [TESTING]"
|
80
|
+
|
81
|
+
# Start test
|
82
|
+
klass.before_filters(:test).each{|p| p.call}
|
83
|
+
resolver.test!
|
84
|
+
klass.after_filters(:test).each{|p| p.call}
|
85
|
+
|
86
|
+
@logger.info "Passed: #{resolver.passed.size} / Failed: #{resolver.failed.size}"
|
87
|
+
|
88
|
+
if WarningShot::Config.configuration[:resolve] && !klass.resolutions.empty?
|
89
|
+
@logger.info "#{resolver.class}; branch: #{klass.branch} [RESOLVING]"
|
90
|
+
|
91
|
+
klass.before_filters(:resolution).each{|p| p.call}
|
92
|
+
resolver.resolve!
|
93
|
+
klass.after_filters(:resolution).each{|p| p.call}
|
94
|
+
|
95
|
+
@logger.info "Resolved: #{resolver.resolved.size} / Unresolved: #{resolver.unresolved.size}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@logger.info "\nResults:"
|
100
|
+
stats.each {|k,v| @logger.info(" ~ #{k}: \t#{v}")}
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
# Loads configuration files
|
105
|
+
#
|
106
|
+
# @api protected
|
107
|
+
def load_configs
|
108
|
+
@config[:config_paths].each do |config_path|
|
109
|
+
#Parse the global/running env configs out of the YAML files.
|
110
|
+
Dir[config_path / WarningShot::ConfigExt].each do |config_file|
|
111
|
+
# Use WarningShot::ConfigExt & regexp on extension to make supporting add'l
|
112
|
+
# file types easier in the future
|
113
|
+
case File.extname(config_file)
|
114
|
+
when /.y(a)?ml/
|
115
|
+
parse_yml config_file
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# parses dependencies info from a yaml file
|
122
|
+
#
|
123
|
+
# @param file [String]
|
124
|
+
# File path to parse
|
125
|
+
#
|
126
|
+
# @notes
|
127
|
+
# yaml file should contain an array of configs,
|
128
|
+
# get name of each config set, find global and current environment
|
129
|
+
# from set, merge into dependency_tree
|
130
|
+
#
|
131
|
+
# @api protected
|
132
|
+
def parse_yml(file)
|
133
|
+
#if only on branch is specified in a yaml file it may not come back as an array
|
134
|
+
branches = YAML::load(File.open(file,'r'))
|
135
|
+
branches = [branches] unless branches.is_a? Array
|
136
|
+
|
137
|
+
branches.each do |branch|
|
138
|
+
branch_name = branch[:branch]
|
139
|
+
dependency_tree[branch_name] ||= []
|
140
|
+
|
141
|
+
#Add current environment's configs to branch
|
142
|
+
current_env = branch[:environments][@environment]
|
143
|
+
@dependency_tree[branch_name].concat(current_env) unless current_env.nil?
|
144
|
+
|
145
|
+
#Add global environment's configs to branch
|
146
|
+
global = branch[:environments][:global]
|
147
|
+
@dependency_tree[branch_name].concat(global) unless global.nil?
|
148
|
+
|
149
|
+
#remove nil's if they made it into branch somehow (bad yaml probably)
|
150
|
+
@dependency_tree[branch_name].delete(nil)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module WarningShot
|
2
|
+
class Growl
|
3
|
+
|
4
|
+
def Growl.say(msg)
|
5
|
+
img = WarningShot.dir_for(:images) / 'warning_shot_sml.png'
|
6
|
+
|
7
|
+
gmsg = %{growlnotify -t "WarningShot" -n "WarningShot" -m "#{msg}"}
|
8
|
+
gmsg += %{ --image #{img}} unless img.nil?
|
9
|
+
|
10
|
+
%x{#{gmsg}}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module WarningShot
|
2
|
+
class LoggerFormatter < Logger::Formatter
|
3
|
+
|
4
|
+
ESCAPE_SEQ = {
|
5
|
+
"INFO" => "\e[37m%s\e[0m",
|
6
|
+
"WARN" => "\e[33m%s\e[0m",
|
7
|
+
"DEBUG" => "\e[34m%s\e[0m",
|
8
|
+
"ERROR" => "\e[31m%s\e[0m",
|
9
|
+
"FATAL" => "\e[1m%s\e[0m"
|
10
|
+
}
|
11
|
+
|
12
|
+
def call(severity, timestamp, progname, msg)
|
13
|
+
@colorize ? sprintf(ESCAPE_SEQ[severity],"#{msg}\n") : "#{msg}\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
def time_format=(fmt)
|
17
|
+
@time_format = fmt
|
18
|
+
end
|
19
|
+
|
20
|
+
def colorize=(color)
|
21
|
+
@colorize = color
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|