isst-vlad 2.0.1

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.
@@ -0,0 +1,73 @@
1
+ require 'rubygems'
2
+ require 'minitest/autorun'
3
+ require 'rake'
4
+ require 'remote_task'
5
+ require 'stringio'
6
+
7
+ class StringIO
8
+ def readpartial(size) read end # suck!
9
+ end
10
+
11
+ module Process
12
+ def self.expected status
13
+ @@expected ||= []
14
+ @@expected << status
15
+ end
16
+
17
+ class << self
18
+ alias :waitpid2_old :waitpid2
19
+
20
+ def waitpid2(pid)
21
+ [ @@expected.shift ]
22
+ end
23
+ end
24
+ end
25
+
26
+ class Rake::RemoteTask
27
+ attr_accessor :commands, :action, :input, :output, :error
28
+
29
+ Status = Struct.new :exitstatus
30
+
31
+ class Status
32
+ def success?() exitstatus == 0 end
33
+ end
34
+
35
+ def system *command
36
+ @commands << command
37
+ self.action ? self.action[command.join(' ')] : true
38
+ end
39
+
40
+ def popen4 *command
41
+ @commands << command
42
+
43
+ @input = StringIO.new
44
+ out = StringIO.new @output.shift.to_s
45
+ err = StringIO.new @error.shift.to_s
46
+
47
+ raise if block_given?
48
+
49
+ status = self.action ? self.action[command.join(' ')] : 0
50
+ Process.expected Status.new(status)
51
+
52
+ return 42, @input, out, err
53
+ end
54
+
55
+ def select reads, writes, errs, timeout
56
+ [reads, writes, errs]
57
+ end
58
+ end
59
+
60
+ class Rake::TestCase < MiniTest::Unit::TestCase
61
+ def setup
62
+ @rake = Rake::RemoteTask
63
+ @rake.reset
64
+ Rake.application.clear
65
+ @task_count = Rake.application.tasks.size
66
+ @rake.set :domain, "example.com"
67
+ end
68
+
69
+ def util_set_hosts
70
+ @rake.host "app.example.com", :app
71
+ @rake.host "db.example.com", :db
72
+ end
73
+ end
@@ -0,0 +1,78 @@
1
+ require 'rubygems'
2
+ require 'thread'
3
+ require 'remote_task'
4
+
5
+ $TESTING ||= false
6
+
7
+ ##
8
+ # Vlad the Deployer - Pragmatic application deployment automation,
9
+ # without mercy.
10
+ #
11
+ # Please read doco/getting_started.txt or http://rubyhitsquad.com/
12
+ #
13
+ # === Basic scenario:
14
+ #
15
+ # 1. rake vlad:setup (first time only)
16
+ # 2. rake vlad:update
17
+ # 3. rake vlad:migrate (optional)
18
+ # 4. rake vlad:start
19
+
20
+ module Vlad
21
+
22
+ ##
23
+ # This is the version of Vlad you are running.
24
+ VERSION = '2.0.0'
25
+
26
+ ##
27
+ # Loads tasks file +tasks_file+ and various recipe styles as a hash
28
+ # of category/style pairs. Recipes default to:
29
+ #
30
+ # :app => :passenger
31
+ # :config => 'config/deploy.rb'
32
+ # :core => :core
33
+ # :scm => :subversion
34
+ # :web => :apache
35
+ #
36
+ # You can override individual values and/or set to nil to
37
+ # deactivate. :config will get loaded last to ensure that user
38
+ # variables override default values.
39
+ #
40
+ # And by all means, feel free to skip this entirely if it doesn't
41
+ # fit for you. All it does is a fancy-pants require. Require
42
+ # whatever files you need as you see fit straight from your
43
+ # Rakefile. YAY for simple and clean!
44
+
45
+ def self.load options = {}
46
+ options = {:config => options} if String === options
47
+ order = [:core, :type, :app, :config, :scm, :web]
48
+ order += options.keys - order
49
+
50
+ recipes = {
51
+ :app => :passenger,
52
+ :type => :sinatra,
53
+ :config => 'config/deploy.rb',
54
+ :core => :core,
55
+ :scm => :subversion,
56
+ :web => :nginx,
57
+ }.merge(options)
58
+
59
+ order.each do |flavor|
60
+ recipe = recipes[flavor]
61
+ next if recipe.nil? or flavor == :config
62
+ require "vlad/#{recipe}"
63
+ end
64
+
65
+ Kernel.load recipes[:config]
66
+ Kernel.load "config/deploy_#{ENV['to']}.rb" if ENV['to']
67
+ end
68
+ end
69
+
70
+ class String #:nodoc:
71
+ def cleanup
72
+ if ENV['FULL'] then
73
+ gsub(/\s+/, ' ').strip
74
+ else
75
+ self[/\A.*?\./]
76
+ end
77
+ end
78
+ 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,163 @@
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 += shared_paths.keys.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 #{shared_paths.values.map { |p| File.join(latest_release, p) }.join(' ')}",
60
+ "mkdir -p #{mkdirs.map { |d| File.join(latest_release, d) }.join(' ')}"
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
+ ops = shared_paths.map do |sp, rp|
80
+ "ln -s #{shared_path}/#{sp} #{latest_release}/#{rp}"
81
+ end
82
+ run ops.join(' && ')
83
+ end
84
+
85
+ desc "Invoke a single command on every remote server. This is useful for
86
+ performing one-off commands that may not require a full task to be written
87
+ for them. Simply specify the command to execute via the COMMAND
88
+ environment variable. To execute the command only on certain roles,
89
+ specify the ROLES environment variable as a comma-delimited list of role
90
+ names.
91
+
92
+ $ rake vlad:invoke COMMAND='uptime'".cleanup
93
+
94
+ remote_task :invoke do
95
+ command = ENV["COMMAND"]
96
+ abort "Please specify a command to execute on the remote servers (via the COMMAND environment variable)" unless command
97
+ run(command)
98
+ end
99
+
100
+ desc "Copy arbitrary files to the currently deployed version using
101
+ FILES=a,b,c. This is useful for updating files piecemeal when you
102
+ need to quickly deploy only a single file.
103
+
104
+ To use this task, specify the files and directories you want to copy as a
105
+ comma-delimited list in the FILES environment variable. All directories
106
+ will be processed recursively, with all files being pushed to the
107
+ deployment servers. Any file or directory starting with a '.' character
108
+ will be ignored.
109
+
110
+ $ rake vlad:upload FILES=templates,controller.rb".cleanup
111
+
112
+ remote_task :upload do
113
+ file_list = (ENV["FILES"] || "").split(",")
114
+
115
+ files = file_list.map do |f|
116
+ f = f.strip
117
+ File.directory?(f) ? Dir["#{f}/**/*"] : f
118
+ end.flatten
119
+
120
+ files = files.reject { |f| File.directory?(f) || File.basename(f)[0] == ?. }
121
+
122
+ abort "Please specify at least one file to update (via the FILES environment variable)" if files.empty?
123
+
124
+ files.each do |file|
125
+ rsync file, File.join(current_path, file)
126
+ end
127
+ end
128
+
129
+ desc "Rolls back to a previous version and restarts. This is handy if you
130
+ ever discover that you've deployed a lemon; 'rake vlad:rollback' and
131
+ you're right back where you were, on the previously deployed
132
+ version.".cleanup
133
+
134
+ remote_task :rollback do
135
+ if releases.length < 2 then
136
+ abort "could not rollback the code because there is no prior release"
137
+ else
138
+ run "rm -f #{current_path}; ln -s #{previous_release} #{current_path} && rm -rf #{current_release}"
139
+ end
140
+
141
+ Rake::Task['vlad:start'].invoke
142
+ end
143
+
144
+ desc "Clean up old releases. By default, the last 5 releases are kept on
145
+ each server (though you can change this with the keep_releases variable).
146
+ All other deployed revisions are removed from the servers.".cleanup
147
+
148
+ remote_task :cleanup do
149
+ max = keep_releases
150
+ if releases.length <= max then
151
+ puts "no old releases to clean up #{releases.length} <= #{max}"
152
+ else
153
+ puts "keeping #{max} of #{releases.length} deployed releases"
154
+
155
+ directories = (releases - releases.last(max)).map { |release|
156
+ File.join(releases_path, release)
157
+ }.join(" ")
158
+
159
+ run "rm -rf #{directories}"
160
+ end
161
+ end
162
+
163
+ end # namespace vlad
@@ -0,0 +1,86 @@
1
+ class Vlad::Git
2
+
3
+ # Duh.
4
+ VERSION = "2.1.0"
5
+
6
+ set :source, Vlad::Git.new
7
+ set :git_cmd, "git"
8
+
9
+ ##
10
+ # Returns the command that will check out +revision+ from the
11
+ # repository into directory +destination+. +revision+ can be any
12
+ # SHA1 or equivalent (e.g. branch, tag, etc...)
13
+
14
+ def checkout(revision, destination)
15
+ destination = File.join(destination, 'repo')
16
+ revision = 'HEAD' if revision =~ /head/i
17
+
18
+ if fast_checkout_applicable?(revision, destination)
19
+ [ "cd #{destination}",
20
+ "#{git_cmd} checkout -q origin",
21
+ "#{git_cmd} fetch",
22
+ "#{git_cmd} reset --hard #{revision}",
23
+ "#{git_cmd} submodule init",
24
+ "#{git_cmd} submodule update",
25
+ "#{git_cmd} branch -f deployed-#{revision} #{revision}",
26
+ "#{git_cmd} checkout deployed-#{revision}",
27
+ "cd -"
28
+ ].join(" && ")
29
+ else
30
+ [ "rm -rf #{destination}",
31
+ "#{git_cmd} clone #{repository} #{destination}",
32
+ "cd #{destination}",
33
+ "#{git_cmd} submodule init",
34
+ "#{git_cmd} submodule update",
35
+ "#{git_cmd} checkout -f -b deployed-#{revision} #{revision}",
36
+ "cd -"
37
+ ].join(" && ")
38
+ end
39
+ end
40
+
41
+ ##
42
+ # Returns the command that will export +revision+ from the current directory
43
+ # into the directory +destination+.
44
+ # Expects to be run from +scm_path+ after Vlad::Git#checkout
45
+
46
+ def export(revision, destination)
47
+ revision = 'HEAD' if revision =~ /head/i
48
+ revision = "deployed-#{revision}"
49
+
50
+ [ "mkdir -p #{destination}",
51
+ "cd repo",
52
+ "#{git_cmd} archive --format=tar #{revision} | (cd #{destination} && tar xf -)",
53
+ "#{git_cmd} submodule foreach '#{git_cmd} archive --format=tar $sha1 | (cd #{destination}/$path && tar xf -)'",
54
+ "cd -",
55
+ "cd .."
56
+ ].join(" && ")
57
+ end
58
+
59
+ ##
60
+ # Returns a command that maps human-friendly revision identifier +revision+
61
+ # into a git SHA1.
62
+
63
+ def revision(revision)
64
+ revision = 'HEAD' if revision =~ /head/i
65
+
66
+ "`#{git_cmd} rev-parse #{revision}`"
67
+ end
68
+
69
+ private
70
+
71
+ # Checks if fast-checkout is applicable
72
+ def fast_checkout_applicable?(revision, destination)
73
+ revision = 'HEAD' if revision =~ /head/i
74
+
75
+ begin
76
+ cmd = [ "if cd #{destination}",
77
+ "#{git_cmd} rev-parse #{revision}",
78
+ "#{git_cmd} remote -v | grep -q #{repository}",
79
+ "cd -; then exit 0; else exit 1; fi &>/dev/null" ].join(" && ")
80
+ run cmd
81
+ return true
82
+ rescue Vlad::CommandFailedError
83
+ return false
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,20 @@
1
+ require 'vlad'
2
+
3
+ ##
4
+ # See the following documents for recipes:
5
+ #
6
+ # * http://clarkware.com/cgi/blosxom/2007/01/05/CustomMaintenancePages
7
+ # * http://blog.nodeta.fi/2009/03/11/stopping-your-rails-application-with-phusion-passenger/
8
+ #
9
+
10
+ namespace :vlad do
11
+ namespace :maintenance do
12
+ remote_task :on, :roles => [:web] do
13
+ run "cp -f #{shared_path}/config/maintenance.html #{shared_path}/system/"
14
+ end
15
+
16
+ remote_task :off, :roles => [:web] do
17
+ run "rm -f #{shared_path}/system/maintenance.html"
18
+ end
19
+ end
20
+ end