AppTower-ubistrano 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 AppTower
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,110 @@
1
+ Ubistrano
2
+ =========
3
+
4
+ Provision and deploy to an Ubuntu/God/Apache/Passenger stack using Capistrano.
5
+
6
+ Goals
7
+ -----
8
+
9
+ * Provision a solid Ubuntu Hardy application server in one command (<code>cap ubuntu</code>)
10
+ * Deploy PHP, Rails, and Sinatra apps
11
+ * Be descriptive about what is going on and allow the user to opt out
12
+ * Simplify the deploy.rb file
13
+
14
+ The stack
15
+ ---------
16
+
17
+ * Apache
18
+ * Git
19
+ * MySQL
20
+ * MySQLTuner
21
+ * Perl
22
+ * PHP
23
+ * Postfix (relay)
24
+ * Ruby
25
+ * RubyGems
26
+ * Passenger (mod\_rails)
27
+ * God
28
+ * Rails
29
+ * Sinatra
30
+ * Sphinx
31
+
32
+
33
+ Getting started
34
+ ---------------
35
+
36
+ ### Install gem
37
+
38
+ gem install AppTower-ubistrano
39
+
40
+ ### Capify your project
41
+
42
+ capify .
43
+
44
+ ### Edit config/deploy.rb
45
+
46
+ <pre>
47
+ set :ubistrano, {
48
+ :application => :my_app,
49
+ :platform => :rails, # :php, :rails, :sinatra
50
+ :repository => 'git@github.com:user/my-app.git',
51
+
52
+ :production => {
53
+ :domain => [ 'myapp.com', 'www.myapp.com' ],
54
+ :host => '127.0.0.1'
55
+ },
56
+
57
+ :staging => {
58
+ :domain => 'staging.myapp.com',
59
+ :host => '127.0.0.1'
60
+ }
61
+ }
62
+
63
+ require 'ubistrano'
64
+ </pre>
65
+
66
+ Ubistrano uses the same Capistrano options you've come to love, but provides defaults and a few extra options as well.
67
+
68
+ Feel free to add standard options like :user to the stage groups.
69
+
70
+ Set up your Ubuntu Hardy server
71
+ -------------------------------
72
+
73
+ ### From your app directory
74
+
75
+ <pre>cap ubuntu</pre>
76
+
77
+ ### Example output
78
+
79
+ <pre>
80
+ =================================================================================
81
+ Let's set up an Ubuntu server! (Tested with 8.04 LTS Hardy)
82
+
83
+ With each task, Ubistrano will describe what it is doing, and wait for a yes/no.
84
+
85
+ =================================================================================
86
+ Have you already created the user defined in deploy.rb? (y/n)
87
+ </pre>
88
+
89
+ Deploy your app
90
+ ---------------
91
+
92
+ All apps should have a <code>public</code> directory.
93
+
94
+ ### First deploy
95
+
96
+ cap deploy:first
97
+
98
+ ### Subsequent deploys
99
+
100
+ cap deploy
101
+
102
+
103
+ Deploy to staging
104
+ -----------------
105
+
106
+ Use any capistrano task, but replace `cap` with `cap staging`.
107
+
108
+ ### Example
109
+
110
+ cap staging deploy:first
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+
3
+ task :default => 'ubistrano.gemspec'
4
+
5
+ file 'ubistrano.gemspec' => FileList['{example,lib,templates}/**','Rakefile'] do |f|
6
+ # read spec file and split out manifest section
7
+ spec = File.read(f.name)
8
+ parts = spec.split(" # = MANIFEST =\n")
9
+ fail 'bad spec' if parts.length != 3
10
+ # determine file list from git ls-files
11
+ files = `git ls-files`.
12
+ split("\n").
13
+ sort.
14
+ reject{ |file| file =~ /^\./ }.
15
+ reject { |file| file =~ /^doc/ }.
16
+ map{ |file| " #{file}" }.
17
+ join("\n")
18
+ # piece file back together and write...
19
+ parts[1] = " s.files = %w[\n#{files}\n ]\n"
20
+ spec = parts.join(" # = MANIFEST =\n")
21
+ File.open(f.name, 'w') { |io| io.write(spec) }
22
+ puts "Updated #{f.name}"
23
+ end
data/changelog.rdoc ADDED
@@ -0,0 +1,25 @@
1
+
2
+ Version 2.0.0
3
+ * Tested on Ubuntu 8.04 LTS Hardy
4
+ * Deploys PHP, Rails, and Sinatra apps
5
+ * Redux version with new stack
6
+ * Apache
7
+ * Git
8
+ * God
9
+ * MySQL
10
+ * Passenger (mod_rails)
11
+ * Rails
12
+ * Ruby Enterprise Edition
13
+
14
+ Version 1.1.0
15
+ * Tested on Debian Etch
16
+ * Git
17
+ * Nginx
18
+ * Mongrel cluster
19
+ * Monit
20
+ * MySQL
21
+ * PHP (Nginx w/ spawn-fcgi)
22
+ * Rails
23
+ * Ruby
24
+ * RubyGems
25
+ * Sphinx
data/example/deploy.rb ADDED
@@ -0,0 +1,17 @@
1
+ set :ubistrano, {
2
+ :application => :my_app,
3
+ :platform => :rails, # :php, :rails, :sinatra
4
+ :repository => 'git@github.com:user/my-app.git',
5
+
6
+ :production => {
7
+ :domain => [ 'myapp.com', 'www.myapp.com' ],
8
+ :host => '127.0.0.1'
9
+ },
10
+
11
+ :staging => {
12
+ :domain => 'staging.myapp.com',
13
+ :host => '127.0.0.1'
14
+ }
15
+ }
16
+
17
+ require 'ubistrano'
data/lib/ubistrano.rb ADDED
@@ -0,0 +1,71 @@
1
+
2
+ # Require helpers and recipes
3
+ Dir["#{File.dirname(__FILE__)}/ubistrano/*.rb"].each { |f| require f }
4
+
5
+
6
+ Capistrano::Configuration.instance(:must_exist).load do
7
+
8
+ # Reference ROOT when namespaces clash
9
+ ROOT = self
10
+
11
+ # Default package versions
12
+ ubistrano[:versions] ||= {}
13
+ ubistrano[:versions].merge!(
14
+ :git => '1.6.0.4',
15
+ :mysecureshell => '1.1',
16
+ :rails => '2.2.2',
17
+ :ruby => '1.8.7-p72',
18
+ :rubygems => '1.3.1',
19
+ :sphinx => '0.9.8.1'
20
+ )
21
+
22
+ # Merge ubistrano hash with capistrano
23
+ ubistrano.each do |key, value|
24
+ value.respond_to?(:keys) ?
25
+ value.each { |k, v| set "#{key}_#{k}".intern, v } :
26
+ set(key, value)
27
+ end
28
+
29
+ # Set default capistrano values
30
+ set :db_user, fetch(:db_user, 'app')
31
+ set :db_pass, fetch(:db_pass, '')
32
+ set :platform, fetch(:platform, :rails)
33
+ set :port, fetch(:port, 22)
34
+ set :stage, fetch(:stage, :production)
35
+ set :use_sudo, fetch(:use_sudo, false)
36
+ set :user, fetch(:user, 'deploy')
37
+ set :sources, fetch(:sources, {
38
+ :git => "http://kernel.org/pub/software/scm/git/git-#{versions_git}.tar.gz",
39
+ :mysecureshell => "http://internap.dl.sourceforge.net/sourceforge/mysecureshell/MySecureShell-#{versions_mysecureshell}_source.tgz",
40
+ :mysqltuner => "http://mysqltuner.com/mysqltuner.pl",
41
+ :ruby => "ftp://ftp.ruby-lang.org/pub/ruby/#{versions_ruby.split('.')[0..1].join('.')}/ruby-#{versions_ruby}.tar.gz",
42
+ :rubygems => "http://rubyforge.org/frs/download.php/45905/rubygems-#{versions_rubygems}.tgz",
43
+ :sphinx => "http://www.sphinxsearch.com/downloads/sphinx-#{versions_sphinx}.tar.gz"
44
+ })
45
+
46
+ # Rails plugins
47
+ set :app_helpers, fetch(:app_helpers, false)
48
+ set :rails_widget, fetch(:rails_widget, false)
49
+ set :ultrasphinx, fetch(:ultrasphinx, false)
50
+ set :thinking_sphinx, fetch(:thinking_sphinx, false)
51
+ set :attachment_fu, fetch(:attachment_fu, false)
52
+ set :asset_packager, fetch(:asset_packager, false)
53
+ after('deploy:update_code', 'rails:config:app_helpers') if app_helpers
54
+ after('deploy:update_code', 'rails:config:asset_packager') if asset_packager
55
+ after('deploy:update_code', 'rails:config:attachment_fu') if attachment_fu
56
+ after('deploy:update_code', 'rails:config:rails_widget') if rails_widget
57
+ after('deploy:update_code', 'rails:config:ultrasphinx') if ultrasphinx
58
+ after('deploy:update_code', 'rails:config:thinking_sphinx') if thinking_sphinx
59
+
60
+ # Git by default
61
+ set :scm, :git
62
+ set :deploy_via, :remote_cache
63
+ set :repository_cache, 'git_cache'
64
+ ssh_options[:paranoid] = false
65
+
66
+ # Events
67
+ on :before, 'setup_stage', :except => [ :staging, :testing ] # Executed before every task
68
+ after('deploy:update_code', 'rails:config:to_app' ) if platform == :rails
69
+ after('deploy:update_code', 'sinatra:config:to_app') if platform == :sinatra
70
+
71
+ end
@@ -0,0 +1,38 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+
3
+ namespace :apache do
4
+ desc "Reload apache settings"
5
+ task :reload, :roles => :web do
6
+ sudo_puts "/etc/init.d/apache2 reload"
7
+ end
8
+
9
+ desc "Restart apache"
10
+ task :restart, :roles => :web do
11
+ sudo_puts "/etc/init.d/apache2 restart"
12
+ end
13
+
14
+ namespace :virtual_host do
15
+ desc "Create a new virtual host"
16
+ task :create, :roles => :web do
17
+ upload_from_erb "/etc/apache2/sites-available/#{application}_#{stage}", binding, :name => 'virtual_host', :folder => 'ubuntu'
18
+ end
19
+
20
+ desc "Enable a virtual host"
21
+ task :enable, :roles => :web do
22
+ sudo_puts "a2ensite #{application}_#{stage}"
23
+ end
24
+
25
+ desc "Destroy a virtual host"
26
+ task :destroy, :roles => :web do
27
+ apache.virtual_host.disable
28
+ sudo_each "rm /etc/apache2/sites-available/#{application}_#{stage}"
29
+ end
30
+
31
+ desc "Disable a virtual host"
32
+ task :disable, :roles => :web do
33
+ sudo_puts "a2dissite #{application}_#{stage}"
34
+ end
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,52 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+
3
+ namespace :deploy do
4
+ desc "Restart application"
5
+ task :restart, :roles => :app, :except => { :no_release => true } do
6
+ run_each [
7
+ "mkdir #{current_path}/tmp",
8
+ "touch #{current_path}/tmp/restart.txt"
9
+ ]
10
+ end
11
+
12
+ desc "Start application"
13
+ task :start, :roles => :app do
14
+ apache.virtual_host.enable
15
+ end
16
+
17
+ desc "Stop application"
18
+ task :stop, :roles => :app do
19
+ apache.virtual_host.disable
20
+ end
21
+
22
+ desc "Deploy for the first time"
23
+ task :first, :roles => :app do
24
+ sudo_each [
25
+ "mkdir -p #{base_dir}",
26
+ "chown -R #{user}:#{user} #{base_dir}"
27
+ ]
28
+ mysql.create.db
29
+ deploy.setup
30
+ deploy.update
31
+ apache.virtual_host.create
32
+ case platform
33
+ when :rails
34
+ rails.config.default
35
+ deploy.migrate
36
+ when :sinatra
37
+ sinatra.config.default
38
+ sinatra.install
39
+ end
40
+ deploy.start
41
+ apache.reload
42
+ end
43
+
44
+ desc "Stop servers and destroy all files"
45
+ task :destroy, :roles => :app do
46
+ deploy.stop
47
+ sudo "rm -Rf #{deploy_to}"
48
+ mysql.destroy.db
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,29 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+
3
+ namespace :gems do
4
+ desc "List gems on remote server"
5
+ task :list, :roles => :app do
6
+ run_puts "gem list"
7
+ end
8
+
9
+ desc "Update gems on remote server"
10
+ task :update, :roles => :app do
11
+ sudo_each [
12
+ "gem update --system",
13
+ "gem update"
14
+ ]
15
+ end
16
+
17
+ desc "Install a remote gem"
18
+ task :install, :roles => :app do
19
+ gem_install ask('Enter the name of the gem to install:')
20
+ end
21
+
22
+ desc "Uninstall a remote gem"
23
+ task :uninstall, :roles => :app do
24
+ gem_name = ask 'Enter the name of the gem to remove:'
25
+ sudo "gem uninstall #{gem_name}"
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,237 @@
1
+ require 'erb'
2
+
3
+ Capistrano::Configuration.instance(:must_exist).load do
4
+
5
+ # Install
6
+
7
+ def gem_install(name, options='')
8
+ sudo_puts "gem install #{name} #{options} --no-rdoc --no-ri -q"
9
+ end
10
+
11
+ def install_source(source)
12
+ path, source = unpack_source source
13
+ yield path
14
+ sudo "rm -Rf #{source}"
15
+ end
16
+
17
+ def make_install(path)
18
+ ";cd #{path} && ./configure && make && sudo make install"
19
+ end
20
+
21
+ def unpack_source(source)
22
+ url = sources[source]
23
+ name = File.basename url
24
+ src = "/home/#{user}/sources"
25
+ base = nil
26
+ [ 'tar.gz', 'tgz' ].each do |ext|
27
+ base = name[0..((ext.length + 2) * -1)] if name.include?(ext)
28
+ end
29
+ run_each [
30
+ "mkdir -p #{src}",
31
+ "cd #{src} && wget --quiet #{url}",
32
+ "tar -xzvf #{src}/#{name} -C #{src}"
33
+ ]
34
+ [ "#{src}/#{base}", src ]
35
+ end
36
+
37
+
38
+ # Files
39
+
40
+ def add_line(file, *lines)
41
+ lines.each do |line|
42
+ sudo_each "echo \"#{line}\" | sudo tee -a #{file}"
43
+ end
44
+ end
45
+
46
+ def change_line(file, from, to)
47
+ sudo_each "sed 's/#{from}/#{to}/' #{file}"
48
+ end
49
+
50
+ def remove_line(file, *lines)
51
+ lines.each do |line|
52
+ change_line file, line, ''
53
+ end
54
+ end
55
+
56
+ def get_ssh_keys
57
+ keys = Dir[File.expand_path('~/.ssh/*.pub')].collect do |f|
58
+ File.open(f).collect { |line| line.strip.empty? ? nil : line.strip }.compact
59
+ end
60
+ keys.flatten.join("\n").strip
61
+ end
62
+
63
+ def upload_from_erb(destination, bind=nil, options={})
64
+ # options[ :chown => owner of file (default: deploy user),
65
+ # :chmod => 0644 etc
66
+ # :folder => 'postfix' etc,
67
+ # :name => name of template if differs from destination ]
68
+ if destination.respond_to?(:uniq)
69
+ destination.each { |d| upload_from_erb d, bind, options }
70
+ else
71
+ template = File.basename destination
72
+ template = template[1..-1] if template[0..0] == '.'
73
+ folder = options[:folder] ? options[:folder] + '/' : ''
74
+ template = File.expand_path("../../templates/#{folder}#{options[:name]||template}.erb", File.dirname(__FILE__))
75
+ template = File.read template
76
+ sudo "touch #{destination}"
77
+ sudo "chown #{user} #{destination}"
78
+ put ERB.new(template).result(bind || binding), destination
79
+ sudo("chown #{options[:chown]} #{destination}") if options[:chown]
80
+ sudo("chmod #{options[:chmod]} #{destination}") if options[:chmod]
81
+ end
82
+ end
83
+
84
+
85
+ # MySQL
86
+
87
+ def mysql_run(sql)
88
+ if sql.respond_to?(:uniq)
89
+ sql.each { |s| mysql_run s }
90
+ else
91
+ run "echo \"#{sql}\" | #{mysql_call}"
92
+ end
93
+ end
94
+
95
+ def mysql_call
96
+ @mysql_root_password = @mysql_root_password || ask("Password for mysql root:")
97
+ "mysql -u root --password=#{@mysql_root_password}"
98
+ end
99
+
100
+
101
+ # Questions
102
+
103
+ def ask(question, default='')
104
+ question = "\n" + question.join("\n") if question.respond_to?(:uniq)
105
+ answer = Capistrano::CLI.ui.ask(space(question)).strip
106
+ answer.empty? ? default : answer
107
+ end
108
+
109
+ def yes(question)
110
+ question = "\n" + question.join("\n") if question.respond_to?(:uniq)
111
+ question += ' (y/n)'
112
+ ask(question).downcase.include? 'y'
113
+ end
114
+
115
+ def space(str)
116
+ "\n#{'=' * 90}\n#{str}"
117
+ end
118
+
119
+
120
+ # Runners
121
+
122
+ def run_each(*args, &block)
123
+ cmd = args[0]
124
+ sudo = args[1]
125
+ if cmd.respond_to?(:uniq)
126
+ cmd.each { |c| run_each c, sudo, &block }
127
+ elsif sudo
128
+ puts space("sudo #{cmd}")
129
+ sudo(cmd) { |ch, st, data| block.call(data) if block }
130
+ else
131
+ puts space(cmd)
132
+ run(cmd) { |ch, st, data| block.call(data) if block }
133
+ end
134
+ end
135
+
136
+ def sudo_each(cmds, &block)
137
+ run_each cmds, true, &block
138
+ end
139
+
140
+ def run_puts(cmds, &block)
141
+ run_each(cmds) { |data| puts data }
142
+ end
143
+
144
+ def sudo_puts(cmds, &block)
145
+ sudo_each(cmds) { |data| puts data }
146
+ end
147
+
148
+
149
+ # Messages
150
+
151
+ def msg(type)
152
+ case type
153
+ when :about_templates
154
+ "Let's set up an Ubuntu server! (Tested with 8.04 LTS Hardy)
155
+
156
+ With each task, Ubistrano will describe what it is doing, and wait for a yes/no."
157
+ when :aptitude_default
158
+ "Do you want me to run aptitude update, upgrade, and install build-essential?
159
+ If not, instructions for doing it manually will be displayed."
160
+ when :aptitude_instructions
161
+ "Please run these manually:
162
+ sudo aptitude update
163
+ sudo aptitude upgrade
164
+ sudo aptitude build-essential"
165
+ when :create_keys
166
+ "May I generate an rsa ssh key pair in your ~/.ssh folder?"
167
+ when :create_server_keys
168
+ "May I generate an rsa ssh key pair in the server's ~/.ssh folder?
169
+ The public key will be displayed."
170
+ when :god
171
+ "May I install God?"
172
+ when :god_apache
173
+ "Would you like God to monitor apache?
174
+ See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/ubuntu/apache.god.erb"
175
+ when :god_mysql
176
+ "Would you like God to monitor mysql?
177
+ See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/ubuntu/mysql.god.erb"
178
+ when :god_apache
179
+ "Would you like God to monitor sshd?
180
+ See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/ubuntu/sshd.god.erb"
181
+ when :have_keys
182
+ "Do you have rsa key pairs installed on your system? (see ~/.ssh/id_rsa)"
183
+ when :iptables
184
+ "May I update your server's iptables, limiting access to SSH, HTTP, HTTPS, and ping only?
185
+ See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/ubuntu/iptables.rules.erb"
186
+ when :logrotate
187
+ "May I add a logrotate entry for this application?
188
+ See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/log/rotate.conf.erb"
189
+ when :mysqltuner
190
+ "Would you like to install MySQLTuner and receive instructions for running it?"
191
+ when :mysqltuner_instructions
192
+ "Please ssh to your server and run `sudo mysqltuner`.
193
+ Continue?"
194
+ when :passenger
195
+ "Please run `sudo passenger-install-apache2-module` manually.
196
+ The apache config file is found at /etc/apache2/apache2.conf.
197
+ Reload apache?"
198
+ when :secure_mysql
199
+ "It is highly recommended you run mysql_secure_installation manually.
200
+ See http://dev.mysql.com/doc/refman/5.1/en/mysql-secure-installation.html
201
+ Continue?"
202
+ when :ssh_keys_create
203
+ "Create rsa ssh key pairs locally or remotely? (default: remote)"
204
+ when :ssh_keys_upload
205
+ "Press enter to copy all public keys (~/.ssh/*.pub), or paste a key: "
206
+ when :sshd_config
207
+ "May I update your server's sshd_config with the following settings?
208
+ Port #{port}
209
+ PermitRootLogin no
210
+ X11Forwarding no
211
+ UsePAM no
212
+ UseDNS no
213
+ "
214
+ when :sudoers
215
+ "May I add sudo-without-password privileges for the deploy user?
216
+ I will have to edit /etc/sudoers."
217
+ when :ubuntu_restart
218
+ "Would you like to restart the server now?"
219
+ when :ubuntu_finished
220
+ "That's it! Glad you made it.
221
+
222
+ Use `cap deploy:first` to set up your PHP, Rails, or Sinatra app.
223
+ Use `cap deploy` for all subsequent deploys.
224
+
225
+ "
226
+ when :upload_keys
227
+ "May I copy all of your public keys in ~/.ssh to the server's authorized_keys?"
228
+ when :visudo
229
+ "Please run the following commands:
230
+ ssh root@#{host}:#{port}
231
+ adduser #{user}
232
+
233
+ "
234
+ end
235
+ end
236
+
237
+ end