JasonKing-pulp 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.markdown +38 -0
  2. data/bin/pulp +13 -0
  3. data/lib/passenger/config.rb +426 -0
  4. metadata +65 -0
@@ -0,0 +1,38 @@
1
+ pulp
2
+ ====
3
+
4
+ This is a simple helper for [passenger](http://modrails.com/) in development. It does two things:
5
+
6
+ 1. Simplifies the setup of new passenger virtual hosts by adding the necessary
7
+ records to your apache config as well as to your hosts file.
8
+ 2. Checks your apache config to make sure that you have the right settings, and
9
+ right order of settings (like that your passenger vhosts are **after** your
10
+ `NameVirtualHost` directive).
11
+
12
+ It works magically, by querying apache itself to find the location of your conf
13
+ files. It **isn't** magical enough to work on Windows.
14
+
15
+ Installation
16
+ ------------
17
+
18
+ To perform a system wide installation:
19
+
20
+ gem source -a http://gems.github.com
21
+ gem install JasonKing-pulp
22
+
23
+ Usage
24
+ -----
25
+
26
+ Typically you'll setup a new rails app with the `rails` executable, and then
27
+ call `pulp` afterwards, like this:
28
+
29
+ > rails foobar
30
+ > sudo pulp foobar
31
+
32
+ Done. You will now be able to visit http://foobar.dev/ and see your new rails
33
+ site running.
34
+
35
+ Contributors
36
+ ------------
37
+
38
+ * Jason King (JasonKing)
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require File.join( File.dirname(__FILE__), '..', 'lib', 'passenger', 'config')
3
+
4
+ if %w(--version -v).include? ARGV.first
5
+ puts "#{File.basename(__FILE__)} #{Passenger::Config::VERSION}"
6
+ exit(0)
7
+ end
8
+
9
+ c = Passenger::Config.new( :root => ARGV[0] )
10
+ c.update_hosts
11
+ c.update_apache
12
+
13
+ c.consolidate
@@ -0,0 +1,426 @@
1
+ require 'fileutils'
2
+ require 'etc'
3
+ require 'find'
4
+ require 'grep'
5
+
6
+ module Passenger
7
+ class Config
8
+ VERSION = '0.8.1'
9
+ attr_reader :domain
10
+ attr_reader :ip
11
+ attr_reader :hosts
12
+
13
+ @@marker_string = "#----- #{File.basename($0, '.rb')} marker line -- DO NOT DELETE -----#"
14
+
15
+ @@httpd_cmd = 'httpd'
16
+ @@apachectl = 'apachectl'
17
+
18
+ @@user_re = /^\s*User\s+(\S+)/i
19
+ @@group_re = /^\s*Group\s+(\S+)/i
20
+
21
+ def initialize(p)
22
+ self.root = p[:root]
23
+ @domain = (p[:domain] || '.dev').sub(/^\.*/,'.')
24
+ @hosts = p[:hosts] || '/etc/hosts'
25
+ @ip = p[:ip] || '127.0.0.1'
26
+
27
+ @check_for_user = lambda {|l|
28
+ if m = l.match(@@user_re)
29
+ @user = m[1]
30
+ end
31
+ }
32
+ @check_for_group = lambda {|l|
33
+ if m = l.match(@@user_re)
34
+ @group = m[1]
35
+ end
36
+ }
37
+
38
+ read_conf
39
+ check_root_perms
40
+
41
+ setup_vhost
42
+ end
43
+
44
+ def consolidate
45
+ _hosts = list_hosts
46
+ _vhosts = list_vhosts
47
+
48
+ _hosts.each_index do |i|
49
+ case _hosts[i] <=> _vhosts[i]
50
+ when 0
51
+ next
52
+ when 1
53
+ puts "Missing from #{hosts}: #{_vhosts[i]}"
54
+ _hosts.insert(i, _vhosts[i])
55
+ when -1
56
+ puts "Missing from #{passenger_conf}: #{_hosts[i]}"
57
+ _vhosts.insert(i, _hosts[i])
58
+ end
59
+ end
60
+ end
61
+
62
+ def update_hosts
63
+ _had_domain_line = false
64
+ File.open(hosts, 'r+') do |f|
65
+ f.flock File::LOCK_EX
66
+ _buffer = []
67
+ f.each do |l|
68
+ if l.match(domain_re) # add it to the same line that the other #{domain} entries are on
69
+ _had_domain_line = true
70
+ unless l.match(host_re) # already exists - don't re-add
71
+ l = "#{l.chomp} #{host}\n"
72
+ end
73
+ end
74
+ _buffer << l
75
+ end
76
+
77
+ unless _had_domain_line # if there was no #{domain} line then just add it to the end
78
+ _buffer << "#{ip} #{host}\n"
79
+ end
80
+ f.seek 0
81
+ f.truncate 0
82
+ f << _buffer
83
+ end
84
+ end
85
+
86
+ def update_apache
87
+ File.open(passenger_conf, 'r+') do |f|
88
+ f.flock File::LOCK_EX
89
+ catch(:out) do
90
+ f.each("\n\n") do |ir|
91
+ if ir == @vhost_entry
92
+ f.seek 0, IO::SEEK_END
93
+ throw :out
94
+ end
95
+ end
96
+ f.puts @vhost_entry
97
+ end
98
+ end
99
+ system("#{@@apachectl} restart")
100
+ end
101
+
102
+ private
103
+ def list_hosts
104
+ _hosts = []
105
+ File.open(hosts).each do |l|
106
+ if l.match(domain_re)
107
+ _hosts += l.scan(/\S+#{Regexp.quote(domain)}(?!\S)/)
108
+ end
109
+ end
110
+ _hosts.sort.uniq
111
+ end
112
+
113
+ def list_vhosts
114
+ _h = []
115
+ File.open(passenger_conf).each do |l|
116
+ if m = l.match( %r{^\s*ServerName\s+(\S+#{Regexp.quote(domain)})(?!\S)})
117
+ _h << m[1]
118
+ end
119
+ end
120
+ _h.sort.uniq
121
+ end
122
+
123
+ def setup_vhost
124
+ @vhost_entry =<<-__EOI
125
+ <VirtualHost #{vhost}>
126
+ DocumentRoot #{root}
127
+ ServerName #{host}
128
+ <Location />
129
+ Order allow,deny
130
+ Allow from all
131
+ </Location>
132
+ </VirtualHost>
133
+
134
+ __EOI
135
+ end
136
+
137
+ def root=(_d)
138
+ @root = File.expand_path(_d || '.')
139
+ end
140
+
141
+ def root
142
+ File.join(@root, 'public')
143
+ end
144
+
145
+ def app
146
+ @app ||= File.basename(@root)
147
+ end
148
+
149
+ def host
150
+ @host ||= app + domain
151
+ end
152
+
153
+ def localhost_re
154
+ @localhost_re ||= %r{^\s*#{Regexp.quote(ip)}\s.*}
155
+ end
156
+
157
+ def domain_re
158
+ @domain_re ||= %r{#{localhost_re}\b#{Regexp.quote(domain)}(?!\S)}
159
+ end
160
+
161
+ def host_re
162
+ @host_re ||= %r{#{localhost_re}\s#{Regexp.quote(host)}(?!\S)}
163
+ end
164
+
165
+ def conf
166
+ @conf ||= from_server('SERVER_CONFIG_FILE')
167
+ end
168
+
169
+ def dir
170
+ @dir ||= File.dirname(self.conf)
171
+ end
172
+
173
+ def server_root
174
+ @server_root ||= File.open(conf).grep(/^\s*ServerRoot\s+['"]?([^'"\s]+)['"]?/i).first[:match][1] || from_server('HTTPD_ROOT')
175
+ end
176
+
177
+ def dir_handle
178
+ @dir_handle ||= Dir.open(dir)
179
+ end
180
+
181
+ def user
182
+ @user ||= get_user
183
+ end
184
+
185
+ def group
186
+ @group ||= get_group
187
+ end
188
+
189
+ def get_user
190
+ File.open(conf).each do |line|
191
+ return @user if @check_for_user.call(line)
192
+ end
193
+ end
194
+
195
+ def check_root_perms
196
+ system("sudo -u #{user} ls #{root} 1>/dev/null 2>&1") or raise %Q{Your Apache user "#{user}" can't read your document root "#{root}"}
197
+ end
198
+
199
+ def get_group
200
+ File.open(conf).each do |line|
201
+ return @group if @check_for_group.call(line)
202
+ end
203
+ end
204
+
205
+ def retrieve_passenger_conf
206
+ File.open(conf) do |f|
207
+ f.each do |line|
208
+ @check_for_user.call(line)
209
+ @check_for_group.call(line)
210
+ if line.chomp == @@marker_string
211
+ until line.match(/^\s*[^#]/)
212
+ line = f.gets
213
+ unless m = line.match(/^\s*Include\s+(\S+)/)
214
+ raise "Non-include following marker: #{@@marker_string} in #{conf}"
215
+ end
216
+ return m[1]
217
+ end
218
+ end
219
+ end
220
+ end
221
+ return nil
222
+ end
223
+
224
+ def passenger_conf
225
+ return @passenger_conf if @passenger_conf || @passenger_conf = retrieve_passenger_conf
226
+
227
+ # the logic for where we're going to add our vhosts is a little complex
228
+ #
229
+ # 1. We use the RailsEnv file as long as it comes after the
230
+ # NameVirtualHost entry
231
+ # 2. Otherwise we're going to create our own file, include it from the
232
+ # main conf, and use that
233
+
234
+ if @conf_files[:renv][:order] > @conf_files[:vhost][:order]
235
+ if @conf_files[:renv][:file] != conf
236
+ return @passenger_conf = @conf_files[:renv][:file]
237
+ end
238
+ end
239
+
240
+ @passenger_conf = make_new_file
241
+ end
242
+
243
+ def read_conf
244
+ @conf_files = {}
245
+ # Strings that we're looking for:
246
+ #
247
+ # NameVirtualHost *:80
248
+ # LoadModule passenger_module /opt/local/lib/ruby/gems/1.8/gems/passenger-2.0.6/ext/apache2/mod_passenger.so
249
+ # PassengerRoot /opt/local/lib/ruby/gems/1.8/gems/passenger-2.0.6
250
+ # PassengerRuby /opt/local/bin/ruby
251
+ #
252
+ @_conf_strings = {
253
+ :vhost => /^\s*NameVirtualHost\s+(\S+)/i,
254
+ :load => %r{^\s*LoadModule\s+passenger_module\s+((/\S+)/lib/ruby/gems/\S+/passenger-[\d.]+)/ext/apache2/mod_passenger.so}i,
255
+ :root => %r{^\s*PassengerRoot\s+(['"]?)(/\S+/lib/ruby/gems/\S+/passenger-[\d.]+)\1}i,
256
+ :ruby => %r{^\s*PassengerRuby\s+(['"]?)(/\S+)/bin/ruby\1}i,
257
+ :renv => %r{^\s*RailsEnv\s+(['"]?)(\S+)\1}i
258
+ }
259
+
260
+ # change into the server root so that any relative paths in the conf files work
261
+ Dir.chdir(server_root) do |p|
262
+
263
+ # start at the conf and drill down through all includes looking for our
264
+ # strings and recording where they are - we're just going to barf if we
265
+ # find the strings in more than one location - let the user sort that
266
+ # out
267
+ drill_down(conf)
268
+ end
269
+ ensure_conf
270
+ end
271
+
272
+ def ensure_dir(d)
273
+ _umask = File.umask(022)
274
+ _dir = File.mkdirs(File.join(dir, d))
275
+ File.umask(_umask)
276
+ _dir
277
+ end
278
+
279
+ def make_new_file
280
+ _dir = ensure_dir('extra')
281
+
282
+ # we're going to make sure that we get a filename that doesn't exist
283
+ %w{passenger.conf mod_rails.conf httpd-passenger.conf httpd-mod_rails.conf j20qmcjidhe93knd.conf}.each do |fn|
284
+ _f = File.join(_dir,fn)
285
+ if !File.exist?(_f)
286
+ FileUtils.touch _f
287
+ File.open(conf, 'a') do |f|
288
+ f.puts @@marker_string
289
+ f.puts "Include #{_f}"
290
+ end
291
+ return _f
292
+ end
293
+ end
294
+ raise "Couldn't find a unique filename in #{_dir}."
295
+ end
296
+
297
+ def ensure_conf
298
+ _need_re_read = false
299
+ unless @conf_files[:root]
300
+ add_to_conf( "PassengerRoot #{@conf_files[:load][:match][1]}", :load, :root )
301
+ _need_re_read = true
302
+ end
303
+
304
+ unless @conf_files[:ruby]
305
+ add_to_conf( "PassengerRuby #{@conf_files[:load][:match][2]}/bin/ruby", :root, :ruby )
306
+ _need_re_read = true
307
+ end
308
+
309
+ unless @conf_files[:renv]
310
+ add_to_conf( "RailsEnv #{ENV['RAILS_ENV'] || 'development'}\n", :ruby, :renv )
311
+ _need_re_read = true
312
+ end
313
+
314
+ unless @conf_files[:vhost]
315
+ add_to_conf( "NameVirtualHost *:80\n", :renv, :vhost )
316
+ _need_re_read = true
317
+ end
318
+
319
+ if _need_re_read
320
+ read_conf
321
+ end
322
+ check_conf
323
+ end
324
+
325
+ def add_to_conf( str, from, to)
326
+ p = @conf_files[from]
327
+ File.open( p[:file], 'r+' ) do |f|
328
+ f.flock File::LOCK_EX
329
+ _buffer = []
330
+ f.each do |l|
331
+ _buffer << l
332
+ if $. == p[:lineno]
333
+ @conf_files[to] = { :file => p[:file], :lineno => $. + 1 + str.count("\n") }
334
+ _buffer << str + "\n"
335
+ end
336
+ end
337
+ f.truncate 0
338
+ f.seek 0
339
+ f << _buffer
340
+ end
341
+ end
342
+
343
+ def check_conf
344
+ # we can add everything except the LoadModule line
345
+ unless @conf_files[:load]
346
+ raise "LoadModule line missing, maybe you forgot to run: passenger-install-apache2-module"
347
+ end
348
+
349
+ _load_order = @conf_files[:load][:order]
350
+ [ :root, :ruby, :renv ].each do |s|
351
+ next unless @conf_files[s]
352
+ if _load_order > @conf_files[s][:order]
353
+ raise "Passenger module loaded too late: #{@conf_files[:load][:file]},#{@conf_files[:load][:lineno]} needs to happen before #{@conf_files[s][:file]},#{@conf_files[s][:lineno]}"
354
+ end
355
+ end
356
+
357
+ _load_passenger_location = @conf_files[:load][:match][1]
358
+ if @conf_files[:root]
359
+ unless _load_passenger_location == _root_passenger_location = @conf_files[:root][:match][2]
360
+ raise "Passenger module location #{_load_passenger_location} and PassengerRoot setting #{_root_passenger_location} should be the same."
361
+ end
362
+ end
363
+
364
+ _load_passenger_prefix = @conf_files[:load][:match][2]
365
+ if @conf_files[:ruby]
366
+ unless _load_passenger_prefix == _ruby_passenger_prefix = @conf_files[:ruby][:match][2]
367
+ raise "Passenger module prefix #{_load_passenger_prefix} and PassengerRuby prefix #{_ruby_passenger_prefix} should be the same."
368
+ end
369
+ end
370
+ end
371
+
372
+ def vhost
373
+ @vhost ||= @conf_files[:vhost][:match][1]
374
+ end
375
+
376
+ def drill_down(_c)
377
+ @_order ||= 0
378
+ _c = File.expand_path(_c)
379
+ File.open(_c).each_line do |line|
380
+ @_conf_strings.each do |k,v|
381
+ if m = line.match(v)
382
+ _exists = @conf_files[k]
383
+ raise "#{k} was already found in #{_exists[:file]},#{_exists[:lineno]} for #{_c},#{$.}" if _exists
384
+ @conf_files[k] = { :file => _c, :lineno => $., :match => m, :order => @_order += 1 }
385
+ end
386
+ end
387
+
388
+ if m = line.match(/^\s*Include\s+(['"]?)(\S+)\1/i)
389
+ # apache includes are commonly file globs
390
+ Dir.glob(m[2]).each do |f|
391
+ drill_down(f)
392
+ end
393
+ end
394
+ end
395
+ end
396
+
397
+
398
+ def recursive_tracking(_c)
399
+ _c.each do |c|
400
+ _filename = c[:file]
401
+ _new = dir_handle.grep(/^\s*Include.*#{Regexp.escape(_filename)}/i)
402
+ unless _new.size > 0
403
+ # do some other things
404
+ end
405
+
406
+ if _new.find{|c| c[:file] == conf}
407
+ return true
408
+ end
409
+
410
+ ret = recursive_tracking(_new)
411
+ if ret
412
+ @includes << c
413
+ else
414
+ @includes.pop
415
+ end
416
+ return ret
417
+ end
418
+ return false
419
+ end
420
+
421
+ def from_server(s)
422
+ @httpd_string ||= `#{@@httpd_cmd} -V`
423
+ @httpd_string.match(/#{s}="([^"]+)"/i)[1]
424
+ end
425
+ end
426
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: JasonKing-pulp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Jason King
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-12 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: JasonKing-grep
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.2
24
+ version:
25
+ description:
26
+ email: jk@handle.it
27
+ executables:
28
+ - pulp
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - README.markdown
35
+ - lib/passenger/config.rb
36
+ - bin/pulp
37
+ has_rdoc: true
38
+ homepage: http://github.com/JasonKing/pulp
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --inline-source
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.2.0
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: pulp - Passenger Helper - for simple setup of Ruby Apps using Passenger
64
+ test_files: []
65
+