caplets 1.0.0

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/CHANGELOG ADDED
@@ -0,0 +1,2 @@
1
+ == 1.0.0
2
+ * Initial public release
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Dean Strelau, Mint Digital
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/MODULES.md ADDED
@@ -0,0 +1,148 @@
1
+ Caplets Modules
2
+ ===============
3
+
4
+ caplets/deploy
5
+ --------------
6
+
7
+ #### Variables
8
+
9
+ `:bin_cmd` - Command used to execute binaries within the context of the app.
10
+ For instance, `caplets/bundle` sets this to `gem exec`. (default: `nil`)
11
+
12
+ `:environment` - The environment for this deployment, used as RAILS_ENV among
13
+ other things. (default: `production`)
14
+
15
+ `:required_children` - Directories that need to exist in the project root.
16
+ These will be created during `deploy:setup`. This is the caplets replacement
17
+ of `:shared_children`. (default: `%w[config log public tmp/pids]`)
18
+
19
+ `:server_processes` - Number of backend processes (ie, mongrels or unicorn
20
+ workers) to run. (default: `2`)
21
+
22
+ `:server_port` - Port number on which to bind backend processes.
23
+ (default: `8000`)
24
+
25
+ `:user` - UNIX user as which to login and run deploys. (default: `deploy`)
26
+
27
+ ### caplets/bundle
28
+
29
+ Bundler 0.9+ support. By default, run after every code update, this `bundle
30
+ install`s gems into the project (not into system gems to avoid using `sudo`).
31
+
32
+ #### Variables
33
+
34
+ `:bundle_exclude` - Bundler groups to exclude from installation. Passed to
35
+ bundler's `--without` switch. (default: `%w[development test]`)
36
+
37
+ `:bundle_roles` - The server roles on which to bundle. (default: `[:app]`)
38
+
39
+ `:bundle_to` - Subdirectory of the application in which to put installed gems.
40
+ Passed as an argument to `bundle install`. (default: `vendor/bundled_gems`)
41
+
42
+ #### Tasks
43
+
44
+ `deploy:bundle:install` - Runs a `bundle install` to install needed gems from
45
+ the application's Gemfile.
46
+
47
+ #### Hooks
48
+
49
+ `deploy:bundle:install` after `deploy:update_code`
50
+
51
+ ### caplets/db
52
+
53
+ Tasks to support using ActiveRecord within your applications. This module
54
+ adds functionality to write out your `database.yml` file and provides a
55
+ `deploy:migrations` task.
56
+
57
+ Differently from capistrano defaults, caplets does not expect your code to be
58
+ deployed to your DB server. This means you don't even have to list it in your
59
+ deploy file. Instead, it expects to run your migrations from your `:primary
60
+ :app` server.
61
+
62
+ #### Variables
63
+
64
+ `:db_host` - default: `localhost`
65
+ `:db_adapter` - default: `mysql`
66
+ `:db_database` - default: `<application>_<environment>`
67
+ `:db_username` - default: `<user>`
68
+ `:db_password` - default: `<prompt for value>`
69
+ `:db_encoding` - default: `utf8'
70
+
71
+ These variables are used to set the corresponding values in your
72
+ `database.yml` file. These values will be scoped appropriately under a key
73
+ for your current `:environment`. If you'd rather, you can specify the entire
74
+ `database.yml` yourself using `:db_confg`
75
+
76
+ `:db_extra` - A hash of values merged into your :db_config, outside of any
77
+ environment key. Useful for adding access to slaves or other DBs.
78
+ (default: {})
79
+
80
+ #### Tasks
81
+
82
+ `deploy:db:config` - Write a generated `database.yml` to your project's config
83
+ directory.
84
+
85
+ `deploy:migrate` - Run `rake db:migrate` on your primary app server.
86
+
87
+ `deploy:migrations` - Do a deploy with migrations, including doing a
88
+ `web:disable` first and restarting (not reloading) the backends
89
+
90
+ #### Hooks
91
+
92
+ `deploy:db:config` after `deploy:setup`
93
+
94
+ ### caplets/git-tag
95
+
96
+ Automatically tag a revision on deploy.
97
+
98
+ #### Tasks
99
+
100
+ `git:tag_current_release` - Tag the `:current_revision` with a tag like
101
+ `deploy-<environment>-<timestamp>` and push it to the origin.
102
+
103
+ #### Hooks
104
+
105
+ `git:tag_current_release` after `deploy:migrations`
106
+ `git:tag_current_release` after `deploy:quick`
107
+ `git:tag_current_release` after `deploy:rebuild`
108
+
109
+ ### caplets/memcached
110
+
111
+ Support for keeping track of memcached nodes and writing a `memcached.yml`
112
+ config file based on the `:memcached` role.
113
+
114
+ ### Variables
115
+
116
+ `:memcached_servers` - An array of 'host:port' strings of memcached servers.
117
+ If any of your servers have the `:memcached` role, this is constructed
118
+ dynamically based on those servers' `:private_ip`s. Otherwise:
119
+ (default: `%w[localhost:11211]`)
120
+
121
+ ### Tasks
122
+
123
+ `deploy:memcached:config` - Write a generated `memcached.yml` to your
124
+ projet's config directory.
125
+
126
+ ### Hooks
127
+
128
+ `deploy:memcached:config` after `deploy:setup`
129
+
130
+ ### caplets/mongrel
131
+
132
+ ### caplets/networkfs
133
+
134
+ ## caplets/passenger
135
+
136
+ ## caplets/thinking-sphinx
137
+
138
+ ## caplets/unicorn
139
+
140
+ ## caplets/unicorn_rails
141
+
142
+ ## caplets/utils
143
+
144
+ ## caplets/web
145
+
146
+ ## caplets/whenever
147
+
148
+ ## caplets/yaml
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ Caplets
2
+ =======
3
+
4
+ Capistrano is old and busted, right? Time to move on? WRONG! Caplets makes
5
+ capistrano the new hotness once again. It bring capistrano into the future
6
+ with all your favorite goodies: git, bundler, unicorn, and more.
7
+
8
+
9
+ WARNING: Caplets has evolved out of Mint Digital's real-world deployments of
10
+ many large Rails/Rack applications. Although we have tried to keep it as
11
+ generic and customizable as possible, it is still very opinionated. That is
12
+ to say, it is largely tailored to how we like to deploy applications. If
13
+ caplets is missing something important to you, [submit an issue][issues] or
14
+ [fork away][fork].
15
+
16
+ [issues]: http://github.com/mintdigital/caplets/issues
17
+ [fork]: http://github.com/mintdigital/caplets/fork
18
+
19
+ Quickstart
20
+ ----------
21
+
22
+
23
+ $ gem install caplets
24
+
25
+ # config/deploy.rb
26
+ require 'caplets'
27
+ load 'caplets/memcached'
28
+ load 'caplets/whenever'
29
+ # etc...
30
+
31
+ Roles
32
+ -----
33
+
34
+ Caplets depends heavily on capistrano's concept of server roles to apply tasks
35
+ only to the servers that require them. Most often, your sever definitions will
36
+ look like this:
37
+
38
+ server 'app1.mintdigital.com',
39
+ :app, :memcached, :sphinx, :assets,
40
+ :primary => true
41
+
42
+ Modules
43
+ -------
44
+
45
+ Caplets is a series of `load`able capistrano extensions. This given you
46
+ maximum flexibility in your deployments without having to remember to
47
+ set/unset loads of capistrano variables. Only the tasks you need get run.
48
+
49
+ See the [MODULES][] file for descriptions of all the available modules, along
50
+ with what tasks they add and what variables they use.
51
+
52
+ [MODULES]: http://github.com/mintdigital/caplets/blob/master/MODULES.md
53
+
54
+ caplets/deploy
55
+ --------------
56
+
57
+ The one module you will _always_ get, even if you just require
58
+ `caplets/basic` is `caplets/deploy`. This file is the heart of caplets; it
59
+ sets up the essentials of what caplets considers a modern deployment.
60
+
61
+ The biggest change to the standard capistrano setup is that we use git to
62
+ manage releases. Let me say that again as it's important:
63
+
64
+ ** Instead of using subdirectories and symlinks to manage releases, caplets
65
+ uses git.**
66
+
67
+ That means:
68
+
69
+ - You will not have `releases` or `shared` directories.
70
+ - Your code will be directly inside your `:deploy_to` directory.
71
+ - `:current_path`, `:current_release`, `:release_path`, `:latest_release`,
72
+ and `:shared_path` will all be the same.
73
+ - Rollbacks still work, through the power of `git reset`
74
+ - "shared" files are really just files written to your project root but not
75
+ under git version control -- no symlinks needed.
76
+
77
+ Here are a few other changes that `caplets/deploy` makes:
78
+
79
+ - `:use_sudo` is false by default
80
+ - `:rails_env` is replaced with `:environment`, which caplets expects you to
81
+ set in your deploy file.
82
+ - `:user` and `:group` default to `deploy`
83
+ - `:shared_children` is not used. Use `:required_children` instead.
84
+ - The standard `deploy` task is disabled. Use `deploy:quick` to do an
85
+ `deploy:update` + `deploy:reload`. Load `caplets/db` for caplets'
86
+ replacement `deploy:migrations` task.
87
+ - Capistrano's built-in `deploy:web:enable` and `deploy:web:disable` are
88
+ disabled. Load `caplets/web` for caplets' replacements.
89
+
90
+ require 'caplets'
91
+ -----------------
92
+
93
+ Requiring `caplets` gets you the most common modules in one go. If you don't
94
+ want them all, you can always require `caplets/basic` instead.
95
+
96
+ # config/deploy.rb
97
+ require 'caplets'
98
+
99
+ # Equivalent to...
100
+ require 'caplets/basic'
101
+ load 'caplets/bundle'
102
+ load 'caplets/db'
103
+ load 'caplets/logs'
104
+ load 'caplets/web'
105
+ load 'caplets/yaml'
data/lib/caplets.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'caplets/basic'
2
+
3
+ config = Capistrano::Configuration.instance(:raise_on_error)
4
+ config.load 'caplets/bundle'
5
+ config.load 'caplets/db'
6
+ config.load 'caplets/logs'
7
+ config.load 'caplets/web'
8
+ config.load 'caplets/yaml'
@@ -0,0 +1,11 @@
1
+ require 'caplets/version'
2
+ require 'caplets/utils'
3
+
4
+ config = Capistrano::Configuration.instance(:raise_on_error)
5
+
6
+ # Apparently $: isn't good enough for Capistrano.
7
+ config.instance_eval do
8
+ @load_paths.unshift File.expand_path('..',File.dirname(__FILE__))
9
+ end
10
+
11
+ config.load 'caplets/deploy'
@@ -0,0 +1,19 @@
1
+ set :bundle_roles, [:app]
2
+ set :bundle_exclude, %w[development test]
3
+ set :bundle_to, 'vendor/bundled_gems'
4
+ set :bin_cmd, 'bundle exec'
5
+
6
+ ## Tasks
7
+ namespace :deploy do
8
+ namespace :bundle do
9
+ desc "Install gems from Gemfile"
10
+ task :install, :roles => lambda { fetch(:bundle_roles) },
11
+ :except => {:no_release => true} do
12
+ without = fetch(:bundle_exclude).map{|g| "--without #{g}"}.join(' ')
13
+ run_current "bundle install #{fetch(:bundle_to)} #{without}"
14
+ end
15
+ end
16
+ end
17
+
18
+ ## Hooks
19
+ after 'deploy:update_code', 'deploy:bundle:install'
@@ -0,0 +1,11 @@
1
+ ## Defaults
2
+ _cset :cache_dir, 'cache'
3
+
4
+ ## Tasks
5
+ namespace :deploy do
6
+ namespace :cache do
7
+ task :clear, :roles => :app, :only => { :primary => true } do
8
+ run "find #{fetch(:current_path)}/public/#{fetch(:cache_dir)} -type f -delete"
9
+ end
10
+ end
11
+ end
data/lib/caplets/db.rb ADDED
@@ -0,0 +1,61 @@
1
+ # In deploy:migrations, we use web.enable and web.disable
2
+ # so we need to make sure this has been loaded.
3
+ load 'caplets/web'
4
+
5
+ ## Defaults
6
+ _cset :db_host, 'localhost'
7
+ _cset :db_adapter, 'mysql'
8
+ _cset(:db_database) { "#{fetch(:application)}_#{fetch(:environment)}" }
9
+ _cset(:db_username) { user }
10
+ _cset(:db_password) {
11
+ Capistrano::CLI.password_prompt(
12
+ "Please enter MySQL password for user #{fetch(:db_username)}: "
13
+ )
14
+ }
15
+ _cset :db_encoding, 'utf8'
16
+ _cset(:db_config) {
17
+ fetch(:db_extra).merge({
18
+ fetch(:environment).to_s => {
19
+ 'host' => fetch(:db_host).to_s,
20
+ 'adapter' => fetch(:db_adapter).to_s,
21
+ 'database' => fetch(:db_database).to_s,
22
+ 'username' => fetch(:db_username).to_s,
23
+ 'password' => fetch(:db_password).to_s,
24
+ 'encoding' => fetch(:db_encoding).to_s
25
+ }
26
+ })
27
+ }
28
+ _cset :db_extra, {}
29
+
30
+ ## Tasks
31
+ namespace :deploy do
32
+ namespace :db do
33
+ desc "write out database.yml"
34
+ task :config, :roles => :app do
35
+ put YAML.dump(fetch(:db_config)), "#{shared_path}/config/database.yml"
36
+ end
37
+ end
38
+
39
+ # migrate runs on the app server (no need for the code on the DB server)
40
+ task :migrate, :roles => :app, :only => { :primary => true } do
41
+ rake 'db:migrate'
42
+ end
43
+
44
+ desc <<-DESC
45
+ Do a deploy with migrations.
46
+
47
+ Use this when you need to completely disable the site and run migrations.
48
+ This will put up a maintenance page, run the migrations and completely
49
+ restart the app server processes.
50
+ DESC
51
+ task :migrations do
52
+ web.disable
53
+ update
54
+ migrate
55
+ restart
56
+ web.enable
57
+ end
58
+ end
59
+
60
+ ## Hooks
61
+ after 'deploy:setup', 'deploy:db:config'
@@ -0,0 +1,148 @@
1
+ # A lot of this taken from GitHub's deployment:
2
+ # http://github.com/blog/470-deployment-script-spring-cleaning
3
+
4
+ # Overwrite cap defaults
5
+ set :use_sudo, false
6
+ set :deploy_via, :git # not used
7
+ set :shared_children, %w[] # not relevant
8
+ set(:rails_env) { environment } ## some built-in tasks use :rails_env
9
+ default_run_options[:pty] = true
10
+
11
+ # Setup basics
12
+ _cset :user, 'deploy'
13
+ _cset(:group) { user }
14
+ _cset :environment, 'production'
15
+ _cset :required_children, %w[config log public tmp/pids]
16
+
17
+ # Server basics
18
+ _cset :server_processes, 2
19
+ _cset :server_port, 8000
20
+
21
+ # Use one directory for everything
22
+ set(:current_path) { fetch(:deploy_to) }
23
+ set(:latest_release) { fetch(:current_path) }
24
+ set(:release_path) { fetch(:current_path) }
25
+ set(:current_release) { fetch(:current_path) }
26
+ set(:shared_path) { fetch(:current_path) }
27
+
28
+ # Behold, the power of git
29
+ set(:current_revision) {
30
+ capture("cd #{current_path}; git rev-parse --short HEAD").strip
31
+ }
32
+ set(:latest_revision) {
33
+ capture("cd #{current_path}; git rev-parse --short HEAD").strip
34
+ }
35
+ set(:previous_revision) {
36
+ capture("cd #{current_path}; git rev-parse --short HEAD@{1}").strip
37
+ }
38
+
39
+ ###########
40
+ ## Tasks ##
41
+ ###########
42
+
43
+ namespace :deploy do
44
+ [:default, :cleanup, :symlink].each do |t|
45
+ task t do
46
+ abort "Task disabled by caplets."
47
+ end
48
+ end
49
+ [:enable, :disable].each do |t|
50
+ namespace :web do
51
+ task t do
52
+ abort "Task disabled by caplets. Load 'caplets/web' to re-enable."
53
+ end
54
+ end
55
+ end
56
+
57
+ desc "Setup a Git-based deploy"
58
+ task :setup, :except => {:no_release => true} do
59
+ run_multi(
60
+ "if [ -d #{fetch(:current_path)}/.git ]",
61
+ "then cd #{fetch(:current_path)}",
62
+ "#{try_sudo} git fetch",
63
+ "else #{try_sudo} git clone #{fetch(:repository)} #{fetch(:current_path)}",
64
+ "fi",
65
+ "mkdir -p " + fetch(:required_children,[]).
66
+ map {|dir| "#{fetch(:current_path)}/#{dir}" }.
67
+ join(' ')
68
+ )
69
+ end
70
+
71
+ desc <<-DESC
72
+ Do a zero-downtime deploy.
73
+
74
+ Use this when you have small code changes that you want to go out with
75
+ minimal impact. This does not put up the maintanance page nor run migrations,
76
+ and will tell your app servers to 'reload' in-place instead of restarting
77
+ completely if they support it.
78
+ DESC
79
+ task :quick do
80
+ update
81
+ reload
82
+ end
83
+
84
+ task :update do
85
+ update_code
86
+ end
87
+
88
+ desc "Update the deployed code from git"
89
+ task :update_code, :except => { :no_release => true } do
90
+ run_multi "cd #{fetch(:current_path)}",
91
+ "git fetch origin",
92
+ "git reset --hard #{fetch(:branch)}"
93
+ finalize_update
94
+ end
95
+
96
+ # Just timestamps, please
97
+ task :finalize_update, :except => { :no_release => true } do
98
+ if fetch(:normalize_asset_timestamps, false)
99
+ stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
100
+ asset_paths = %w(images stylesheets javascripts).map {|p|
101
+ "#{latest_release}/public/#{p}"
102
+ }.join(" ")
103
+ run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true",
104
+ :env => { "TZ" => "UTC" }
105
+ end
106
+ end
107
+
108
+ %w[start stop restart reload].each do |taskname|
109
+ desc "#{taskname.capitalize} the application server(s)"
110
+ task taskname, :roles => :app, :except => {:no_release => true} do
111
+ deploy.send(fetch(:_server)).send(taskname)
112
+ end
113
+ end
114
+
115
+ namespace :rollback do
116
+ task :code do
117
+ abort "Task disabled by caplets"
118
+ end
119
+
120
+ desc <<-DESC
121
+ [internal] Rollback repo.
122
+
123
+ Moves the repo back one release by checking out HEAD@{1}
124
+ DESC
125
+ task :repo, :except => { :no_release => true } do
126
+ set :branch, "HEAD@{1}"
127
+ deploy.update_code
128
+ end
129
+
130
+ desc <<-DESC
131
+ [internal] Rewrite git reflog.
132
+ This makes HEAD@{1} continue to point at the next previous release.
133
+ DESC
134
+ task :cleanup, :except => { :no_release => true } do
135
+ run_multi "cd #{fetch(:current_path)}",
136
+ 'git reflog delete --rewrite HEAD@{1}',
137
+ 'git reflog delete --rewrite HEAD@{1}'
138
+ end
139
+
140
+ desc "Rolls back to the previously deployed version."
141
+ task :default do
142
+ rollback.repo
143
+ rollback.cleanup
144
+ deploy.reload
145
+ end
146
+ end
147
+
148
+ end
@@ -0,0 +1,16 @@
1
+ ## Tasks
2
+ namespace :git do
3
+ desc 'Tag and push tag for current release'
4
+ task :tag_current_release, :roles => :app, :only => {:primary => true} do
5
+ if ENV['NO_TAG'].nil?
6
+ tag = "deploy-#{environment}-#{Time.now.to_i}"
7
+ `git tag #{tag} #{fetch(:current_revision)}`
8
+ `git push origin #{tag}`
9
+ end
10
+ end
11
+ end
12
+
13
+ ## Hooks
14
+ after 'deploy:migrations', 'git:tag_current_release'
15
+ after 'deploy:quick', 'git:tag_current_release'
16
+ after 'deploy:rebuild', 'git:tag_current_release'
@@ -0,0 +1,3 @@
1
+ task :logs, :roles => :app do
2
+ stream "tail -n 0 -f #{shared_path}/log/*.log"
3
+ end
@@ -0,0 +1,30 @@
1
+ ## Defaults
2
+ _cset(:memcached_servers) do
3
+ if roles[:memcached].servers.any?
4
+ unless roles[:memcached].servers.all? {|s| s.options[:private_ip] }
5
+ abort "Set :private_ip for all :memcached servers or "+
6
+ "set :memcached_servers explicitly."
7
+ end
8
+ roles[:memcached].servers.map {|s| s.options[:private_ip] + ":11211" }
9
+ else
10
+ %w[localhost:11211]
11
+ end
12
+ end
13
+
14
+ ## Tasks
15
+ namespace :deploy do
16
+ namespace :memcached do
17
+ desc "Generate the memcached.yml in the shared directory"
18
+ task :config, :roles => :app do
19
+ config = {
20
+ environment => {
21
+ 'servers' => fetch(:memcached_servers)
22
+ }
23
+ }
24
+ put YAML.dump(config), "#{shared_path}/config/memcached.yml"
25
+ end
26
+ end
27
+ end
28
+
29
+ ## Hooks
30
+ after 'deploy:setup', 'deploy:memcached:config'
@@ -0,0 +1,46 @@
1
+ set :_server, :mongrel
2
+
3
+ namespace :deploy do
4
+ namespace :mongrel do
5
+ desc "Write the cluster config."
6
+ task :config, :roles => :app, :except => {:no_release => true} do
7
+ config = {
8
+ 'user' => user,
9
+ 'group' => group,
10
+ 'cwd' => current_path,
11
+ 'environment' => environment,
12
+ 'port' => server_port,
13
+ 'address' => '0.0.0.0',
14
+ 'pid_file' => "#{shared_path}/tmp/pids/mongrel.pid",
15
+ 'servers' => server_processes
16
+ }
17
+ put YAML.dump(config), "#{shared_path}/config/cluster.yml"
18
+ end
19
+
20
+ desc "Start the mongrel_cluster"
21
+ task :start, :roles => :app do
22
+ run "cd #{current_path} && " +
23
+ "mongrel_rails cluster::start -C #{shared_path}/config/cluster.yml --clean"
24
+ end
25
+
26
+ desc "Stop the mongrel_cluster"
27
+ task :stop, :roles => :app do
28
+ run "cd #{current_path} && " +
29
+ "mongrel_rails cluster::stop -C #{shared_path}/config/cluster.yml --clean"
30
+ end
31
+
32
+ desc "Restart the mongrel_cluster"
33
+ task :restart, :roles => :app do
34
+ run "cd #{current_path} && " +
35
+ "mongrel_rails cluster::restart -C #{shared_path}/config/cluster.yml --clean"
36
+ end
37
+
38
+ desc "Restart the mongrel_cluster, as mongrel does not support reloading"
39
+ task :reload, :roles => :app do
40
+ restart
41
+ end
42
+ end
43
+ end
44
+
45
+ ## Hooks
46
+ after 'deploy:setup', 'deploy:mongrel:config'
@@ -0,0 +1,30 @@
1
+ # loading this file will modify your deploy for use with a networked filesystem
2
+ # Mostly, this means you can define paths that will get symlinked to the FS
3
+
4
+ ## Defaults
5
+ _cset :networkfs_path, '/srv/share'
6
+ _cset :networkfs_resources, []
7
+
8
+ ## Tasks
9
+ namespace :deploy do
10
+ namespace :networkfs do
11
+ desc "Create the application's directory on the network FS"
12
+ task :setup, :roles => :app, :only => {:primary => true} do
13
+ try_sudo "mkdir -p #{fetch(:networkfs_path)}/#{fetch(:application)}"
14
+ end
15
+
16
+ desc "Symlink networkfs_resources to the network FS"
17
+ task :symlink, :roles => [:app, :web] do
18
+ cmds = fetch(:networkfs_resources, []).map do |resource|
19
+ "ln -nfs" +
20
+ " #{fetch(:networkfs_path)}/#{fetch(:application)}/#{resource}" +
21
+ " #{fetch(:release_path)}/#{resource}"
22
+ end
23
+ try_sudo cmds.join(" && ")
24
+ end
25
+ end
26
+ end
27
+
28
+ ## Hooks
29
+ after 'deploy:setup', 'deploy:networkfs:setup'
30
+ after 'deploy:setup', 'deploy:networkfs:symlink'
@@ -0,0 +1,19 @@
1
+ set :_server, :passenger
2
+
3
+ namespace :deploy do
4
+ namespace :passenger do
5
+ # These are no-ops for Passenger
6
+ task :start, :roles => :app, :except => {:no_release => true} do; end
7
+ task :stop, :roles => :app, :except => {:no_release => true} do; end
8
+
9
+ desc 'Restart the Passenger processes by touching tmp/restart.txt'
10
+ task :restart, :roles => :app, :except => {:no_release => true} do
11
+ run "touch #{current_path}/tmp/restart.txt"
12
+ end
13
+
14
+ desc 'Restart the Passenger process by touching tmp/restart.txt'
15
+ task :restart, :roles => :app, :except => {:no_release => true} do
16
+ run "touch #{current_path}/tmp/restart.txt"
17
+ end
18
+ end # namespace :passenger
19
+ end # namespace :deploy
@@ -0,0 +1,72 @@
1
+ # We require web:enable and web:disable for the rebuild task
2
+ load 'caplets/web'
3
+
4
+ ## Defaults
5
+ _cset(:sphinx_address) {
6
+ roles[:sphinx].servers.detect {|s| s.options[:primary] }.options[:private_ip] ||
7
+ '127.0.0.1'
8
+ }
9
+ _cset(:sphinx_max_matches) { 1000 }
10
+
11
+ ## Tasks
12
+ namespace :deploy do
13
+ desc <<-DESC
14
+ Do a deploy with migrations and a sphinx rebuild.
15
+
16
+ Use this when you need to completely rebuild the sphinx index on deploy,
17
+ for instance if you have changed the index definitions. This task puts up
18
+ the maintenance page, runs migrations, rebuilds the sphinx index and restarts
19
+ the app server processes.
20
+ DESC
21
+ task :rebuild do
22
+ web.disable
23
+ update
24
+ migrate
25
+ ts.rebuild
26
+ restart
27
+ web.enable
28
+ end
29
+
30
+ namespace :sphinx do
31
+ desc "Generate the sphinx.yml file in the shared directory"
32
+ task :config, :roles => [:app, :sphinx] do
33
+ config = {
34
+ fetch(:environment) => {
35
+ 'address' => fetch(:sphinx_address),
36
+ 'max_matches' => fetch(:sphinx_max_matches)
37
+ }
38
+ }
39
+ put YAML.dump(config), "#{shared_path}/config/sphinx.yml"
40
+ end
41
+ end
42
+
43
+ namespace :ts do
44
+ desc "Generate the Thinking Sphinx config file"
45
+ task :config, :roles => [:app, :sphinx] do
46
+ rake 'ts:config'
47
+ end
48
+
49
+ desc "Rebuild Thinking Sphinx config and indexes and restart Sphinx"
50
+ task :rebuild, :roles => :sphinx do
51
+ rake 'ts:rebuild'
52
+ end
53
+
54
+ desc "Run a Sphinx index via Thinking Sphinx"
55
+ task :index, :roles => :sphinx do
56
+ rake 'ts:in'
57
+ end
58
+
59
+ desc "Start Sphinx searchd via Thinking Sphinx"
60
+ task :start, :roles => :sphinx do
61
+ rake 'ts:start'
62
+ end
63
+
64
+ desc "Stop Sphinx searchd via Thinking Sphinx"
65
+ task :stop, :roles => :sphinx do
66
+ rake 'ts:stop'
67
+ end
68
+ end
69
+ end
70
+
71
+ ## Hooks
72
+ after 'deploy:setup', 'deploy:sphinx:config'
@@ -0,0 +1,83 @@
1
+ set :_server, :unicorn
2
+
3
+ namespace :deploy do
4
+ namespace :unicorn do
5
+
6
+ def start_cmd
7
+ "#{fetch(:_unicorn_cmd,'unicorn')} -D" +
8
+ " -c config/unicorn.rb" +
9
+ " -E #{fetch(:environment)}"
10
+ end
11
+
12
+ def stop_cmd(signal='QUIT', pid='unicorn.pid')
13
+ pid = "tmp/pids/#{pid}"
14
+ "if [ -f #{pid} ] ; then kill -#{signal} $(cat #{pid}) ; fi"
15
+ end
16
+
17
+ def wait_for_pid(pid, to_disappear=false)
18
+ "while [ #{'!' unless to_disappear} -f tmp/pids/#{pid} ] ; do sleep 1; done"
19
+ end
20
+
21
+ desc "Write the unicorn config to the shared directory."
22
+ task :config, :roles => :app, :except => {:no_release => true} do
23
+ config = File.read(__FILE__).split(/^__END__$/, 2)[1]
24
+ put ERB.new(config,nil,'-').result(binding.dup),
25
+ fetch(:current_path) + '/config/unicorn.rb'
26
+ end
27
+
28
+ desc "Start the unicorn master and workers."
29
+ task :start, :roles => :app, :except => {:no_release => true} do
30
+ run_current start_cmd
31
+ end
32
+
33
+ desc "Stop the unicorn master and workers."
34
+ task :stop, :roles => :app, :except => {:no_release => true} do
35
+ run_current stop_cmd
36
+ end
37
+
38
+ desc "Restart the unicorn master and workers."
39
+ task :restart, :roles => :app, :except => {:no_release => true} do
40
+ run_current stop_cmd,
41
+ wait_for_pid('unicorn.pid', :to_disappear),
42
+ start_cmd,
43
+ wait_for_pid('unicorn.pid'),
44
+ 'sleep 5' # app start-up
45
+ end
46
+
47
+ desc "Reload the unicorn master and workers with zero downtime."
48
+ task :reload, :roles => :app, :except => {:no_release => true} do
49
+ run_current stop_cmd('USR2'),
50
+ wait_for_pid('unicorn.pid.oldbin'),
51
+ wait_for_pid('unicorn.pid'),
52
+ 'sleep 5', # app start-up
53
+ stop_cmd('QUIT', 'unicorn.pid.oldbin')
54
+ end
55
+ end
56
+ end
57
+
58
+ ## Hooks
59
+ after 'deploy:setup', 'deploy:unicorn:config'
60
+
61
+ __END__
62
+ pid '<%= fetch(:current_path) %>/tmp/pids/unicorn.pid'
63
+
64
+ working_directory "<%= fetch(:current_path) %>"
65
+
66
+ worker_processes <%= fetch(:server_processes, 4) %>
67
+
68
+ # Pre-load for fast worker forks and COW-friendliness
69
+ preload_app <%= fetch(:preload_app, true) %>
70
+
71
+ timeout <%= fetch(:server_timeout, 60) %>
72
+
73
+ listen '0.0.0.0:<%= fetch(:server_port, 8000) %>'
74
+
75
+ stderr_path "<%= current_path %>/log/unicorn.log"
76
+ stdout_path "<%= current_path %>/log/unicorn.log"
77
+
78
+ # http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
79
+ GC.respond_to?(:copy_on_write_friendly=) and
80
+ GC.copy_on_write_friendly = true
81
+
82
+ <%= fetch(:_unicorn_ar_config,'') %>
83
+ <%= fetch(:unicorn_extra_config,'') %>
@@ -0,0 +1,17 @@
1
+ set :_server, :unicorn
2
+ set :_unicorn_cmd, 'unicorn_rails'
3
+ set :_unicorn_ar_config, <<-CONF
4
+ before_fork do |server, worker|
5
+ # master process doesn't need a connection
6
+ defined?(ActiveRecord::Base) and
7
+ ActiveRecord::Base.connection.disconnect!
8
+ end
9
+
10
+ after_fork do |server, worker|
11
+ # workers need to connect individually
12
+ defined?(ActiveRecord::Base) and
13
+ ActiveRecord::Base.establish_connection
14
+ end
15
+ CONF
16
+
17
+ load 'caplets/unicorn'
@@ -0,0 +1,16 @@
1
+ # This is just a collection of handy methods to use while writing tasks
2
+ module Caplets::Utils
3
+ def rake(cmd)
4
+ run_current "RAILS_ENV=#{fetch(:environment)} rake #{cmd}"
5
+ end
6
+
7
+ def run_current(*cmds)
8
+ run ["cd #{fetch(:current_path)}"].concat(cmds).join(' && ')
9
+ end
10
+
11
+ def run_multi(*cmds)
12
+ run cmds.join(' ; ')
13
+ end
14
+ end
15
+
16
+ Capistrano::Configuration.send :include, Caplets::Utils
@@ -0,0 +1,10 @@
1
+ module Caplets
2
+ module Version
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 0
6
+ BUILD = nil
7
+ STRING = [MAJOR,MINOR,TINY,BUILD].compact.join('.')
8
+ end
9
+ VERSION = Version::STRING
10
+ end
@@ -0,0 +1,13 @@
1
+ namespace :deploy do
2
+ namespace :web do
3
+ desc 'Enable maintenance mode'
4
+ task :disable, :roles => :web do
5
+ try_sudo "touch #{fetch(:current_release)}/public/SHOW_MAINTENANCE_PAGE"
6
+ end
7
+
8
+ desc 'Disable maintenance mode'
9
+ task :enable, :roles => :web do
10
+ try_sudo "rm -f #{fetch(:current_release)}/public/SHOW_MAINTENANCE_PAGE"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ # Uses the whenever gem to update the server's crontab, using a combination
2
+ # of role and environment to pick which crontab to use.
3
+ #
4
+ # Put per-server crontabs inside whenever/{environment}.{role}.rb
5
+ #
6
+ namespace :deploy do
7
+ namespace :whenever do
8
+ def update_cmd_for(role)
9
+ load_file = "config/whenever/#{fetch(:environment)}.#{role}.rb"
10
+ [ "cd #{fetch(:current_path)} &&",
11
+ "if [ -f #{load_file} ] ; then",
12
+ "#{fetch(:bin_cmd)} whenever",
13
+ "--update-crontab #{fetch(:application)}.#{fetch(:environment)}.#{role}",
14
+ "--load-file #{load_file}",
15
+ "--set environment=#{fetch(:environment)}",
16
+ "; fi"
17
+ ].join(' ')
18
+ end
19
+
20
+ desc "Update the crontab files by running 'whenever'"
21
+ task :update, :except => { :no_release => true } do
22
+ parallel do |session|
23
+ roles.keys.each do |role|
24
+ session.when "in?(#{role.inspect})", update_cmd_for(role)
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+
32
+ after "deploy:update", "deploy:whenever:update"
@@ -0,0 +1,17 @@
1
+ ## Defaults
2
+ _cset :config_files, {}
3
+
4
+ ## Tasks
5
+ namespace :deploy do
6
+ namespace :yaml do
7
+ desc "Write any defined custom YAML config files."
8
+ task :config, :except => { :no_release => true } do
9
+ fetch(:config_files,{}).each do |name, data|
10
+ put YAML.dump(data), "#{fetch(:shared_path)}/config/#{name}.yml"
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ ## Hooks
17
+ after 'deploy:setup', 'deploy:yaml:config'
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: caplets
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Dean Strelau
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-08 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: capistrano
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 5
30
+ - 0
31
+ version: 2.5.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: " Caplets modernizes your capistrano deployments. At its most basic, it\n provides a fast, efficient git-based deployment without copying release\n trees or symlink tomfoolery. In addition, it includes modules for common\n tasks such as writing config files and crontabs, working with bundler,\n and using a networked filesystem.\n"
35
+ email: dean@mintdigital.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files: []
41
+
42
+ files:
43
+ - lib/caplets/basic.rb
44
+ - lib/caplets/bundle.rb
45
+ - lib/caplets/cache.rb
46
+ - lib/caplets/db.rb
47
+ - lib/caplets/deploy.rb
48
+ - lib/caplets/git-tag.rb
49
+ - lib/caplets/logs.rb
50
+ - lib/caplets/memcached.rb
51
+ - lib/caplets/mongrel.rb
52
+ - lib/caplets/networkfs.rb
53
+ - lib/caplets/passenger.rb
54
+ - lib/caplets/thinking-sphinx.rb
55
+ - lib/caplets/unicorn.rb
56
+ - lib/caplets/unicorn_rails.rb
57
+ - lib/caplets/utils.rb
58
+ - lib/caplets/version.rb
59
+ - lib/caplets/web.rb
60
+ - lib/caplets/whenever.rb
61
+ - lib/caplets/yaml.rb
62
+ - lib/caplets.rb
63
+ - CHANGELOG
64
+ - MIT-LICENSE
65
+ - README.md
66
+ - MODULES.md
67
+ has_rdoc: true
68
+ homepage: http://mintdigital.github.com/caplets
69
+ licenses: []
70
+
71
+ post_install_message:
72
+ rdoc_options: []
73
+
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirements: []
91
+
92
+ rubyforge_project:
93
+ rubygems_version: 1.3.6
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Capistrano super powers
97
+ test_files: []
98
+