lastobelus-vlad 1.4.0

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/lib/vlad.rb ADDED
@@ -0,0 +1,91 @@
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.4.0'
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 => :passenger
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
+ order = [:core, :app, :config, :scm, :web]
62
+ order += options.keys - order
63
+
64
+ recipes = {
65
+ :app => :passenger,
66
+ :config => 'config/deploy.rb',
67
+ :core => :core,
68
+ :scm => :subversion,
69
+ :web => :apache,
70
+ }.merge(options)
71
+
72
+ order.each do |flavor|
73
+ recipe = recipes[flavor]
74
+ next if recipe.nil? or flavor == :config
75
+ require "vlad/#{recipe}"
76
+ end
77
+
78
+ Kernel.load recipes[:config]
79
+ Kernel.load "config/deploy_#{ENV['to']}.rb" if ENV['to']
80
+ end
81
+ end
82
+
83
+ class String #:nodoc:
84
+ def cleanup
85
+ if ENV['FULL'] then
86
+ gsub(/\s+/, ' ').strip
87
+ else
88
+ self[/\A.*?\./]
89
+ end
90
+ end
91
+ 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
data/lib/vlad/core.rb ADDED
@@ -0,0 +1,181 @@
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(system log pids).map { |d| File.join(shared_path, d) }
44
+ run "umask #{umask} && mkdir -p #{dirs.join(' ')}"
45
+ end
46
+
47
+ desc "Updates your application server to the latest revision. Syncs
48
+ a copy of the repository, exports it as the latest release, fixes
49
+ up your symlinks, symlinks the latest revision to current and logs
50
+ the update.".cleanup
51
+
52
+ remote_task :update, :roles => :app do
53
+ symlink = false
54
+ begin
55
+ run [ "cd #{scm_path}",
56
+ "#{source.checkout revision, scm_path}",
57
+ "#{source.export revision, release_path}",
58
+ "chmod -R g+w #{latest_release}",
59
+ "rm -rf #{latest_release}/log #{latest_release}/public/system #{latest_release}/tmp/pids",
60
+ "mkdir -p #{latest_release}/db #{latest_release}/tmp"
61
+ ].join(" && ")
62
+ Rake::Task['vlad:update_symlinks'].invoke
63
+
64
+ symlink = true
65
+ run "rm -f #{current_path} && ln -s #{latest_release} #{current_path}"
66
+
67
+ run "echo #{now} $USER #{revision} #{File.basename release_path} >> #{deploy_to}/revisions.log"
68
+ rescue => e
69
+ run "rm -f #{current_path} && ln -s #{previous_release} #{current_path}" if
70
+ symlink
71
+ run "rm -rf #{release_path}"
72
+ raise e
73
+ end
74
+ end
75
+
76
+ desc "Updates the symlinks for shared paths".cleanup
77
+
78
+ remote_task :update_symlinks, :roles => :app do
79
+ run [ "ln -s #{shared_path}/log #{latest_release}/log",
80
+ "ln -s #{shared_path}/system #{latest_release}/public/system",
81
+ "ln -s #{shared_path}/pids #{latest_release}/tmp/pids" ].join(" && ")
82
+ end
83
+
84
+ desc "Run the migrate rake task for the the app. By default this is run in
85
+ the latest app directory. You can run migrations for the current app
86
+ directory by setting :migrate_target to :current. Additional environment
87
+ variables can be passed to rake via the migrate_env variable.".cleanup
88
+
89
+ # No application files are on the DB machine, also migrations should only be
90
+ # run once.
91
+ remote_task :migrate, :roles => :app do
92
+ break unless target_host == Rake::RemoteTask.hosts_for(:app).first
93
+
94
+ directory = case migrate_target.to_sym
95
+ when :current then current_path
96
+ when :latest then current_release
97
+ else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
98
+ end
99
+
100
+ run "cd #{directory}; #{rake_cmd} RAILS_ENV=#{rails_env} db:migrate #{migrate_args}"
101
+ end
102
+
103
+ desc "Invoke a single command on every remote server. This is useful for
104
+ performing one-off commands that may not require a full task to be written
105
+ for them. Simply specify the command to execute via the COMMAND
106
+ environment variable. To execute the command only on certain roles,
107
+ specify the ROLES environment variable as a comma-delimited list of role
108
+ names.
109
+
110
+ $ rake vlad:invoke COMMAND='uptime'".cleanup
111
+
112
+ remote_task :invoke do
113
+ command = ENV["COMMAND"]
114
+ abort "Please specify a command to execute on the remote servers (via the COMMAND environment variable)" unless command
115
+ puts run(command)
116
+ end
117
+
118
+ desc "Copy arbitrary files to the currently deployed version using
119
+ FILES=a,b,c. This is useful for updating files piecemeal when you
120
+ need to quickly deploy only a single file.
121
+
122
+ To use this task, specify the files and directories you want to copy as a
123
+ comma-delimited list in the FILES environment variable. All directories
124
+ will be processed recursively, with all files being pushed to the
125
+ deployment servers. Any file or directory starting with a '.' character
126
+ will be ignored.
127
+
128
+ $ rake vlad:upload FILES=templates,controller.rb".cleanup
129
+
130
+ remote_task :upload do
131
+ file_list = (ENV["FILES"] || "").split(",")
132
+
133
+ files = file_list.map do |f|
134
+ f = f.strip
135
+ File.directory?(f) ? Dir["#{f}/**/*"] : f
136
+ end.flatten
137
+
138
+ files = files.reject { |f| File.directory?(f) || File.basename(f)[0] == ?. }
139
+
140
+ abort "Please specify at least one file to update (via the FILES environment variable)" if files.empty?
141
+
142
+ files.each do |file|
143
+ rsync file, File.join(current_path, file)
144
+ end
145
+ end
146
+
147
+ desc "Rolls back to a previous version and restarts. This is handy if you
148
+ ever discover that you've deployed a lemon; 'rake vlad:rollback' and
149
+ you're right back where you were, on the previously deployed
150
+ version.".cleanup
151
+
152
+ remote_task :rollback do
153
+ if releases.length < 2 then
154
+ abort "could not rollback the code because there is no prior release"
155
+ else
156
+ run "rm -f #{current_path}; ln -s #{previous_release} #{current_path} && rm -rf #{current_release}"
157
+ end
158
+
159
+ Rake::Task['vlad:start'].invoke
160
+ end
161
+
162
+ desc "Clean up old releases. By default, the last 5 releases are kept on
163
+ each server (though you can change this with the keep_releases variable).
164
+ All other deployed revisions are removed from the servers.".cleanup
165
+
166
+ remote_task :cleanup do
167
+ max = keep_releases
168
+ if releases.length <= max then
169
+ puts "no old releases to clean up #{releases.length} <= #{max}"
170
+ else
171
+ puts "keeping #{max} of #{releases.length} deployed releases"
172
+
173
+ directories = (releases - releases.last(max)).map { |release|
174
+ File.join(releases_path, release)
175
+ }.join(" ")
176
+
177
+ run "rm -rf #{directories}"
178
+ end
179
+ end
180
+
181
+ end # namespace vlad
data/lib/vlad/darcs.rb ADDED
@@ -0,0 +1,24 @@
1
+ class Vlad::Darcs
2
+
3
+ set :source, Vlad::Darcs.new
4
+ set :darcs_cmd, "darcs"
5
+
6
+ ##
7
+ # Ignores +revision+ for now, exports into directory +destination+
8
+
9
+ def checkout(revision, destination)
10
+ [ %{(test ! -d #{destination}/_darcs && #{darcs_cmd} init "--repodir=#{destination}") || true},
11
+ %{#{darcs_cmd} pull -a "--repodir=#{destination}" #{repository}},
12
+ ].join(" && ")
13
+ end
14
+
15
+ def export(revision, destination)
16
+ [ %{mkdir -p #{destination}},
17
+ %{ls | grep ^[^_] | xargs -I vlad cp -R vlad #{destination}}
18
+ ].join(" && ")
19
+ end
20
+
21
+ def revision(revision)
22
+ revision
23
+ end
24
+ end
data/lib/vlad/git.rb ADDED
@@ -0,0 +1,49 @@
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
+ # repository into directory +destination+. +revision+ can be any
9
+ # SHA1 or equivalent (e.g. branch, tag, etc...)
10
+
11
+ def checkout(revision, destination)
12
+ destination = File.join(destination, 'repo')
13
+ revision = 'HEAD' if revision =~ /head/i
14
+
15
+ [ "rm -rf #{destination}",
16
+ "#{git_cmd} clone #{repository} #{destination}",
17
+ "cd #{destination}",
18
+ "#{git_cmd} checkout -f -b deployed-#{revision} #{revision}",
19
+ "cd -"
20
+ ].join(" && ")
21
+ end
22
+
23
+ ##
24
+ # Returns the command that will export +revision+ from the current directory
25
+ # into the directory +destination+.
26
+ # Expects to be run from +scm_path+ after Vlad::Git#checkout
27
+
28
+ def export(revision, destination)
29
+ revision = 'HEAD' if revision =~ /head/i
30
+ revision = "deployed-#{revision}"
31
+
32
+ [ "mkdir -p #{destination}",
33
+ "cd repo",
34
+ "#{git_cmd} archive --format=tar #{revision} | (cd #{destination} && tar xf -)",
35
+ "cd -",
36
+ "cd .."
37
+ ].join(" && ")
38
+ end
39
+
40
+ ##
41
+ # Returns a command that maps human-friendly revision identifier +revision+
42
+ # into a git SHA1.
43
+
44
+ def revision(revision)
45
+ revision = 'HEAD' if revision =~ /head/i
46
+
47
+ "`#{git_cmd} rev-parse #{revision}`"
48
+ end
49
+ end
data/lib/vlad/god.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'vlad'
2
+
3
+ namespace :vlad do
4
+ ## God module for merb app server
5
+
6
+ desc "Prepares application servers for deployment.".cleanup
7
+
8
+ remote_task :setup_app, :roles => :app do
9
+ # do nothing?
10
+ end
11
+
12
+ desc "Restart the app servers"
13
+
14
+ remote_task :start_app, :roles => :app do
15
+ run "god restart #{cluster_name}"
16
+ end
17
+
18
+ desc "Stop the app servers"
19
+
20
+ remote_task :stop_app, :roles => :app do
21
+ run "god stop #{cluster_name}"
22
+ end
23
+ 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