capistrano-sync 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+