hell 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -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 + "\n\n"
51
+ message = {:message => ansi_escape(message)}.to_json
49
52
  end
50
53
 
51
- def process_line(line, out, io)
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 SENTINEL_STRINGS.any? { |w| line =~ /#{w}/ }
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 = Time.now.to_i.to_s + '.' + SecureRandom.hex(2)
82
+ log_file = random_id
67
83
  cmd = [
68
- "cd #{APP_ROOT}",
69
- "echo '#{background_cmd}' >> #{HELL_LOG_PATH}/#{log_file}.log 2>&1",
70
- "#{background_cmd} >> #{HELL_LOG_PATH}/#{log_file}.log 2>&1",
71
- "echo 'Hellish Task Completed' >> #{HELL_LOG_PATH}/#{log_file}.log 2>&1",
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 ENVIRONMENTS.include?(cmd.first)
104
+ cmd.shift if cap.environments.include?(cmd.first)
90
105
  cmd = cmd.join(' ')
91
106
 
92
- tasks = cap.task_index(cmd, {:exact => true})
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 'json'
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
- (function(e){e.bootstrapGrowl=function(t,n){var n=e.extend({},e.bootstrapGrowl.default_options,n);var r=e("<div>");r.attr("class","bootstrap-growl alert");if(n.type){r.addClass("alert-"+n.type)}if(n.allow_dismiss){r.append('<a class="close" data-dismiss="alert" href="#">×</a>')}r.append(t);if(n.top_offset){n.offset={from:"top",amount:n.top_offset}}var i=e(".bootstrap-growl",n.ele);offsetAmount=n.offset.amount;e.each(i,function(){offsetAmount=offsetAmount+e(this).outerHeight()+n.stackup_spacing});css={position:"absolute",border:"1px solid "+r.css("color"),margin:0,"z-index":"9999",display:"none"};css[n.offset.from]=offsetAmount+"px";r.css(css);if(n.width!=="auto"){r.css("width",n.width+"px")}e(n.ele).append(r);switch(n.align){case"center":r.css({left:"50%","margin-left":"-"+r.outerWidth()/2+"px"});break;case"left":r.css("left","20px");break;default:r.css("right","20px")}r.fadeIn();if(n.delay>=0){r.delay(n.delay).fadeOut("slow",function(){e(this).remove()})}};e.bootstrapGrowl.default_options={ele:"body",type:null,offset:{from:"top",amount:20},align:"right",width:250,delay:4e3,allow_dismiss:true,stackup_spacing:10}})(jQuery)
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="#">&times;</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);