porter 0.1.4 → 0.1.5

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/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