porter 0.1.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.
- data/.gitignore +1 -0
- data/LICENSE +20 -0
- data/README.rdoc +35 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/generators/porter/lib/insert_commands.rb +34 -0
- data/generators/porter/porter_generator.rb +18 -0
- data/generators/porter/templates/porter.rake +74 -0
- data/generators/porter/templates/porter_config.yml +20 -0
- data/lib/porter/recipes/porter.rb +16 -0
- data/lib/porter.rb +1 -0
- data/porter.gemspec +59 -0
- data/test/helper.rb +10 -0
- metadata +107 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Kenny Johnston
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
= Porter Gem
|
2
|
+
|
3
|
+
The Porter gem is comprised of Capistrano and Rake tasks that make cloning your production server data down to your development environment a cinch.
|
4
|
+
|
5
|
+
== Overview
|
6
|
+
|
7
|
+
* A mysqldump command is remotely issued (via Capistrano) to the remote production server, saving the result as a compressed (gz) file
|
8
|
+
* The database backup file from the server is retrieved (via scp) and decompressed
|
9
|
+
* The development database is dropped, recreated, and restored from the backup
|
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
|
12
|
+
|
13
|
+
== Dependencies
|
14
|
+
|
15
|
+
* Capistrano (and a config/deploy.rb file)
|
16
|
+
* Rake
|
17
|
+
* A Rails App
|
18
|
+
* rsync (locally and remotely)
|
19
|
+
|
20
|
+
== Installation
|
21
|
+
|
22
|
+
* sudo gem install porter
|
23
|
+
* config.gem 'porter'
|
24
|
+
* script/generate porter
|
25
|
+
* See the generated config/porter_config.yml - it's pretty straight-forward
|
26
|
+
|
27
|
+
== Usage
|
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)
|
32
|
+
|
33
|
+
== Copyright
|
34
|
+
|
35
|
+
Copyright (c) 2010 Kenny Johnston. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "porter"
|
8
|
+
gem.summary = "Capistrano and Rake tasks for cloning production database and assets to development."
|
9
|
+
gem.description = "Capistrano and Rake tasks for cloning production database and assets to development."
|
10
|
+
gem.email = "info@appcreations.com"
|
11
|
+
gem.homepage = "http://github.com/kjohnston/porter"
|
12
|
+
gem.authors = ["Kenny Johnston"]
|
13
|
+
gem.add_development_dependency "capistrano", ">= 0"
|
14
|
+
gem.add_development_dependency "rake", ">= 0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/test_*.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "porter #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Mostly pinched from http://github.com/ryanb/nifty-generators/tree/master
|
2
|
+
|
3
|
+
Rails::Generator::Commands::Base.class_eval do
|
4
|
+
def file_contains?(relative_destination, line)
|
5
|
+
File.read(destination_path(relative_destination)).include?(line)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Rails::Generator::Commands::Create.class_eval do
|
10
|
+
def append_to(file, line)
|
11
|
+
logger.insert "#{line} appended to #{file}"
|
12
|
+
unless options[:pretend] || file_contains?(file, line)
|
13
|
+
File.open(file, "a") do |file|
|
14
|
+
file.puts
|
15
|
+
file.puts line
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Rails::Generator::Commands::Destroy.class_eval do
|
22
|
+
def append_to(file, line)
|
23
|
+
logger.remove "#{line} removed from #{file}"
|
24
|
+
unless options[:pretend]
|
25
|
+
gsub_file file, "\n#{line}", ''
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Rails::Generator::Commands::List.class_eval do
|
31
|
+
def append_to(file, line)
|
32
|
+
logger.insert "#{line} appended to #{file}"
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/lib/insert_commands.rb")
|
2
|
+
|
3
|
+
class PorterGenerator < Rails::Generator::Base
|
4
|
+
|
5
|
+
attr_accessor :app, :domain
|
6
|
+
|
7
|
+
def manifest
|
8
|
+
@app = Dir.glob(RAILS_ROOT).to_s.split('/').last
|
9
|
+
@domain = @app + (@app.include?('.') ? '' : '.com')
|
10
|
+
|
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'"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,74 @@
|
|
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
|
+
model = config['assets']['model'].constantize
|
48
|
+
column = config['assets']['column']
|
49
|
+
exclusions = config['assets']['exclusions'].blank? ? '' : config['assets']['exclusions'].split(',').map { |i| i.strip }
|
50
|
+
rsync_options = config['assets']['rsync_options']
|
51
|
+
|
52
|
+
excluding = exclusions.blank? ? '' : "(excluding: #{config['assets']['exclusions']} files) "
|
53
|
+
puts "Building a list of assets #{excluding}to rsync down..."
|
54
|
+
|
55
|
+
unless exclusions.blank?
|
56
|
+
rsync_file_list = File.new('rsync_file_list.txt', "w")
|
57
|
+
attachments = model.find(:all, :conditions => ["#{column} NOT IN (?)", exclusions])
|
58
|
+
attachments.each do |a|
|
59
|
+
rsync_file_list.send((a == attachments.last ? :print : :puts), ('attachments/' + a.partitioned_path.join('/')))
|
60
|
+
end
|
61
|
+
rsync_file_list.close
|
62
|
+
end
|
63
|
+
|
64
|
+
rsync_list_command = exclusions.blank? ? '' : "--files-from=#{root}/rsync_file_list.txt "
|
65
|
+
|
66
|
+
puts "Synchronizing with production assets..."
|
67
|
+
system "rsync #{rsync_list_command}#{rsync_options} #{user}@#{domain}:#{dir}/shared/public/ public"
|
68
|
+
|
69
|
+
system "rm #{root}/rsync_file_list.txt" if File.exists?("#{root}/rsync_file_list.txt")
|
70
|
+
|
71
|
+
puts "Production asset synchronization complete"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Config file for porter gem
|
2
|
+
|
3
|
+
server:
|
4
|
+
user: deploy
|
5
|
+
domain: <%= domain %>
|
6
|
+
dir: /opt/apps/<%= app %>
|
7
|
+
|
8
|
+
assets:
|
9
|
+
model: Attachment
|
10
|
+
column: attachable_type
|
11
|
+
exclusions:
|
12
|
+
rsync_options: --verbose --progress --stats --recursive --times --compress
|
13
|
+
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# assets:
|
17
|
+
# model: Attachment
|
18
|
+
# column: attachable_type
|
19
|
+
# exclusions: NULL, Design, Item, Placement
|
20
|
+
# rsync_options: --verbose --progress --stats --recursive --times --compress
|
@@ -0,0 +1,16 @@
|
|
1
|
+
if defined?(Capistrano)
|
2
|
+
Capistrano::Configuration.instance.load do
|
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"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/porter.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/porter/recipes/porter"
|
data/porter.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{porter}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Kenny Johnston"]
|
12
|
+
s.date = %q{2010-06-17}
|
13
|
+
s.description = %q{Capistrano and Rake tasks for cloning production database and assets to development.}
|
14
|
+
s.email = %q{info@appcreations.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"generators/porter/lib/insert_commands.rb",
|
26
|
+
"generators/porter/porter_generator.rb",
|
27
|
+
"generators/porter/templates/porter.rake",
|
28
|
+
"generators/porter/templates/porter_config.yml",
|
29
|
+
"lib/porter.rb",
|
30
|
+
"lib/porter/recipes/porter.rb",
|
31
|
+
"porter.gemspec",
|
32
|
+
"test/helper.rb"
|
33
|
+
]
|
34
|
+
s.homepage = %q{http://github.com/kjohnston/porter}
|
35
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubygems_version = %q{1.3.7}
|
38
|
+
s.summary = %q{Capistrano and Rake tasks for cloning production database and assets to development.}
|
39
|
+
s.test_files = [
|
40
|
+
"test/helper.rb"
|
41
|
+
]
|
42
|
+
|
43
|
+
if s.respond_to? :specification_version then
|
44
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
45
|
+
s.specification_version = 3
|
46
|
+
|
47
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
48
|
+
s.add_development_dependency(%q<capistrano>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
50
|
+
else
|
51
|
+
s.add_dependency(%q<capistrano>, [">= 0"])
|
52
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
53
|
+
end
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<capistrano>, [">= 0"])
|
56
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: porter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Kenny Johnston
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-17 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: capistrano
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rake
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
description: Capistrano and Rake tasks for cloning production database and assets to development.
|
50
|
+
email: info@appcreations.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files:
|
56
|
+
- LICENSE
|
57
|
+
- README.rdoc
|
58
|
+
files:
|
59
|
+
- .gitignore
|
60
|
+
- LICENSE
|
61
|
+
- README.rdoc
|
62
|
+
- Rakefile
|
63
|
+
- VERSION
|
64
|
+
- generators/porter/lib/insert_commands.rb
|
65
|
+
- generators/porter/porter_generator.rb
|
66
|
+
- generators/porter/templates/porter.rake
|
67
|
+
- generators/porter/templates/porter_config.yml
|
68
|
+
- lib/porter.rb
|
69
|
+
- lib/porter/recipes/porter.rb
|
70
|
+
- porter.gemspec
|
71
|
+
- test/helper.rb
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://github.com/kjohnston/porter
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options:
|
78
|
+
- --charset=UTF-8
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 3
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.3.7
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: Capistrano and Rake tasks for cloning production database and assets to development.
|
106
|
+
test_files:
|
107
|
+
- test/helper.rb
|