forked from jsomers/lesswrong
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Rakefile
362 lines (311 loc) · 8.66 KB
/
Rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
require 'fileutils'
require 'tempfile'
require 'pathname'
require 'shellwords'
begin
require 'rspec/core/rake_task'
rescue LoadError
end
namespace :test do
desc "Interactively run through the deployment test script."
task :manual do
# These are in here so they aren't required on the production server
$:.unshift 'tasks/manual_test_script/lib'
require 'active_support'
require 'manual_test_script'
ManualTestScript.run('test/manual.txt')
end
end
# Borrowed from capistrano
def sudo(command, options = {})
user = options[:as] && "-u #{options.delete(:as)}"
sudo_prompt_option = "-p 'sudo password: '"
sudo_command = ["sudo", sudo_prompt_option, user, command].compact.join(" ")
run sudo_command
end
def run(command)
puts "running `#{command}'"
unless system(command)
raise RuntimeError.new("Error running command: '#{command}'")
end
end
def basepath
Pathname(__FILE__).dirname.realpath
end
def shared_path
basepath.parent.parent + 'shared'
end
def r2_path
basepath + "r2"
end
def db_dump_path
path = basepath + "db" + "dumps"
path.mkpath unless path.exist?
path
end
def inifile
ENV['INI'] || (r2_path + "#{environment}.ini")
end
def user
ENV['APPLICATION_USER'] || raise("APPLICATION_USER environment variable must be set to run this task")
end
def application
ENV['APPLICATION'] || raise("APPLICATION environment variable variable must be set to run this task")
end
def environment
ENV['APPLICATION_ENV'] || raise("APPLICATION_ENV environment variable must be set to run this task")
end
def databases
@databases ||= begin
dbs = ENV['DATABASES'] || raise("DATABASES environment variable must be set to run this task")
dbs.split(/\s*,\s*/)
end
end
def db_dump_prefix
ENV['DB_DUMP_PREFIX'] || ''
end
def app_server(action)
return unless [:start, :stop, :restart].include?(action)
sudo "#{action} paster"
end
# These tasks assume they are running in a capistrano managed directory structure.
namespace :app do
desc "Start the Application"
task :start do
app_server(:start)
end
desc "Stop the Application"
task :stop do
app_server(:stop)
end
desc "Restart the Application"
task :restart do
app_server(:restart)
end
end
namespace :deploy do
desc 'Run Reddit setup routine'
task :setup do
Dir.chdir r2_path
sudo "python setup.py install"
Dir.chdir basepath
sudo "chown -R #{user} #{r2_path}"
end
desc "Symlink the INI files into the release path"
task :symlink_ini do
Dir["/usr/local/etc/reddit/#{application}.*.ini"].each do |ini|
if File.basename(ini) =~ /#{Regexp.escape(application)}\.([^\.]+)\.ini/
target = "#{r2_path}/#{$1}.ini"
FileUtils.ln_sf(ini, target, :verbose => true)
end
end
end
desc 'Compress and concetenate JS and generate MD5 files'
task :process_static_files do
Dir.chdir r2_path
run "./compress_js.sh"
end
# For compatibilty
desc "Restart the Application"
task :restart do
Rake::Task['app:stop'].invoke
Rake::Task['app:start'].invoke
end
desc "Copy the lesswrong crontab to /etc/cron.d in production. Requires root permissions"
task :crontab do
crontab = basepath + 'config' + 'crontab'
target = "/etc/cron.d/lesswrong"
if environment == "production"
sudo "/bin/cp #{crontab} #{target}"
else
# Don't want the cron jobs running in non-production environments
sudo "/bin/rm -f #{target}"
end
end
end
desc "Hook for tasks that should run after code update"
task :after_update_code => %w[
deploy:symlink_ini
deploy:setup
deploy:process_static_files
deploy:crontab
]
def conf
@conf ||= begin
conf = {}
File.open(inifile.to_s) do |ini|
ini.each_line do |line|
next if line =~ /^\s*#/ # skip comments
next if line =~ /^\s*\[[^\]]+\]/ # skip sections
if line =~ /\s*([^\s=]+)\s*=\s*(.*)$/
conf[$1] = $2
end
end
end
conf
end
end
# Set the databases variable in your local deploy configuration
# expects an array of PostgreSQL database names
# Example:
# set :databases, %w[reddit change query_queue]
namespace :postgresql do
def db_conf(db, var)
key = [db, 'db', var].join('_')
conf[key]
end
# Common options
def postgresql_opts(database)
opts = []
opts << "--host=#{db_conf database, 'host'}"
opts << "--username=#{db_conf database, 'user'}"
opts << "--no-password" # Never prompt for password, its read from the file below
opts.join(" ")
end
def with_pgpass(db)
# Setup the pgpass file
pgpass = Tempfile.new("pgpass")
ENV['PGPASSFILE'] = pgpass.path
pgpass.puts [
db_conf(db, 'host'),
'*', # port
db_conf(db, 'name'),
db_conf(db, 'user'),
db_conf(db, 'pass')
].join(':')
pgpass.close
begin
yield
ensure
pgpass.unlink
end
end
def dump_file_path(db)
(db_dump_path + "#{db_dump_prefix}#{db}.psql").to_s.shellescape
end
desc 'Dump the database'
task :dump do
databases.each do |db|
with_pgpass(db) do
run "pg_dump #{postgresql_opts(db)} -f #{dump_file_path(db)} -Fc #{db_conf(db, 'name')}"
end
end
end
desc 'Restore the latest database dump'
task :restore do
databases.each do |db|
with_pgpass(db) do
run "pg_restore #{postgresql_opts(db)} --no-owner --clean -d #{db_conf(db, 'name')} #{dump_file_path(db)} || true"
end
end
end
end
namespace :memcached do
def memcached_pid_path
basepath + "memcached.#{environment}.pid"
end
task :start do
port = conf['memcaches'].split(':').last
pid = fork do
exec('memcached','-p',port)
end
File.open(memcached_pid_path, "w") do |f|
f.puts pid
end
end
task :stop do
pid = File.read(memcached_pid_path).to_i
Process.kill("TERM", pid)
File.unlink(memcached_pid_path)
end
end
namespace :db do
namespace :test do
desc "Clean the test db, and re-populate it"
task :prepare do
ENV['APPLICATION_ENV'] = 'test'
ENV['DATABASES'] = 'main'
ENV['DB_DUMP_PREFIX'] = 'test-'
Rake::Task['db:test:truncate'].invoke
Rake::Task['postgresql:restore'].invoke
end
task :truncate do
databases.each do |db|
with_pgpass(db) do
tables = `psql -t -A #{postgresql_opts(db)} -d #{db_conf(db, 'name')} -c "select tablename from pg_tables where schemaname='public'"`
tables.lines.each do |table|
run "psql #{postgresql_opts(db)} -d #{db_conf(db, 'name')} -c 'TRUNCATE TABLE #{table.chomp}'"
end
sequences = `psql -t -A #{postgresql_opts(db)} -d #{db_conf(db, 'name')} -c "select sequence_name from information_schema.sequences where sequence_schema='public'"`
sequences.lines.each do |seq|
run %{psql #{postgresql_opts(db)} -d #{db_conf(db, 'name')} -c "SELECT setval('#{seq.chomp}', 1, false)"}
end
end
end
end
end
end
namespace :test do
def paster_pid_path
basepath + "paster.#{environment}.pid"
end
def paster_log_file_path
basepath + "paster.#{environment}.log"
end
namespace :paster do
task :start do
ENV['APPLICATION_ENV'] = 'test'
FileUtils.cd r2_path do |d|
system(
'paster','serve',inifile.to_s,
'--pid-file',paster_pid_path.to_s,
'--log-file',paster_log_file_path.to_s,
'--daemon','--reload'
)
end
end
task :stop do
ENV['APPLICATION_ENV'] = 'test'
FileUtils.cd r2_path do |d|
system('paster','serve',inifile.to_s,'--pid-file',paster_pid_path.to_s,'--stop-daemon')
end
end
end
desc "Start the server in test mode for specs"
task :start do
ENV['APPLICATION_ENV'] = 'test'
Rake::Task['db:test:prepare'].invoke
Rake::Task['memcached:start'].invoke
Rake::Task['test:paster:start'].invoke
end
desc "Stop the test server"
task :stop do
ENV['APPLICATION_ENV'] = 'test'
Rake::Task['test:paster:stop'].invoke
Rake::Task['memcached:stop'].invoke
end
desc "Start+stop test server, and run selenium spec tests"
task :run do
begin
Rake::Task['test:start'].invoke
Rake::Task['spec:setup'].invoke
Rake::Task['spec:test'].invoke
ensure
Rake::Task['test:stop'].invoke
end
end
end
if defined?(RSpec)
namespace :spec do
desc "Run the setup selenium spec"
RSpec::Core::RakeTask.new(:setup) do |t|
t.rspec_opts = ['--options', "\"#{basepath}/spec/spec.opts\""]
t.pattern = 'spec/selenium-setup.rb'
end
desc "Run the selenium spec"
RSpec::Core::RakeTask.new(:test) do |t|
t.rspec_opts = ['--options', "\"#{basepath}/spec/spec.opts\""]
t.pattern = 'spec/**/*_spec.rb'
end
end
end