hell 0.0.1 → 0.1.0

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.
@@ -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);