capistrano-sync 0.0.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/README +15 -0
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/capistrano-sync.gemspec +41 -0
- data/lib/capistrano-sync/sync.rb +229 -0
- data/lib/capistrano-sync.rb +1 -0
- metadata +72 -0
data/README
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
== capistrano-sync
|
2
|
+
|
3
|
+
Recipies for download and replace database or directories from production or staging to development.
|
4
|
+
|
5
|
+
Defined three task:
|
6
|
+
|
7
|
+
* cap sync:db [RAILS_ENV=env]
|
8
|
+
* cap sync:dir FROM=path_relative_to_deploy TO=path
|
9
|
+
* cap sync:dir:public_system
|
10
|
+
|
11
|
+
== Installation
|
12
|
+
|
13
|
+
Add to you Gmefile:
|
14
|
+
|
15
|
+
gem "capistrano-sync", :git => "git://github.com/dima-exe/capistrano-sync.git"
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
4
|
+
gemspec.name = "capistrano-sync"
|
5
|
+
gemspec.summary = "Sync db or directories recipes for Capistrano"
|
6
|
+
gemspec.description = "Sync db or directories recipes for Capistrano"
|
7
|
+
gemspec.email = "dima.exe@gmail.com"
|
8
|
+
gemspec.homepage = "http://github.com/dima-exe/capistrano-sync"
|
9
|
+
gemspec.authors = ["Dmitry Galinsky"]
|
10
|
+
end
|
11
|
+
rescue LoadError
|
12
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
13
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{capistrano-sync}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Dmitry Galinsky"]
|
12
|
+
s.date = %q{2011-10-06}
|
13
|
+
s.description = %q{Sync db or directories recipes for Capistrano}
|
14
|
+
s.email = %q{dima.exe@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"README",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION",
|
22
|
+
"capistrano-sync.gemspec",
|
23
|
+
"lib/capistrano-sync.rb",
|
24
|
+
"lib/capistrano-sync/sync.rb"
|
25
|
+
]
|
26
|
+
s.homepage = %q{http://github.com/dima-exe/capistrano-sync}
|
27
|
+
s.require_paths = ["lib"]
|
28
|
+
s.rubygems_version = %q{1.3.7}
|
29
|
+
s.summary = %q{Sync db or directories recipes for Capistrano}
|
30
|
+
|
31
|
+
if s.respond_to? :specification_version then
|
32
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
33
|
+
s.specification_version = 3
|
34
|
+
|
35
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
36
|
+
else
|
37
|
+
end
|
38
|
+
else
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'action_view'
|
3
|
+
require 'capistrano'
|
4
|
+
|
5
|
+
module CapistranoSyncTask
|
6
|
+
module Helper
|
7
|
+
def get_ssh_command()
|
8
|
+
server = cap.find_servers.first
|
9
|
+
srv = server.host
|
10
|
+
srv += ":#{server.port}" if server.port
|
11
|
+
["ssh -l #{cap[:user]} #{srv}", srv]
|
12
|
+
end
|
13
|
+
|
14
|
+
def log(*args)
|
15
|
+
cap.logger.info *args
|
16
|
+
end
|
17
|
+
|
18
|
+
# missing capture task
|
19
|
+
def _capture(command, options={})
|
20
|
+
output = ""
|
21
|
+
cap.invoke_command(command, options.merge(:once => true)) do |ch, stream, data|
|
22
|
+
case stream
|
23
|
+
when :out then output << data
|
24
|
+
when :err then warn "[err :: #{ch[:server]}] #{data}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
output
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Db
|
32
|
+
include Helper
|
33
|
+
attr_accessor :rails_env, :local_rails_env, :tables, :cap
|
34
|
+
|
35
|
+
def initialize(cap, rails_env, local_rails_env, tables)
|
36
|
+
self.rails_env = rails_env
|
37
|
+
self.local_rails_env = local_rails_env
|
38
|
+
self.tables = tables
|
39
|
+
self.cap = cap
|
40
|
+
end
|
41
|
+
|
42
|
+
def sync
|
43
|
+
remote_config = remote_database_config
|
44
|
+
local_config = local_database_config
|
45
|
+
|
46
|
+
dump_method = remote_config[:adapter] + "_dump"
|
47
|
+
load_method = local_config[:adapter] + "_load"
|
48
|
+
unless self.private_methods.include?(dump_method)
|
49
|
+
puts "FATAL: Can't dump: unknown adapter #{remote_config[:adapter].inspect}"
|
50
|
+
exit(1)
|
51
|
+
end
|
52
|
+
unless self.private_methods.include?(load_method)
|
53
|
+
puts "FATAL: Can't load: unknown adapter #{local_config[:adapter].inspect}"
|
54
|
+
exit(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
dump_command = __send__(dump_method, remote_config)
|
58
|
+
load_command = __send__(load_method, local_config)
|
59
|
+
ssh_cmd, server = get_ssh_command
|
60
|
+
|
61
|
+
log "Drop and create local database"
|
62
|
+
drop_and_create_local_db
|
63
|
+
log "Dump from #{server} and load to local #{local_rails_env} db (see progress)"
|
64
|
+
|
65
|
+
cmd = "#{ssh_cmd} #{dump_command} | pv | #{load_command}"
|
66
|
+
system cmd
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def drop_and_create_local_db
|
71
|
+
system "bundle exec rake -q db:drop db:create RAILS_ENV=#{local_rails_env} 2&>1 /dev/null"
|
72
|
+
end
|
73
|
+
|
74
|
+
def remote_database_config
|
75
|
+
unless @remove_database_config
|
76
|
+
remote_config = _capture "cat #{cap.current_path}/config/database.yml"
|
77
|
+
@remove_database_config = load_database_config(remote_config, rails_env)
|
78
|
+
end
|
79
|
+
@remove_database_config
|
80
|
+
end
|
81
|
+
|
82
|
+
def local_database_config
|
83
|
+
unless @local_database_config
|
84
|
+
local_config = IO.read("config/database.yml")
|
85
|
+
@local_database_config = load_database_config(local_config, self.local_rails_env)
|
86
|
+
end
|
87
|
+
@local_database_config
|
88
|
+
end
|
89
|
+
|
90
|
+
def load_database_config(io, env)
|
91
|
+
config = YAML.load(io)[env]
|
92
|
+
raise "Can't read #{env} entry from database.yml" unless config
|
93
|
+
{
|
94
|
+
:adapter => config["adapter"],
|
95
|
+
:user => config["username"] || config["user"],
|
96
|
+
:pass => config["password"],
|
97
|
+
:dbname => config["database"],
|
98
|
+
:socket => config["socket"],
|
99
|
+
:host => config["host"],
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def postgresql_dump(config)
|
104
|
+
dump_cmd = "pg_dump"
|
105
|
+
dump_cmd << " --no-owner --no-privileges --disable-triggers --inserts"
|
106
|
+
dump_cmd << " --username=#{config[:user]}" if config[:user]
|
107
|
+
#dump_cmd << " --password=#{config[:pass]}" if config[:pass]
|
108
|
+
dump_cmd << " --host=#{config[:host]}" if config[:host]
|
109
|
+
dump_cmd << " #{config[:dbname]}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def postgresql_load(config)
|
113
|
+
load_cmd = "psql --single-transaction --quiet -o /dev/null"
|
114
|
+
load_cmd << " --username=#{config[:user]}" if config[:user]
|
115
|
+
load_cmd << " --password=#{config[:pass]}" if config[:pass]
|
116
|
+
load_cmd << " --host=#{config[:host]}" if config[:host]
|
117
|
+
load_cmd << " #{config[:dbname]}"
|
118
|
+
load_cmd
|
119
|
+
end
|
120
|
+
|
121
|
+
def mysql2_dump(config)
|
122
|
+
dump_cmd = "mysqldump"
|
123
|
+
dump_cmd << " --quick --single-transaction"
|
124
|
+
dump_cmd << " --user=#{config[:user]}" if config[:user]
|
125
|
+
dump_cmd << " --password=#{config[:pass]}" if config[:pass]
|
126
|
+
dump_cmd << " --socket=#{config[:socket]}" if config[:socket]
|
127
|
+
dump_cmd << " --host=#{config[:host]}" if config[:host]
|
128
|
+
dump_cmd << " --opt #{config[:dbname]} #{config[:tables]}"
|
129
|
+
dump_cmd
|
130
|
+
end
|
131
|
+
|
132
|
+
def mysql2_load(config)
|
133
|
+
load_cmd = "mysql"
|
134
|
+
load_cmd << " --user=#{config[:user]}" if config[:user]
|
135
|
+
#load_cmd << " --password=#{config[:pass]}" if config[:pass]
|
136
|
+
load_cmd << " --socket=#{config[:socket]}" if config[:socket]
|
137
|
+
load_cmd << " --host=#{config[:host]}" if config[:host]
|
138
|
+
load_cmd << " #{config[:dbname]}"
|
139
|
+
load_cmd
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class Dir
|
144
|
+
include Helper
|
145
|
+
attr_accessor :from, :to, :cap
|
146
|
+
def initialize(cap, from, to)
|
147
|
+
self.from = from
|
148
|
+
self.to = to
|
149
|
+
self.cap = cap
|
150
|
+
end
|
151
|
+
|
152
|
+
def sync
|
153
|
+
check_deps
|
154
|
+
ssh_cmd, server = get_ssh_command
|
155
|
+
log "Download and extract #{server}:#{from} -> local:#{to} (see progress)"
|
156
|
+
cmd = "#{ssh_cmd} \"tar -cC #{from} .\" |pv -s #{total} | tar -x -C #{to}"
|
157
|
+
system cmd
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
def check_deps
|
162
|
+
system "which pv > /dev/null"
|
163
|
+
unless ($?.to_i == 0)
|
164
|
+
puts "FATAL: pv (Pipe Viewer) command not found, please install 'port install pv' or 'brew install pv'"
|
165
|
+
exit(1)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
def total
|
169
|
+
unless @total
|
170
|
+
log "Calculate files size"
|
171
|
+
@total = _capture("du -sb #{from} | awk '{print $1}'", :once => true).to_i
|
172
|
+
log "TOTAL: #{proxy.number_to_human_size @total}"
|
173
|
+
end
|
174
|
+
@total
|
175
|
+
end
|
176
|
+
|
177
|
+
def proxy
|
178
|
+
@proxy ||= HelperProxy.new
|
179
|
+
end
|
180
|
+
|
181
|
+
def log(*args)
|
182
|
+
cap.logger.info *args
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class HelperProxy
|
187
|
+
include ActionView::Helpers::NumberHelper
|
188
|
+
end
|
189
|
+
|
190
|
+
module Capistrano
|
191
|
+
def self.load_into(configuration)
|
192
|
+
configuration.load do
|
193
|
+
namespace :sync do
|
194
|
+
namespace :dir do
|
195
|
+
desc "sync directory"
|
196
|
+
task :default, :roles => :app do
|
197
|
+
from_path = deploy_to + "/" + ENV["FROM"]
|
198
|
+
to_path = ENV["TO"]
|
199
|
+
if !from_path || !to_path
|
200
|
+
puts "Usage cap sync:dir FROM=<..> TO=<..>"
|
201
|
+
exit(1)
|
202
|
+
end
|
203
|
+
s = CapistranoSyncTask::Dir.new(self, from_path, to_path)
|
204
|
+
s.sync
|
205
|
+
end
|
206
|
+
|
207
|
+
desc "sync public/system directory"
|
208
|
+
task :public_system, :roles => :app do
|
209
|
+
s = CapistranoSyncTask::Dir.new(self, "#{deploy_to}/current/public/system/", "public/system/")
|
210
|
+
s.sync
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
desc "sync databases"
|
215
|
+
task :db, :roles => :db do
|
216
|
+
local_rails_env = ENV["RAILS_ENV"] || "development"
|
217
|
+
tables = ENV["TABLES"] || ""
|
218
|
+
s = CapistranoSyncTask::Db.new(self, rails_env, local_rails_env, tables)
|
219
|
+
s.sync
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
if Capistrano::Configuration.instance
|
228
|
+
CapistranoSyncTask::Capistrano.load_into(Capistrano::Configuration.instance)
|
229
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'capistrano-sync/sync'
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: capistrano-sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Dmitry Galinsky
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-10-06 00:00:00 +04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Sync db or directories recipes for Capistrano
|
23
|
+
email: dima.exe@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README
|
30
|
+
files:
|
31
|
+
- README
|
32
|
+
- Rakefile
|
33
|
+
- VERSION
|
34
|
+
- capistrano-sync.gemspec
|
35
|
+
- lib/capistrano-sync.rb
|
36
|
+
- lib/capistrano-sync/sync.rb
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/dima-exe/capistrano-sync
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
hash: 3
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.3.7
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: Sync db or directories recipes for Capistrano
|
71
|
+
test_files: []
|
72
|
+
|