hell 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -2
- data/README.markdown +0 -1
- data/VERSION +1 -1
- data/bin/hell +28 -10
- data/config.ru +7 -0
- data/hell.gemspec +107 -0
- data/lib/hell.rb +4 -0
- data/lib/hell/app.rb +108 -32
- data/lib/hell/lib/capistrano/cli.rb +57 -0
- data/lib/hell/lib/capistrano/configuration.rb +14 -0
- data/lib/hell/lib/capistrano/task_definition.rb +18 -0
- data/lib/hell/lib/cli.rb +167 -0
- data/lib/hell/lib/helpers.rb +32 -13
- data/lib/hell/lib/monkey_patch.rb +4 -70
- data/lib/hell/public/assets/js/bootstrap.growl.js +85 -1
- data/lib/hell/public/assets/js/bootstrap.js +7 -2025
- data/lib/hell/public/assets/js/hashchange.js +300 -0
- data/lib/hell/public/assets/js/hell.js +63 -21
- data/lib/hell/public/assets/js/jquery.js +9555 -0
- data/lib/hell/public/assets/js/{underscore.min.js → underscore.js} +1 -1
- data/lib/hell/views/index.erb +14 -25
- data/unicorn +2 -0
- metadata +63 -12
- data/lib/hell/config.ru +0 -4
- data/lib/hell/hell.rb +0 -0
- data/lib/hell/public/assets/js/backbone.min.js +0 -38
- data/lib/hell/public/assets/js/bootstrap.min.js +0 -6
- data/lib/hell/public/assets/js/hashchange.min.js +0 -9
- data/lib/hell/public/assets/js/jquery.min.js +0 -4
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'capistrano/configuration/loading'
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
class Configuration
|
5
|
+
module Loading
|
6
|
+
alias_method :original_initialize_with_loading, :initialize_with_loading
|
7
|
+
def initialize_with_loading(*args) #:nodoc:
|
8
|
+
orig = original_initialize_with_loading(*args)
|
9
|
+
@load_paths.unshift(HELL_APP_ROOT)
|
10
|
+
return orig
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'capistrano/task_definition'
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
class TaskDefinition
|
5
|
+
|
6
|
+
def to_hash
|
7
|
+
# Roles should always be a hash, to ease developer frustration
|
8
|
+
@options[:roles] = Array(@options[:roles])
|
9
|
+
{
|
10
|
+
:name => name,
|
11
|
+
:fully_qualified_name => fully_qualified_name,
|
12
|
+
:description => description == brief_description ? false : description,
|
13
|
+
:brief_description => brief_description,
|
14
|
+
:options => @options,
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/hell/lib/cli.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'unicorn/launcher'
|
2
|
+
require 'unicorn/configurator'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module Hell
|
6
|
+
class CLI
|
7
|
+
def self.default_options(unicorn_path=nil, sinatra_opts=false)
|
8
|
+
rackup_opts = Unicorn::Configurator::RACKUP || {}
|
9
|
+
rackup_opts[:port] = 4567
|
10
|
+
|
11
|
+
options = rackup_opts[:options] || {}
|
12
|
+
options[:config_file] = unicorn_path unless unicorn_path.nil?
|
13
|
+
options[:listeners] = ['0.0.0.0:4567']
|
14
|
+
|
15
|
+
options[:log_path] = ENV.fetch('HELL_LOG_PATH', File.join(Dir.pwd, 'log'))
|
16
|
+
|
17
|
+
if sinatra_opts
|
18
|
+
options[:app_root] = ENV.fetch('HELL_APP_ROOT', Dir.pwd)
|
19
|
+
options[:base_path] = ENV.fetch('HELL_BASE_PATH', '/')
|
20
|
+
options[:require_env] = !!ENV.fetch('HELL_REQUIRE_ENV', true)
|
21
|
+
options[:sentinel] = ENV.fetch('HELL_SENTINEL_STRINGS', 'Hellish Task Completed').split(',')
|
22
|
+
options[:pusher_app_id] = ENV.fetch('HELL_PUSHER_APP_ID', nil)
|
23
|
+
options[:pusher_key] = ENV.fetch('HELL_PUSHER_KEY', nil)
|
24
|
+
options[:pusher_secret] = ENV.fetch('HELL_PUSHER_SECRET', nil)
|
25
|
+
end
|
26
|
+
|
27
|
+
options
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.option_parser(args, unicorn_path=nil, sinatra_opts=false)
|
31
|
+
options = Hell::CLI.default_options(unicorn_path, sinatra_opts)
|
32
|
+
|
33
|
+
op = OptionParser.new("", 24, ' ') do |opts|
|
34
|
+
cmd = File.basename($0)
|
35
|
+
opts.banner = "Usage: #{cmd} " \
|
36
|
+
"[ruby options] [#{cmd} options] [rackup config file]"
|
37
|
+
opts.separator "Ruby options:"
|
38
|
+
|
39
|
+
lineno = 1
|
40
|
+
opts.on("-e", "--eval LINE", "evaluate a LINE of code") do |line|
|
41
|
+
eval line, TOPLEVEL_BINDING, "-e", lineno
|
42
|
+
lineno += 1
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") do
|
46
|
+
$DEBUG = true
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on("-w", "--warn", "turn warnings on for your script") do
|
50
|
+
$-w = true
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("-I", "--include PATH",
|
54
|
+
"specify $LOAD_PATH (may be used more than once)") do |path|
|
55
|
+
$LOAD_PATH.unshift(*path.split(/:/))
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on("-r", "--require LIBRARY",
|
59
|
+
"require the library, before executing your script") do |library|
|
60
|
+
require library
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.separator "Unicorn options:"
|
64
|
+
|
65
|
+
# some of these switches exist for rackup command-line compatibility,
|
66
|
+
|
67
|
+
opts.on("-o", "--host HOST",
|
68
|
+
"listen on HOST (default: 0.0.0.0)") do |h|
|
69
|
+
rackup_opts[:host] = h || '0.0.0.0'
|
70
|
+
rackup_opts[:set_listener] = true
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on("-p", "--port PORT",
|
74
|
+
"use PORT (default: 4567)") do |p|
|
75
|
+
rackup_opts[:port] = p || 4567
|
76
|
+
rackup_opts[:port] = rackup_opts[:port].to_i
|
77
|
+
rackup_opts[:set_listener] = true
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on("-E", "--env RACK_ENV",
|
81
|
+
"use RACK_ENV for defaults (default: development)") do |e|
|
82
|
+
ENV["RACK_ENV"] = e
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on("-D", "--daemonize", "run daemonized in the background") do |d|
|
86
|
+
rackup_opts[:daemonize] = !!d
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on("-P", "--pid FILE", "DEPRECATED") do |f|
|
90
|
+
warn %q{Use of --pid/-P is strongly discouraged}
|
91
|
+
warn %q{Use the 'pid' directive in the Unicorn config file instead}
|
92
|
+
options[:pid] = f
|
93
|
+
end
|
94
|
+
|
95
|
+
opts.on("-s", "--server SERVER",
|
96
|
+
"this flag only exists for compatibility") do |s|
|
97
|
+
warn "-s/--server only exists for compatibility with rackup"
|
98
|
+
end
|
99
|
+
|
100
|
+
# Unicorn-specific stuff
|
101
|
+
opts.on("-l", "--listen {HOST:PORT|PATH}",
|
102
|
+
"listen on HOST:PORT or PATH",
|
103
|
+
"this may be specified multiple times",
|
104
|
+
"(default: 0.0.0.0:4567)") do |address|
|
105
|
+
options[:listeners] << address || '0.0.0.0:4567'
|
106
|
+
end
|
107
|
+
|
108
|
+
opts.on("-c", "--config-file FILE", "Unicorn-specific config file") do |f|
|
109
|
+
options[:config_file] = unicorn_path
|
110
|
+
end
|
111
|
+
|
112
|
+
# I'm avoiding Unicorn-specific config options on the command-line.
|
113
|
+
# IMNSHO, config options on the command-line are redundant given
|
114
|
+
# config files and make things unnecessarily complicated with multiple
|
115
|
+
# places to look for a config option.
|
116
|
+
|
117
|
+
opts.separator "Common options:"
|
118
|
+
|
119
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
120
|
+
puts opts.to_s.gsub(/^.*DEPRECATED.*$/s, '')
|
121
|
+
exit
|
122
|
+
end
|
123
|
+
|
124
|
+
opts.on_tail("-v", "--version", "Show version") do
|
125
|
+
puts "#{cmd} v#{Unicorn::Const::UNICORN_VERSION}"
|
126
|
+
exit
|
127
|
+
end
|
128
|
+
|
129
|
+
opts.on('-a', '--app-root APP_ROOT', 'directory from which capistrano should run') do |opt|
|
130
|
+
options[:app_root] = opt if opt && sinatra_opts
|
131
|
+
end
|
132
|
+
|
133
|
+
opts.on('-b', '--base-path BASE_PATH', 'base directory path to use in the web ui') do |opt|
|
134
|
+
options[:base_path] = opt if opt && sinatra_opts
|
135
|
+
end
|
136
|
+
|
137
|
+
opts.on('-L', '--log-path LOG_PATH', 'directory path to hell logs') do |opt|
|
138
|
+
options[:log_path] = opt if opt
|
139
|
+
end
|
140
|
+
|
141
|
+
opts.on('-R', '--require-env REQUIRE_ENV', 'whether or not to require specifying an environment') do |opt|
|
142
|
+
options[:require_env] = !!opt if opt && sinatra_opts
|
143
|
+
end
|
144
|
+
|
145
|
+
opts.on('-S', '--sentinel SENTINAL_PHRASE', 'sentinel phrase used to denote the end of a task run') do |opt|
|
146
|
+
options[:sentinel] = opt.split(',') if opt && sinatra_opts
|
147
|
+
end
|
148
|
+
|
149
|
+
opts.on('--pusher-app-id PUSHER_APP_ID', 'pusher app id') do |opt|
|
150
|
+
options[:pusher_app_id] = opt if opt && sinatra_opts
|
151
|
+
end
|
152
|
+
|
153
|
+
opts.on('--pusher-key PUSHER_KEY', 'pusher key') do |opt|
|
154
|
+
options[:pusher_key] = opt if opt && sinatra_opts
|
155
|
+
end
|
156
|
+
|
157
|
+
opts.on('--pusher-secret PUSHER_SECRET', 'pusher secret') do |opt|
|
158
|
+
options[:pusher_secret] = opt if opt && sinatra_opts
|
159
|
+
end
|
160
|
+
|
161
|
+
opts.parse! ARGV
|
162
|
+
end
|
163
|
+
|
164
|
+
[options, op]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
data/lib/hell/lib/helpers.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'pusher'
|
3
|
+
|
1
4
|
module Hell
|
2
5
|
class TailDone < StandardError; end
|
3
6
|
|
@@ -45,13 +48,22 @@ module Hell
|
|
45
48
|
end
|
46
49
|
|
47
50
|
def ws_message(message)
|
48
|
-
message = {:message => ansi_escape(message)}.to_json
|
51
|
+
message = {:message => ansi_escape(message)}.to_json
|
49
52
|
end
|
50
53
|
|
51
|
-
def
|
54
|
+
def stream_line(task_id, line, out, io)
|
52
55
|
begin
|
53
|
-
out << "data: " + ws_message(line) unless out.closed?
|
54
|
-
raise TailDone if
|
56
|
+
out << "data: " + ws_message(line) + "\n\n" unless out.closed?
|
57
|
+
raise TailDone if HELL_SENTINEL_STRINGS.any? { |w| line =~ /#{w}/ }
|
58
|
+
rescue
|
59
|
+
Process.kill("KILL", io.pid)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def push_line(task_id, line, out, io)
|
64
|
+
begin
|
65
|
+
Pusher[task_id].trigger('message', ws_message(line))
|
66
|
+
raise TailDone if HELL_SENTINEL_STRINGS.any? { |w| line =~ /#{w}/ }
|
55
67
|
rescue
|
56
68
|
Process.kill("KILL", io.pid)
|
57
69
|
end
|
@@ -62,14 +74,17 @@ module Hell
|
|
62
74
|
out.close
|
63
75
|
end
|
64
76
|
|
77
|
+
def random_id
|
78
|
+
Time.now.to_i.to_s + '.' + SecureRandom.hex(2)
|
79
|
+
end
|
80
|
+
|
65
81
|
def run_in_background!(background_cmd)
|
66
|
-
log_file =
|
82
|
+
log_file = random_id
|
67
83
|
cmd = [
|
68
|
-
"cd #{
|
69
|
-
"
|
70
|
-
"#{
|
71
|
-
|
72
|
-
].join(" && ")
|
84
|
+
"cd #{HELL_APP_ROOT} && echo '#{background_cmd}' >> #{HELL_LOG_PATH}/#{log_file}.log 2>&1",
|
85
|
+
"cd #{HELL_APP_ROOT} && #{background_cmd} >> #{HELL_LOG_PATH}/#{log_file}.log 2>&1",
|
86
|
+
"cd #{HELL_APP_ROOT} && echo 'Hellish Task Completed' >> #{HELL_LOG_PATH}/#{log_file}.log 2>&1",
|
87
|
+
].join(" ; ")
|
73
88
|
system("sh -c \"#{cmd}\" &")
|
74
89
|
|
75
90
|
# Wait up to three seconds in case of server load
|
@@ -84,12 +99,12 @@ module Hell
|
|
84
99
|
end
|
85
100
|
|
86
101
|
def verify_task(cap, name)
|
87
|
-
original_cmd = name.gsub('+', ' ')
|
102
|
+
original_cmd = name.gsub('+', ' ').gsub!(/\s+/, ' ').strip
|
88
103
|
cmd = original_cmd.split(' ')
|
89
|
-
cmd.shift if
|
104
|
+
cmd.shift if cap.environments.include?(cmd.first)
|
90
105
|
cmd = cmd.join(' ')
|
91
106
|
|
92
|
-
tasks = cap.
|
107
|
+
tasks = cap.tasks(cmd, {:exact => true})
|
93
108
|
return tasks, original_cmd
|
94
109
|
end
|
95
110
|
|
@@ -115,5 +130,9 @@ module Hell
|
|
115
130
|
s
|
116
131
|
end
|
117
132
|
end
|
133
|
+
|
134
|
+
def json_encode(data)
|
135
|
+
MultiJson.dump(data)
|
136
|
+
end
|
118
137
|
end
|
119
138
|
end
|
@@ -1,64 +1,9 @@
|
|
1
|
-
require '
|
2
|
-
require 'capistrano'
|
1
|
+
require 'hell/lib/capistrano/configuration'
|
2
|
+
require 'hell/lib/capistrano/cli'
|
3
|
+
require 'hell/lib/capistrano/task_definition'
|
4
|
+
|
3
5
|
require 'capistrano/cli'
|
4
6
|
require 'capistrano/cli/help'
|
5
|
-
require 'capistrano/task_definition'
|
6
|
-
|
7
|
-
module Capistrano
|
8
|
-
class TaskDefinition
|
9
|
-
def to_hash
|
10
|
-
{
|
11
|
-
:name => name,
|
12
|
-
:fully_qualified_name => fully_qualified_name,
|
13
|
-
:description => description == brief_description ? false : description,
|
14
|
-
:brief_description => brief_description,
|
15
|
-
}
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class CLI
|
20
|
-
module JsonTaskList
|
21
|
-
def task_before
|
22
|
-
config = instantiate_configuration(options)
|
23
|
-
config.debug = options[:debug]
|
24
|
-
config.dry_run = options[:dry_run]
|
25
|
-
config.preserve_roles = options[:preserve_roles]
|
26
|
-
config.logger.level = options[:verbose]
|
27
|
-
|
28
|
-
set_pre_vars(config)
|
29
|
-
load_recipes(config)
|
30
|
-
|
31
|
-
config.trigger(:load)
|
32
|
-
[config, options]
|
33
|
-
end
|
34
|
-
|
35
|
-
def task_after(config)
|
36
|
-
config.trigger(:exit)
|
37
|
-
config
|
38
|
-
end
|
39
|
-
|
40
|
-
def task_index(pattern = nil, opts = {})
|
41
|
-
config, options = task_before
|
42
|
-
|
43
|
-
tasks = config.task_list(:all)
|
44
|
-
if opts.fetch(:exact, false) && pattern.is_a?(String)
|
45
|
-
tasks.select! {|t| t.fully_qualified_name == pattern}
|
46
|
-
elsif pattern.is_a?(String)
|
47
|
-
tasks.select! {|t| t.fully_qualified_name =~ /#{pattern}/}
|
48
|
-
end
|
49
|
-
|
50
|
-
tasks.reject! {|t| BLACKLIST.include?(t.fully_qualified_name)}
|
51
|
-
tasks.reject! {|t| ENVIRONMENTS.include?(t.fully_qualified_name)}
|
52
|
-
tasks.reject! {|t| t.service.include?(opts.fetch(:service))} if opts.fetch(:service, false)
|
53
|
-
|
54
|
-
tasks = Hash[tasks.map {|task| [task.fully_qualified_name, task.to_hash]}]
|
55
|
-
task_after(config)
|
56
|
-
|
57
|
-
tasks
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
7
|
|
63
8
|
module Capistrano
|
64
9
|
class CLI
|
@@ -66,14 +11,3 @@ module Capistrano
|
|
66
11
|
include Help
|
67
12
|
end
|
68
13
|
end
|
69
|
-
|
70
|
-
module Capistrano
|
71
|
-
class TaskDefinition
|
72
|
-
attr_reader :service
|
73
|
-
|
74
|
-
def service
|
75
|
-
@service ||= Array(@options.delete(:service))
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
@@ -1,2 +1,86 @@
|
|
1
1
|
/* https://github.com/ifightcrime/bootstrap-growl */
|
2
|
-
|
2
|
+
|
3
|
+
|
4
|
+
(function($) {
|
5
|
+
$.bootstrapGrowl = function(message, options) {
|
6
|
+
|
7
|
+
var options = $.extend({}, $.bootstrapGrowl.default_options, options);
|
8
|
+
|
9
|
+
var $alert = $('<div>');
|
10
|
+
|
11
|
+
$alert.attr('class', 'bootstrap-growl alert');
|
12
|
+
|
13
|
+
if (options.type) {
|
14
|
+
$alert.addClass('alert-' + options.type);
|
15
|
+
}
|
16
|
+
|
17
|
+
if (options.allow_dismiss) {
|
18
|
+
$alert.append('<a class="close" data-dismiss="alert" href="#">×</a>');
|
19
|
+
}
|
20
|
+
|
21
|
+
$alert.append(message);
|
22
|
+
|
23
|
+
// Prevent BC breaks
|
24
|
+
if (options.top_offset) {
|
25
|
+
options.offset = {from: 'top', amount: options.top_offset};
|
26
|
+
}
|
27
|
+
var current = $('.bootstrap-growl', options.ele);
|
28
|
+
|
29
|
+
// calculate any 'stack-up'
|
30
|
+
var offsetAmount = options.offset.amount;
|
31
|
+
$.each(current, function() {
|
32
|
+
offsetAmount = Math.max(offsetAmount, parseInt($(this).css(options.offset.from), 10) + $(this).outerHeight() + options.stackup_spacing);
|
33
|
+
});
|
34
|
+
|
35
|
+
css = {
|
36
|
+
'position': 'absolute',
|
37
|
+
'margin': 0,
|
38
|
+
'z-index': '9999',
|
39
|
+
'display': 'none'
|
40
|
+
};
|
41
|
+
css[options.offset.from] = offsetAmount + 'px';
|
42
|
+
$alert.css(css);
|
43
|
+
|
44
|
+
if (options.width !== 'auto') {
|
45
|
+
$alert.css('width', options.width + 'px');
|
46
|
+
}
|
47
|
+
|
48
|
+
// have to append before we can use outerWidth()
|
49
|
+
$(options.ele).append($alert);
|
50
|
+
|
51
|
+
switch(options.align) {
|
52
|
+
case 'center':
|
53
|
+
$alert.css({
|
54
|
+
'left': '50%',
|
55
|
+
'margin-left': '-' + ($alert.outerWidth() / 2) + 'px'
|
56
|
+
});
|
57
|
+
break;
|
58
|
+
case 'left':
|
59
|
+
$alert.css('left', '20px');
|
60
|
+
break;
|
61
|
+
default:
|
62
|
+
$alert.css('right', '20px');
|
63
|
+
}
|
64
|
+
|
65
|
+
$alert.fadeIn();
|
66
|
+
// Only remove after delay if delay is more than 0
|
67
|
+
if(options.delay >= 0){
|
68
|
+
$alert.delay(options.delay).fadeOut('slow', function() {
|
69
|
+
$(this).remove();
|
70
|
+
});
|
71
|
+
}
|
72
|
+
|
73
|
+
};
|
74
|
+
|
75
|
+
$.bootstrapGrowl.default_options = {
|
76
|
+
ele: 'body',
|
77
|
+
type: null,
|
78
|
+
offset: {from: 'top', amount: 20},
|
79
|
+
align: 'right', // (left, right, or center)
|
80
|
+
width: 250,
|
81
|
+
delay: 4000,
|
82
|
+
allow_dismiss: true,
|
83
|
+
stackup_spacing: 10
|
84
|
+
};
|
85
|
+
|
86
|
+
})(jQuery);
|