capitate 0.2.6 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +9 -1
- data/Manifest.txt +2 -0
- data/docs/recipes/deploy.txt +2 -2
- data/docs/recipes/index.txt +3 -2
- data/docs/recipes/monit-centos.txt +11 -0
- data/docs/recipes/monit.txt +1 -1
- data/docs/recipes/sphinx-centos.txt +13 -0
- data/docs/recipes/sphinx.txt +3 -3
- data/lib/capitate/cap_ext/connections.rb +1 -1
- data/lib/capitate/cap_ext/variables.rb +52 -2
- data/lib/capitate/plugins/base.rb +4 -9
- data/lib/capitate/plugins/build.rb +68 -0
- data/lib/capitate/plugins/gem.rb +2 -2
- data/lib/capitate/plugins/prompt.rb +1 -1
- data/lib/capitate/plugins/script.rb +0 -59
- data/lib/capitate/plugins/upload.rb +3 -0
- data/lib/capitate/plugins/utils.rb +28 -0
- data/lib/capitate/plugins/yum.rb +0 -1
- data/lib/capitate/task_node.rb +0 -1
- data/lib/capitate/version.rb +1 -1
- data/lib/capitate.rb +2 -1
- data/lib/deployment/install-centos-rubyweb.rb +90 -58
- data/lib/recipes/centos/imagemagick.rb +1 -1
- data/lib/recipes/centos/memcached.rb +1 -1
- data/lib/recipes/centos/monit.rb +14 -1
- data/lib/recipes/centos/nginx.rb +1 -1
- data/lib/recipes/centos/ruby.rb +2 -2
- data/lib/recipes/centos/sphinx.rb +18 -2
- data/lib/recipes/sphinx.rb +5 -8
- data/lib/templates/sphinx/sphinx_app.initd.centos.erb +18 -0
- data/test/test_roles.rb +47 -0
- data/website/index.html +2 -2
- metadata +5 -2
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 0.2.7 2008-03-10
|
2
|
+
|
3
|
+
* Add clean method to sphinx init script
|
4
|
+
* Moved install and make_install into build plugin
|
5
|
+
* Added symlink option to make_install
|
6
|
+
* Added fetch_role and fetch_roles methods
|
7
|
+
* Recipe fixes
|
8
|
+
|
1
9
|
== 0.2.6 2008-02-29
|
2
10
|
|
3
11
|
* Fix SSH connect debug when connecting to more than 1 server.
|
@@ -51,7 +59,7 @@
|
|
51
59
|
|
52
60
|
* Renamed to 'capitate'
|
53
61
|
* Moving centos specific recipes into separate namespace
|
54
|
-
* Plugin:
|
62
|
+
* Plugin: build.make_install
|
55
63
|
* Documentation and explicitly fetching vars (for install tasks), showing useful error messages
|
56
64
|
|
57
65
|
== 0.1.4 2008-02-07
|
data/Manifest.txt
CHANGED
@@ -45,11 +45,13 @@ lib/capitate/cap_ext/roles.rb
|
|
45
45
|
lib/capitate/cap_ext/run_via.rb
|
46
46
|
lib/capitate/cap_ext/variables.rb
|
47
47
|
lib/capitate/plugins/base.rb
|
48
|
+
lib/capitate/plugins/build.rb
|
48
49
|
lib/capitate/plugins/gem.rb
|
49
50
|
lib/capitate/plugins/prompt.rb
|
50
51
|
lib/capitate/plugins/script.rb
|
51
52
|
lib/capitate/plugins/templates.rb
|
52
53
|
lib/capitate/plugins/upload.rb
|
54
|
+
lib/capitate/plugins/utils.rb
|
53
55
|
lib/capitate/plugins/yum.rb
|
54
56
|
lib/capitate/recipes.rb
|
55
57
|
lib/capitate/task_node.rb
|
data/docs/recipes/deploy.txt
CHANGED
@@ -111,9 +111,9 @@ Rolls back to the previously deployed version. The `current' symlink will be
|
|
111
111
|
|
112
112
|
h3(#deploy:setup). deploy:setup
|
113
113
|
|
114
|
-
Prepares one or more servers for deployment. Before you can use any of the Capistrano deployment tasks with your project, you will need to make sure all of your servers have been prepared with `cap setup'. When you add a new server to your cluster, you can easily run the setup task on just that server by specifying the HOSTS environment variable:
|
114
|
+
Prepares one or more servers for deployment. Before you can use any of the Capistrano deployment tasks with your project, you will need to make sure all of your servers have been prepared with `cap deploy:setup'. When you add a new server to your cluster, you can easily run the setup task on just that server by specifying the HOSTS environment variable:
|
115
115
|
|
116
|
-
$ cap HOSTS=new.server.com setup
|
116
|
+
$ cap HOSTS=new.server.com deploy:setup
|
117
117
|
|
118
118
|
It is safe to run this task on servers that have already been set up; it will not destroy any deployed revisions or data.
|
119
119
|
|
data/docs/recipes/index.txt
CHANGED
@@ -11,12 +11,13 @@ h2. Namespaces
|
|
11
11
|
* "deploy:web":deploy-web.html (2)
|
12
12
|
* "docs":docs.html (1)
|
13
13
|
* "imagemagick:centos":imagemagick-centos.html (1)
|
14
|
+
* "install":install.html (2)
|
14
15
|
* "memcached:centos":memcached-centos.html (1)
|
15
16
|
* "memcached:monit":memcached-monit.html (1)
|
16
17
|
* "mongrel-cluster:centos":mongrel-cluster-centos.html (1)
|
17
18
|
* "mongrel-cluster:monit":mongrel-cluster-monit.html (1)
|
18
19
|
* "monit":monit.html (3)
|
19
|
-
* "monit:centos":monit-centos.html (
|
20
|
+
* "monit:centos":monit-centos.html (2)
|
20
21
|
* "mysql":mysql.html (1)
|
21
22
|
* "mysql:centos":mysql-centos.html (1)
|
22
23
|
* "mysql:monit":mysql-monit.html (1)
|
@@ -26,5 +27,5 @@ h2. Namespaces
|
|
26
27
|
* "rails":rails.html (5)
|
27
28
|
* "ruby:centos":ruby-centos.html (1)
|
28
29
|
* "sphinx":sphinx.html (4)
|
29
|
-
* "sphinx:centos":sphinx-centos.html (
|
30
|
+
* "sphinx:centos":sphinx-centos.html (3)
|
30
31
|
* "sphinx:monit":sphinx-monit.html (1)
|
@@ -6,6 +6,7 @@ h1. monit:centos
|
|
6
6
|
h2. Tasks
|
7
7
|
|
8
8
|
* "monit:centos:install":#monit:centos:install
|
9
|
+
* "monit:centos:iptables":#monit:centos:iptables
|
9
10
|
|
10
11
|
|
11
12
|
h2. Task documentation
|
@@ -36,3 +37,13 @@ Install monit.
|
|
36
37
|
|
37
38
|
|
38
39
|
|
40
|
+
h3(#monit:centos:iptables). monit:centos:iptables
|
41
|
+
|
42
|
+
Install monit firewall rule.
|
43
|
+
|
44
|
+
*monit_port*: Monit port. _Defaults to 2812_
|
45
|
+
|
46
|
+
@set :monit_port, 2812@
|
47
|
+
|
48
|
+
|
49
|
+
|
data/docs/recipes/monit.txt
CHANGED
@@ -6,6 +6,7 @@ h1. sphinx:centos
|
|
6
6
|
h2. Tasks
|
7
7
|
|
8
8
|
* "sphinx:centos:install":#sphinx:centos:install
|
9
|
+
* "sphinx:centos:iptables":#sphinx:centos:iptables
|
9
10
|
* "sphinx:centos:setup":#sphinx:centos:setup
|
10
11
|
|
11
12
|
|
@@ -29,6 +30,16 @@ set :sphinx_build_options, {
|
|
29
30
|
|
30
31
|
|
31
32
|
|
33
|
+
h3(#sphinx:centos:iptables). sphinx:centos:iptables
|
34
|
+
|
35
|
+
Install sphinx firewall rule.
|
36
|
+
|
37
|
+
*sphinx_port*: Sphinx port. _Defaults to 3312_
|
38
|
+
|
39
|
+
@set :sphinx_port, 3312@
|
40
|
+
|
41
|
+
|
42
|
+
|
32
43
|
h3(#sphinx:centos:setup). sphinx:centos:setup
|
33
44
|
|
34
45
|
Setup sphinx for application.
|
@@ -39,5 +50,7 @@ Setup sphinx for application.
|
|
39
50
|
|
40
51
|
*sphinx_conf_path*: Path to sphinx.conf. _Defaults to "[shared_path]/config/sphinx.conf"_
|
41
52
|
|
53
|
+
*sphinx_index_path*: Path to sphinx indexes. _Defaults to "[shared_path]/var/index"_
|
54
|
+
|
42
55
|
|
43
56
|
|
data/docs/recipes/sphinx.txt
CHANGED
@@ -5,7 +5,7 @@ h1. sphinx
|
|
5
5
|
|
6
6
|
h2. Namespaces
|
7
7
|
|
8
|
-
* "sphinx:centos":sphinx-centos.html (
|
8
|
+
* "sphinx:centos":sphinx-centos.html (3)
|
9
9
|
* "sphinx:monit":sphinx-monit.html (1)
|
10
10
|
|
11
11
|
|
@@ -73,9 +73,9 @@ Update sphinx for application.
|
|
73
73
|
*sphinx_db_port*: Sphinx DB port. _Defaults to db_port_
|
74
74
|
|
75
75
|
|
76
|
-
*sphinx_db_host*: Sphinx DB host. _Defaults to
|
76
|
+
*sphinx_db_host*: Sphinx DB host. _Defaults to db_host_
|
77
77
|
|
78
|
-
*
|
78
|
+
*sphinx_conf_host*: Sphinx DB host to listen on. _Defaults to 127.0.0.1_
|
79
79
|
|
80
80
|
|
81
81
|
|
@@ -113,7 +113,7 @@ class Capistrano::SSH
|
|
113
113
|
@connect_mutex ||= Mutex.new
|
114
114
|
|
115
115
|
@connect_mutex.synchronize do
|
116
|
-
puts "
|
116
|
+
puts "--- Connecting to #{server} with user: #{server.user || options[:user]}"
|
117
117
|
end
|
118
118
|
|
119
119
|
connect_without_logging(server, options, &block)
|
@@ -12,8 +12,6 @@ module Capitate
|
|
12
12
|
#
|
13
13
|
# See capistrano fetch for usage info.
|
14
14
|
#
|
15
|
-
# Recipe docs are in lib/doc/the_namespace.yml
|
16
|
-
#
|
17
15
|
def fetch_with_capitate(variable, *args)
|
18
16
|
begin
|
19
17
|
fetch_without_capitate(variable, *args)
|
@@ -51,6 +49,58 @@ module Capitate
|
|
51
49
|
end
|
52
50
|
default
|
53
51
|
end
|
52
|
+
|
53
|
+
# Fetch roles with name and options
|
54
|
+
#
|
55
|
+
# ==== Options
|
56
|
+
# +name+:: Role name to look for
|
57
|
+
# +options+:: Options to match on, e.g. :primary => true
|
58
|
+
#
|
59
|
+
# ==== Examples
|
60
|
+
# fetch_roles(:db) => [ "10.0.6.71", "10.0.6.72" ]
|
61
|
+
# fetch_roles(:db, :primary => true) => [ "10.0.6.71" ]
|
62
|
+
#
|
63
|
+
def fetch_roles(name, options = {})
|
64
|
+
matched_roles = Set.new
|
65
|
+
|
66
|
+
roles.each do |role_info|
|
67
|
+
role_name = role_info.first
|
68
|
+
|
69
|
+
next unless role_name == name
|
70
|
+
|
71
|
+
role = role_info.last
|
72
|
+
role.each do |server|
|
73
|
+
|
74
|
+
abort = false
|
75
|
+
options.each do |k, v|
|
76
|
+
unless server.options[k] == v
|
77
|
+
abort = true
|
78
|
+
break
|
79
|
+
end
|
80
|
+
end
|
81
|
+
next if abort
|
82
|
+
|
83
|
+
matched_roles << server
|
84
|
+
end
|
85
|
+
end
|
86
|
+
matched_roles.to_a
|
87
|
+
end
|
88
|
+
|
89
|
+
# Fetch first role with name and options
|
90
|
+
#
|
91
|
+
# ==== Options
|
92
|
+
# +name+:: Role name to look for
|
93
|
+
# +options+:: Options to match on, e.g. :primary => true
|
94
|
+
#
|
95
|
+
# ==== Examples
|
96
|
+
# fetch_roles(:db) => [ "10.0.6.71", "10.0.6.72" ]
|
97
|
+
# fetch_roles(:db, :primary => true) => [ "10.0.6.71" ]
|
98
|
+
#
|
99
|
+
def fetch_role(name, options = {})
|
100
|
+
matched = fetch_roles(name, options)
|
101
|
+
return matched.first if matched
|
102
|
+
nil
|
103
|
+
end
|
54
104
|
|
55
105
|
end
|
56
106
|
end
|
@@ -1,10 +1,3 @@
|
|
1
|
-
#--
|
2
|
-
# =============================================================================
|
3
|
-
# Copyright (c) 2007, Gabriel Handford (gabrielh@gmail.com)
|
4
|
-
# All rights reserved.
|
5
|
-
# =============================================================================
|
6
|
-
#++
|
7
|
-
|
8
1
|
require 'erb'
|
9
2
|
require 'yaml'
|
10
3
|
|
@@ -70,13 +63,15 @@ module Capitate::Plugins::Base
|
|
70
63
|
EOS
|
71
64
|
end
|
72
65
|
|
73
|
-
|
66
|
+
if current_task
|
67
|
+
message += <<-EOS
|
74
68
|
|
75
69
|
Usage:
|
76
70
|
|
77
71
|
#{indent_doc(current_task.desc)}
|
78
72
|
|
79
|
-
|
73
|
+
EOS
|
74
|
+
end
|
80
75
|
end
|
81
76
|
|
82
77
|
# Indent string block.
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Capitate::Plugins::Build
|
2
|
+
|
3
|
+
# Configure, make, make install.
|
4
|
+
#
|
5
|
+
# ==== Options
|
6
|
+
# +name+:: Name for app
|
7
|
+
# +options+:: Options
|
8
|
+
# - +build_dest+:: Place to build from, ex. /usr/src, defaults to /tmp/name
|
9
|
+
# - +url+:: URL to download package from
|
10
|
+
# - +configure_options+:: Options for ./configure
|
11
|
+
# - +symlink+:: After install, list of source, dest pairs to symlink
|
12
|
+
# [ { "/usr/local/sphinx-0.9.8-rc1" => "/usr/local/sphinx" }, ... ]
|
13
|
+
# ln -s /usr/local/sphinx-0.9.8-rc1 /usr/local/sphinx
|
14
|
+
#
|
15
|
+
# - +unpack_dir+:: Directory that is unpacked from tgz (if not matching the file name)
|
16
|
+
# - +to_log+:: If specified, will redirect output to this path
|
17
|
+
#
|
18
|
+
# ==== Examples (in capistrano task)
|
19
|
+
# build.make_install("nginx", { :url => "http://sysoev.ru/nginx/nginx-0.5.35.tar.gz", ... })
|
20
|
+
#
|
21
|
+
def make_install(name, options)
|
22
|
+
install(name, options) do |dir|
|
23
|
+
configure_options = options[:configure_options] || ""
|
24
|
+
|
25
|
+
# Whether to capture build output
|
26
|
+
unless options.has_key?(:to_log)
|
27
|
+
to_log = ">> debug.log"
|
28
|
+
else
|
29
|
+
to_log = ""
|
30
|
+
to_log = ">> #{options[:to_log]}" unless options[:to_log].blank?
|
31
|
+
end
|
32
|
+
|
33
|
+
script.run_all <<-CMDS
|
34
|
+
sh -c "cd #{dir} && ./configure #{configure_options} #{to_log}"
|
35
|
+
sh -c "cd #{dir} && make #{to_log}"
|
36
|
+
sh -c "cd #{dir} && make install #{to_log}"
|
37
|
+
CMDS
|
38
|
+
|
39
|
+
utils.ln(options[:symlink]) unless options[:symlink].blank?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Download, unpack and yield (unpacked source director).
|
44
|
+
#
|
45
|
+
# ==== Options
|
46
|
+
# +name+:: Name for app
|
47
|
+
# +options+:: Options
|
48
|
+
# - +build_dest+:: Place to build from, ex. /usr/src, defaults to /tmp/name
|
49
|
+
# - +url+:: URL to download package from
|
50
|
+
#
|
51
|
+
# ==== Examples (in capistrano task)
|
52
|
+
# script.make("rubygems", { :url => :url => "http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz" }) do |dir|
|
53
|
+
# sudo "echo 'Running setup...' && cd #{dir} && ruby #{dir}/setup.rb"
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
def install(name, options, &block)
|
57
|
+
build_dest = options[:build_dest]
|
58
|
+
build_dest ||= "/tmp/#{name}"
|
59
|
+
url = options[:url]
|
60
|
+
|
61
|
+
script.unpack(url, build_dest, options) do |dir|
|
62
|
+
yield(dir) if block_given?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
Capistrano.plugin :build, Capitate::Plugins::Build
|
data/lib/capitate/plugins/gem.rb
CHANGED
@@ -6,8 +6,8 @@ module Capitate::Plugins::Gem
|
|
6
6
|
# +gems+:: List of gem names, or a single gem
|
7
7
|
#
|
8
8
|
# ==== Examples (in capistrano task)
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# gems.install("raspell")
|
10
|
+
# gems.install([ "raspell", "foo" ])
|
11
11
|
#
|
12
12
|
def install(gems)
|
13
13
|
# If a single object, wrap in array
|
@@ -12,7 +12,7 @@ module Capitate::Plugins::Prompt
|
|
12
12
|
# +label+:: Label
|
13
13
|
# +options+:: Options
|
14
14
|
# - +verify+:: If true, prompt twice and verify
|
15
|
-
# - +lazy+:: If true, returns a
|
15
|
+
# - +lazy+:: If true, returns a Proc. _Defaults to true_
|
16
16
|
# - +check_hash+:: If present, checks that md5 is same as password md5
|
17
17
|
#
|
18
18
|
def password(label, options = {})
|
@@ -1,63 +1,5 @@
|
|
1
1
|
module Capitate::Plugins::Script
|
2
2
|
|
3
|
-
# Configure, make, make install.
|
4
|
-
#
|
5
|
-
# ==== Options
|
6
|
-
# +name+:: Name for app
|
7
|
-
# +options+:: Options
|
8
|
-
# - +build_dest+:: Place to build from, ex. /usr/src, defaults to /tmp/name
|
9
|
-
# - +url+:: URL to download package from
|
10
|
-
# - +configure_options+:: Options for ./configure
|
11
|
-
# - +unpack_dir+:: Directory that is unpacked from tgz (if not matching the file name)
|
12
|
-
# - +to_log+:: If specified, will redirect output to this path
|
13
|
-
#
|
14
|
-
# ==== Examples (in capistrano task)
|
15
|
-
# script.make_install("nginx", { :url => "http://sysoev.ru/nginx/nginx-0.5.35.tar.gz", ... })
|
16
|
-
#
|
17
|
-
def make_install(name, options)
|
18
|
-
install(name, options) do |dir|
|
19
|
-
configure_options = options[:configure_options] || ""
|
20
|
-
|
21
|
-
# Whether to capture build output
|
22
|
-
unless options.has_key?(:to_log)
|
23
|
-
to_log = ">> debug.log"
|
24
|
-
else
|
25
|
-
to_log = ""
|
26
|
-
to_log = ">> #{options[:to_log]}" unless options[:to_log].blank?
|
27
|
-
end
|
28
|
-
|
29
|
-
run_all <<-CMDS
|
30
|
-
sh -c "cd #{dir} && ./configure #{configure_options} #{to_log}"
|
31
|
-
sh -c "cd #{dir} && make #{to_log}"
|
32
|
-
sh -c "cd #{dir} && make install #{to_log}"
|
33
|
-
CMDS
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Download, unpack and yield (unpacked source director).
|
38
|
-
#
|
39
|
-
# ==== Options
|
40
|
-
# +name+:: Name for app
|
41
|
-
# +options+:: Options
|
42
|
-
# - +build_dest+:: Place to build from, ex. /usr/src, defaults to /tmp/name
|
43
|
-
# - +url+:: URL to download package from
|
44
|
-
#
|
45
|
-
# ==== Examples (in capistrano task)
|
46
|
-
# script.make("rubygems", { :url => :url => "http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz" }) do |dir|
|
47
|
-
# sudo "echo 'Running setup...' && cd #{dir} && ruby #{dir}/setup.rb"
|
48
|
-
# end
|
49
|
-
#
|
50
|
-
def install(name, options, &block)
|
51
|
-
build_dest = options[:build_dest]
|
52
|
-
build_dest ||= "/tmp/#{name}"
|
53
|
-
url = options[:url]
|
54
|
-
|
55
|
-
unpack(url, build_dest, options) do |dir|
|
56
|
-
yield(dir) if block_given?
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
|
61
3
|
# Run (sh) script. If script has .erb extension it will evaluate it.
|
62
4
|
#
|
63
5
|
# ==== Options
|
@@ -132,7 +74,6 @@ module Capitate::Plugins::Script
|
|
132
74
|
end
|
133
75
|
|
134
76
|
# Run all commands (separated by newlines).
|
135
|
-
# Runs with <tt>sh -c</tt>, so sudo can work with any command
|
136
77
|
#
|
137
78
|
# ==== Options
|
138
79
|
# +cmds+:: Commands (separated by newlines)
|
@@ -8,6 +8,9 @@ module Capitate::Plugins::Upload
|
|
8
8
|
# +dest_path:: Remote destination path
|
9
9
|
# +options+:: Options (see capistrano 'put')
|
10
10
|
#
|
11
|
+
# ==== Examples
|
12
|
+
# upload.file("/tmp/large_file.tgz", "#{shared_path}/tmp/large_file.tgz")
|
13
|
+
#
|
11
14
|
def file(src_path, dest_path, options = {})
|
12
15
|
data = FileData.new(src_path)
|
13
16
|
put(data, dest_path, options)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Capitate::Plugins::Utils
|
2
|
+
|
3
|
+
# Symlink source to dest
|
4
|
+
#
|
5
|
+
# ==== Options
|
6
|
+
# +args+:: Arguments. If argument is hash, then key is symlinked to value.
|
7
|
+
#
|
8
|
+
# ==== Examples
|
9
|
+
# ln("src/foo" => "dest/foo") # Run: ln -nfs src/foo dest/foo
|
10
|
+
#
|
11
|
+
def ln(*args)
|
12
|
+
args.each do |arg|
|
13
|
+
if arg.is_a?(Hash)
|
14
|
+
arg.each do |src, dest|
|
15
|
+
run_via "ln -nfs #{src} #{dest}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def install_template(template_path, destination)
|
22
|
+
put template.load("memcached/memcached.monitrc.erb"), "/tmp/memcached.monitrc"
|
23
|
+
run_via "install -o root /tmp/memcached.monitrc #{monit_conf_dir}/memcached.monitrc"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
Capistrano.plugin :utils, Capitate::Plugins::Utils
|
data/lib/capitate/plugins/yum.rb
CHANGED
data/lib/capitate/task_node.rb
CHANGED
data/lib/capitate/version.rb
CHANGED
data/lib/capitate.rb
CHANGED
@@ -15,6 +15,8 @@ end
|
|
15
15
|
|
16
16
|
require 'capitate/plugins/base'
|
17
17
|
require 'capitate/plugins/gem'
|
18
|
+
require 'capitate/plugins/build'
|
19
|
+
require 'capitate/plugins/utils'
|
18
20
|
require 'capitate/plugins/script'
|
19
21
|
require 'capitate/plugins/prompt'
|
20
22
|
require 'capitate/plugins/templates'
|
@@ -23,7 +25,6 @@ require 'capitate/plugins/yum'
|
|
23
25
|
|
24
26
|
# Extensions + Patches
|
25
27
|
require "capitate/cap_ext/connections"
|
26
|
-
require "capitate/cap_ext/extension_proxy"
|
27
28
|
require "capitate/cap_ext/variables"
|
28
29
|
require "capitate/cap_ext/run_via"
|
29
30
|
require "capitate/cap_ext/roles"
|
@@ -1,68 +1,90 @@
|
|
1
|
-
#
|
2
|
-
# This is an EXAMPLE deployment script based on some recipes in Capitate.
|
3
|
-
#
|
4
|
-
|
5
1
|
#
|
6
2
|
# For installing apps on the thoughpolice centos 5.1 image
|
7
3
|
#
|
8
|
-
|
4
|
+
|
5
|
+
task :check_role do
|
6
|
+
# Can use cap HOSTS=192.168.1.111 install
|
7
|
+
# Otherwise prompt for the service
|
8
|
+
role :install, prompt.ask("Server: ") if find_servers_for_task(current_task).blank?
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :install do
|
12
|
+
|
13
|
+
task :default do
|
9
14
|
|
10
|
-
|
11
|
-
|
15
|
+
# Add a templates dir
|
16
|
+
# set :templates_dirs, [ File.dirname(__FILE__) + "/../templates" ]
|
12
17
|
|
13
|
-
|
14
|
-
|
18
|
+
set :user, "root"
|
19
|
+
set :run_method, :run
|
15
20
|
|
16
|
-
|
17
|
-
# Otherwise prompt for the service
|
18
|
-
role :install, prompt.ask("Server: ") if find_servers_for_task(current_task).blank?
|
21
|
+
check_role
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
# Setup for web
|
24
|
+
# * Add admin group
|
25
|
+
# * Change inittab to runlevel 3
|
26
|
+
# * Create web apps directory
|
27
|
+
# * Add admin group to suders ALL=(ALL) ALL
|
28
|
+
script.run_all <<-CMDS
|
29
|
+
egrep "^admin" /etc/group || /usr/sbin/groupadd admin
|
30
|
+
sed -i -e 's/^id:5:initdefault:/id:3:initdefault:/g' /etc/inittab
|
31
|
+
mkdir -p /var/www/apps
|
32
|
+
egrep "^%admin" /etc/sudoers || echo "%admin ALL=(ALL) ALL" >> /etc/sudoers
|
33
|
+
CMDS
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
# Package installs
|
36
|
+
yum.remove [ "openoffice.org-*", "ImageMagick" ]
|
37
|
+
#yum.update
|
38
|
+
yum.install [ "gcc", "kernel-devel", "libevent-devel", "libxml2-devel", "openssl", "openssl-devel",
|
39
|
+
"aspell", "aspell-devel", "aspell-en", "aspell-es" ]
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
+
#
|
42
|
+
# App installs
|
43
|
+
#
|
41
44
|
|
42
|
-
|
43
|
-
# Fix ruby install openssl
|
44
|
-
script.sh("ruby/fix_openssl.sh")
|
45
|
+
ruby.centos.install
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
nginx.centos.install
|
48
|
+
mysql.centos.install
|
49
|
+
sphinx.centos.install
|
50
|
+
monit.centos.install
|
51
|
+
imagemagick.centos.install
|
52
|
+
memcached.centos.install
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
#
|
55
|
+
# Install monit hooks
|
56
|
+
#
|
57
|
+
nginx.monit.install
|
58
|
+
mysql.monit.install
|
59
|
+
memcached.monit.install
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
# Install gems
|
62
|
+
gems.install(fetch(:gems_to_install))
|
63
|
+
|
64
|
+
# Cleanup
|
65
|
+
yum.clean
|
66
|
+
end
|
63
67
|
|
64
|
-
|
65
|
-
|
68
|
+
desc "Install gems only"
|
69
|
+
task :gems_only do
|
70
|
+
gems.install(fetch(:gems_to_install))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# To be root, cap as_root nginx:centos:install
|
76
|
+
#
|
77
|
+
desc "Be root"
|
78
|
+
task :as_root do
|
79
|
+
set :user, "root"
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Fixes
|
84
|
+
#
|
85
|
+
after "ruby:centos:install" do
|
86
|
+
# Fix ruby install openssl
|
87
|
+
script.sh("ruby/fix_openssl.sh")
|
66
88
|
end
|
67
89
|
|
68
90
|
|
@@ -70,9 +92,13 @@ end
|
|
70
92
|
# Install options
|
71
93
|
#
|
72
94
|
|
95
|
+
set :gems_to_install, [ "rake", "mysql -- --with-mysql-include=/usr/include/mysql --with-mysql-lib=/usr/lib/mysql --with-mysql-config",
|
96
|
+
"raspell", "rmagick", "mongrel", "mongrel_cluster", "json", "mime-types" ]
|
97
|
+
|
98
|
+
|
73
99
|
# Ruby install
|
74
100
|
set :ruby_build_options, {
|
75
|
-
:url => "http://
|
101
|
+
:url => "http://capitate.s3.amazonaws.com/ruby-1.8.6-p110.tar.gz",
|
76
102
|
:build_dest => "/usr/src",
|
77
103
|
:configure_options => "--prefix=/usr",
|
78
104
|
:clean => false
|
@@ -93,6 +119,7 @@ set :memcached_build_options, {
|
|
93
119
|
|
94
120
|
# Monit install
|
95
121
|
set :monit_conf_dir, "/etc/monit"
|
122
|
+
set :monit_pid_path, "/var/run/monit.pid"
|
96
123
|
set :monit_port, 2812
|
97
124
|
set :monit_build_options, {
|
98
125
|
:url => "http://www.tildeslash.com/monit/dist/monit-4.10.1.tar.gz"
|
@@ -107,23 +134,28 @@ set :nginx_build_options, {
|
|
107
134
|
:url => "http://sysoev.ru/nginx/nginx-0.5.35.tar.gz",
|
108
135
|
:configure_options => "--sbin-path=#{nginx_bin_path} --conf-path=#{nginx_conf_path} \
|
109
136
|
--pid-path=#{nginx_pid_path} --error-log-path=/var/log/nginx_master_error.log --lock-path=/var/lock/nginx \
|
110
|
-
--prefix=#{nginx_prefix_path} --with-md5=auto/lib/md5 --with-sha1=auto/lib/sha1 --with-http_ssl_module
|
137
|
+
--prefix=#{nginx_prefix_path} --with-md5=auto/lib/md5 --with-sha1=auto/lib/sha1 --with-http_ssl_module \
|
138
|
+
--with-http_flv_module"
|
111
139
|
}
|
112
140
|
|
113
141
|
# Sphinx install
|
114
|
-
set :sphinx_prefix, "/usr/local/sphinx"
|
142
|
+
set :sphinx_prefix, "/usr/local/sphinx-0.9.8-rc1"
|
115
143
|
set :sphinx_build_options, {
|
116
|
-
:url => "http://www.sphinxsearch.com/downloads/sphinx-0.9.
|
144
|
+
:url => "http://www.sphinxsearch.com/downloads/sphinx-0.9.8-rc1.tar.gz",
|
145
|
+
#:url => "http://www.sphinxsearch.com/downloads/sphinx-0.9.7.tar.gz",
|
117
146
|
:configure_options => "--with-mysql-includes=/usr/include/mysql --with-mysql-libs=/usr/lib/mysql \
|
118
|
-
--prefix=#{sphinx_prefix}"
|
147
|
+
--prefix=#{sphinx_prefix}",
|
148
|
+
:symlink => { sphinx_prefix => "/usr/local/sphinx" }
|
119
149
|
}
|
120
150
|
|
121
|
-
|
151
|
+
|
152
|
+
|
153
|
+
# For mysql:install
|
122
154
|
set :mysql_pid_path, "/var/run/mysqld/mysqld.pid"
|
123
155
|
set :db_port, 3306
|
124
156
|
|
125
157
|
# Imagemagick install
|
126
158
|
set :imagemagick_build_options, {
|
127
|
-
:url => "
|
159
|
+
:url => "http://capitate.s3.amazonaws.com/ImageMagick.tar.gz",
|
128
160
|
:unpack_dir => "ImageMagick-*"
|
129
161
|
}
|
@@ -20,7 +20,7 @@ namespace :imagemagick do
|
|
20
20
|
yum.install([ "libjpeg-devel", "libpng-devel", "glib2-devel", "fontconfig-devel", "zlib-devel",
|
21
21
|
"libwmf-devel", "freetype-devel", "libtiff-devel" ])
|
22
22
|
|
23
|
-
|
23
|
+
build.make_install("imagemagick", imagemagick_build_options)
|
24
24
|
end
|
25
25
|
|
26
26
|
end
|
@@ -28,7 +28,7 @@ namespace :memcached do
|
|
28
28
|
fetch(:memcached_build_options)
|
29
29
|
|
30
30
|
# Build
|
31
|
-
|
31
|
+
build.make_install("memcached", memcached_build_options)
|
32
32
|
|
33
33
|
# Install initscript, service
|
34
34
|
put template.load("memcached/memcached.initd.centos.erb"), "/tmp/memcached.initd"
|
data/lib/recipes/centos/monit.rb
CHANGED
@@ -29,7 +29,7 @@ namespace :monit do
|
|
29
29
|
yum.install([ "flex", "byacc" ])
|
30
30
|
|
31
31
|
# Build
|
32
|
-
|
32
|
+
build.make_install("monit", monit_build_options)
|
33
33
|
|
34
34
|
# Install initscript
|
35
35
|
put template.load("monit/monit.initd.centos.erb"), "/tmp/monit.initd"
|
@@ -47,6 +47,19 @@ namespace :monit do
|
|
47
47
|
script.sh("monit/cert.sh")
|
48
48
|
end
|
49
49
|
|
50
|
+
desc <<-DESC
|
51
|
+
Install monit firewall rule.
|
52
|
+
|
53
|
+
*monit_port*: Monit port. _Defaults to 2812_\n
|
54
|
+
@set :monit_port, 2812@\n
|
55
|
+
DESC
|
56
|
+
task :iptables do
|
57
|
+
# Settings
|
58
|
+
fetch_or_default(:monit_port, 2812)
|
59
|
+
run_via "iptables -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport #{monit_port} -j ACCEPT"
|
60
|
+
run_via "/sbin/service iptables save"
|
61
|
+
end
|
62
|
+
|
50
63
|
end
|
51
64
|
|
52
65
|
end
|
data/lib/recipes/centos/nginx.rb
CHANGED
@@ -36,7 +36,7 @@ namespace :nginx do
|
|
36
36
|
yum.install([ "pcre-devel", "openssl", "openssl-devel" ])
|
37
37
|
|
38
38
|
# Build
|
39
|
-
|
39
|
+
build.make_install("nginx", nginx_build_options)
|
40
40
|
|
41
41
|
# Install initscript, and turn it on
|
42
42
|
put template.load("nginx/nginx.initd.erb"), "/tmp/nginx.initd"
|
data/lib/recipes/centos/ruby.rb
CHANGED
@@ -26,10 +26,10 @@ namespace :ruby do
|
|
26
26
|
yum.install([ "zlib", "zlib-devel", "readline-devel" ])
|
27
27
|
|
28
28
|
# Install ruby 1.8.6
|
29
|
-
|
29
|
+
build.make_install("ruby", ruby_build_options)
|
30
30
|
|
31
31
|
# Install rubygems
|
32
|
-
|
32
|
+
build.install("rubygems", rubygems_build_options) do |dir|
|
33
33
|
run_via "cd #{dir} && ruby setup.rb > install.log", { :shell => true }
|
34
34
|
end
|
35
35
|
|
@@ -24,7 +24,7 @@ namespace :sphinx do
|
|
24
24
|
yum.install([ "gcc-c++" ])
|
25
25
|
|
26
26
|
# Build
|
27
|
-
|
27
|
+
build.make_install("sphinx", sphinx_build_options)
|
28
28
|
end
|
29
29
|
|
30
30
|
desc <<-DESC
|
@@ -32,6 +32,7 @@ namespace :sphinx do
|
|
32
32
|
*sphinx_prefix*: Sphinx install prefix. _Defaults to "/usr/local/sphinx"_\n
|
33
33
|
*sphinx_pid_path*: Directory to sphinx pid. _Defaults to "[shared_path]/pids/searchd.pid"_\n
|
34
34
|
*sphinx_conf_path*: Path to sphinx.conf. _Defaults to "[shared_path]/config/sphinx.conf"_\n
|
35
|
+
*sphinx_index_path*: Path to sphinx indexes. _Defaults to "[shared_path]/var/index"_\n
|
35
36
|
DESC
|
36
37
|
task :setup do
|
37
38
|
|
@@ -39,6 +40,7 @@ namespace :sphinx do
|
|
39
40
|
fetch_or_default(:sphinx_prefix, "/usr/local/sphinx")
|
40
41
|
fetch_or_default(:sphinx_pid_path, "#{shared_path}/pids/searchd.pid")
|
41
42
|
fetch_or_default(:sphinx_conf_path, "#{shared_path}/config/sphinx.conf")
|
43
|
+
fetch_or_default(:sphinx_index_root, "#{shared_path}/var/index")
|
42
44
|
|
43
45
|
# Install initscript
|
44
46
|
put template.load("sphinx/sphinx_app.initd.centos.erb"), "/tmp/sphinx.initd"
|
@@ -48,7 +50,21 @@ namespace :sphinx do
|
|
48
50
|
run_via "/sbin/chkconfig --level 345 sphinx_#{application} on"
|
49
51
|
|
50
52
|
# Create app indexes dir
|
51
|
-
|
53
|
+
run "mkdir -p #{shared_path}/var/index"
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
desc <<-DESC
|
58
|
+
Install sphinx firewall rule.
|
59
|
+
|
60
|
+
*sphinx_port*: Sphinx port. _Defaults to 3312_\n
|
61
|
+
@set :sphinx_port, 3312@\n
|
62
|
+
DESC
|
63
|
+
task :iptables do
|
64
|
+
# Settings
|
65
|
+
fetch_or_default(:sphinx_port, 3312)
|
66
|
+
run_via "iptables -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport #{sphinx_port} -j ACCEPT"
|
67
|
+
run_via "/sbin/service iptables save"
|
52
68
|
end
|
53
69
|
end
|
54
70
|
|
data/lib/recipes/sphinx.rb
CHANGED
@@ -36,8 +36,8 @@ namespace :sphinx do
|
|
36
36
|
*sphinx_db_name*: Sphinx DB name. _Defaults to db_name_\n
|
37
37
|
*sphinx_db_port*: Sphinx DB port. _Defaults to db_port_\n
|
38
38
|
|
39
|
-
*sphinx_db_host*: Sphinx DB host. _Defaults to
|
40
|
-
*
|
39
|
+
*sphinx_db_host*: Sphinx DB host. _Defaults to db_host_\n
|
40
|
+
*sphinx_conf_host*: Sphinx DB host to listen on. _Defaults to 127.0.0.1_\n
|
41
41
|
|
42
42
|
DESC
|
43
43
|
task :update_conf do
|
@@ -54,12 +54,9 @@ namespace :sphinx do
|
|
54
54
|
fetch_or_default(:sphinx_db_pass, db_pass)
|
55
55
|
fetch_or_default(:sphinx_db_name, db_name)
|
56
56
|
fetch_or_default(:sphinx_db_port, db_port)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
raise "No :db roles, and no :sphinx_db_host setting specified" unless exists?(:sphinx_db_host)
|
61
|
-
raise "No :search roles, and no :sphinx_host setting specified" unless exists?(:sphinx_host)
|
62
|
-
|
57
|
+
fetch_or_default(:sphinx_db_host, db_host)
|
58
|
+
fetch_or_default(:sphinx_conf_host, "127.0.0.1")
|
59
|
+
|
63
60
|
put template.load(sphinx_conf_template), sphinx_conf_path
|
64
61
|
end
|
65
62
|
|
@@ -17,6 +17,7 @@ NAME=searchd
|
|
17
17
|
DAEMON=<%= sphinx_prefix %>/bin/searchd
|
18
18
|
CONFIGFILE=<%= sphinx_conf_path %>
|
19
19
|
PIDFILE=<%= sphinx_pid_path %>
|
20
|
+
INDEX_DIR=<%= sphinx_index_root %>
|
20
21
|
|
21
22
|
start() {
|
22
23
|
daemon --user <%= user %> $DAEMON --config $CONFIGFILE
|
@@ -33,6 +34,18 @@ reload() {
|
|
33
34
|
kill -HUP `cat $PIDFILE` || echo -n " can't reload"
|
34
35
|
}
|
35
36
|
|
37
|
+
clean() {
|
38
|
+
pid=`cat $PIDFILE`
|
39
|
+
|
40
|
+
if checkpid $pid; then
|
41
|
+
echo -n " can't clean; running"
|
42
|
+
else
|
43
|
+
rm -f $INDEX_DIR/*.spl
|
44
|
+
return $?
|
45
|
+
fi
|
46
|
+
return 1
|
47
|
+
}
|
48
|
+
|
36
49
|
case "$1" in
|
37
50
|
start)
|
38
51
|
echo -n "Starting $DESC: $NAME"
|
@@ -58,6 +71,11 @@ case "$1" in
|
|
58
71
|
start
|
59
72
|
RETVAL=$?;
|
60
73
|
;;
|
74
|
+
clean)
|
75
|
+
echo -n "Cleaning $DESC"
|
76
|
+
clean
|
77
|
+
RETVAL=$?;
|
78
|
+
;;
|
61
79
|
*)
|
62
80
|
echo "Usage: $0 {start|stop|restart|force-reload}" >&2
|
63
81
|
RETVAL=3;
|
data/test/test_roles.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
require 'capistrano/configuration'
|
3
|
+
|
4
|
+
class TestRoles < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@config = Capistrano::Configuration.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_fetch
|
11
|
+
|
12
|
+
@config.load do
|
13
|
+
|
14
|
+
role :test, "10.0.6.20"
|
15
|
+
role :test_with_options, "10.0.6.21", :op1 => true
|
16
|
+
role :test_with_options, "10.0.6.22", :op2 => true
|
17
|
+
|
18
|
+
role :test_with_other_options, "10.0.6.23", :sphinx_port => 3312
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
test_role = @config.fetch_role(:test)
|
23
|
+
|
24
|
+
assert test_role
|
25
|
+
assert_equal "10.0.6.20", test_role.host
|
26
|
+
|
27
|
+
test_roles = @config.fetch_roles(:test_with_options)
|
28
|
+
assert test_roles
|
29
|
+
assert_equal [ "10.0.6.21", "10.0.6.22" ], test_roles.collect(&:host)
|
30
|
+
|
31
|
+
test_roles_with_opts = @config.fetch_roles(:test_with_options, :op1 => true)
|
32
|
+
assert test_roles_with_opts
|
33
|
+
assert_equal [ "10.0.6.21" ], test_roles_with_opts.collect(&:host)
|
34
|
+
|
35
|
+
test_role_with_opts = @config.fetch_role(:test_with_options, :op2 => true)
|
36
|
+
assert test_role_with_opts
|
37
|
+
assert_equal "10.0.6.22", test_role_with_opts.host
|
38
|
+
|
39
|
+
|
40
|
+
test_role_with_other_opts = @config.fetch_role(:test_with_other_options)
|
41
|
+
assert test_role_with_other_opts
|
42
|
+
assert_equal "10.0.6.23", test_role_with_other_opts.host
|
43
|
+
assert_equal 3312, test_role_with_other_opts.options[:sphinx_port]
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/website/index.html
CHANGED
@@ -38,7 +38,7 @@
|
|
38
38
|
|
39
39
|
<div id="version" class="clickable box" onclick='document.location = "http://rubyforge.org/projects/capitate"; return false'>
|
40
40
|
<p>Get Version</p>
|
41
|
-
<a href="http://rubyforge.org/projects/capitate" class="numbers">0.2.
|
41
|
+
<a href="http://rubyforge.org/projects/capitate" class="numbers">0.2.7</a>
|
42
42
|
</div>
|
43
43
|
|
44
44
|
<div id="recipes">
|
@@ -137,7 +137,7 @@
|
|
137
137
|
<p>Comments are welcome. Send an email to <a href="mailto:gabrielh@gmail.com">Gabriel Handford</a> via the <a href="http://groups.google.com/group/capitate">forum</a></p>
|
138
138
|
</div>
|
139
139
|
<p class="coda">
|
140
|
-
<a href="mailto:gabrielh@gmail.com">Gabriel Handford</a>,
|
140
|
+
<a href="mailto:gabrielh@gmail.com">Gabriel Handford</a>, 10th March 2008<br>
|
141
141
|
Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
|
142
142
|
</p>
|
143
143
|
</div>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capitate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ""
|
6
6
|
authors:
|
7
7
|
- Gabriel Handford
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-03-10 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -112,11 +112,13 @@ files:
|
|
112
112
|
- lib/capitate/cap_ext/run_via.rb
|
113
113
|
- lib/capitate/cap_ext/variables.rb
|
114
114
|
- lib/capitate/plugins/base.rb
|
115
|
+
- lib/capitate/plugins/build.rb
|
115
116
|
- lib/capitate/plugins/gem.rb
|
116
117
|
- lib/capitate/plugins/prompt.rb
|
117
118
|
- lib/capitate/plugins/script.rb
|
118
119
|
- lib/capitate/plugins/templates.rb
|
119
120
|
- lib/capitate/plugins/upload.rb
|
121
|
+
- lib/capitate/plugins/utils.rb
|
120
122
|
- lib/capitate/plugins/yum.rb
|
121
123
|
- lib/capitate/recipes.rb
|
122
124
|
- lib/capitate/task_node.rb
|
@@ -210,4 +212,5 @@ test_files:
|
|
210
212
|
- test/test_helper.rb
|
211
213
|
- test/test_plugin_upload.rb
|
212
214
|
- test/test_recipes.rb
|
215
|
+
- test/test_roles.rb
|
213
216
|
- test/test_templates.rb
|