rick-vlad 1.2.0.4

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/core.rb ADDED
@@ -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
data/lib/vlad/git.rb ADDED
@@ -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
@@ -0,0 +1,21 @@
1
+ require 'vlad'
2
+ namespace :vlad do
3
+ set :merb_env, 'production'
4
+
5
+ remote_task :stop_app, :roles => [:app] do
6
+ run "sudo /usr/bin/god stop #{application}"
7
+ end
8
+ remote_task :start_app, :roles => [:app] do
9
+ run "sudo /usr/bin/god restart #{application}"
10
+ end
11
+
12
+ remote_task :symlink_configs, :roles => [:app] do
13
+ run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml && mkdir -p #{release_path}/tmp/cache"
14
+ end
15
+
16
+ namespace :dm do
17
+ remote_task :migrate, :roles => [:db] do
18
+ run "cd #{current_path}; MERB_ENV=#{merb_env} rake dm:db:migrate"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ class Vlad::Mercurial
2
+
3
+ set :source, Vlad::Mercurial.new
4
+
5
+ ##
6
+ # Returns the command that will check out +revision+ from the code repo
7
+ # into directory +destination+
8
+
9
+ def checkout(revision, destination)
10
+ revision = 'tip' if revision =~ /^head$/i
11
+ "hg pull -r #{revision} -R #{destination} #{code_repo}"
12
+ end
13
+
14
+ ##
15
+ # Returns the command that will export +revision+ from the code repo into
16
+ # the directory +destination+.
17
+
18
+ def export(revision_or_source, destination)
19
+ revision_or_source = 'tip' if revision_or_source =~ /^head$/i
20
+ if revision_or_source =~ /^(\d+|tip)$/i then
21
+ "hg archive -r #{revision_or_source} -R #{code_repo} #{destination}"
22
+ else
23
+ "hg archive -R #{revision_or_source} #{destination}"
24
+ end
25
+ end
26
+
27
+ ##
28
+ # Returns a command that maps human-friendly revision identifier +revision+
29
+ # into a subversion revision specification.
30
+
31
+ def revision(revision)
32
+ "`hg identify -R #{code_repo} | cut -f1 -d\\ `"
33
+ end
34
+ end
@@ -0,0 +1,61 @@
1
+ require 'vlad'
2
+
3
+ namespace :vlad do
4
+ ##
5
+ # Mongrel app server
6
+
7
+ set :mongrel_address, "127.0.0.1"
8
+ set :mongrel_clean, false
9
+ set :mongrel_command, 'mongrel_rails'
10
+ set(:mongrel_conf) { "#{shared_path}/mongrel_cluster.conf" }
11
+ set :mongrel_config_script, nil
12
+ set :mongrel_environment, "production"
13
+ set :mongrel_group, nil
14
+ set :mongrel_log_file, nil
15
+ set :mongrel_pid_file, nil
16
+ set :mongrel_port, 8000
17
+ set :mongrel_prefix, nil
18
+ set :mongrel_servers, 2
19
+ set :mongrel_user, nil
20
+
21
+ desc "Prepares application servers for deployment. Mongrel
22
+ configuration is set via the mongrel_* variables.".cleanup
23
+
24
+ remote_task :setup_app, :roles => :app do
25
+ cmd = [
26
+ "#{mongrel_command} cluster::configure",
27
+ "-N #{mongrel_servers}",
28
+ "-p #{mongrel_port}",
29
+ "-e #{mongrel_environment}",
30
+ "-a #{mongrel_address}",
31
+ "-c #{current_path}",
32
+ "-C #{mongrel_conf}",
33
+ ("-P #{mongrel_pid_file}" if mongrel_pid_file),
34
+ ("-l #{mongrel_log_file}" if mongrel_log_file),
35
+ ("--user #{mongrel_user}" if mongrel_user),
36
+ ("--group #{mongrel_group}" if mongrel_group),
37
+ ("--prefix #{mongrel_prefix}" if mongrel_prefix),
38
+ ("-S #{mongrel_config_script}" if mongrel_config_script),
39
+ ].compact.join ' '
40
+
41
+ run cmd
42
+ end
43
+
44
+ def mongrel(cmd) # :nodoc:
45
+ cmd = "#{mongrel_command} #{cmd} -C #{mongrel_conf}"
46
+ cmd << ' --clean' if mongrel_clean
47
+ cmd
48
+ end
49
+
50
+ desc "Restart the app servers"
51
+
52
+ remote_task :start_app, :roles => :app do
53
+ run mongrel("cluster::restart")
54
+ end
55
+
56
+ desc "Stop the app servers"
57
+
58
+ remote_task :stop_app, :roles => :app do
59
+ run mongrel("cluster::stop")
60
+ end
61
+ end
data/lib/vlad/nginx.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'vlad'
2
+
3
+ namespace :vlad do
4
+ ##
5
+ # Nginx web server on Gentoo/Debian init.d systems
6
+
7
+ set :web_command, "/etc/init.d/nginx"
8
+
9
+ desc "(Re)Start the web servers"
10
+
11
+ remote_task :start_web, :roles => :web do
12
+ if use_sudo
13
+ sudo "#{web_command} restart"
14
+ else
15
+ run "#{web_command} restart"
16
+ end
17
+ end
18
+
19
+ desc "Stop the web servers"
20
+
21
+ remote_task :stop_web, :roles => :web do
22
+ if use_sudo
23
+ sudo "#{web_command} stop"
24
+ else
25
+ run "#{web_command} stop"
26
+ end
27
+ end
28
+
29
+ ##
30
+ # Everything HTTP.
31
+
32
+ desc "(Re)Start the web and app servers"
33
+ remote_task :start do
34
+ Rake::Task['vlad:start_app'].invoke
35
+ Rake::Task['vlad:start_web'].invoke
36
+ end
37
+
38
+ desc "Stop the web and app servers"
39
+
40
+ remote_task :stop do
41
+ Rake::Task['vlad:stop_app'].invoke
42
+ Rake::Task['vlad:stop_web'].invoke
43
+ end
44
+ end
@@ -0,0 +1,117 @@
1
+ class Vlad::Perforce
2
+
3
+ set :p4_cmd, "p4"
4
+ set :source, Vlad::Perforce.new
5
+
6
+ ##
7
+ # Returns the p4 command that will checkout +revision+ into the directory
8
+ # +destination+.
9
+
10
+ def checkout(revision, destination)
11
+ "#{p4_cmd} sync ...#{rev_no(revision)}"
12
+ end
13
+
14
+ ##
15
+ # Returns the p4 command that will export +revision+ into the directory
16
+ # +directory+.
17
+
18
+ def export(revision_or_source, destination)
19
+ if revision_or_source =~ /^(\d+|head)$/i then
20
+ "(cd #{destination} && #{p4_cmd} sync ...#{rev_no(revision_or_source)})"
21
+ else
22
+ "cp -r #{revision_or_source} #{destination}"
23
+ end
24
+ end
25
+
26
+ ##
27
+ # Returns a command that maps human-friendly revision identifier +revision+
28
+ # into a Perforce revision specification.
29
+
30
+ def revision(revision)
31
+ "`#{p4_cmd} changes -s submitted -m 1 ...#{rev_no(revision)} | cut -f 2 -d\\ `"
32
+ end
33
+
34
+ ##
35
+ # Maps revision +revision+ into a Perforce revision.
36
+
37
+ def rev_no(revision)
38
+ case revision.to_s
39
+ when /head/i then
40
+ "#head"
41
+ when /^\d+$/ then
42
+ "@#{revision}"
43
+ else
44
+ revision
45
+ end
46
+ end
47
+ end
48
+
49
+ namespace :vlad do
50
+ remote_task :setup_app, :roles => :app do
51
+ p4data = p4port = p4user = p4passwd = nil
52
+
53
+ if ENV['P4CONFIG'] then
54
+ p4config_name = ENV['P4CONFIG']
55
+ p4config = nil
56
+ orig_dir = Dir.pwd.split File::SEPARATOR
57
+
58
+ until orig_dir.length == 1 do
59
+ p4config = orig_dir + [p4config_name]
60
+ p4config = File.join p4config
61
+ break if File.exist? p4config
62
+ orig_dir.pop
63
+ end
64
+
65
+ raise "couldn't find .p4config" unless File.exist? p4config
66
+
67
+ p4data = File.readlines(p4config).map { |line| line.strip.split '=', 2 }
68
+ p4data = Hash[*p4data.flatten]
69
+ else
70
+ p4data = ENV
71
+ end
72
+
73
+ p4port = p4data['P4PORT']
74
+ p4user = p4data['P4USER']
75
+ p4passwd = p4data['P4PASSWD']
76
+
77
+ raise "couldn't get P4PORT" if p4port.nil?
78
+ raise "couldn't get P4USER" if p4user.nil?
79
+ raise "couldn't get P4PASSWD" if p4passwd.nil?
80
+
81
+ p4client = [p4user, target_host, application].join '-'
82
+
83
+ require 'tmpdir'
84
+ require 'tempfile'
85
+
86
+ put File.join(scm_path, '.p4config'), 'vlad.p4config' do
87
+ [ "P4PORT=#{p4port}",
88
+ "P4USER=#{p4user}",
89
+ "P4PASSWD=#{p4passwd}",
90
+ "P4CLIENT=#{p4client}" ].join("\n")
91
+ end
92
+
93
+ p4client_path = File.join deploy_to, 'p4client.tmp'
94
+
95
+ put p4client_path, 'vlad.p4client' do
96
+ conf = <<-"CLIENT"
97
+ Client: #{p4client}
98
+
99
+ Owner: #{p4user}
100
+
101
+ Root: #{scm_path}
102
+
103
+ View:
104
+ #{code_repo}/... //#{p4client}/...
105
+ CLIENT
106
+ end
107
+
108
+ cmds = [
109
+ "cd #{scm_path}",
110
+ "p4 client -i < #{p4client_path}",
111
+ "rm #{p4client_path}",
112
+ ]
113
+
114
+ run cmds.join(' && ')
115
+ end
116
+ end
117
+
@@ -0,0 +1,34 @@
1
+ class Vlad::Subversion
2
+
3
+ set :source, Vlad::Subversion.new
4
+ set :svn_cmd, "svn"
5
+
6
+ ##
7
+ # Returns the command that will check out +revision+ from the code repo
8
+ # into directory +destination+
9
+
10
+ def checkout(revision, destination)
11
+ "#{svn_cmd} co -r #{revision} #{code_repo} #{destination}"
12
+ end
13
+
14
+ ##
15
+ # Returns the command that will export +revision+ from the code repo into
16
+ # the directory +destination+.
17
+
18
+ def export(revision_or_source, destination)
19
+ if revision_or_source =~ /^(\d+|head)$/i then
20
+ "#{svn_cmd} export -r #{revision_or_source} #{code_repo} #{destination}"
21
+ else
22
+ "#{svn_cmd} export #{revision_or_source} #{destination}"
23
+ end
24
+ end
25
+
26
+ ##
27
+ # Returns a command that maps human-friendly revision identifier +revision+
28
+ # into a subversion revision specification.
29
+
30
+ def revision(revision)
31
+ "`#{svn_cmd} info #{code_repo} | grep 'Revision:' | cut -f2 -d\\ `"
32
+ end
33
+ end
34
+