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