reploy 0.1

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