Add unicorn_rails script for Rails 2.3.2
[unicorn.git] / bin / unicorn_rails
blob0deace15c6a4e0e33d1f0787e131a3b133624072
1 #!/home/ew/bin/ruby
2 $stdin.sync = $stdout.sync = $stderr.sync = true
3 require 'unicorn' # require this first to populate Unicorn::DEFAULT_START_CTX
4 require 'optparse'
5 require 'fileutils'
7 rails_pid = File.join(Unicorn::HttpServer::DEFAULT_START_CTX[:cwd],
8 "/tmp/pids/unicorn.pid")
9 cmd = File.basename($0)
10 daemonize = false
11 listeners = []
12 options = { :listeners => listeners }
13 host, port = Unicorn::Const::DEFAULT_HOST, 3000
14 ENV['RAILS_ENV'] ||= "development"
15 map_path = ENV['RAILS_RELATIVE_URL_ROOT']
17 opts = OptionParser.new("", 24, ' ') do |opts|
18 opts.banner = "Usage: #{cmd} " \
19 "[ruby options] [#{cmd} options] [rackup config file]"
20 opts.separator "Ruby options:"
22 lineno = 1
23 opts.on("-e", "--eval LINE", "evaluate a LINE of code") do |line|
24 eval line, TOPLEVEL_BINDING, "-e", lineno
25 lineno += 1
26 end
28 opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") do
29 $DEBUG = true
30 end
32 opts.on("-w", "--warn", "turn warnings on for your script") do
33 $-w = true
34 end
36 opts.on("-I", "--include PATH",
37 "specify $LOAD_PATH (may be used more than once)") do |path|
38 $LOAD_PATH.unshift(*path.split(/:/))
39 end
41 opts.on("-r", "--require LIBRARY",
42 "require the library, before executing your script") do |library|
43 require library
44 end
46 opts.separator "#{cmd} options:"
48 # some of these switches exist for rackup command-line compatibility,
50 opts.on("-o", "--host HOST",
51 "listen on HOST (default: #{Unicorn::Const::DEFAULT_HOST})") do |h|
52 host = h
53 end
55 opts.on("-p", "--port PORT", "use PORT (default: #{port})") do |p|
56 port = p.to_i
57 end
59 opts.on("-E", "--env ENVIRONMENT",
60 "use ENVIRONMENT for defaults (default: development)") do |e|
61 ENV['RAILS_ENV'] = e
62 end
64 opts.on("-D", "--daemonize", "run daemonized in the background") do |d|
65 daemonize = d ? true : false
66 end
68 # Unicorn-specific stuff
69 opts.on("-l", "--listen {HOST:PORT|PATH}",
70 "listen on HOST:PORT or PATH",
71 "this may be specified multiple times",
72 "(default: #{Unicorn::Const::DEFAULT_LISTEN})") do |address|
73 listeners << address
74 end
76 opts.on("-c", "--config-file FILE", "Unicorn-specific config file") do |f|
77 options[:config_file] = File.expand_path(f)
78 end
80 opts.on("-P", "--path PATH", "Runs Rails app mounted at a specific path.",
81 "(default: /") do |v|
82 map_path = v
83 end
85 # I'm avoiding Unicorn-specific config options on the command-line.
86 # IMNSHO, config options on the command-line are redundant given
87 # config files and make things unnecessarily complicated with multiple
88 # places to look for a config option.
90 opts.separator "Common options:"
92 opts.on_tail("-h", "--help", "Show this message") do
93 puts opts
94 exit
95 end
97 opts.on_tail("-v", "--version", "Show version") do
98 puts " v#{Unicorn::Const::UNICORN_VERSION}"
99 exit
102 opts.parse! ARGV
105 require 'pp' if $DEBUG
107 # Loads Rails and the private version of Rack it bundles. Returns a
108 # lambda of arity==0 that will return *another* lambda of arity==1
109 # suitable for using inside Rack::Builder.new block.
110 rails_loader = lambda do ||
111 begin
112 require 'config/boot'
113 defined?(::RAILS_ROOT) or abort "RAILS_ROOT not defined by config/boot"
114 defined?(::RAILS_ENV) or abort "RAILS_ENV not defined by config/boot"
115 defined?(::Rails::VERSION::STRING) or
116 abort "Rails::VERSION::STRING not defined by config/boot"
117 rescue LoadError
118 abort "#$0 must be run inside RAILS_ROOT (#{::RAILS_ROOT})"
121 if ENV['UNICORN_RAILS_USE_SYSTEM_RACK'].to_i == 0
122 rails_ver = Rails::VERSION::STRING
124 # maps Rails versions to the vendorized Rack version they bundle
125 version_map = {
126 '2.3.2' => '1.0',
127 # 3.0.0 => false, # assuming 3.0.0 doesn't need vendorized Rack anymore
129 rack_ver = version_map[rails_ver] or
130 warn "Possibly unsupported Rails version: v#{rails_ver}"
132 rack_path = nil
133 case rack_ver
134 when String, NilClass
135 version_map.values.find_all { |v| String === v }.sort.each do |v|
136 $LOAD_PATH.grep(%r{/actionpack-[\d\.]+/lib/?\z}).each do |path|
137 rack_path = File.join(path, "action_controller/vendor/rack-#{v}")
138 File.directory?(rack_path) and break
139 rack_path = nil
141 break if rack_path
143 rack_path or abort(
144 "Unable to find Rails-vendorized Rack library.\n" \
145 "Perhaps this script is no longer with your" \
146 "Rails version (#{rails_ver}).\n")
147 puts "vendorized Rack load path #{rack_path}"
148 $LOAD_PATH.unshift(rack_path)
149 when FalseClass
150 # using non-vendorized rack library (most likely via gems)
152 end # Vendorized Rack LOAD_PATH finder
154 # require Rack as late as possible in case $LOAD_PATH is modified
155 # in config.ru or command-line
156 require 'rack'
158 # return the lambda
159 config = ::ARGV[0] || (File.exist?('config.ru') ? 'config.ru' : nil)
160 case config
161 when nil
162 lambda do ||
163 require "#{RAILS_ROOT}/config/environment"
164 ActionController::Dispatcher.new
166 when /\.ru$/
167 raw = File.open(config, "rb") { |fp| fp.sysread(fp.stat.size) }
168 # parse embedded command-line options in config.ru comments
169 if raw[/^#\\(.*)/]
170 opts.parse! $1.split(/\s+/)
171 require 'pp' if $DEBUG
173 lambda { || eval("Rack::Builder.new {(#{raw}\n)}.to_app", nil, config) }
174 else
175 lambda do ||
176 require config
177 Object.const_get(File.basename(config, '.rb').capitalize)
182 # this won't run until after forking if preload_app is false
183 app = lambda do ||
184 inner_app = rails_loader.call
185 require 'active_support'
186 require 'action_controller'
187 ActionController::Base.relative_url_root = map_path if map_path
188 Rack::Builder.new do
189 use Rails::Rack::LogTailer unless daemonize
190 use Rails::Rack::Debugger if $DEBUG
191 map(map_path || '/') do
192 use Rails::Rack::Static
193 run inner_app.call
195 end.to_app
198 if listeners.empty?
199 listener = "#{host}:#{port}"
200 listeners << listener
203 if $DEBUG
204 pp({
205 :unicorn_options => options,
206 :app => app,
207 :daemonize => daemonize,
211 # only daemonize if we're not inheriting file descriptors from our parent
212 if daemonize
213 options[:pid] = rails_pid
214 $stdin.reopen("/dev/null")
215 unless ENV['UNICORN_FD']
216 exit if fork
217 Process.setsid
218 exit if fork
221 # We don't do a lot of standard daemonization stuff:
222 # * $stderr/$stderr can/will be redirected separately
223 # * umask is whatever was set by the parent process at startup
224 # and can be set in config.ru and config_file, so making it
225 # 0000 and potentially exposing sensitive log data can be bad
226 # policy.
227 # * Don't bother to chdir here since Unicorn is designed to
228 # run inside APP_ROOT. Unicorn will also re-chdir() to
229 # the directory it was started in when being re-executed
230 # to pickup code changes if the original deployment directory
231 # is a symlink or otherwise got replaced.
234 # ensure Rails standard tmp paths exist
235 %w(cache pids sessions sockets).each do |dir|
236 FileUtils.mkdir_p("tmp/#{dir}")
238 Unicorn.run(app, options)