jtrupiano-capistrano-extensions 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,10 +3,13 @@ ISSUES.txt
3
3
  Manifest.txt
4
4
  README.txt
5
5
  Rakefile
6
+ TODO
6
7
  bin/capistrano-extensions-sync-content
7
8
  bin/capistrano-extensions-sync-db
8
9
  capistrano-extensions.gemspec
9
10
  lib/capistrano-extensions.rb
10
11
  lib/capistrano-extensions/deploy.rb
11
12
  lib/capistrano-extensions/geminstaller_dependency.rb
13
+ lib/capistrano-extensions/recipes/content_sync.rb
14
+ lib/capistrano-extensions/recipes/db_sync.rb
12
15
  lib/capistrano-extensions/version.rb
@@ -0,0 +1,32 @@
1
+ = capistrano-extensions
2
+
3
+ * Stable API: http://johntrupiano.rubyforge.org/capistrano-extensions
4
+ * Source: http://github.com/jtrupiano/capistrano-extensions
5
+
6
+ == DESCRIPTION/FEATURES:
7
+
8
+ This gem provides a base set of {Capistrano}[http://www.capify.org/] extensions including the following:
9
+ * a new :gemfile RemoteDependency type
10
+ * tasks/helpers for handling public and private asset folders (e.g. created by the file_column plugin)
11
+ * tasks/helpers for auto-syncing server gems (via integration with the {GemInstaller}[http://geminstaller.rubyforge.org/] gem)
12
+ * helpers for dealing with multiple deployable environments (e.g. staging, prodtest, production)
13
+ * tasks for working with remote logfiles
14
+ * tasks for database/asset synchronization from production back to local environments
15
+ * integration with {environmentalist}[http://johntrupiano.rubyforge.org/environmentalist]
16
+
17
+ For a detailed exploration of these features, check out the wiki: http://github.com/jtrupiano/capistrano-extensions/wikis/home
18
+
19
+ == SYNOPSIS:
20
+
21
+ FIX (code sample of usage)
22
+
23
+ == REQUIREMENTS:
24
+
25
+ * Capistrano ~> 2.5.5
26
+ * GemInstaller ~> 0.5.1
27
+
28
+ == INSTALL:
29
+
30
+ * sudo gem install capistrano-extensions (stable from rubyforge)
31
+ * sudo gem install jtrupiano-capistrano-extensions (HEAD of repo from github)
32
+
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 7
@@ -1,9 +1,8 @@
1
1
  require 'capistrano-extensions/geminstaller_dependency'
2
- require 'capistrano/server_definition'
3
2
 
4
3
  # Overrides the majority of recipes from Capistrano's deploy recipe set.
5
4
  Capistrano::Configuration.instance(:must_exist).load do
6
- # Add sls_recipes to the load path
5
+ # Add us to the load path
7
6
  @load_paths << File.expand_path(File.dirname(__FILE__))
8
7
 
9
8
  # ========================================================================
@@ -27,10 +26,10 @@ Capistrano::Configuration.instance(:must_exist).load do
27
26
  # changes if you do decide to muck with these!
28
27
  # =========================================================================
29
28
 
30
- set(:use_sudo, false) # we don't want to use sudo-- we don't have to!
31
- set(:deploy_via, :export) # we don't want our .svn folders on the server!
32
- _cset(:deploy_to) { "/var/www/vhosts/#{application}" }
33
- _cset(:deployable_environments, [:production])
29
+ set(:use_sudo, false) # we don't want to use sudo-- we don't have to!
30
+ set(:deploy_via, :copy) # no need to have subversion on the production server
31
+ _cset(:deploy_to) { "/var/vhosts/#{application}" }
32
+ _cset(:deployable_environments, [:staging])
34
33
 
35
34
  _cset(:rails_config_path) { File.join(latest_release, 'config') }
36
35
  _cset(:db_conf) {
@@ -45,11 +44,36 @@ Capistrano::Configuration.instance(:must_exist).load do
45
44
  _cset(:public_path) { File.join(latest_release, 'public') }
46
45
  _cset(:log_path) { "/var/log/#{application}" }
47
46
 
47
+ # Local Properties
48
+ _cset(:tmp_dir, "tmp/cap")
49
+ # when local:syncing, should we keep backups just in case of failure?
50
+ _cset(:store_dev_backups, false)
51
+ # how long to allow remote backups to be valid (at both cache levels)
52
+ _cset(:remote_backup_expires, 172800) # 2 days in seconds.
53
+ # when remote:syncing, should we keep backups just in case of failure?
54
+ _cset(:store_remote_backups, true)
55
+ # paths to exclude during deployment
56
+ _cset(:exclude_paths, [])
57
+
58
+ _cset(:copy_cache) { File.expand_path("~/.capistrano/#{application}") }
59
+ set(:copy_exclude) {
60
+ # don't deploy the other environment directories
61
+ envs = fetch(:deployable_environments).dup
62
+ envs.delete_if { |env| rails_env.to_sym == env.to_sym }
63
+ envs.map! { |env| File.join("config", "#{env}") }
64
+
65
+ envs + fetch(:exclude_paths)
66
+ }
67
+
68
+ _cset(:zip, "gzip")
69
+ _cset(:unzip, "gunzip")
70
+ _cset(:zip_ext, "gz")
71
+
48
72
  # Allow recipes to ask for a certain local environment
49
73
  def local_db_conf(env = nil)
50
74
  env ||= fetch(:rails_env)
51
75
  fetch(:config_structure, :rails).to_sym == :sls ?
52
- File.join('config', env, 'database.yml') :
76
+ File.join('config', env.to_s, 'database.yml') :
53
77
  File.join('config', 'database.yml')
54
78
  end
55
79
 
@@ -79,9 +103,8 @@ Capistrano::Configuration.instance(:must_exist).load do
79
103
  # Now, let's actually include our common recipes!
80
104
  namespace :deploy do
81
105
  desc <<-DESC
82
- [capistrano-extensions] Creates shared directories and symbolic links to them by the
83
- :content_directories and :shared_content properties. See the README for
84
- further explanation.
106
+ [capistrano-extensions] Creates shared directories and symbolic links to them by reading the
107
+ :content_directories and :shared_content properties. See the README for further explanation.
85
108
  DESC
86
109
  task :create_shared_file_column_dirs, :roles => :app, :except => { :no_release => true } do
87
110
  mappings = content_directories.inject(shared_content) { |hsh, dir| hsh.merge({"content/#{dir}" => "public/#{dir}"}) }
@@ -115,88 +138,17 @@ Capistrano::Configuration.instance(:must_exist).load do
115
138
  filesystem
116
139
  DESC
117
140
  task :pull do
118
- tmp_location = "#{shared_path}/#{rails_env}.log.gz"
119
- run "cp #{log_path}/#{rails_env}.log #{shared_path}/ && gzip #{shared_path}/#{rails_env}.log"
120
- get "#{tmp_location}", "#{application}-#{rails_env}.log.gz"
141
+ tmp_location = "#{shared_path}/#{rails_env}.log.#{zip_ext}"
142
+ run "cp #{log_path}/#{rails_env}.log #{shared_path}/ && #{zip} #{shared_path}/#{rails_env}.log"
143
+ get "#{tmp_location}", "#{application}-#{rails_env}.log.#{zip_ext}"
121
144
  run "rm #{tmp_location}"
122
145
  end
123
146
  end
124
147
 
148
+ load 'recipes/db_sync'
149
+ load 'recipes/content_sync'
150
+
125
151
  namespace :remote do
126
- desc <<-DESC
127
- [capistrano-extensions] Uploads the backup file downloaded from local:backup_db (specified via the FROM env variable),
128
- copies it to the remove environment specified by RAILS_ENV, and imports (via mysql command line tool) it back into the
129
- remote database.
130
- DESC
131
- task :restore_db, :roles => :db do
132
- env = ENV['FROM'] || 'production'
133
-
134
- puts "\033[1;41m Restoring database backup to #{rails_env} environment \033[0m"
135
- if deployable_environments.include?(rails_env.to_sym)
136
- # remote environment
137
- local_backup_file = "#{application}-#{env}-db.sql.gz"
138
- remote_file = "#{shared_path}/restore_db.sql"
139
- if !File.exists?(local_backup_file)
140
- puts "Could not find backup file: #{local_backup_file}"
141
- exit 1
142
- end
143
- upload(local_backup_file, "#{remote_file}.gz")
144
-
145
- pass_str = pluck_pass_str(db)
146
- run "gunzip -f #{remote_file}.gz"
147
- run "mysql -u#{db['username']} #{pass_str} #{db['database']} < #{remote_file}"
148
- run "rm -f #{remote_file}"
149
- end
150
- end
151
-
152
- desc <<-DESC
153
- [capistrano-extensions]: Backs up target deployable environment's database (identified
154
- by the FROM environment variable, which defaults to 'production') and restores it to
155
- the remote database identified by the TO environment variable, which defaults to "staging."
156
- DESC
157
- task :sync_db do
158
- system("capistrano-extensions-sync-db #{ENV['FROM'] || 'production'} #{ENV['TO'] || 'staging'}")
159
- end
160
-
161
- desc <<-DESC
162
- [capistrano-extensions]: Uploads the backup file downloaded from local:backup_content (specified via the
163
- FROM env variable), copies it to the remote environment specified by RAILS_ENV, and unpacks it into the
164
- shared/ directory.
165
- DESC
166
- task :restore_content do
167
- from = ENV['FROM'] || 'production'
168
-
169
- if deployable_environments.include?(rails_env.to_sym)
170
- local_backup_file = "#{application}-#{from}-content_backup.tar.gz"
171
- remote_file = "#{shared_path}/content_backup.tar.gz"
172
-
173
- if !File.exists?(local_backup_file)
174
- puts "Could not find backup file: #{local_backup_file}"
175
- exit 1
176
- end
177
-
178
- upload(local_backup_file, "#{remote_file}")
179
- remote_dirs = ["content"] + shared_content.keys
180
-
181
- run("cd #{shared_path} && rm -rf #{remote_dirs.join(' ')} && tar xzf #{remote_file} -C #{shared_path}/")
182
- end
183
- end
184
-
185
- desc <<-DESC
186
- [capistrano-extensions]: Backs up target deployable environment's shared content (identified by the FROM environment
187
- variable, which defaults to 'production') and restores it to the remote environment identified
188
- by the TO envrionment variable, which defaults to "staging."
189
-
190
- Because multiple capistrano configurations must be loaded, an external executable
191
- (capistrano-extensions-sync_content) is invoked, which independently calls capistrano. See the
192
- executable at $GEM_HOME/capistrano-extensions-0.1.2/bin/capistrano-extensions-sync_content
193
-
194
- $> cap remote:sync_content FROM=production TO=staging
195
- DESC
196
- task :sync_content do
197
- system("capistrano-extensions-sync-content #{ENV['FROM'] || 'production'} #{ENV['TO'] || 'staging'}")
198
- end
199
-
200
152
  desc <<-DESC
201
153
  [capistrano-extensions]: Wrapper fro remote:sync_db and remote:sync_content.
202
154
  $> cap remote:sync FROM=production TO=staging
@@ -209,116 +161,42 @@ Capistrano::Configuration.instance(:must_exist).load do
209
161
 
210
162
  namespace :local do
211
163
  desc <<-DESC
212
- [capistrano-extensions]: Backs up deployable environment's database (identified by the
213
- RAILS_ENV environment variable, which defaults to 'production') and copies it to the local machine
214
- DESC
215
- task :backup_db, :roles => :db do
216
- pass_str = pluck_pass_str(db)
217
-
218
- run "mysqldump -u#{db['username']} #{pass_str} #{db['database']} > #{shared_path}/db_backup.sql"
219
- run "gzip #{shared_path}/db_backup.sql"
220
- get "#{shared_path}/db_backup.sql.gz", "#{application}-#{rails_env}-db.sql.gz"
221
- run "rm -f #{shared_path}/db_backup.sql.gz #{shared_path}/db_backup.sql"
222
- end
223
-
224
- desc <<-DESC
225
- [capistrano-extensions] Untars the backup file downloaded from local:backup_db (specified via the FROM env
226
- variable, which defalts to RAILS_ENV), and imports (via mysql command line tool) it back into the database
227
- defined in the RAILS_ENV env variable.
228
-
229
- ToDo: implement proper rollback: currently, if the mysql import succeeds, but the rm fails,
230
- the database won't be rolled back. Not sure this is even all that important or necessary, since
231
- it's a local database that doesn't demand integrity (in other words, you're still going to have to
232
- fix it, but it's not mission critical).
233
- DESC
234
- task :restore_db, :roles => :db do
235
- on_rollback { "gzip #{application}-#{from}-db.sql"}
236
-
237
- from = ENV['FROM'] || rails_env
238
-
239
- env = ENV['RESTORE_ENV'] || 'development'
240
- y = YAML.load_file(local_db_conf(env))[env]
241
- db, user = y['database'], (y['username'] || 'root') # update me!
242
-
243
- pass_str = pluck_pass_str(y)
244
-
245
- puts "\033[1;41m Restoring database backup to #{env} environment \033[0m"
246
- # local
247
- system <<-CMD
248
- gunzip #{application}-#{from}-db.sql.gz &&
249
- mysql -u#{user} #{pass_str} #{db} < #{application}-#{from}-db.sql
250
- CMD
251
- end
252
-
253
- desc <<-DESC
254
- [capistrano-extensions]: Downloads a tarball of uploaded content (that lives in public/
255
- directory, as specified by the :content_directories property) from the production site
256
- back to the local filesystem
164
+ [capistrano-extensions]: Wrapper for local:sync_db and local:sync_content
165
+ $> cap local:sync RAILS_ENV=production RESTORE_ENV=development
257
166
  DESC
258
- task :backup_content do
259
- folders = ["content"] + shared_content.keys
260
-
261
- run "cd #{shared_path} && tar czf #{shared_path}/content_backup.tar.gz #{folders.join(' ')}"
262
-
263
- #run "cd #{content_path} && tar czf #{shared_path}/content_backup.tar.gz *"
264
- download("#{shared_path}/content_backup.tar.gz", "#{application}-#{rails_env}-content_backup.tar.gz")
265
- run "rm -f #{shared_path}/content_backup.tar.gz"
167
+ task :sync do
168
+ sync_db
169
+ sync_content
266
170
  end
171
+ end
172
+
173
+ namespace :util do
267
174
 
268
- desc <<-DESC
269
- [capistrano-extensions]: Restores the backed up content (evn var FROM specifies which environment
270
- was backed up, defaults to RAILS_ENV) to the local development environment app
271
- DESC
272
- task :restore_content do
273
- from = ENV['FROM'] || rails_env
274
-
275
- system "mkdir -p tmp/content-#{from}"
276
- system "tar xzf #{application}-#{from}-content_backup.tar.gz -C tmp/content-#{from}"
277
- system "rm -f #{application}-#{from}-content_backup.tar.gz"
278
-
279
- shared_content.each_pair do |remote, local|
280
- system "rm -rf #{local} && mv tmp/content-#{from}/#{remote} #{local}"
175
+ namespace :tmp do
176
+ desc "[capistrano-extensions]: Displays warning if :tmp_dir has more than 10 files or is greater than 50MB"
177
+ task :check do
178
+ #[ 5 -le "`ls -1 tmp/cap | wc -l`" ] && echo "Display Me"
179
+ cmd = %Q{ [ 10 -le "`ls -1 #{tmp_dir} | wc -l`" ] || [ 50 -le "`du -sh #{tmp_dir} | awk '{print int($1)}'`" ] && printf "\033[1;41m Clean up #{tmp_dir} directory \033[0m\n" && du -sh #{tmp_dir}/* }
180
+ system(cmd)
281
181
  end
282
182
 
283
- content_directories.each do |public_dir|
284
- system "rm -rf public/#{public_dir}"
285
- system "mv tmp/content-#{from}/content/#{public_dir} public/"
286
- end
287
-
288
- end
289
-
290
- desc <<-DESC
291
- [capistrano-extensions]: Wrapper for local:backup_db and local:restore_db.
292
- $> cap local:sync_db RAILS_ENV=production RESTORE_ENV=development
293
- DESC
294
- task :sync_db do
295
- transaction do
296
- backup_db
297
- ENV['FROM'] = rails_env
298
- restore_db
299
- end
300
- end
301
-
302
- desc <<-DESC
303
- [capistrano-extensions]: Wrapper for local:backup_content and local:restore_content
304
- $> cap local:sync_content RAILS_ENV=production RESTORE_ENV=development
305
- DESC
306
- task :sync_content do
307
- transaction do
308
- backup_content
309
- restore_content
183
+ desc "[capistrano-extensions]: Remove the current remote env's backups from :tmp_dir"
184
+ task :clean_remote do
185
+ system("rm -f #{tmp_dir}/#{application}-#{rails_env}*")
310
186
  end
311
- end
312
187
 
313
- desc <<-DESC
314
- [capistrano-extensions]: Wrapper for local:sync_db and local:sync_content
315
- $> cap local:sync RAILS_ENV=production RESTORE_ENV=development
316
- DESC
317
- task :sync do
318
- sync_db
319
- sync_content
188
+ # desc "Removes all but a single backup from :tmp_dir"
189
+ # task :clean do
190
+ #
191
+ # end
192
+ #
193
+ # desc "Removes all tmp files from :tmp_dir"
194
+ # task :remove do
195
+ #
196
+ # end
320
197
  end
321
198
  end
199
+
322
200
  end
323
201
 
324
202
  def pluck_pass_str(db_config)
@@ -327,4 +205,42 @@ def pluck_pass_str(db_config)
327
205
  pass_str = "-p#{pass_str.gsub('$', '\$')}"
328
206
  end
329
207
  pass_str || ''
330
- end
208
+ end
209
+
210
+ module LocalUtils
211
+ def current_timestamp
212
+ @current_timestamp ||= Time.now.to_i
213
+ end
214
+
215
+ def local_db_backup_file(args = {})
216
+ env = args[:env] || rails_env
217
+ timestamp = args[:timestamp] || current_timestamp
218
+ "#{tmp_dir}/#{application}-#{env}-db-#{timestamp}.sql"
219
+ end
220
+
221
+ def local_content_backup_dir(args={})
222
+ env = args[:env] || rails_env
223
+ timestamp = args[:timestamp] || current_timestamp
224
+ "#{tmp_dir}/#{application}-#{env}-content-#{timestamp}"
225
+ end
226
+
227
+ def retrieve_local_files(env, type)
228
+ `ls -r #{tmp_dir} | awk -F"-" '{ if ($2 ~ /#{env}/ && $3 ~ /#{type}/) { print $4; } }'`.split(' ')
229
+ end
230
+
231
+ def most_recent_local_backup(env, type)
232
+ retrieve_local_files(env, type).first.to_i
233
+ end
234
+ end
235
+
236
+ module RemoteUtils
237
+ def last_mod_time(path)
238
+ capture("stat -c%Y #{path}").to_i
239
+ end
240
+
241
+ def server_cache_valid?(path)
242
+ capture("[ -f #{path} ] || echo '1'").empty? && ((Time.now.to_i - last_mod_time(path)) <= remote_backup_expires) # two days in seconds
243
+ end
244
+ end
245
+
246
+ include LocalUtils, RemoteUtils
@@ -0,0 +1,103 @@
1
+ namespace :remote do
2
+ desc <<-DESC
3
+ [capistrano-extensions]: Uploads the backup file downloaded from local:backup_content (specified via the
4
+ FROM env variable), copies it to the remote environment specified by RAILS_ENV, and unpacks it into the
5
+ shared/ directory.
6
+ DESC
7
+ task :restore_content do
8
+ from = ENV['FROM'] || 'production'
9
+
10
+ if deployable_environments.include?(rails_env.to_sym)
11
+ generate_remote_content_backup if store_remote_backups
12
+
13
+ local_backup_file = local_content_backup_dir(:timestamp => most_recent_local_backup(from, 'content'), :env => from) + ".tar.#{zip_ext}"
14
+ remote_dir = "#{shared_path}/restore_#{from}_content"
15
+ remote_file = "#{remote_dir}.tar.#{zip_ext}"
16
+
17
+ if !File.exists?(local_backup_file)
18
+ puts "Could not find backup file: #{local_backup_file}"
19
+ exit 1
20
+ end
21
+
22
+ upload(local_backup_file, "#{remote_file}")
23
+ remote_dirs = [content_dir] + shared_content.keys
24
+
25
+ run("cd #{shared_path} && rm -rf #{remote_dirs.join(' ')} && tar xzf #{remote_file} -C #{shared_path}/")
26
+ end
27
+ end
28
+
29
+ desc <<-DESC
30
+ [capistrano-extensions]: Backs up remote server's shared content and restores it to a separate remote server.
31
+ $> cap remote:sync_content FROM=production TO=staging
32
+ DESC
33
+ task :sync_content do
34
+ system("capistrano-extensions-sync-content #{ENV['FROM'] || 'production'} #{ENV['TO'] || 'staging'}")
35
+ end
36
+ end
37
+
38
+ namespace :local do
39
+ desc <<-DESC
40
+ [capistrano-extensions]: Downloads a tarball of shared content (identified by the :shared_content and
41
+ :content_directories properties) from a deployable environment (RAILS_ENV) to the local filesystem.
42
+ DESC
43
+ task :backup_content do
44
+ # sort by last alphabetically (forcing the most recent timestamp to the top)
45
+ files = retrieve_local_files(rails_env, 'content')
46
+
47
+ if files.empty?
48
+ # pull it from the server
49
+ generate_remote_content_backup unless server_cache_valid?(content_backup_file)
50
+ system("mkdir -p #{tmp_dir}")
51
+ download(content_backup_file, "#{local_content_backup_dir}.tar.#{zip_ext}")
52
+ else
53
+ # set us up to use our local cache
54
+ @current_timestamp = files.first.to_i # actually has the extension hanging off of it, but shouldn't be a problem
55
+ end
56
+ # Notify user if :tmp_dir is too large
57
+ util::tmp::check
58
+ end
59
+
60
+ desc <<-DESC
61
+ [capistrano-extensions]: Restores the backed up content (env var FROM specifies which environment
62
+ was backed up, defaults to RAILS_ENV) to the local development environment app
63
+ DESC
64
+ task :restore_content do
65
+ from = ENV['FROM'] || rails_env
66
+
67
+ local_dir = local_content_backup_dir(:env => from)
68
+ system "mkdir -p #{local_dir}"
69
+ system "tar xzf #{local_dir}.tar.#{zip_ext} -C #{local_dir}"
70
+
71
+ shared_content.each_pair do |remote, local|
72
+ system "rm -rf #{local} && mv #{local_dir}/#{remote} #{local}"
73
+ end
74
+
75
+ content_directories.each do |public_dir|
76
+ system "rm -rf public/#{public_dir}"
77
+ system "mv #{local_dir}/content/#{public_dir} public/"
78
+ end
79
+
80
+ system "rm -rf #{local_dir}"
81
+ end
82
+
83
+
84
+ desc <<-DESC
85
+ [capistrano-extensions]: Wrapper for local:backup_content and local:restore_content
86
+ $> cap local:sync_content RAILS_ENV=production RESTORE_ENV=development
87
+ DESC
88
+ task :sync_content do
89
+ transaction do
90
+ backup_content
91
+ restore_content
92
+ end
93
+ end
94
+ end
95
+
96
+ def content_backup_file
97
+ "#{shared_path}/backup_#{rails_env}_content.tar.#{zip_ext}"
98
+ end
99
+
100
+ def generate_remote_content_backup
101
+ folders = [content_dir] + shared_content.keys
102
+ run "cd #{shared_path} && tar czf #{content_backup_file} #{folders.join(' ')}"
103
+ end
@@ -0,0 +1,136 @@
1
+ namespace :remote do
2
+ desc <<-DESC
3
+ [capistrano-extensions] Uploads the backup file downloaded from local:backup_db (specified via the FROM env variable),
4
+ copies it to the remote environment specified by RAILS_ENV, and imports (via mysql command line tool) it back into the
5
+ remote database.
6
+ DESC
7
+ task :restore_db, :roles => :db do
8
+ env = ENV['FROM'] || 'production'
9
+
10
+ puts "\033[1;41m Restoring database backup to #{rails_env} environment \033[0m"
11
+ if deployable_environments.include?(rails_env.to_sym)
12
+ generate_remote_db_backup if store_remote_backups
13
+
14
+ # remote environment
15
+ local_backup_file = local_db_backup_file(:timestamp => most_recent_local_backup(env, 'db'), :env => env) + ".#{zip_ext}"
16
+ remote_file = "#{shared_path}/restore_#{env}_db.sql"
17
+
18
+ if !File.exists?(local_backup_file)
19
+ puts "Could not find backup file: #{local_backup_file}"
20
+ exit 1
21
+ end
22
+ upload(local_backup_file, "#{remote_file}.#{zip_ext}")
23
+
24
+ pass_str = pluck_pass_str(db)
25
+ run "#{unzip} -c #{remote_file}.#{zip_ext} > #{remote_file}"
26
+ run "mysql -u#{db['username']} #{pass_str} #{db['database']} < #{remote_file}"
27
+ run "rm -f #{remote_file}"
28
+ end
29
+ end
30
+
31
+ desc <<-DESC
32
+ [capistrano-extensions]: Backs up target deployable environment's database (identified
33
+ by the FROM environment variable, which defaults to 'production') and restores it to
34
+ the remote database identified by the TO environment variable, which defaults to "staging."
35
+ DESC
36
+ task :sync_db do
37
+ system("capistrano-extensions-sync-db #{ENV['FROM'] || 'production'} #{ENV['TO'] || 'staging'}")
38
+ end
39
+ end
40
+
41
+ namespace :local do
42
+ desc <<-DESC
43
+ [capistrano-extensions]: Backs up deployable environment's database (identified by the
44
+ RAILS_ENV environment variable, which defaults to 'production') and copies it to the local machine
45
+ DESC
46
+ task :backup_db, :roles => :db do
47
+
48
+ # sort by last alphabetically (forcing the most recent timestamp to the top)
49
+ files = retrieve_local_files(rails_env, 'db')
50
+
51
+ if files.empty?
52
+ # pull it from the server
53
+ generate_remote_db_backup unless server_cache_valid?(db_backup_zip_file)
54
+ system "mkdir -p #{tmp_dir}"
55
+ download(db_backup_zip_file, "#{local_db_backup_file}.#{zip_ext}")
56
+ else
57
+ # set us up to use our local cache
58
+ @current_timestamp = files.first.to_i # actually has the extension hanging off of it, but shouldn't be a problem
59
+ end
60
+ end
61
+
62
+ desc <<-DESC
63
+ [capistrano-extensions] Untars the backup file downloaded from local:backup_db (specified via the FROM env
64
+ variable, which defalts to RAILS_ENV), and imports (via mysql command line tool) it back into the database
65
+ defined in the RESTORE_ENV env variable (defaults to development).
66
+ DESC
67
+ task :restore_db, :roles => :db do
68
+ from = ENV['FROM'] || rails_env
69
+ env = ENV['RESTORE_ENV'] || 'development'
70
+
71
+ y = YAML.load_file(local_db_conf(env))[env]
72
+ db, user = y['database'], (y['username'] || 'root') # update me!
73
+
74
+ pass_str = pluck_pass_str(y)
75
+ mysql_str = "mysql -u#{user} #{pass_str} #{db}"
76
+ mysql_dump = "mysqldump --add-drop-database -u#{user} #{pass_str} #{db}"
77
+
78
+ local_backup_file = local_db_backup_file(:env => env)
79
+ remote_backup_file = local_db_backup_file(:env => from)
80
+
81
+ puts "\033[1;41m Restoring database backup to #{env} environment \033[0m"
82
+
83
+ # local
84
+ cmd = ""
85
+ if store_dev_backups
86
+ cmd << <<-CMD
87
+ mkdir -p #{tmp_dir} &&
88
+ #{mysql_dump} | #{zip} > #{local_backup_file}.#{zip_ext} &&
89
+ CMD
90
+ end
91
+ cmd << <<-CMD
92
+ #{unzip} -c #{remote_backup_file}.#{zip_ext} > #{remote_backup_file} &&
93
+ #{mysql_str} < #{remote_backup_file} &&
94
+ rm -f #{remote_backup_file}
95
+ CMD
96
+ system(cmd.strip)
97
+
98
+ # Notify user if :tmp_dir is too large
99
+ util::tmp::check
100
+ end
101
+
102
+ desc <<-DESC
103
+ [capistrano-extensions]: Wrapper for local:backup_db and local:restore_db.
104
+ $> cap local:sync_db RAILS_ENV=production RESTORE_ENV=development
105
+ DESC
106
+ task :sync_db do
107
+ transaction do
108
+ backup_db
109
+ ENV['FROM'] = rails_env
110
+ restore_db
111
+ end
112
+ end
113
+
114
+ desc <<-DESC
115
+ [capistrano-extensions]: Ensure that a fresh remote data dump is retrieved before syncing to the local environment.
116
+ DESC
117
+ task :resync_db do
118
+ util::tmp::clean_remote
119
+ sync_db
120
+ end
121
+
122
+ end
123
+
124
+ def db_backup_file
125
+ "#{shared_path}/backup_#{rails_env}_db.sql"
126
+ end
127
+
128
+ def db_backup_zip_file
129
+ "#{db_backup_file}.#{zip_ext}"
130
+ end
131
+
132
+ def generate_remote_db_backup
133
+ pass_str = pluck_pass_str(db)
134
+ run "mysqldump --add-drop-database -u#{db['username']} #{pass_str} #{db['database']} > #{db_backup_file}"
135
+ run "rm -f #{db_backup_zip_file} && #{zip} #{db_backup_file} && rm -f #{db_backup_file}"
136
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jtrupiano-capistrano-extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Trupiano
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-11 00:00:00 -07:00
12
+ date: 2009-04-27 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -32,16 +32,6 @@ dependencies:
32
32
  - !ruby/object:Gem::Version
33
33
  version: 0.5.1
34
34
  version:
35
- - !ruby/object:Gem::Dependency
36
- name: hoe
37
- type: :development
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: 1.9.0
44
- version:
45
35
  description: A base set of Capistrano extensions-- aids with the file_column plugin, the GemInstaller gem, multiple deployable environments, logfile helpers, and database/asset synchronization from production to local environment
46
36
  email: jtrupiano@gmail.com
47
37
  executables:
@@ -50,29 +40,28 @@ executables:
50
40
  extensions: []
51
41
 
52
42
  extra_rdoc_files:
53
- - History.txt
54
- - ISSUES.txt
55
- - Manifest.txt
56
- - README.txt
43
+ - README.rdoc
57
44
  files:
58
45
  - History.txt
59
46
  - ISSUES.txt
60
47
  - Manifest.txt
61
- - README.txt
62
- - Rakefile
48
+ - README.rdoc
49
+ - VERSION.yml
63
50
  - bin/capistrano-extensions-sync-content
64
51
  - bin/capistrano-extensions-sync-db
65
- - capistrano-extensions.gemspec
66
- - lib/capistrano-extensions.rb
52
+ - lib/capistrano-extensions
67
53
  - lib/capistrano-extensions/deploy.rb
68
54
  - lib/capistrano-extensions/geminstaller_dependency.rb
69
- - lib/capistrano-extensions/version.rb
55
+ - lib/capistrano-extensions/recipes
56
+ - lib/capistrano-extensions/recipes/content_sync.rb
57
+ - lib/capistrano-extensions/recipes/db_sync.rb
58
+ - lib/capistrano-extensions.rb
70
59
  has_rdoc: true
71
60
  homepage: http://github.com/jtrupiano/capistrano-extensions
72
61
  post_install_message:
73
62
  rdoc_options:
74
- - --main
75
- - README.txt
63
+ - --inline-source
64
+ - --charset=UTF-8
76
65
  require_paths:
77
66
  - lib
78
67
  required_ruby_version: !ruby/object:Gem::Requirement
data/README.txt DELETED
@@ -1,55 +0,0 @@
1
- = capistrano-extensions
2
-
3
- * Stable API: http://johntrupiano.rubyforge.org/capistrano-extensions
4
- * Source: http://github.com/jtrupiano/capistrano-extensions
5
-
6
- == DESCRIPTION/FEATURES:
7
-
8
- This gem provides a base set of Capistrano extensions including the following:
9
- * a new :gemfile RemoteDependency type
10
- * tasks/helpers for handling public and private asset folders (e.g. created by the file_column plugin)
11
- * tasks/helpers for auto-syncing server gems (via integration with Chad Wooley's GemInstaller gem)
12
- * helpers for dealing with multiple deployable environments (e.g. staging, prodtest, production)
13
- * tasks for working with remote logfiles
14
- * tasks for database/asset synchronization from production back to local environments
15
-
16
- For a detailed exploration of these features, check out the wiki: http://github.com/jtrupiano/capistrano-extensions/wikis/home
17
-
18
- == SYNOPSIS:
19
-
20
- FIX (code sample of usage)
21
-
22
- == REQUIREMENTS:
23
-
24
- * Capistrano ~> 2.5.5
25
- * GemInstaller ~> 0.5.1
26
-
27
- == INSTALL:
28
-
29
- * sudo gem install capistrano-extensions (stable from rubyforge)
30
- * sudo gem install jtrupiano-capistrano-extensions (HEAD of repo from github)
31
-
32
- == LICENSE:
33
-
34
- (The MIT License)
35
-
36
- Copyright (c) 2008 FIX
37
-
38
- Permission is hereby granted, free of charge, to any person obtaining
39
- a copy of this software and associated documentation files (the
40
- 'Software'), to deal in the Software without restriction, including
41
- without limitation the rights to use, copy, modify, merge, publish,
42
- distribute, sublicense, and/or sell copies of the Software, and to
43
- permit persons to whom the Software is furnished to do so, subject to
44
- the following conditions:
45
-
46
- The above copyright notice and this permission notice shall be
47
- included in all copies or substantial portions of the Software.
48
-
49
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
50
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
51
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
52
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
53
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
54
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
55
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile DELETED
@@ -1,35 +0,0 @@
1
- # -*- ruby -*-
2
-
3
- require 'rubygems'
4
- require 'hoe'
5
- require './lib/capistrano-extensions.rb'
6
- require "./lib/capistrano-extensions/version"
7
-
8
- PKG_NAME = "capistrano-extensions"
9
- PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
- version = CapistranoExtensions::Version::STRING.dup
11
- if ENV['SNAPSHOT'].to_i == 1
12
- version << "." << Time.now.utc.strftime("%Y%m%d%H%M%S")
13
- end
14
- PKG_VERSION = version
15
- PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
16
-
17
- Hoe.new(PKG_NAME, PKG_VERSION) do |p|
18
- p.rubyforge_name = 'johntrupiano' # if different than lowercase project name
19
- p.developer('John Trupiano', 'jtrupiano@gmail.com')
20
- p.name = PKG_NAME
21
- p.version = PKG_VERSION
22
- #p.platform = Gem::Platform::RUBY
23
- p.author = "John Trupiano"
24
- p.email = "jtrupiano@gmail.com"
25
- p.description = %q(A base set of Capistrano extensions-- aids with the file_column plugin, the GemInstaller gem, multiple deployable environments, logfile helpers, and database/asset synchronization from production to local environment)
26
- p.summary = p.description # More details later??
27
- p.remote_rdoc_dir = PKG_NAME # Release to /PKG_NAME
28
- # p.changes = p.paragraphs_of('CHANGELOG', 0..1).join("\n\n")
29
- p.extra_deps << ["capistrano", "~> 2.5.5"]
30
- p.extra_deps << ["geminstaller", "~> 0.5.1"]
31
- p.need_zip = true
32
- p.need_tar = false
33
- end
34
-
35
- # vim: syntax=Ruby
@@ -1,41 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- Gem::Specification.new do |s|
4
- s.name = %q{capistrano-extensions}
5
- s.version = "0.1.6"
6
-
7
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
- s.authors = ["John Trupiano"]
9
- s.date = %q{2009-03-11}
10
- s.description = %q{A base set of Capistrano extensions-- aids with the file_column plugin, the GemInstaller gem, multiple deployable environments, logfile helpers, and database/asset synchronization from production to local environment}
11
- s.email = %q{jtrupiano@gmail.com}
12
- s.executables = ["capistrano-extensions-sync-content", "capistrano-extensions-sync-db"]
13
- s.extra_rdoc_files = ["History.txt", "ISSUES.txt", "Manifest.txt", "README.txt"]
14
- s.files = ["History.txt", "ISSUES.txt", "Manifest.txt", "README.txt", "Rakefile", "bin/capistrano-extensions-sync-content", "bin/capistrano-extensions-sync-db", "capistrano-extensions.gemspec", "lib/capistrano-extensions.rb", "lib/capistrano-extensions/deploy.rb", "lib/capistrano-extensions/geminstaller_dependency.rb", "lib/capistrano-extensions/version.rb"]
15
- s.has_rdoc = true
16
- s.homepage = %q{http://github.com/jtrupiano/capistrano-extensions}
17
- s.rdoc_options = ["--main", "README.txt"]
18
- s.require_paths = ["lib"]
19
- s.rubyforge_project = %q{johntrupiano}
20
- s.rubygems_version = %q{1.3.1}
21
- s.summary = %q{A base set of Capistrano extensions-- aids with the file_column plugin, the GemInstaller gem, multiple deployable environments, logfile helpers, and database/asset synchronization from production to local environment}
22
-
23
- if s.respond_to? :specification_version then
24
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
- s.specification_version = 2
26
-
27
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
- s.add_runtime_dependency(%q<capistrano>, ["~> 2.5.5"])
29
- s.add_runtime_dependency(%q<geminstaller>, ["~> 0.5.1"])
30
- s.add_development_dependency(%q<hoe>, [">= 1.9.0"])
31
- else
32
- s.add_dependency(%q<capistrano>, ["~> 2.5.5"])
33
- s.add_dependency(%q<geminstaller>, ["~> 0.5.1"])
34
- s.add_dependency(%q<hoe>, [">= 1.9.0"])
35
- end
36
- else
37
- s.add_dependency(%q<capistrano>, ["~> 2.5.5"])
38
- s.add_dependency(%q<geminstaller>, ["~> 0.5.1"])
39
- s.add_dependency(%q<hoe>, [">= 1.9.0"])
40
- end
41
- end
@@ -1,20 +0,0 @@
1
- module CapistranoExtensions
2
- module Version #:nodoc:
3
- # A method for comparing versions of required modules. It expects two
4
- # arrays of integers as parameters, the first being the minimum version
5
- # required, and the second being the actual version available. It returns
6
- # true if the actual version is at least equal to the required version.
7
- def self.check(required, actual) #:nodoc:
8
- required = required.map { |v| "%06d" % v }.join(".")
9
- actual = actual.map { |v| "%06d" % v }.join(".")
10
- return actual >= required
11
- end
12
-
13
- MAJOR = 0
14
- MINOR = 1
15
- TINY = 6
16
-
17
- STRING = [MAJOR, MINOR, TINY].join(".")
18
- end
19
- end
20
-