capistrano-former03 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/capistrano/former03.rb +3 -0
- data/lib/capistrano/former03/assets.rb +102 -0
- data/lib/capistrano/former03/backup.rb +56 -0
- data/lib/capistrano/former03/mysql.rb +149 -0
- data/lib/capistrano/former03/rsync.rb +6 -1
- data/lib/capistrano/former03/templates/deploy.rb.erb +34 -0
- data/lib/capistrano/former03/templates/stage.rb.erb +13 -0
- data/lib/capistrano/former03/version.rb +1 -1
- data/lib/capistrano/tasks/former03.rake +55 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9826b5d4e73dfdac22b474e4bb8bbbd6e875fe6d
|
4
|
+
data.tar.gz: 7f32d7bdc321264751b5cc5b34943b6100a0a615
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf00abf2fdbc8797c71903c1498bd8ac965365d5a82d9e6b072675fbd0080b6aa6957578a31485f345afbd9921698c42bb0c79139d154843d9166c06c0905f6f
|
7
|
+
data.tar.gz: 8c254a94b18da059f25237de1f6046e6a9eb6c04279ad946c8efd68032878e74c407043fb096c1a4b6a84dee3a1ece7c739d14f7de69ed217460941d8d1556bf
|
data/lib/capistrano/former03.rb
CHANGED
@@ -2,6 +2,9 @@ require 'capistrano/former03/version'
|
|
2
2
|
require 'capistrano/former03/deploy_command'
|
3
3
|
require 'capistrano/former03/symlink'
|
4
4
|
require 'capistrano/former03/rsync'
|
5
|
+
require 'capistrano/former03/assets'
|
6
|
+
require 'capistrano/former03/mysql'
|
7
|
+
require 'capistrano/former03/backup'
|
5
8
|
load File.expand_path('../tasks/former03.rake', __FILE__)
|
6
9
|
|
7
10
|
module Capistrano
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
module Former03
|
5
|
+
class Assets
|
6
|
+
def initialize
|
7
|
+
end
|
8
|
+
|
9
|
+
def task
|
10
|
+
@task
|
11
|
+
end
|
12
|
+
|
13
|
+
def asset_paths
|
14
|
+
ret_val = fetch(:linked_dirs, [])
|
15
|
+
ret_val += fetch(:linked_files, []).map{|path| File.dirname path}
|
16
|
+
ret_val.uniq
|
17
|
+
end
|
18
|
+
|
19
|
+
def include_paths
|
20
|
+
asset_paths.map do |path|
|
21
|
+
path.split('/').first
|
22
|
+
end.uniq
|
23
|
+
end
|
24
|
+
|
25
|
+
def sync_includes
|
26
|
+
paths = []
|
27
|
+
|
28
|
+
# Add parent dirs
|
29
|
+
asset_paths.each do |p|
|
30
|
+
dir = p
|
31
|
+
paths += ["'/#{p}/***'"]
|
32
|
+
while true
|
33
|
+
dir = File.dirname(dir)
|
34
|
+
break if dir == '.'
|
35
|
+
paths += ["'/#{dir}/'"]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
paths.reverse.map{|p| ['--include', p]}.flatten
|
39
|
+
end
|
40
|
+
|
41
|
+
def sync(t)
|
42
|
+
@task = t
|
43
|
+
if fetch(:assets_source, nil).nil?
|
44
|
+
fail 'No asset source given'
|
45
|
+
end
|
46
|
+
|
47
|
+
if fetch(:stage) == :production
|
48
|
+
fail 'No asset syncing in stage :production'
|
49
|
+
end
|
50
|
+
|
51
|
+
include_opts = sync_includes
|
52
|
+
|
53
|
+
shared_dir = self.shared_dir
|
54
|
+
task.on release_roles :all do
|
55
|
+
rsync = Capistrano::Former03::Rsync.new(
|
56
|
+
:rsync_path => fetch(:deploy_rsync_path),
|
57
|
+
:src_path => fetch(:assets_source),
|
58
|
+
:dest_path => shared_dir,
|
59
|
+
:rsync_options => [
|
60
|
+
'-v',
|
61
|
+
*include_opts,
|
62
|
+
'--exclude', "'/**'",
|
63
|
+
]
|
64
|
+
)
|
65
|
+
execute(*rsync.command)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def shared_dir
|
70
|
+
deploy_path.join('shared')
|
71
|
+
end
|
72
|
+
|
73
|
+
def backup(t)
|
74
|
+
@task = t
|
75
|
+
shared_dir = self.shared_dir
|
76
|
+
include_paths = self.include_paths
|
77
|
+
task.on release_roles :all do
|
78
|
+
output_dir = fetch(:backup_dest_path)
|
79
|
+
|
80
|
+
# test for backup dir
|
81
|
+
execute "test -d \"#{output_dir}\" || mkdir -p \"#{output_dir}\""
|
82
|
+
|
83
|
+
if include_paths.length < 1
|
84
|
+
fail "No assets found to backup"
|
85
|
+
end
|
86
|
+
|
87
|
+
existing_dirs = capture(:ls, '-xtr', shared_dir).split
|
88
|
+
exclude_dirs = existing_dirs - include_paths
|
89
|
+
|
90
|
+
# destination dir
|
91
|
+
output = File.join(output_dir, 'assets.tar.gz')
|
92
|
+
|
93
|
+
# build exclude options
|
94
|
+
exclude_opts = exclude_dirs.map {|d| ['--exclude', d] }.flatten
|
95
|
+
|
96
|
+
# compress asset
|
97
|
+
execute(:tar, '-C', shared_dir, '-czf', output, '.', *exclude_opts)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
module Former03
|
5
|
+
class Backup
|
6
|
+
def initialize
|
7
|
+
end
|
8
|
+
|
9
|
+
def cleanup(t)
|
10
|
+
# minimum age in days to keep backups
|
11
|
+
backup_min_age = fetch(:backup_min_age, 7)
|
12
|
+
# minimum backup count to keep
|
13
|
+
backup_min_count = fetch(:backup_min_count, 5)
|
14
|
+
|
15
|
+
on release_roles :all do
|
16
|
+
backup_dir_path = fetch(:backup_dir_path)
|
17
|
+
backups = capture(:ls, '-xtr', backup_dir_path).split
|
18
|
+
count = 0
|
19
|
+
|
20
|
+
# Loop through existing backups
|
21
|
+
backups.reverse.each do |backup|
|
22
|
+
count += 1
|
23
|
+
created = DateTime.strptime(backup, '%Y%m%d%H%M%S')
|
24
|
+
age = (DateTime.now - created)
|
25
|
+
|
26
|
+
# If backup is older and count is higher than minimums
|
27
|
+
if age.to_i > backup_min_age and count > backup_min_count
|
28
|
+
execute 'rm', '-rf', File.join(backup_dir_path, backup)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def prepare(t)
|
35
|
+
t.set(:backup_dir_path, File.join(t.fetch(:deploy_to), t.fetch(:backup_dir, 'backups')))
|
36
|
+
on release_roles :all do
|
37
|
+
# set destination path
|
38
|
+
t.set(
|
39
|
+
:backup_dest_path,
|
40
|
+
File.join(
|
41
|
+
fetch(:backup_dir_path),
|
42
|
+
fetch(:release_timestamp,'not_time')
|
43
|
+
)
|
44
|
+
)
|
45
|
+
# ensure directories
|
46
|
+
[fetch(:backup_dir_path)].each do |dir|
|
47
|
+
if not test("test -d #{dir}")
|
48
|
+
execute :mkdir, dir
|
49
|
+
execute :chmod, '0700', dir
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
module Former03
|
5
|
+
class Namespace
|
6
|
+
def initialize(hash)
|
7
|
+
hash.each do |key, value|
|
8
|
+
singleton_class.send(:define_method, key) { value }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_binding
|
13
|
+
binding
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class MySQL
|
18
|
+
def initialize
|
19
|
+
end
|
20
|
+
|
21
|
+
def task
|
22
|
+
if @task.nil?
|
23
|
+
fail "No object handle"
|
24
|
+
end
|
25
|
+
@task
|
26
|
+
end
|
27
|
+
|
28
|
+
def sync(t)
|
29
|
+
@task = t
|
30
|
+
src_stage = :production
|
31
|
+
dst_stage = fetch(:stage)
|
32
|
+
|
33
|
+
if src_stage == dst_stage
|
34
|
+
fail "Source stage (#{src_stage}) is equal to destination stage (#{dst_stage})"
|
35
|
+
end
|
36
|
+
|
37
|
+
src_database = config(src_stage)[:database]
|
38
|
+
dst_database = config(dst_stage)[:database]
|
39
|
+
src_cnf = mysql_cnf_upload(:src, src_stage)
|
40
|
+
dst_cnf = mysql_cnf_upload(:dst, dst_stage)
|
41
|
+
|
42
|
+
@task.on release_roles :all do
|
43
|
+
|
44
|
+
# remove all tables on dest
|
45
|
+
tables = capture "mysql --defaults-file=#{dst_cnf} -e'SHOW TABLES;' #{dst_database}"
|
46
|
+
tables = tables.split
|
47
|
+
if tables.length > 1
|
48
|
+
tables = tables[1..-1].map{|name| "`#{name}`"}.join(',')
|
49
|
+
execute "mysql --defaults-file=#{dst_cnf} -e'DROP TABLES #{tables};' #{dst_database}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# sync production to stage
|
53
|
+
execute "mysqldump --defaults-file=#{src_cnf} #{src_database} | mysql --defaults-file=#{dst_cnf} #{dst_database}"
|
54
|
+
|
55
|
+
# remove credentials
|
56
|
+
execute "rm #{src_cnf}"
|
57
|
+
execute "rm #{dst_cnf}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def config(stage=nil)
|
62
|
+
@config ||= task.fetch(:mysql_connections)
|
63
|
+
stage ||= task.fetch(:stage)
|
64
|
+
@config[stage]
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def deploy_to
|
69
|
+
@task.fetch(:deploy_to)
|
70
|
+
end
|
71
|
+
|
72
|
+
def cnf_path(name)
|
73
|
+
File.join(deploy_to, 'shared', ".my.cnf_#{name}")
|
74
|
+
end
|
75
|
+
|
76
|
+
def mysql_cnf_upload(name, stage=nil)
|
77
|
+
dest = cnf_path(name)
|
78
|
+
src = Tempfile.new('my.cnf')
|
79
|
+
src.write(mysql_cnf(stage))
|
80
|
+
src.flush
|
81
|
+
@task.on release_roles :all do
|
82
|
+
upload!(src.path, dest)
|
83
|
+
execute "chmod 600 #{dest}"
|
84
|
+
end
|
85
|
+
|
86
|
+
# remove tempfile
|
87
|
+
src.delete
|
88
|
+
|
89
|
+
dest
|
90
|
+
end
|
91
|
+
|
92
|
+
def mysql_cnf(stage=nil)
|
93
|
+
c=config(stage)
|
94
|
+
output = "[client]\n"
|
95
|
+
output +="user=#{c[:username]}\n" if c.has_key?(:username)
|
96
|
+
output +="password=#{c[:password]}\n" if c.has_key?(:password)
|
97
|
+
output +="host=#{c[:host]}\n" if c.has_key?(:host)
|
98
|
+
output +="port=#{c[:port]}\n" if c.has_key?(:port)
|
99
|
+
end
|
100
|
+
|
101
|
+
def backup(t)
|
102
|
+
@task = t
|
103
|
+
cnf = mysql_cnf_upload(:backup)
|
104
|
+
database = config[:database]
|
105
|
+
# run backup
|
106
|
+
@task.on release_roles :all do
|
107
|
+
output_dir = fetch(:backup_dest_path)
|
108
|
+
|
109
|
+
# test for backup dir
|
110
|
+
execute "test -d \"#{output_dir}\" || mkdir -p \"#{output_dir}\""
|
111
|
+
|
112
|
+
# backup mysqldb
|
113
|
+
output = File.join(output_dir, 'mysql.sql.gz')
|
114
|
+
execute "mysqldump --defaults-file=#{cnf} #{database} | gzip > #{output}"
|
115
|
+
execute "chmod 600 #{output}"
|
116
|
+
|
117
|
+
# remove credentials
|
118
|
+
execute "rm #{cnf}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def templates(t)
|
123
|
+
@task = t
|
124
|
+
fetch(:mysql_templates, []).each do |c|
|
125
|
+
|
126
|
+
dest = File.join(fetch(:local_stage_path), c[:destination])
|
127
|
+
|
128
|
+
puts("Evaluting template '#{c[:template]}' to '#{dest}'")
|
129
|
+
|
130
|
+
# Read template
|
131
|
+
erb = ERB.new(
|
132
|
+
File.read(c[:template]),
|
133
|
+
nil,
|
134
|
+
'-',
|
135
|
+
)
|
136
|
+
|
137
|
+
File.open(dest, 'w') do |file|
|
138
|
+
namespace = Namespace.new({
|
139
|
+
config: config(),
|
140
|
+
stage: fetch(:stage),
|
141
|
+
})
|
142
|
+
file.write(erb.result(namespace.get_binding))
|
143
|
+
end
|
144
|
+
File.exist?(c[:destination])
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -107,7 +107,12 @@ module Capistrano
|
|
107
107
|
options += fetch(:rsync_options)
|
108
108
|
|
109
109
|
# Add remote options if needed
|
110
|
-
options += remote_options
|
110
|
+
options += remote_options
|
111
|
+
|
112
|
+
# Add supplied options
|
113
|
+
if not @rsync_options.nil?
|
114
|
+
options += @rsync_options
|
115
|
+
end
|
111
116
|
|
112
117
|
# Add source and destination
|
113
118
|
options += [src_path, dest_path]
|
@@ -22,6 +22,40 @@ set :application, '<MY_APP_NAME>'
|
|
22
22
|
# 'wwwroot/public_writable_dir' => '777',
|
23
23
|
#}
|
24
24
|
|
25
|
+
# Set mysql connnections settings
|
26
|
+
#set :mysql_connections, {
|
27
|
+
# :production => {
|
28
|
+
# :host => 'host1',
|
29
|
+
# :port => 3306,
|
30
|
+
# :database => 'dbname',
|
31
|
+
# :username => 'user1',
|
32
|
+
# :password => 'password',
|
33
|
+
# },
|
34
|
+
# :staging => {
|
35
|
+
# :host => 'host1',
|
36
|
+
# :port => 3306,
|
37
|
+
# :database => 'dbname',
|
38
|
+
# :username => 'user1',
|
39
|
+
# :password => 'password',
|
40
|
+
# },
|
41
|
+
#}
|
42
|
+
|
43
|
+
# Create files from database settings
|
44
|
+
#set :mysql_templates, [
|
45
|
+
# {
|
46
|
+
# :destination => 'wwwroot/wp-database.php',
|
47
|
+
# :template => 'config/wp-database.php.erb',
|
48
|
+
# }
|
49
|
+
#]
|
50
|
+
# Enable mysql templates
|
51
|
+
#before 'former03:local:fix_rights', 'former03:mysql:templates'
|
52
|
+
|
53
|
+
# Create mysqldump before deploy
|
54
|
+
#before 'former03:remote:release', 'former03:mysql:backup'
|
55
|
+
|
56
|
+
# Perform asset backup before deploy
|
57
|
+
#before 'former03:remote:release', 'former03:assets:backup'
|
58
|
+
|
25
59
|
# Execute custom task after local stage (example default task)
|
26
60
|
=begin
|
27
61
|
namespace :custom do
|
@@ -16,6 +16,19 @@ set :ssh_options, {
|
|
16
16
|
}
|
17
17
|
=end
|
18
18
|
|
19
|
+
# Sync database from production
|
20
|
+
# * use in non production stages only
|
21
|
+
# * performs mysqldump backup
|
22
|
+
#before 'former03:remote:release', 'former03:mysql:sync'
|
23
|
+
|
24
|
+
# Sync asstes from production
|
25
|
+
# * use in non production stages only
|
26
|
+
# * no backup is performed
|
27
|
+
=begin
|
28
|
+
set(:assets_source, '/path/to/productions/shared/')
|
29
|
+
before 'former03:remote:release', 'former03:assets:sync'
|
30
|
+
=end
|
31
|
+
|
19
32
|
# Destination directory for deployment
|
20
33
|
set :deploy_to, '<DESTDIR>'
|
21
34
|
|
@@ -19,6 +19,9 @@ namespace :former03 do
|
|
19
19
|
set :relative_symlinks, true
|
20
20
|
set :current_path_real_dir, false
|
21
21
|
|
22
|
+
set :mysql_connections, {}
|
23
|
+
set :mysql_templates, []
|
24
|
+
|
22
25
|
set :ensure_file_mode, nil
|
23
26
|
set :ensure_dir_mode, nil
|
24
27
|
set :ensure_path_mode, {}
|
@@ -169,6 +172,21 @@ namespace :former03 do
|
|
169
172
|
|
170
173
|
namespace :remote do
|
171
174
|
|
175
|
+
desc 'Cleanup remote backup'
|
176
|
+
task :backup_cleanup => :backup_prepare do
|
177
|
+
@backup.cleanup self
|
178
|
+
end
|
179
|
+
|
180
|
+
desc 'Prepare remote backup'
|
181
|
+
task :backup_prepare => :"deploy:new_release_path" do
|
182
|
+
@backup ||= Capistrano::Former03::Backup.new
|
183
|
+
@backup.prepare self
|
184
|
+
end
|
185
|
+
|
186
|
+
desc 'Do a remote backup'
|
187
|
+
task :backup => :backup_cleanup do
|
188
|
+
end
|
189
|
+
|
172
190
|
desc 'Check the deployment hosts'
|
173
191
|
task :check => [
|
174
192
|
:mkdir_stage,
|
@@ -277,4 +295,41 @@ namespace :former03 do
|
|
277
295
|
end
|
278
296
|
end
|
279
297
|
end
|
298
|
+
|
299
|
+
namespace :assets do
|
300
|
+
task :prepare do
|
301
|
+
@assets ||= Capistrano::Former03::Assets.new
|
302
|
+
end
|
303
|
+
|
304
|
+
desc 'Create a backup of assets'
|
305
|
+
task :backup => [:prepare, :'former03:remote:backup'] do
|
306
|
+
@assets.backup(self)
|
307
|
+
end
|
308
|
+
|
309
|
+
desc 'Sync production assets to my stage'
|
310
|
+
task :sync => [:prepare] do
|
311
|
+
@assets.sync(self)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
namespace :mysql do
|
316
|
+
task :prepare do
|
317
|
+
@mysql ||= Capistrano::Former03::MySQL.new
|
318
|
+
end
|
319
|
+
|
320
|
+
desc 'Sync production database to my stage'
|
321
|
+
task :sync => [:backup, :prepare] do
|
322
|
+
@mysql.sync(self)
|
323
|
+
end
|
324
|
+
|
325
|
+
desc 'Create a mysqldump of the remote databases'
|
326
|
+
task :backup => [:prepare, :'former03:remote:backup'] do
|
327
|
+
@mysql.backup(self)
|
328
|
+
end
|
329
|
+
|
330
|
+
desc 'Create mysql files from templates'
|
331
|
+
task :templates => :prepare do
|
332
|
+
@mysql.templates(self)
|
333
|
+
end
|
334
|
+
end
|
280
335
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capistrano-former03
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Simon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capistrano
|
@@ -96,8 +96,11 @@ files:
|
|
96
96
|
- bin/f03capinstall
|
97
97
|
- capistrano-former03.gemspec
|
98
98
|
- lib/capistrano/former03.rb
|
99
|
+
- lib/capistrano/former03/assets.rb
|
100
|
+
- lib/capistrano/former03/backup.rb
|
99
101
|
- lib/capistrano/former03/deploy_command.rb
|
100
102
|
- lib/capistrano/former03/install.rb
|
103
|
+
- lib/capistrano/former03/mysql.rb
|
101
104
|
- lib/capistrano/former03/rsync.rb
|
102
105
|
- lib/capistrano/former03/symlink.rb
|
103
106
|
- lib/capistrano/former03/templates/Capfile
|