Skip to content

Commit f387c6b

Browse files
committed
Initial commit
0 parents  commit f387c6b

26 files changed

+1401
-0
lines changed

.gitignore

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
*.gem
2+
*.rbc
3+
.bundle
4+
.config
5+
coverage
6+
InstalledFiles
7+
lib/bundler/man
8+
pkg
9+
rdoc
10+
spec/reports
11+
test/tmp
12+
test/version_tmp
13+
tmp
14+
doc
15+
.idea/
16+

Gemfile

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
source 'https://rubygems.org'
2+
3+
# Specify your gem's dependencies in capistrano_recipes.gemspec
4+
gemspec

Gemfile.lock

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
PATH
2+
remote: .
3+
specs:
4+
capistrano_recipes (0.0.0)
5+
capistrano (= 2.15.4)
6+
7+
GEM
8+
remote: https://rubygems.org/
9+
specs:
10+
capistrano (2.15.4)
11+
highline
12+
net-scp (>= 1.0.0)
13+
net-sftp (>= 2.0.0)
14+
net-ssh (>= 2.0.14)
15+
net-ssh-gateway (>= 1.1.0)
16+
diff-lcs (1.2.5)
17+
highline (1.6.20)
18+
net-scp (1.1.2)
19+
net-ssh (>= 2.6.5)
20+
net-sftp (2.1.2)
21+
net-ssh (>= 2.6.5)
22+
net-ssh (2.7.0)
23+
net-ssh-gateway (1.2.0)
24+
net-ssh (>= 2.6.5)
25+
rspec (2.14.1)
26+
rspec-core (~> 2.14.0)
27+
rspec-expectations (~> 2.14.0)
28+
rspec-mocks (~> 2.14.0)
29+
rspec-core (2.14.7)
30+
rspec-expectations (2.14.4)
31+
diff-lcs (>= 1.1.3, < 2.0)
32+
rspec-mocks (2.14.4)
33+
34+
PLATFORMS
35+
ruby
36+
37+
DEPENDENCIES
38+
capistrano_recipes!
39+
rspec

LICENSE

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Permission is hereby granted, free of charge, to any person obtaining a copy of
2+
this software and associated documentation files (the 'Software'), to deal in
3+
the Software without restriction, including without limitation the rights to
4+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
5+
of the Software, and to permit persons to whom the Software is furnished to do
6+
so, subject to the following conditions:
7+
8+
The above copyright notice and this permission notice shall be included in all
9+
copies or substantial portions of the Software.
10+
11+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17+
SOFTWARE.

README.md

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
## capistrano_recipes
2+
3+
Bunch of capistrano recipes
4+
5+
GitHub: https://github.com/rubydev/capistrano_recipes
6+
7+
## Installation
8+
9+
Add `capistrano_recipes` to your application's Gemfile:
10+
11+
gem 'capistrano_recipes', :git => 'git://github.com/rubydev/capistrano_recipes.git'
12+
13+
And then install the bundle:
14+
15+
$ bundle
16+
17+
## Usage
18+
19+
First, initialize capistrano:
20+
21+
capify .
22+
23+
This will create `./Capfile ` and `./config/deploy.rb`. Edit `./config/deploy.rb` and load recipes required
24+
for your application:
25+
26+
require "capistrano_recipes/nginx"
27+
require "capistrano_recipes/mysql"
28+
require "capistrano_recipes/host"
29+
30+
Then run `cap host:setup deploy:initial` to make your first deploy. Use `cap deploy:migrations` for next deploys.
31+
32+
### Example of rails app deployment
33+
34+
`config/deploy.rb`:
35+
36+
default_run_options[:pty] = true # Must be set for the password prompt to work
37+
set :use_sudo, false # Don't use sudo (deploy user must have very limited permissions in system)
38+
39+
set :default_stage, "staging" # Deploy on staging server by default
40+
41+
# Configure capistrano deploy strategy
42+
set :repository, '.'
43+
set :deploy_via, :copy
44+
set :copy_exclude, [".git", "coverage", "results", "tmp", "public/system", "builder"]
45+
46+
set :root_user, "user" # User with root privileges on server. You will need him only for host:setup
47+
# and *:setup_host tasks. This will not be used during deploy,
48+
# deploy:migrations and other common tasks.
49+
set :user, "app_name" # Deployer user
50+
set :group, "app_name" # Deployer group
51+
set :user_home_path, "/home/www" # Deployer home on target system (used in host:setup when creating new user)
52+
set(:deploy_to) { "#{user_home_path}/#{application}" } # Path where to deploy your application
53+
54+
role(:web) { domain } # Your HTTP server, Apache/etc
55+
role(:app) { domain } # This may be the same as your `Web` server
56+
role(:db, :primary => true) { domain } # This is where Rails migrations will run
57+
58+
require "capistrano_colors" # Colorized output in console (gem install capistrano_colors)
59+
require "capistrano/ext/multistage" # Enable multistage deployment
60+
require "capistrano_recipes/nginx" # Use nginx as web-server
61+
require "capistrano_recipes/mysql" # Use mysql as db-server
62+
require "capistrano_recipes/host" # Require host:setup task
63+
require "capistrano_recipes/monit" # Use monit as monitoring system
64+
require "capistrano_recipes/ruby/thin" # Use thing as app-server
65+
require "capistrano_recipes/ruby/rails" # Load rails-specific recipes
66+
require "bundler/capistrano" # Use bundler on server
67+
load "deploy/assets" # Use rails assets pipeline
68+
69+
`config/deploy/production.rb`
70+
71+
set :application, "app_name" # Application name for production
72+
# (it's used in some configs and paths)
73+
set :application_domain, "app_domain.com" # Domain for production server
74+
set :domain, "192.168.1.1" # Server address where to deploy production
75+
76+
`config/deploy/staging.rb`
77+
78+
set :application, "app_name_test" # Application name for staging server
79+
set :application_domain, "qa.app_domain.com" # Domain for staging server
80+
set :domain, "192.168.1.1" # Server address where to deploy staging
81+
82+
Then execute `cap host:setup deploy:initial nginx:enable` to create user and config files and perform initial deploy,
83+
then symlink `/etc/nginx/sites-available/app_name.conf` to `/etc/nginx/sites-enabled/app_name.conf`.
84+
On next deploy just run `cap deploy:migrations` to deploy new application version.
85+
86+
`cap host:setup` will perform:
87+
88+
* `host:create_user` - creates `:user` on target system (if doesn't exist yet)
89+
* `host:ssh_copy_id` - adds `~/.ssh/id_rsa.pub` from local machine to `authorized_keys` on target machine for `:user`
90+
* Then it performs `*:setup_host` for all loaded recipes:
91+
* `nginx:setup_host` - creates nginx config file in `/etc/nginx/sites-available/app_name.conf`, grants user
92+
permissions to modify it. Creates `#{deploy_to}/shared/logs/nginx` directory and grants nginx permissions to
93+
write there.
94+
* `mysql:setup_host` - creates mysql user with random password grants him administrative permissions for application
95+
database (`:database_name`). Then creates `:database_config_template` in `#{deploy_to}/shared/config/database.yml`,
96+
grants it 440 permissions (only user and group can read it). It will be symlinked to
97+
`#{deploy_to}/current/config/database.yml` on each deploy.
98+
* `monit:setup_host` - creates monit config file for thin in `/etc/monit/conf.d/app_name-thin`, grants user
99+
permissions to modify it.
100+
* `thin:setup_host` - creates directory for thin sockets (if `:thin_socket` is set), creates thin config file in
101+
`#{deploy_to}/shared/config/thin.yml`
102+
103+
## Config templates
104+
105+
Capistrano-scrip uses ERB to parse nginx/monit/thin/etc templates. You can see default config templates at github:
106+
https://github.com/rubydev/capistrano_recipes/tree/master/templates
107+
108+
If you don't like any of this, you can replace it with you own:
109+
110+
* Put your template in `config/deploy/templates/#{template_name}`
111+
* Or change `*_template` variable value to reflect your template path: `set :monit_config_template, 'deploy/monit.erb'`
112+
113+
## Contributing
114+
115+
1. Fork
116+
2. Create your feature branch (`git checkout -b my_branch`)
117+
3. Commit your changes (`git commit -am "my cool feature"`)
118+
4. Push to the branch (`git push origin my_branch`)
119+
5. Create new Pull Request
120+
121+
### Editing documentation
122+
123+
1. Install dependencies: `bundle install`
124+
2. Run yard server: `bundle exec yard server -r`
125+
3. Edit documentation
126+
4. Preview your changes at http://localhost:8808/
127+
5. Commit: `git commit -am "Updated documentation for ..."

Rakefile

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
require "bundler/gem_tasks"
2+
3+
Dir['tasks/**/*.rake'].each { |rake| load rake }

capistrano_recipes.gemspec

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# -*- encoding: utf-8 -*-
2+
$:.push File.expand_path("../lib", __FILE__)
3+
require "capistrano_recipes/version"
4+
5+
Gem::Specification.new do |s|
6+
s.name = "capistrano_recipes"
7+
s.version = CapistranoRecipes::VERSION
8+
s.platform = Gem::Platform::RUBY
9+
s.authors = ["Marian Mrózek", "Jan Uhlář"]
10+
11+
s.homepage = "https://github.com/rubydev/capistrano_recipes"
12+
s.summary = "Bunch of capistrano recipes"
13+
s.description = "Some useful recipes for capistrano"
14+
15+
s.files = `git ls-files`.split("\n")
16+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18+
s.require_paths = %w(lib)
19+
s.extra_rdoc_files = %w(LICENSE)
20+
21+
s.add_dependency "capistrano", "2.15.4"
22+
s.add_development_dependency 'rspec'
23+
24+
end

lib/capistrano_recipes/host.rb

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
require 'capistrano_recipes/utils'
2+
3+
# System tasks (users/permissions management)
4+
#
5+
# Main task is +host:setup+ which will prepare servers for deploy - create config files
6+
# and directories required for deploy.
7+
Capistrano::Configuration.instance.load do
8+
namespace :host do
9+
# User with root privileges on server. +host:setup+ and all +*:setup_host+ tasks are performed
10+
# on behalf of this user.
11+
_cset(:root_user) {"root"}
12+
# Home path for deployer
13+
# (used in host:create_user task)
14+
_cset(:user_home_path) { "/home/www/#{fetch(:deploy_user, user)}" }
15+
# Path to public key (or public key itself as string)
16+
_cset(:ssh_public_key) { "~/.ssh/id_rsa.pub" }
17+
18+
desc "Creates user, enables ssh authorization"
19+
host_task :create_user do
20+
script = <<-eos
21+
set -e;
22+
if #{sudo} id -u #{deploy_user} >/dev/null 2>&1 ; then
23+
echo "User '#{deploy_user}' already exists";
24+
else
25+
#{sudo} useradd #{deploy_user} -d #{user_home_path};
26+
#{sudo} mkdir -p #{user_home_path};
27+
#{sudo} chown #{deploy_user}:#{group} #{user_home_path};
28+
echo "Created user #{deploy_user} with home #{user_home_path}";
29+
fi;
30+
eos
31+
if exists?(:rvm_ruby_string)
32+
script << <<-eos
33+
if #{sudo} getent group rvm >/dev/null 2>&1; then
34+
#{sudo} usermod -a -G rvm #{deploy_user};
35+
fi;
36+
eos
37+
end
38+
run script
39+
end
40+
41+
desc <<-EOF
42+
Copy your public key to server. Path to key can be configured via {ssh_public_key}
43+
44+
You can also provide public key string itself as ssh_public_key:
45+
46+
set(:ssh_public_key) { "ssh-rsa AAAAB3N....2zQ== host@user" }
47+
48+
Also you can specify ssh_public_key via CLI:
49+
50+
!!!bash
51+
cap host:ssh_copy_id -s ssh_public_key="AAAAB3N....2zQ== host@user"
52+
EOF
53+
host_task :ssh_copy_id do
54+
key_path = File.expand_path(ssh_public_key)
55+
if File.exist?(key_path)
56+
key_string = IO.readlines(key_path)[0]
57+
else
58+
key_string = ssh_public_key
59+
end
60+
run <<-eos
61+
set -e;
62+
#{sudo :as => deploy_user} mkdir -p ~#{deploy_user}/.ssh;
63+
if #{sudo} grep -q -s -F '#{key_string}' ~#{deploy_user}/.ssh/authorized_keys ; then
64+
echo "Key already in authorized keys.";
65+
else
66+
#{sudo :as => deploy_user} touch ~#{deploy_user}/.ssh/authorized_keys;
67+
echo '#{key_string}' | #{sudo :as => deploy_user} tee -a ~#{deploy_user}/.ssh/authorized_keys;
68+
echo "Added #{key_path} to authorized_keys of #{deploy_user}";
69+
fi;
70+
eos
71+
end
72+
73+
desc <<-EOF
74+
Creates deploy user on target system, adds him to sudoers, etc.
75+
76+
Also performs deploy:setup and *:setup_host for all loaded recipes, so it will
77+
create configs for monit / nginx / whatever you loaded via require "capistrano-script/.."
78+
79+
It is safe to run this task on servers that have already been set up;
80+
it will not create extra users / destroy config files or data.
81+
EOF
82+
task :setup do
83+
# It's not "host" task because inner tasks are host_tasks (not all of them)
84+
create_user
85+
ssh_copy_id
86+
87+
# We use `after` here to ensure that deploy:setup will latest of all `after "host:setup"` tasks
88+
after "host:setup", "deploy:setup"
89+
end
90+
end
91+
end

lib/capistrano_recipes/monit.rb

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
require 'capistrano_recipes/utils'
2+
3+
# monit is a free, open source process supervision tool for Unix and Linux.
4+
Capistrano::Configuration.instance.load do
5+
# Path to monit configuration template
6+
_cset(:monit_config_template) { "monit/monit_#{app_server}.conf.erb" }
7+
# The remote location of monit's config file
8+
_cset(:monit_config_path) { "/etc/monit/conf.d/#{application}-#{app_server}" }
9+
# Path to monit binary on server
10+
_cset(:monit_command) { "monit" }
11+
12+
namespace :monit do
13+
desc "Reloads monit"
14+
task :reload, :roles => :app do
15+
run "#{sudo} #{monit_command} reload"
16+
end
17+
18+
namespace :status do
19+
desc "Status summary"
20+
task :default do
21+
run "#{sudo} #{monit_command} summary"
22+
end
23+
24+
desc "Full status"
25+
task :full do
26+
sudo "#{monit_command} status"
27+
end
28+
end
29+
30+
desc <<-EOF
31+
Parses the configuration template file :{monit_config_template} through ERB to fetch our variables \
32+
and uploads the result to :{monit_config_path}
33+
EOF
34+
task :setup, :roles => :app , :except => { :no_release => true } do
35+
generate_config(monit_config_template, monit_config_path)
36+
end
37+
38+
desc "Parses config file and outputs it to STDOUT (internal task) "
39+
task :parse_config, :roles => :app , :except => { :no_release => true } do
40+
puts parse_template(monit_config_template)
41+
end
42+
43+
desc <<-EOF
44+
Creates empty monit configuration file for this application, grants user permissions to modify it
45+
EOF
46+
host_task :setup_host do
47+
run "#{sudo} touch #{monit_config_path} && " \
48+
"#{sudo} chown #{deploy_user}:#{group} #{monit_config_path}"
49+
50+
with_user deploy_user do
51+
monit.setup
52+
end
53+
end
54+
end
55+
56+
after 'host:setup' do
57+
monit.setup_host #if Capistrano::CLI.ui.agree("Create monit-related files? [y/n]")
58+
end
59+
end

0 commit comments

Comments
 (0)