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