porter 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -4,11 +4,11 @@ The Porter gem is comprised of Capistrano and Rake tasks that make cloning your
4
4
 
5
5
  == Overview
6
6
 
7
- * A mysqldump command is remotely issued (via Capistrano) to the remote production server, saving the result as a compressed (gz) file
7
+ * A mysqldump command is remotely issued (via Capistrano) to the remote server (production or staging environment), saving the result as a compressed (gz) file
8
8
  * The database backup file from the server is retrieved (via scp) and decompressed
9
9
  * The development database is dropped, recreated, and restored from the backup
10
10
  * Assets stored in shared/public are rysnc'd down to your local public directory (exclusions are accepted!)
11
- * Separate rake tasks are included for restoring the db and re-syncing the assets without re-dumping the production db
11
+ * Separate rake tasks are included for restoring the db and re-syncing the assets without re-dumping the remote db
12
12
 
13
13
  == Dependencies
14
14
 
@@ -26,9 +26,11 @@ The Porter gem is comprised of Capistrano and Rake tasks that make cloning your
26
26
 
27
27
  == Usage
28
28
 
29
- * cap porter:production (creates the remote database backup then calls the two rake tasks below)
30
- * rake porter:production:db (if you've already got a db backup pulled down and want to restore from it again)
31
- * rake porter:production:assets (rsync the assets down from the production server again)
29
+ * cap porter:[stage] (creates the remote db backup file then calls the two rake tasks below)
30
+ * rake porter:[stage]:db (if you've already got a db backup on the remote server and want to restore from it again)
31
+ * rake porter:[stage]:assets (rsync the assets down from the remote server again)
32
+
33
+ Note: [stage] is most likely going to be 'production' unless your app has a staging or demo environment and you want to pull from there instead.
32
34
 
33
35
  == Copyright
34
36
 
@@ -9,9 +9,10 @@ class PorterGenerator < Rails::Generator::Base
9
9
  @domain = @app + (@app.include?('.') ? '' : '.com')
10
10
 
11
11
  record do |m|
12
- m.template 'porter_config.yml', File.join('config', 'porter_config.yml')
13
- m.template 'porter.rake', File.join('lib', 'tasks', 'porter.rake')
14
- m.append_to 'config/deploy.rb', "\n\nrequire 'porter'"
12
+ m.template 'porter_config.yml', File.join('config', 'porter_config.yml')
13
+ m.template 'porter.rb', File.join('lib', 'tasks', 'porter.rb')
14
+ m.append_to 'config/deploy.rb', "require 'porter'"
15
+ m.append_to 'Rakefile', "require 'tasks/porter'"
15
16
  end
16
17
  end
17
18
 
@@ -0,0 +1,4 @@
1
+ $VERBOSE = nil
2
+ if porter = Gem.searcher.find('porter')
3
+ Dir["#{porter.full_gem_path}/lib/tasks/*.rake"].each { |ext| load ext }
4
+ end
@@ -5,6 +5,20 @@ server:
5
5
  domain: <%= domain %>
6
6
  dir: /opt/apps/<%= app %>
7
7
 
8
+ # If you want to be able to pull from different environments, and the domains,
9
+ # usernames or directories differ, you can delete the catch-all server option
10
+ # above and enable a config for each environment's details like this:
11
+ #
12
+ # production:
13
+ # user: deploy
14
+ # domain: <%= domain %>
15
+ # dir: /opt/apps/<%= app %>
16
+ #
17
+ # staging:
18
+ # user: deploy
19
+ # domain: <%= domain %>
20
+ # dir: /opt/apps/<%= app %>
21
+
8
22
  assets:
9
23
  entire_dirs: public/attachments
10
24
  excludable_dir:
@@ -16,9 +30,9 @@ assets:
16
30
  # Example:
17
31
  #
18
32
  # assets:
19
- # entire_dirs:
33
+ # entire_dirs: public/campaign public/user
20
34
  # excludable_dir: public/attachments
21
35
  # excludable_model: Attachment
22
36
  # excludable_column: attachable_type
23
- # excludable_matches: NULL, Design, Item, Placement
37
+ # excludable_matches: Document Product
24
38
  # rsync_options: --verbose --progress --stats --recursive --times --compress
@@ -1,15 +1,25 @@
1
1
  if defined?(Capistrano)
2
2
  Capistrano::Configuration.instance.load do
3
3
  namespace :porter do
4
- task :production do
5
- require 'yaml'
6
- config = YAML::load_file('config/database.yml')['production']
7
- db = config['database']
8
- user = config['username']
9
- pass = config['password']
10
- run "mysqldump --user=#{user} --password=#{pass} #{db} | gzip > ~/#{db}.sql.gz"
11
- system "rake porter:production:db"
12
- system "rake porter:production:assets"
4
+ CONFIG = YAML::load_file('config/porter_config.yml')
5
+ DATABASES = YAML::load_file('config/database.yml')
6
+ STAGES = DATABASES.keys - %w(development test) # you don't need data out of these
7
+ STAGES.each do |stage|
8
+ # task names for each of your other stages: production, staging, etc.
9
+ # cap porter:production, cap porter:staging, etc.
10
+ task stage do
11
+ src_db = DATABASES[stage]
12
+ db = src_db['database']
13
+ user = src_db['username']
14
+ pass = src_db['password']
15
+
16
+ domain = CONFIG[stage].nil? ? CONFIG['server']['domain'] : CONFIG[stage]['domain']
17
+ server domain, :porter
18
+
19
+ run "mysqldump --user=#{user} --password=#{pass} #{db} | gzip > ~/#{db}.sql.gz", :roles => :porter
20
+ system "rake porter:#{stage}:db"
21
+ system "rake porter:#{stage}:assets"
22
+ end
13
23
  end
14
24
  end
15
25
  end
data/lib/porter.rb CHANGED
@@ -1 +1 @@
1
- require "#{File.dirname(__FILE__)}/porter/recipes/porter"
1
+ require "#{File.dirname(__FILE__)}/porter/recipes/porter"
@@ -0,0 +1,91 @@
1
+ CONFIG = YAML::load_file(File.join(RAILS_ROOT, 'config', 'porter_config.yml'))
2
+ DATABASES = YAML::load_file(File.join(RAILS_ROOT, 'config', 'database.yml'))
3
+ STAGES = DATABASES.keys - %w(development test) # you don't need data out of these
4
+
5
+ namespace :porter do
6
+ STAGES.each do |stage|
7
+ namespace stage do
8
+ task :db => :environment do
9
+ # Optional: You can setup specific username and host
10
+ # combos for each stage in porter_config.yml or use
11
+ # the default 'server' key to apply to all stages
12
+ if CONFIG[stage]
13
+ src_user = CONFIG[stage]['user']
14
+ src_domain = CONFIG[stage]['domain']
15
+ else
16
+ src_user = CONFIG['server']['user']
17
+ src_domain = CONFIG['server']['domain']
18
+ end
19
+
20
+ src_db = ActiveRecord::Base.configurations[stage]
21
+ dest_db = ActiveRecord::Base.configurations[RAILS_ENV]
22
+ dest_root = RAILS_ROOT
23
+
24
+ app = src_db['database'].split('_').first
25
+ root = RAILS_ROOT
26
+
27
+ puts "Retrieving latest compressed database backup from #{stage} server..."
28
+ system "scp #{src_user}@#{src_domain}:~/#{src_db['database']}.sql.gz #{root}"
29
+
30
+ puts "Decompressing database backup..."
31
+ system "gunzip #{root}/#{src_db['database']}.sql.gz"
32
+
33
+ # Drop the database if it exists
34
+ begin
35
+ ActiveRecord::Base.establish_connection(dest_db)
36
+ ActiveRecord::Base.connection # Should raise Mysql::Error if db doesn't exist
37
+ puts "Dropping database: " + dest_db['database']
38
+ Rake::Task['db:drop'].execute
39
+ rescue Mysql::Error => e
40
+ raise e unless e.message =~ /Unknown database/
41
+ end
42
+
43
+ puts "Creating database: " + dest_db['database']
44
+ Rake::Task['db:create'].execute
45
+
46
+ puts "Restoring database from backup..."
47
+ mysql_version = `which mysql`.empty? ? 'mysql5' : 'mysql'
48
+ system "#{mysql_version} -u root #{dest_db['database']} < #{root}/#{src_db['database']}.sql"
49
+
50
+ puts "Removing database backup file..."
51
+ system "rm #{root}/#{src_db['database']}.sql"
52
+
53
+ puts "Production data reload complete"
54
+ end
55
+
56
+ task :assets => :environment do
57
+ root = RAILS_ROOT
58
+ user = CONFIG[stage].nil? ? CONFIG['server']['user'] : CONFIG[stage]['user']
59
+ domain = CONFIG[stage].nil? ? CONFIG['server']['domain'] : CONFIG[stage]['domain']
60
+ dir = CONFIG[stage].nil? ? CONFIG['server']['dir'] : CONFIG[stage]['dir']
61
+ entire_dirs = CONFIG['assets']['entire_dirs'].blank? ? '' : CONFIG['assets']['entire_dirs'].gsub(/,/,'').split(' ').map { |i| i.strip }
62
+ excludable_dir = CONFIG['assets']['excludable_dir']
63
+ model = CONFIG['assets']['excludable_model'].constantize unless CONFIG['assets']['excludable_model'].blank?
64
+ column = CONFIG['assets']['excludable_column']
65
+ exclusions = CONFIG['assets']['excludable_matches'].blank? ? '' : CONFIG['assets']['excludable_matches'].gsub(/,/,'').split(' ').map { |i| i.strip }
66
+ rsync_options = CONFIG['assets']['rsync_options']
67
+
68
+ if exclusions.blank?
69
+ entire_dirs << excludable_dir unless excludable_dir.blank?
70
+ else
71
+ puts "Building a list of excludable assets (excluding: #{exclusions.join(', ')}) to rsync down..."
72
+ rsync_file_list = File.new('rsync_file_list.txt', "w")
73
+ attachments = model.find(:all, :conditions => ["#{column} NOT IN (?)", exclusions])
74
+ attachments.each do |a|
75
+ rsync_file_list.send((a == attachments.last ? :print : :puts), a.partitioned_path.join('/'))
76
+ end
77
+ rsync_file_list.close
78
+ system "rsync --files-from=#{root}/rsync_file_list.txt #{rsync_options} #{user}@#{domain}:#{dir}/shared/#{excludable_dir}/ #{excludable_dir}"
79
+ system "rm #{root}/rsync_file_list.txt" if File.exists?("#{root}/rsync_file_list.txt")
80
+ end
81
+
82
+ entire_dirs.each do |d|
83
+ puts "Synchronizing assets in #{d}..."
84
+ system "rsync #{rsync_options} #{user}@#{domain}:#{dir}/shared/#{d}/ #{d}"
85
+ end
86
+
87
+ puts "Production asset synchronization complete"
88
+ end
89
+ end
90
+ end
91
+ end
data/porter.gemspec CHANGED
@@ -5,10 +5,10 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{porter}
8
- s.version = "0.1.4"
8
+ s.version = "0.1.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Kenny Johnston"]
11
+ s.authors = ["Kenny Johnston", "Robert Bousquet"]
12
12
  s.date = %q{2010-07-02}
13
13
  s.description = %q{Capistrano and Rake tasks for cloning production database and assets to development.}
14
14
  s.email = %q{info@appcreations.com}
@@ -24,10 +24,11 @@ Gem::Specification.new do |s|
24
24
  "VERSION",
25
25
  "generators/porter/lib/insert_commands.rb",
26
26
  "generators/porter/porter_generator.rb",
27
- "generators/porter/templates/porter.rake",
27
+ "generators/porter/templates/porter.rb",
28
28
  "generators/porter/templates/porter_config.yml",
29
29
  "lib/porter.rb",
30
30
  "lib/porter/recipes/porter.rb",
31
+ "lib/tasks/porter.rake",
31
32
  "porter.gemspec",
32
33
  "test/helper.rb"
33
34
  ]
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: porter
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 4
10
- version: 0.1.4
9
+ - 5
10
+ version: 0.1.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kenny Johnston
14
+ - Robert Bousquet
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
@@ -63,10 +64,11 @@ files:
63
64
  - VERSION
64
65
  - generators/porter/lib/insert_commands.rb
65
66
  - generators/porter/porter_generator.rb
66
- - generators/porter/templates/porter.rake
67
+ - generators/porter/templates/porter.rb
67
68
  - generators/porter/templates/porter_config.yml
68
69
  - lib/porter.rb
69
70
  - lib/porter/recipes/porter.rb
71
+ - lib/tasks/porter.rake
70
72
  - porter.gemspec
71
73
  - test/helper.rb
72
74
  has_rdoc: true
@@ -1,76 +0,0 @@
1
- namespace :porter do
2
- namespace :production do
3
- task :db => :environment do
4
- root = RAILS_ROOT
5
- config = YAML.load_file(File.join(RAILS_ROOT, 'config', 'porter_config.yml'))
6
- user = config['server']['user']
7
- domain = config['server']['domain']
8
- dbconfig = ActiveRecord::Base.configurations[RAILS_ENV]
9
- app = dbconfig['database'].gsub('_dev', '')
10
-
11
- puts "Retrieving latest compressed database backup from production server..."
12
- system "scp #{user}@#{domain}:~/#{app}.sql.gz #{root}"
13
-
14
- puts "Decompressing database backup..."
15
- system "gunzip #{root}/#{app}.sql.gz"
16
-
17
- # Drop the database if it exists
18
- begin
19
- ActiveRecord::Base.establish_connection(dbconfig)
20
- ActiveRecord::Base.connection # Should raise Mysql::Error if db doesn't exist
21
- puts "Dropping database: " + dbconfig['database']
22
- Rake::Task['db:drop'].execute
23
- rescue Mysql::Error => e
24
- raise e unless e.message =~ /Unknown database/
25
- end
26
-
27
- puts "Creating database: " + dbconfig['database']
28
- Rake::Task['db:create'].execute
29
-
30
- puts "Restoring database from backup..."
31
- mysql_version = `which mysql`.empty? ? 'mysql5' : 'mysql'
32
- system "#{mysql_version} -u root #{dbconfig['database']} < #{root}/#{app}.sql"
33
-
34
- puts "Removing database backup file..."
35
- system "rm #{root}/#{app}.sql"
36
-
37
- puts "Production data reload complete"
38
- end
39
-
40
- task :assets => :environment do
41
- require 'yaml'
42
- root = RAILS_ROOT
43
- config = YAML.load_file(File.join(RAILS_ROOT, 'config', 'porter_config.yml'))
44
- user = config['server']['user']
45
- domain = config['server']['domain']
46
- dir = config['server']['dir']
47
- entire_dirs = config['assets']['entire_dirs'].blank? ? '' : config['assets']['entire_dirs'].split(',').map { |i| i.strip }
48
- excludable_dir = config['assets']['excludable_dir']
49
- model = config['assets']['excludable_model'].constantize unless config['assets']['excludable_model'].blank?
50
- column = config['assets']['excludable_column']
51
- exclusions = config['assets']['excludable_matches'].blank? ? '' : config['assets']['excludable_matches'].split(',').map { |i| i.strip }
52
- rsync_options = config['assets']['rsync_options']
53
-
54
- if exclusions.blank?
55
- entire_dirs << excludable_dir unless excludable_dir.blank?
56
- else
57
- puts "Building a list of excludable assets (excluding: #{exclusions.join(', ')}) to rsync down..."
58
- rsync_file_list = File.new('rsync_file_list.txt', "w")
59
- attachments = model.find(:all, :conditions => ["#{column} NOT IN (?)", exclusions])
60
- attachments.each do |a|
61
- rsync_file_list.send((a == attachments.last ? :print : :puts), a.partitioned_path.join('/'))
62
- end
63
- rsync_file_list.close
64
- system "rsync --files-from=#{root}/rsync_file_list.txt #{rsync_options} #{user}@#{domain}:#{dir}/shared/#{excludable_dir}/ #{excludable_dir}"
65
- system "rm #{root}/rsync_file_list.txt" if File.exists?("#{root}/rsync_file_list.txt")
66
- end
67
-
68
- entire_dirs.each do |d|
69
- puts "Synchronizing assets in #{d}..."
70
- system "rsync #{rsync_options} #{user}@#{domain}:#{dir}/shared/#{d}/ #{d}"
71
- end
72
-
73
- puts "Production asset synchronization complete"
74
- end
75
- end
76
- end