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.
@@ -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
+