reploy 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/reploy.rb +176 -0
- data/lib/reploy/recipes/git.rb +34 -0
- data/lib/reploy/recipes/merb.rb +57 -0
- data/lib/reploy/recipes/rsync.rb +16 -0
- data/lib/reploy/recipes/scp.rb +39 -0
- metadata +59 -0
data/lib/reploy.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'popen4'
|
4
|
+
|
5
|
+
=begin
|
6
|
+
Reploy is simple deploying tool, similar to Vlad or Capistrano, but even simplier and faster.
|
7
|
+
|
8
|
+
How to use:
|
9
|
+
|
10
|
+
* Install a gem
|
11
|
+
* Write a config
|
12
|
+
* rake reploy:deploy
|
13
|
+
=end
|
14
|
+
|
15
|
+
class Reploy
|
16
|
+
def config
|
17
|
+
@@config
|
18
|
+
end
|
19
|
+
|
20
|
+
def config=(v)
|
21
|
+
@@config=(v)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.[](k)
|
25
|
+
@@config[k]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.[]=(k,v)
|
29
|
+
@@config[k] = v
|
30
|
+
end
|
31
|
+
|
32
|
+
# load Reploy with given config
|
33
|
+
def Reploy.load(opts)
|
34
|
+
@@config = opts
|
35
|
+
@@config.default = {}
|
36
|
+
if opts[:recipes].is_a? Array
|
37
|
+
opts[:recipes].each do |r|
|
38
|
+
if r =~ /\.rb$/ # It's local
|
39
|
+
require r
|
40
|
+
else
|
41
|
+
require "reploy/recipes/#{r}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
@@singleton = Reploy.new
|
46
|
+
self.add_tasks
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.add_tasks
|
50
|
+
namespace :reploy do
|
51
|
+
# tasks for calling reploy from rake
|
52
|
+
# we can't (properly) override rake tasks, so it's methods. :(
|
53
|
+
%w|setup transfer deploy start stop restart nuke|.each do |t|
|
54
|
+
task t.to_sym do
|
55
|
+
self.send(t.to_sym)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.method_missing(s, *args, &block)
|
62
|
+
if args.empty?
|
63
|
+
@@singleton.send(s)
|
64
|
+
else
|
65
|
+
@@singleton.send(s, args)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Run the command on remote host
|
70
|
+
def run(command)
|
71
|
+
@ssh ||= Net::SSH.start(config[:host],config[:user])
|
72
|
+
puts "#{config[:user]}@#{config[:host]}# #{command}"
|
73
|
+
@ssh.open_channel do |chan|
|
74
|
+
chan.on_request("exit-status") do |ch,data|
|
75
|
+
r = data.read_long
|
76
|
+
if r != 0
|
77
|
+
raise "Process failed with exit status #{r}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
chan.on_data do |ch, data|
|
81
|
+
data.each_line do |l|
|
82
|
+
puts "[r] #{l}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
chan.on_extended_data do |ch, type, data|
|
86
|
+
if type == 1
|
87
|
+
data.each_line do |l|
|
88
|
+
puts "[r] [e] #{l}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
chan.exec command do |ch, success|
|
93
|
+
raise "Running command failed." unless success
|
94
|
+
end
|
95
|
+
chan.wait
|
96
|
+
end
|
97
|
+
@ssh.loop
|
98
|
+
end
|
99
|
+
|
100
|
+
# Run the command on remote host, in "current" directory
|
101
|
+
def run_in_current(command)
|
102
|
+
run("cd #{@current} && #{command}")
|
103
|
+
end
|
104
|
+
|
105
|
+
# Create essential dir structure on remote host
|
106
|
+
def setup
|
107
|
+
run("mkdir -p "+(%w|current shared/log shared/tmp backups storage|.map {|v| "#{config[:path]}/#{v}"}.join(" ")))
|
108
|
+
end
|
109
|
+
|
110
|
+
# Check out all the files to be bulk-copied in directory, return its path
|
111
|
+
# This is naive implementation, this method is overridden in recipes like "git", which use version control to provide filelist
|
112
|
+
def checkout
|
113
|
+
yield '.' # dumb
|
114
|
+
end
|
115
|
+
|
116
|
+
# Run the command on local host
|
117
|
+
def run_local(command)
|
118
|
+
puts "localhost# #{command}"
|
119
|
+
status = POpen4::popen4 command do |stdout,stderr|
|
120
|
+
stdout.readlines.each do |l|
|
121
|
+
puts "[l] #{l}"
|
122
|
+
end
|
123
|
+
stderr.readlines.each do |l|
|
124
|
+
puts "[l] [e] #{l}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
raise "Process aborted in totally wrong way" if status.exitstatus != 0
|
128
|
+
end
|
129
|
+
|
130
|
+
# Prepare for transfer: create random named directory wih contents of current
|
131
|
+
def prepare
|
132
|
+
@current = "#{config[:path]}/tmp#{rand(66666)}"
|
133
|
+
run "cp -r #{config[:path]}/current #{@current}"
|
134
|
+
end
|
135
|
+
|
136
|
+
# Perform the file transfer: prepare, then sync, then copy files, then rename directory to current
|
137
|
+
# In case anything goes wrong, rollback and remove traces
|
138
|
+
def transfer
|
139
|
+
begin
|
140
|
+
prepare
|
141
|
+
checkout do |dir|
|
142
|
+
sync dir
|
143
|
+
end
|
144
|
+
copy
|
145
|
+
finalize
|
146
|
+
rescue Exception => e
|
147
|
+
puts "Exception: #{e}. Cleaning up..."
|
148
|
+
cleanup
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Finalize file transfer: rename temporary directory to current, remove or backup original "current"
|
153
|
+
def finalize
|
154
|
+
if config[:leave_backups] == true
|
155
|
+
run "mv #{config[:path]}/current #{config[:path]/backups}/#{Time.now.strftime("%d_%m_%Y_%H_%M")} && mv #{@current} #{config[:path]}/current"
|
156
|
+
else
|
157
|
+
run "rm -r #{config[:path]}/current && mv #{@current} #{config[:path]}/current"
|
158
|
+
end
|
159
|
+
@current = "#{config[:path]}/current"
|
160
|
+
end
|
161
|
+
|
162
|
+
# Remove temporary directory
|
163
|
+
def cleanup
|
164
|
+
run "rm -r #{@current}" if @current
|
165
|
+
end
|
166
|
+
|
167
|
+
# rm -rf in target directory, then setup
|
168
|
+
def nuke
|
169
|
+
run "rm -r #{config[:path]}/*"
|
170
|
+
setup
|
171
|
+
end
|
172
|
+
|
173
|
+
def deploy
|
174
|
+
transfer
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Git recipe for Reploy
|
2
|
+
|
3
|
+
class Reploy
|
4
|
+
def checkout
|
5
|
+
raise "That really shouldn't happen." if Reploy[:git].has_key? :from
|
6
|
+
temp_name = "#{Dir.tmpdir}/reploy#{rand(66666)}"
|
7
|
+
FileUtils.mkdir_p temp_name
|
8
|
+
`git archive HEAD | tar xf - -C #{temp_name}`
|
9
|
+
yield temp_name
|
10
|
+
FileUtils.rm_rf temp_name
|
11
|
+
end
|
12
|
+
if Reploy[:git].has_key? :from
|
13
|
+
def prepare
|
14
|
+
@current = "#{config[:path]}/tmp#{rand(66666)}"
|
15
|
+
run "mkdir -p #{@current}"
|
16
|
+
end
|
17
|
+
def transfer
|
18
|
+
begin
|
19
|
+
prepare
|
20
|
+
# A handy optimization, if repo is local, we can do checkout instead of cloning
|
21
|
+
if Reploy[:git][:from] =~ %r{^/}
|
22
|
+
run_in_current "GIT_DIR='#{Reploy[:git][:from]}' git archive HEAD | tar xf -"
|
23
|
+
else
|
24
|
+
run "export GIT_DIR='#{config[:path]}/storage/.git' && (test -d #{config[:path]}/storage/.git && git fetch master) || (mkdir -p #{config[:path]}/storage/.git && git clone --bare) && git archive HEAD | tar xf -"
|
25
|
+
end
|
26
|
+
copy
|
27
|
+
finalize
|
28
|
+
rescue Exception => e
|
29
|
+
puts "Exception: #{e}. Cleaning up..."
|
30
|
+
cleanup
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class Reploy
|
2
|
+
def merb_params_map
|
3
|
+
{ :host => "-h",
|
4
|
+
:port => "-p",
|
5
|
+
:adapter => "-a",
|
6
|
+
:environment => "-e"
|
7
|
+
}
|
8
|
+
end
|
9
|
+
def merb_get_args
|
10
|
+
(config[:merb] or {}).map do |k,v|
|
11
|
+
if merb_params_map[k]
|
12
|
+
"#{merb_params_map[k]} #{v}"
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end.join(" ")
|
17
|
+
end
|
18
|
+
|
19
|
+
def restart
|
20
|
+
run_in_current "merb -K #{config[:merb][:port]} && merb -d #{merb_get_args}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
run_in_current "merb -d #{merb_get_args}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
run_in_current "merb -K #{config[:merb][:port]}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def autoupgrade
|
32
|
+
run_in_current "MERB_ENV=#{config[:merb][:environment]} rake db:autoupgrade"
|
33
|
+
end
|
34
|
+
|
35
|
+
def symlink_shared
|
36
|
+
run("ln -s #{config[:path]}/shared/* #{config[:path]}/current/")
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def deploy
|
41
|
+
transfer
|
42
|
+
symlink_shared
|
43
|
+
restart
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# recipe-specific tasks
|
48
|
+
|
49
|
+
desc "Perform Merb schema autoupgrade"
|
50
|
+
task "reploy:merb:autoupgrade" do
|
51
|
+
Reploy.autoupgrade
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Deploy Merb"
|
55
|
+
task "reploy:merb:deploy" do
|
56
|
+
Reploy.deploy
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Rsync recipe. Implements sync and copy
|
2
|
+
|
3
|
+
class Reploy
|
4
|
+
def sync(dir)
|
5
|
+
run_local "rsync -avz --delete #{dir}/ #{config[:user]}@#{config[:host]}:#{@current}/"
|
6
|
+
end
|
7
|
+
|
8
|
+
def copy
|
9
|
+
if config[:copy].is_a? Array
|
10
|
+
config[:copy].each do |a|
|
11
|
+
k,v = a
|
12
|
+
run_local "rsync -tvaz #{k} #{config[:user]}@#{config[:host]}:#{@current}/#{v}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'net/scp'
|
2
|
+
|
3
|
+
class Reploy
|
4
|
+
|
5
|
+
def prepare
|
6
|
+
@current = "#{config[:path]}/tmp#{rand(66666)}"
|
7
|
+
run "mkdir -p #{@current}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def sync(dir)
|
11
|
+
if config[:scp].has_key? :tar
|
12
|
+
if %w|gz gzip|.include? config[:scp][:tar]
|
13
|
+
pipes = ["| gzip", "| gunzip" ]
|
14
|
+
elsif %w|bz2 bzip2|.include? config[:scp][:tar]
|
15
|
+
pipes = ["| bzip2", "| bunzip2" ]
|
16
|
+
elsif %w|lzma|.include? config[:scp][:tar]
|
17
|
+
pipes = ["| lzma", "| unlzma" ]
|
18
|
+
else
|
19
|
+
pipes = ["",""]
|
20
|
+
end
|
21
|
+
archive_name = "reploy#{rand(66666)}.tmp"
|
22
|
+
run_local "cd #{dir} && tar c . #{pipes[0]} > #{Dir.tmpdir}/#{archive_name}"
|
23
|
+
run_in_current "rm -r *"
|
24
|
+
Net::SCP.upload!(config[:host], config[:user],"#{Dir.tmpdir}/#{archive_name}", "#{config[:path]}/current")
|
25
|
+
run_in_current "cat #{archive_name} #{pipes[1]} | tar xf -"
|
26
|
+
run_in_current "rm #{archive_name}"
|
27
|
+
else
|
28
|
+
run_in_current "rm -r *"
|
29
|
+
Net::SCP.upload!(config[:host], config[:user],"#{Dir.tmpdir}/#{archive_name}", "#{config[:path]}/current", :recursive)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def copy(files)
|
34
|
+
files.each do |a|
|
35
|
+
k,v = a
|
36
|
+
Net::SCP.upload!(config[:host], config[:user],k, "#{config[:path]}/current/#{v}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: reploy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Voker57
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-06 00:00:00 +04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Simple tool for automation deployment
|
17
|
+
email: voker57@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/reploy/recipes/git.rb
|
26
|
+
- lib/reploy/recipes/merb.rb
|
27
|
+
- lib/reploy/recipes/scp.rb
|
28
|
+
- lib/reploy/recipes/rsync.rb
|
29
|
+
- lib/reploy.rb
|
30
|
+
has_rdoc: true
|
31
|
+
homepage: http://bitcheese.net/wiki/reploy
|
32
|
+
licenses: []
|
33
|
+
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
requirements: []
|
52
|
+
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 1.3.4
|
55
|
+
signing_key:
|
56
|
+
specification_version: 3
|
57
|
+
summary: Simple deploying tool
|
58
|
+
test_files: []
|
59
|
+
|