hell 0.1.0 → 0.1.1

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/Rakefile CHANGED
@@ -23,7 +23,7 @@ Jeweler::Tasks.new do |gem|
23
23
  gem.description = %Q{Hell is an open source web interface that exposes a set of capistrano recipes as a json api, for usage within large teams}
24
24
  gem.email = "jose@seatgeek.com"
25
25
  gem.authors = ["Jose Diaz-Gonzalez"]
26
- # dependencies defined in Gemfile
26
+ gem.executables = ["hell", "hell-pusher"]
27
27
  end
28
28
  Jeweler::RubygemsDotOrgTasks.new
29
29
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/bin/hell CHANGED
@@ -6,7 +6,7 @@ require 'hell/lib/cli'
6
6
  ENV["RACK_ENV"] ||= "development"
7
7
 
8
8
  unicorn_path = File.expand_path('../../unicorn', __FILE__)
9
- options, op = Hell::CLI.option_parser(ARGV, unicorn_path)
9
+ options, op = Hell::CLI.unicorn_options(ARGV, unicorn_path)
10
10
 
11
11
  unless File.directory? options[:log_path]
12
12
  abort('Missing hell log path %s' % options[:log_path])
data/bin/hell-pusher ADDED
@@ -0,0 +1,20 @@
1
+ #!/this/will/be/overwritten/or/wrapped/anyways/do/not/worry/ruby
2
+ # -*- encoding: binary -*-
3
+ require 'pusher'
4
+ require 'hell/lib/helpers'
5
+
6
+ TASK_ID = ENV.fetch('HELL_TASK_ID', nil)
7
+ HELL_LOG_PATH = ENV.fetch('HELL_LOG_PATH', File.join(Dir.pwd, 'log'))
8
+ HELL_SENTINEL_STRINGS = ENV.fetch('HELL_SENTINEL_STRINGS', 'Hellish Task Completed').split(',')
9
+ abort('Missing hell task id') unless TASK_ID
10
+
11
+ unless Hell::Helpers.valid_log(TASK_ID)
12
+ Hell::Helpers::pusher_error(TASK_ID, "log file '#{TASK_ID}' not found")
13
+ abort("log file '#{TASK_ID}' not found")
14
+ end
15
+
16
+ Pusher.app_id = ENV.fetch('HELL_PUSHER_APP_ID', nil)
17
+ Pusher.key = ENV.fetch('HELL_PUSHER_KEY', nil)
18
+ Pusher.secret = ENV.fetch('HELL_PUSHER_SECRET', nil)
19
+
20
+ Hell::Helpers::pusher_success(TASK_ID, "tail -n 1000 -f %s" % File.join(HELL_LOG_PATH, TASK_ID + ".log"))
data/hell.gemspec CHANGED
@@ -5,14 +5,14 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "hell"
8
- s.version = "0.1.0"
8
+ s.version = "0.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jose Diaz-Gonzalez"]
12
12
  s.date = "2013-02-21"
13
13
  s.description = "Hell is an open source web interface that exposes a set of capistrano recipes as a json api, for usage within large teams"
14
14
  s.email = "jose@seatgeek.com"
15
- s.executables = ["hell"]
15
+ s.executables = ["hell", "hell-pusher"]
16
16
  s.extra_rdoc_files = [
17
17
  "LICENSE.txt",
18
18
  "README.markdown"
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
25
25
  "Rakefile",
26
26
  "VERSION",
27
27
  "bin/hell",
28
+ "bin/hell-pusher",
28
29
  "config.ru",
29
30
  "hell.gemspec",
30
31
  "lib/hell.rb",
data/lib/hell/app.rb CHANGED
@@ -14,7 +14,7 @@ require 'hell/lib/cli'
14
14
  require 'hell/lib/monkey_patch'
15
15
  require 'hell/lib/helpers'
16
16
 
17
- options, op = Hell::CLI.option_parser(ARGV, nil, true)
17
+ options, op = Hell::CLI.option_parser(ARGV)
18
18
  op = nil
19
19
 
20
20
  HELL_DIR = Dir.pwd
@@ -112,6 +112,8 @@ module Hell
112
112
  verbose = "LOGGING=debug" if params[:verbose] == "on"
113
113
 
114
114
  task_id = run_in_background!("bundle exec cap -l STDOUT %s %s" % [original_cmd, verbose]) unless tasks.empty?
115
+ tail_in_background!(task_id) if USE_PUSHER
116
+
115
117
  response = {}
116
118
  response[:status] = tasks.empty? ? 404 : 200,
117
119
  response[:message] = tasks.empty? ? "Task not found" : "Running task in background",
@@ -145,16 +147,6 @@ module Hell
145
147
  end
146
148
  end
147
149
 
148
- def self.init_send
149
- if USE_PUSHER
150
- alias_method :send_error, :_pusher_error
151
- alias_method :send_success, :_pusher_success
152
- else
153
- alias_method :send_error, :_stream_error
154
- alias_method :send_success, :_stream_success
155
- end
156
- end
157
-
158
150
  def cap
159
151
  FileUtils.chdir HELL_APP_ROOT do
160
152
  @cap ||= Capistrano::CLI.parse(["-T"])
@@ -162,23 +154,7 @@ module Hell
162
154
  return @cap
163
155
  end
164
156
 
165
- def _pusher_error(task_id, message)
166
- Pusher[task_id].trigger('start', {:data => ''})
167
- Pusher[task_id].trigger('message', ws_message("<p>#{message}</p>"))
168
- Pusher[task_id].trigger('end', {:data => ''})
169
- end
170
-
171
- def _pusher_success(task_id, command, opts = {})
172
- opts = {:prepend => false}.merge(opts)
173
- Pusher[task_id].trigger('start', {:data => ''})
174
- Pusher[task_id].trigger('message', ws_message("<p>#{command}</p>")) unless opts[:prepend] == false
175
- IO.popen(command, 'rb') do |io|
176
- io.each {|line| push_line(task_id, line, out, io)}
177
- end
178
- Pusher[task_id].trigger('end', {:data => ''})
179
- end
180
-
181
- def _stream_error(task_id, message)
157
+ def send_error(task_id, message)
182
158
  stream do |out|
183
159
  out << "event: start\ndata:\n\n" unless out.closed?
184
160
  out << "data: " + ws_message("<p>#{message}</p>") + "\n\n" unless out.closed?
@@ -187,7 +163,7 @@ module Hell
187
163
  end
188
164
  end
189
165
 
190
- def _stream_success(task_id, command, opts = {})
166
+ def send_success(task_id, command, opts = {})
191
167
  opts = {:prepend => false}.merge(opts)
192
168
  stream do |out|
193
169
  out << "event: start\ndata:\n\n" unless out.closed?
@@ -199,6 +175,5 @@ module Hell
199
175
  end
200
176
  end
201
177
 
202
- init_send
203
178
  end
204
179
  end
data/lib/hell/lib/cli.rb CHANGED
@@ -4,7 +4,7 @@ require 'optparse'
4
4
 
5
5
  module Hell
6
6
  class CLI
7
- def self.default_options(unicorn_path=nil, sinatra_opts=false)
7
+ def self.default_options(unicorn_path=nil)
8
8
  rackup_opts = Unicorn::Configurator::RACKUP || {}
9
9
  rackup_opts[:port] = 4567
10
10
 
@@ -14,21 +14,35 @@ module Hell
14
14
 
15
15
  options[:log_path] = ENV.fetch('HELL_LOG_PATH', File.join(Dir.pwd, 'log'))
16
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
17
+ options[:app_root] = ENV.fetch('HELL_APP_ROOT', Dir.pwd)
18
+ options[:base_path] = ENV.fetch('HELL_BASE_PATH', '/')
19
+ options[:require_env] = !!ENV.fetch('HELL_REQUIRE_ENV', true)
20
+ options[:sentinel] = ENV.fetch('HELL_SENTINEL_STRINGS', 'Hellish Task Completed').split(',')
21
+ options[:pusher_app_id] = ENV.fetch('HELL_PUSHER_APP_ID', nil)
22
+ options[:pusher_key] = ENV.fetch('HELL_PUSHER_KEY', nil)
23
+ options[:pusher_secret] = ENV.fetch('HELL_PUSHER_SECRET', nil)
26
24
 
27
25
  options
28
26
  end
29
27
 
30
- def self.option_parser(args, unicorn_path=nil, sinatra_opts=false)
31
- options = Hell::CLI.default_options(unicorn_path, sinatra_opts)
28
+ def self.unicorn_options(args, unicorn_path=nil)
29
+ options, op = Hell::CLI.option_parser(args, unicorn_path)
30
+
31
+ [
32
+ :app_root,
33
+ :base_path,
34
+ :require_env,
35
+ :sentinel,
36
+ :pusher_app_id,
37
+ :pusher_key,
38
+ :pusher_secret,
39
+ ].each {|key| options.delete(key)}
40
+
41
+ [options, op]
42
+ end
43
+
44
+ def self.option_parser(args, unicorn_path=nil)
45
+ options = Hell::CLI.default_options(unicorn_path)
32
46
 
33
47
  op = OptionParser.new("", 24, ' ') do |opts|
34
48
  cmd = File.basename($0)
@@ -114,7 +128,8 @@ module Hell
114
128
  # config files and make things unnecessarily complicated with multiple
115
129
  # places to look for a config option.
116
130
 
117
- opts.separator "Common options:"
131
+ opts.separator "Common options"
132
+ opts.separator "Note: will not work under unicorn, use environment variables instead"
118
133
 
119
134
  opts.on_tail("-h", "--help", "Show this message") do
120
135
  puts opts.to_s.gsub(/^.*DEPRECATED.*$/s, '')
@@ -126,39 +141,39 @@ module Hell
126
141
  exit
127
142
  end
128
143
 
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
144
+ opts.on('-a', '--app-root APP_ROOT', 'directory from which capistrano should run') do |app_root|
145
+ options[:app_root] = app_root if app_root
131
146
  end
132
147
 
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
148
+ opts.on('-b', '--base-path BASE_PATH', 'base directory path to use in the web ui') do |base_path|
149
+ options[:base_path] = base_path if base_path
135
150
  end
136
151
 
137
- opts.on('-L', '--log-path LOG_PATH', 'directory path to hell logs') do |opt|
138
- options[:log_path] = opt if opt
152
+ opts.on('-L', '--log-path LOG_PATH', 'directory path to hell logs') do |log_path|
153
+ options[:log_path] = log_path if log_path
139
154
  end
140
155
 
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
156
+ opts.on('-R', '--require-env REQUIRE_ENV', 'whether or not to require specifying an environment') do |require_env|
157
+ options[:require_env] = !!require_env if require_env
143
158
  end
144
159
 
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
160
+ opts.on('-S', '--sentinel SENTINAL_PHRASE', 'sentinel phrase used to denote the end of a task run') do |sentinel|
161
+ options[:sentinel] = sentinel.split(',') if sentinel
147
162
  end
148
163
 
149
- opts.on('--pusher-app-id PUSHER_APP_ID', 'pusher app id') do |opt|
150
- options[:pusher_app_id] = opt if opt && sinatra_opts
164
+ opts.on('--pusher-app-id PUSHER_APP_ID', 'pusher app id') do |pusher_app_id|
165
+ options[:pusher_app_id] = pusher_app_id if pusher_app_id
151
166
  end
152
167
 
153
- opts.on('--pusher-key PUSHER_KEY', 'pusher key') do |opt|
154
- options[:pusher_key] = opt if opt && sinatra_opts
168
+ opts.on('--pusher-key PUSHER_KEY', 'pusher key') do |pusher_key|
169
+ options[:pusher_key] = pusher_key if pusher_key
155
170
  end
156
171
 
157
- opts.on('--pusher-secret PUSHER_SECRET', 'pusher secret') do |opt|
158
- options[:pusher_secret] = opt if opt && sinatra_opts
172
+ opts.on('--pusher-secret PUSHER_SECRET', 'pusher secret') do |pusher_secret|
173
+ options[:pusher_secret] = pusher_secret if pusher_secret
159
174
  end
160
175
 
161
- opts.parse! ARGV
176
+ opts.parse! args
162
177
  end
163
178
 
164
179
  [options, op]
@@ -5,6 +5,7 @@ module Hell
5
5
  class TailDone < StandardError; end
6
6
 
7
7
  module Helpers
8
+
8
9
  def escape_to_html(data)
9
10
  {
10
11
  1 => :nothing,
@@ -44,11 +45,11 @@ module Hell
44
45
  end
45
46
 
46
47
  def ansi_escape(message)
47
- escape_to_html(utf8_dammit(message))
48
+ Hell::Helpers::escape_to_html(Hell::Helpers::utf8_dammit(message))
48
49
  end
49
50
 
50
51
  def ws_message(message)
51
- message = {:message => ansi_escape(message)}.to_json
52
+ {:message => Hell::Helpers::ansi_escape(message)}
52
53
  end
53
54
 
54
55
  def stream_line(task_id, line, out, io)
@@ -60,11 +61,11 @@ module Hell
60
61
  end
61
62
  end
62
63
 
63
- def push_line(task_id, line, out, io)
64
+ def push_line(task_id, line, io)
64
65
  begin
65
- Pusher[task_id].trigger('message', ws_message(line))
66
+ Pusher[task_id].trigger('message', MultiJson.dump(Hell::Helpers::ws_message(line)))
66
67
  raise TailDone if HELL_SENTINEL_STRINGS.any? { |w| line =~ /#{w}/ }
67
- rescue
68
+ rescue StandardError => e
68
69
  Process.kill("KILL", io.pid)
69
70
  end
70
71
  end
@@ -98,6 +99,11 @@ module Hell
98
99
  log_file
99
100
  end
100
101
 
102
+ def tail_in_background!(task_id)
103
+ cmd = "cd #{HELL_LOG_PATH} && HELL_TASK_ID='#{task_id}' HELL_SENTINEL_STRINGS='#{HELL_SENTINEL_STRINGS.join(',')}' HELL_LOG_PATH='#{HELL_LOG_PATH}' bundle exec hell-pusher"
104
+ system("sh -c \"#{cmd}\" &")
105
+ end
106
+
101
107
  def verify_task(cap, name)
102
108
  original_cmd = name.gsub('+', ' ').gsub!(/\s+/, ' ').strip
103
109
  cmd = original_cmd.split(' ')
@@ -134,5 +140,34 @@ module Hell
134
140
  def json_encode(data)
135
141
  MultiJson.dump(data)
136
142
  end
143
+
144
+ def pusher_error(task_id, message)
145
+ Pusher[task_id].trigger('start', MultiJson.dump(Hell::Helpers::ws_message("<p>start</p>")))
146
+ Pusher[task_id].trigger('message', MultiJson.dump(Hell::Helpers::ws_message("<p>#{message}</p>")))
147
+ Pusher[task_id].trigger('end', MultiJson.dump(Hell::Helpers::ws_message("<p>end</p>")))
148
+ end
149
+
150
+ def pusher_success(task_id, command, opts = {})
151
+ out = nil
152
+ opts = {:prepend => false}.merge(opts)
153
+ Pusher[task_id].trigger('start', MultiJson.dump(Hell::Helpers::ws_message("<p>start</p>")))
154
+ Pusher[task_id].trigger('message', MultiJson.dump(Hell::Helpers::ws_message("<p>#{command}</p>"))) unless opts[:prepend] == false
155
+ IO.popen(command, 'rb') do |io|
156
+ io.each do |line|
157
+ Hell::Helpers::push_line(task_id, line, io)
158
+ end
159
+ end
160
+ Pusher[task_id].trigger('end', MultiJson.dump(Hell::Helpers::ws_message("<p>end</p>")))
161
+ end
162
+
163
+ module_function :pusher_error
164
+ module_function :pusher_success
165
+ module_function :push_line
166
+ module_function :valid_log
167
+ module_function :ws_message
168
+ module_function :utf8_dammit
169
+ module_function :ansi_escape
170
+ module_function :escape_to_html
171
+
137
172
  end
138
173
  end
@@ -139,6 +139,8 @@ $(function() {
139
139
  _executePusher: function (task_id, name) {
140
140
  var that = this;
141
141
 
142
+ HellApp.setupPusher();
143
+
142
144
  // Create a new channel
143
145
  var channel = HellApp.pusher.subscribe(task_id);
144
146
 
@@ -152,13 +154,12 @@ $(function() {
152
154
  channel.bind('end', function(data) {
153
155
  HellApp.set_flash("Received the last response " + name, "info");
154
156
  that.setStatus("finished");
155
- pusher.unsubscribe(task_id);
157
+ HellApp.pusher.unsubscribe(task_id);
156
158
  });
157
159
 
158
160
  // Append the messages to the .task-output div
159
161
  channel.bind('message', function(data) {
160
- var message = jQuery.parseJSON(data);
161
- $('.task-output').append(message.message.replace(/~+$/, ''));
162
+ $('.task-output').append(data.message.replace(/~+$/, ''));
162
163
  $(".task-output").animate({scrollTop: $(".task-output").prop("scrollHeight")}, 1);
163
164
  });
164
165
  },
@@ -454,14 +455,15 @@ $(function() {
454
455
  src = null;
455
456
  }
456
457
  },
458
+ setupPusher: function() {
459
+ if (HellApp.use_pusher) {
460
+ HellApp.pusher = HellApp.pusher || new Pusher(HellApp.pusher_key);
461
+ }
462
+ },
457
463
  collection: new TaskCollectionView(),
458
464
  form: new TaskFormView()
459
465
  });
460
466
 
461
- if (HellApp.use_pusher) {
462
- HellApp.pusher = Pusher(HellApp.pusher_key);
463
- }
464
-
465
467
  // HACK: This should be moved into a Backbone View or Router
466
468
 
467
469
  // Function to activate the tab
@@ -125,6 +125,7 @@
125
125
  HellApp.pusher_secret = <%= json_encode(@pusher_secret) %>;
126
126
  </script>
127
127
 
128
+ <script src="http://js.pusher.com/1.12/pusher.min.js"></script>
128
129
  <%= js :jquery %>
129
130
  <script src="<%= @www_base_dir %>assets/js/bootstrap.js"></script>
130
131
  <script src="<%= @www_base_dir %>assets/js/underscore.js"></script>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hell
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -176,6 +176,7 @@ description: Hell is an open source web interface that exposes a set of capistra
176
176
  email: jose@seatgeek.com
177
177
  executables:
178
178
  - hell
179
+ - hell-pusher
179
180
  extensions: []
180
181
  extra_rdoc_files:
181
182
  - LICENSE.txt
@@ -188,6 +189,7 @@ files:
188
189
  - Rakefile
189
190
  - VERSION
190
191
  - bin/hell
192
+ - bin/hell-pusher
191
193
  - config.ru
192
194
  - hell.gemspec
193
195
  - lib/hell.rb
@@ -236,7 +238,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
236
238
  version: '0'
237
239
  segments:
238
240
  - 0
239
- hash: -2323718854434206367
241
+ hash: -2842994347166960270
240
242
  required_rubygems_version: !ruby/object:Gem::Requirement
241
243
  none: false
242
244
  requirements: