giannichiappetta-vlad 1.2.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,90 @@
1
+ require 'rubygems'
2
+ require 'thread'
3
+ require 'rake_remote_task'
4
+
5
+ $TESTING ||= false
6
+
7
+ ##
8
+ # Vlad the Deployer - Pragmatic application deployment automation, without mercy.
9
+ #
10
+ # Please read doco/getting_started.txt or http://rubyhitsquad.com/
11
+ #
12
+ # === Basic scenario:
13
+ #
14
+ # 1. rake vlad:setup (first time only)
15
+ # 2. rake vlad:update
16
+ # 3. rake vlad:migrate (optional)
17
+ # 4. rake vlad:start
18
+
19
+ module Vlad
20
+
21
+ ##
22
+ # This is the version of Vlad you are running.
23
+ VERSION = '1.2.0.2'
24
+
25
+ ##
26
+ # Base error class for all Vlad errors.
27
+ class Error < RuntimeError; end
28
+
29
+ ##
30
+ # Raised when you have incorrectly configured Vlad.
31
+ class ConfigurationError < Error; end
32
+
33
+ ##
34
+ # Raised when a remote command fails.
35
+ class CommandFailedError < Error; end
36
+
37
+ ##
38
+ # Raised when an environment variable hasn't been set.
39
+ class FetchError < Error; end
40
+
41
+ ##
42
+ # Loads tasks file +tasks_file+ and various recipe styles as a hash
43
+ # of category/style pairs. Recipes default to:
44
+ #
45
+ # :app => :mongrel
46
+ # :config => 'config/deploy.rb'
47
+ # :core => :core
48
+ # :scm => :subversion
49
+ # :web => :apache
50
+ #
51
+ # You can override individual values and/or set to nil to
52
+ # deactivate. :config will get loaded last to ensure that user
53
+ # variables override default values.
54
+ #
55
+ # And by all means, feel free to skip this entirely if it doesn't
56
+ # fit for you. All it does is a fancy-pants require. Require
57
+ # whatever files you need as you see fit straight from your
58
+ # Rakefile. YAY for simple and clean!
59
+ def self.load options = {}
60
+ options = {:config => options} if String === options
61
+
62
+ recipes = {
63
+ :config => 'config/deploy.rb',
64
+ :core => :core,
65
+ :scm => :git,
66
+ :app => :passenger
67
+ }.merge(options)
68
+
69
+ # be sure core comes first so base tasks aren't clobbered
70
+ if core = recipes.delete(:core)
71
+ require "vlad/#{core}"
72
+ end
73
+ recipes.each do |flavor, recipe|
74
+ next if recipe.nil? or flavor == :config
75
+ require "vlad/#{recipe}"
76
+ end
77
+
78
+ Kernel.load recipes[:config]
79
+ end
80
+ end
81
+
82
+ class String #:nodoc:
83
+ def cleanup
84
+ if ENV['FULL'] then
85
+ gsub(/\s+/, ' ').strip
86
+ else
87
+ self[/\A.*?\./]
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,37 @@
1
+ require 'vlad'
2
+
3
+ namespace :vlad do
4
+ ##
5
+ # Apache web server
6
+
7
+ set :web_command, "apachectl"
8
+
9
+ desc "(Re)Start the web servers"
10
+
11
+ remote_task :start_web, :roles => :web do
12
+ run "#{web_command} restart"
13
+ end
14
+
15
+ desc "Stop the web servers"
16
+
17
+ remote_task :stop_web, :roles => :web do
18
+ run "#{web_command} stop"
19
+ end
20
+
21
+ ##
22
+ # Everything HTTP.
23
+
24
+ desc "(Re)Start the web and app servers"
25
+
26
+ remote_task :start do
27
+ Rake::Task['vlad:start_app'].invoke
28
+ Rake::Task['vlad:start_web'].invoke
29
+ end
30
+
31
+ desc "Stop the web and app servers"
32
+
33
+ remote_task :stop do
34
+ Rake::Task['vlad:stop_app'].invoke
35
+ Rake::Task['vlad:stop_web'].invoke
36
+ end
37
+ end
@@ -0,0 +1,178 @@
1
+ require 'vlad'
2
+
3
+ ##
4
+ # used by update, out here so we can ensure all threads have the same value
5
+ def now
6
+ @now ||= Time.now.utc.strftime("%Y%m%d%H%M.%S")
7
+ end
8
+
9
+ namespace :vlad do
10
+ desc "Show the vlad setup. This is all the default variables for vlad
11
+ tasks.".cleanup
12
+
13
+ task :debug do
14
+ require 'yaml'
15
+
16
+ # force them into values
17
+ Rake::RemoteTask.env.keys.each do |key|
18
+ next if key =~ /_release|releases|sudo_password/
19
+ Rake::RemoteTask.fetch key
20
+ end
21
+
22
+ puts "# Environment:"
23
+ puts
24
+ y Rake::RemoteTask.env
25
+ puts "# Roles:"
26
+ y Rake::RemoteTask.roles
27
+ end
28
+
29
+ desc "Setup your servers. Before you can use any of the deployment
30
+ tasks with your project, you will need to make sure all of your
31
+ servers have been prepared with 'rake vlad:setup'. It is safe to
32
+ run this task on servers that have already been set up; it will
33
+ not destroy any deployed revisions or data.".cleanup
34
+
35
+ task :setup do
36
+ Rake::Task['vlad:setup_app'].invoke
37
+ end
38
+
39
+ desc "Prepares application servers for deployment.".cleanup
40
+
41
+ remote_task :setup_app, :roles => :app do
42
+ dirs = [deploy_to, releases_path, scm_path, shared_path]
43
+ dirs += %w(config system log pids).map { |d| File.join(shared_path, d) }
44
+ run [ "umask 02 && mkdir -p #{dirs.join(' ')}",
45
+ "touch #{shared_path}/config/database.yml"
46
+ ].join(" && ")
47
+ end
48
+
49
+ desc "Updates your application server to the latest revision. Syncs
50
+ a copy of the repository, exports it as the latest release, fixes
51
+ up your symlinks, symlinks the latest revision to current and logs
52
+ the update.".cleanup
53
+
54
+ remote_task :update, :roles => :app do
55
+ symlink = false
56
+ begin
57
+ run [ "cd #{scm_path}",
58
+ "#{source.checkout revision, '.'}",
59
+ "#{source.export ".", release_path}",
60
+ "chmod -R g+w #{latest_release}",
61
+ "rm -rf #{latest_release}/log #{latest_release}/public/system #{latest_release}/tmp/pids",
62
+ "mkdir -p #{latest_release}/db #{latest_release}/tmp",
63
+ "ln -s #{shared_path}/log #{latest_release}/log",
64
+ "ln -s #{shared_path}/system #{latest_release}/public/system",
65
+ "ln -s #{shared_path}/pids #{latest_release}/tmp/pids",
66
+ "ln -s #{shared_path}/config/database.yml #{latest_release}/config/database.yml"
67
+ ].join(" && ")
68
+
69
+ symlink = true
70
+ run "rm -f #{current_path} && ln -s #{latest_release} #{current_path}"
71
+
72
+ run "echo #{now} $USER #{revision} #{File.basename release_path} >> #{deploy_to}/revisions.log"
73
+ rescue => e
74
+ run "rm -f #{current_path} && ln -s #{previous_release} #{current_path}" if
75
+ symlink
76
+ run "rm -rf #{release_path}"
77
+ raise e
78
+ end
79
+ end
80
+
81
+ desc "Run the migrate rake task for the the app. By default this is run in
82
+ the latest app directory. You can run migrations for the current app
83
+ directory by setting :migrate_target to :current. Additional environment
84
+ variables can be passed to rake via the migrate_env variable.".cleanup
85
+
86
+ # No application files are on the DB machine, also migrations should only be
87
+ # run once.
88
+ remote_task :migrate, :roles => :app do
89
+ break unless target_host == Rake::RemoteTask.hosts_for(:app).first
90
+
91
+ directory = case migrate_target.to_sym
92
+ when :current then current_path
93
+ when :latest then current_release
94
+ else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
95
+ end
96
+
97
+ run "cd #{current_path}; #{rake_cmd} RAILS_ENV=#{rails_env} db:migrate #{migrate_args}"
98
+ end
99
+
100
+ desc "Invoke a single command on every remote server. This is useful for
101
+ performing one-off commands that may not require a full task to be written
102
+ for them. Simply specify the command to execute via the COMMAND
103
+ environment variable. To execute the command only on certain roles,
104
+ specify the ROLES environment variable as a comma-delimited list of role
105
+ names.
106
+
107
+ $ rake vlad:invoke COMMAND='uptime'".cleanup
108
+
109
+ remote_task :invoke do
110
+ command = ENV["COMMAND"]
111
+ abort "Please specify a command to execute on the remote servers (via the COMMAND environment variable)" unless command
112
+ puts run(command)
113
+ end
114
+
115
+ desc "Copy arbitrary files to the currently deployed version using
116
+ FILES=a,b,c. This is useful for updating files piecemeal when you
117
+ need to quickly deploy only a single file.
118
+
119
+ To use this task, specify the files and directories you want to copy as a
120
+ comma-delimited list in the FILES environment variable. All directories
121
+ will be processed recursively, with all files being pushed to the
122
+ deployment servers. Any file or directory starting with a '.' character
123
+ will be ignored.
124
+
125
+ $ rake vlad:upload FILES=templates,controller.rb".cleanup
126
+
127
+ remote_task :upload do
128
+ file_list = (ENV["FILES"] || "").split(",")
129
+
130
+ files = file_list.map do |f|
131
+ f = f.strip
132
+ File.directory?(f) ? Dir["#{f}/**/*"] : f
133
+ end.flatten
134
+
135
+ files = files.reject { |f| File.directory?(f) || File.basename(f)[0] == ?. }
136
+
137
+ abort "Please specify at least one file to update (via the FILES environment variable)" if files.empty?
138
+
139
+ files.each do |file|
140
+ rsync file, File.join(current_path, file)
141
+ end
142
+ end
143
+
144
+ desc "Rolls back to a previous version and restarts. This is handy if you
145
+ ever discover that you've deployed a lemon; 'rake vlad:rollback' and
146
+ you're right back where you were, on the previously deployed
147
+ version.".cleanup
148
+
149
+ remote_task :rollback do
150
+ if releases.length < 2 then
151
+ abort "could not rollback the code because there is no prior release"
152
+ else
153
+ run "rm #{current_path}; ln -s #{previous_release} #{current_path} && rm -rf #{current_release}"
154
+ end
155
+
156
+ Rake::Task['vlad:start'].invoke
157
+ end
158
+
159
+ desc "Clean up old releases. By default, the last 5 releases are kept on
160
+ each server (though you can change this with the keep_releases variable).
161
+ All other deployed revisions are removed from the servers.".cleanup
162
+
163
+ remote_task :cleanup do
164
+ max = keep_releases
165
+ if releases.length <= max then
166
+ puts "no old releases to clean up #{releases.length} <= #{max}"
167
+ else
168
+ puts "keeping #{max} of #{releases.length} deployed releases"
169
+
170
+ directories = (releases - releases.last(max)).map { |release|
171
+ File.join(releases_path, release)
172
+ }.join(" ")
173
+
174
+ run "rm -rf #{directories}"
175
+ end
176
+ end
177
+
178
+ end # namespace vlad
@@ -0,0 +1,43 @@
1
+ class Vlad::Git
2
+
3
+ set :source, Vlad::Git.new
4
+ set :git_cmd, "git"
5
+
6
+ ##
7
+ # Returns the command that will check out +revision+ from the
8
+ # code repo into directory +destination+. +revision+ can be any
9
+ # SHA1 or equivalent (e.g. branch, tag, etc...)
10
+
11
+ def checkout(revision, destination)
12
+ destination = 'cached-copy' if destination == '.'
13
+ revision = 'HEAD' if revision =~ /head/i
14
+
15
+ [ "([ -d #{destination}/.git ] && echo 'Existing repository found' || #{git_cmd} clone #{code_repo} #{destination})",
16
+ "cd #{destination}",
17
+ "#{git_cmd} fetch",
18
+ "#{git_cmd} reset --hard #{revision}",
19
+ "#{git_cmd} submodule init",
20
+ "#{git_cmd} submodule update"
21
+ ].join(" && ")
22
+ end
23
+
24
+ ##
25
+ # Returns the command that will export +revision+ from the code repo into
26
+ # the directory +destination+.
27
+
28
+ def export(source, destination)
29
+ [ "cp -R #{source} #{destination}",
30
+ "rm -Rf #{destination}/.git"
31
+ ].join(" && ")
32
+ end
33
+
34
+ ##
35
+ # Returns a command that maps human-friendly revision identifier +revision+
36
+ # into a git SHA1.
37
+
38
+ def revision(revision)
39
+ revision = 'HEAD' if revision =~ /head/i
40
+
41
+ "`#{git_cmd} rev-parse #{revision}`"
42
+ end
43
+ end
@@ -0,0 +1,85 @@
1
+ require 'vlad'
2
+
3
+ namespace :vlad do
4
+
5
+ set :lighttpd_port, 65536
6
+ set :web_command, "lighttpd"
7
+ set :lighttpd_user, "nobody"
8
+ set :lighttpd_group, "nobody"
9
+ set(:lighttpd_init) { "#{shared_path}/lighttpd.sh" }
10
+ set(:lighttpd_conf) { "#{shared_path}/lighttpd.conf" }
11
+
12
+ desc "Prepares application servers for deployment. Lighttpd
13
+ configuration is set via the lighttpd_* variables.".cleanup
14
+
15
+ remote_task :setup_lighttpd, :roles => :app do
16
+ require 'tempfile'
17
+
18
+ put lighttpd_conf, 'vlad.lighttpd_config' do
19
+ conf = <<-"EOF"
20
+ server.modules = ( "mod_rewrite",
21
+ "mod_access",
22
+ "mod_fastcgi",
23
+ "mod_compress",
24
+ "mod_accesslog" )
25
+
26
+ server.document-root = "#{current_path}/public"
27
+ server.errorlog = "#{shared_path}/log/lighttpd.error.log"
28
+ accesslog.filename = "#{shared_path}/log/lighttpd.access.log"
29
+ server.pid-file = "#{shared_path}/pids/lighttpd.pid"
30
+ server.port = #{lighttpd_port}
31
+ server.username = "#{lighttpd_user}"
32
+ server.groupname = "#{lighttpd_group}"
33
+ server.error-handler-404 = "/dispatch.fcgi"
34
+ server.indexfiles = ( "index.html", "index.rb" )
35
+ url.access-deny = ( "~", ".inc" )
36
+ compress.cache-dir = "#{shared_path}/tmp/cache/compress"
37
+ compress.filetype = ("text/html","text/plain","text/javascript","text/css")
38
+ server.tag = "lighttpd | TextDriven"
39
+
40
+ fastcgi.server = (
41
+ ".fcgi" => (
42
+ "localhost" => (
43
+ "min-procs" => 1,
44
+ "max-procs" => 1,
45
+ "socket" => "#{shared_path}/pids/rubyholic.socket",
46
+ "bin-path" => "#{current_path}/public/dispatch.fcgi",
47
+ "bin-environment" => ( "RAILS_ENV" => "production" ) ) ) )
48
+ EOF
49
+ end
50
+
51
+ run "mkdir -p \"#{shared_path}/tmp/cache/compress\""
52
+ end
53
+
54
+ desc "(Re)Start the web servers"
55
+
56
+ remote_task :start_web, :roles => :web do
57
+ cmd = %w(lighttpd ruby).map {|app| "(killall #{app} || true)"}.join(" && ")
58
+ cmd += " && #{web_command} -f #{lighttpd_conf} </dev/null >/dev/null 2>&1"
59
+ run cmd
60
+ end
61
+
62
+ desc "Stop the web servers"
63
+ remote_task :stop_web, :roles => :web do
64
+ cmd = %w(lighttpd ruby).map {|app| "(killall #{app} || true)"}.join(" && ")
65
+
66
+ run cmd
67
+ end
68
+
69
+ ##
70
+ # Everything HTTP.
71
+
72
+ desc "(Re)Start the web and app servers"
73
+
74
+ remote_task :start do
75
+ Rake::Task['vlad:start_app'].invoke
76
+ Rake::Task['vlad:start_web'].invoke
77
+ end
78
+
79
+ desc "Stop the web and app servers"
80
+
81
+ remote_task :stop do
82
+ Rake::Task['vlad:stop_app'].invoke
83
+ Rake::Task['vlad:stop_web'].invoke
84
+ end
85
+ end
@@ -0,0 +1,23 @@
1
+ namespace :vlad do
2
+ desc "Remove the maintenance page"
3
+ remote_task :start_web, :roles => [:web] do
4
+ run "if [ -f #{shared_path}/system/maintenance.html ]; then rm -f #{shared_path}/system/maintenance.html; fi"
5
+ end
6
+
7
+ desc "Put the maintenance page in place"
8
+ remote_task :stop_web, :roles => [:web] do
9
+ run "cp -f #{shared_path}/config/maintenance.html #{shared_path}/system/"
10
+ end
11
+
12
+ desc "(Re)Start the web and app servers"
13
+ remote_task :start do
14
+ Rake::Task['vlad:start_app'].invoke
15
+ Rake::Task['vlad:start_web'].invoke
16
+ end
17
+
18
+ desc "Stop the web and app servers"
19
+ remote_task :stop do
20
+ Rake::Task['vlad:stop_app'].invoke
21
+ Rake::Task['vlad:stop_web'].invoke
22
+ end
23
+ end