michaeldwan-divvy 0.1.2
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/.gitignore +1 -0
- data/LICENSE +20 -0
- data/README.rdoc +27 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/bin/divvy +54 -0
- data/divvy.gemspec +79 -0
- data/examples/packages/build_essential.rb +14 -0
- data/examples/packages/databases/mysql.rb +20 -0
- data/examples/packages/databases/sqlite3.rb +21 -0
- data/examples/packages/ruby/rails.rb +11 -0
- data/examples/packages/ruby/ruby.rb +17 -0
- data/examples/packages/ruby/ruby_gems.rb +19 -0
- data/examples/packages/scm/git.rb +11 -0
- data/examples/packages/utilities/screen.rb +5 -0
- data/lib/divvy/duplicate_package_error.rb +11 -0
- data/lib/divvy/package.rb +33 -0
- data/lib/divvy/package_runner.rb +51 -0
- data/lib/divvy/plugins/apt.rb +21 -0
- data/lib/divvy/plugins/file_utilities.rb +23 -0
- data/lib/divvy/plugins/gem.rb +21 -0
- data/lib/divvy/plugins/rails.rb +11 -0
- data/lib/divvy/plugins/source.rb +140 -0
- data/lib/divvy/provisioner.rb +41 -0
- data/lib/divvy/server.rb +77 -0
- data/lib/divvy/verification.rb +37 -0
- data/lib/divvy/verifiers.rb +62 -0
- data/lib/divvy.rb +94 -0
- data/test/divvy_test.rb +61 -0
- data/test/package_test.rb +45 -0
- data/test/test_helper.rb +11 -0
- metadata +94 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Michael Dwan
|
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.rdoc
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
= Divvy
|
2
|
+
|
3
|
+
Divvy is an easy to configure, easy to extend server provisioning framework written in Ruby.
|
4
|
+
|
5
|
+
I created this for use on a specific project and have not yet gotten around to documenting or testing it :)
|
6
|
+
|
7
|
+
Stay tuned.
|
8
|
+
|
9
|
+
== Principals
|
10
|
+
|
11
|
+
Divvy was created to
|
12
|
+
|
13
|
+
- There is a difference between preparing a server and managing instances.
|
14
|
+
- Provisioning a server is more than just installing required software.
|
15
|
+
- Capistrano and Vlad are great for deployment, but are overkill for busting out SSH commands.
|
16
|
+
- Specific scripts should extend Divvy as necessary.
|
17
|
+
|
18
|
+
== Credits
|
19
|
+
|
20
|
+
Divvy was created and currently maintained by Michael Dwan.
|
21
|
+
|
22
|
+
Inspiration came from Marcus Crafter's Sprinkle, Capistrano, Evan Petrie from Metromix, and
|
23
|
+
several other scripts and frameworks I have attempted to use over the years.
|
24
|
+
|
25
|
+
== Copyright
|
26
|
+
|
27
|
+
Copyright (c) 2009 Michael Dwan. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "divvy"
|
8
|
+
gem.summary = %Q{Divvy is an easy to configure, easy to extend server provisioning framework written in Ruby.}
|
9
|
+
gem.email = "mpdwan@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/mdwan/divvy"
|
11
|
+
gem.authors = ["Michael Dwan"]
|
12
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'rake/rdoctask'
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
44
|
+
if File.exist?('VERSION.yml')
|
45
|
+
config = YAML.load(File.read('VERSION.yml'))
|
46
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
47
|
+
else
|
48
|
+
version = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "divvy #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
56
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.2
|
data/bin/divvy
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/../lib/divvy"
|
4
|
+
require 'optparse'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
options = OpenStruct.new
|
8
|
+
options.verbose = false
|
9
|
+
options.test = false
|
10
|
+
|
11
|
+
parser = OptionParser.new do |opts|
|
12
|
+
opts.banner = <<BANNER
|
13
|
+
Divvy
|
14
|
+
=====
|
15
|
+
|
16
|
+
Divvy is a software provisioning tool you canuse to build remote servers with.
|
17
|
+
|
18
|
+
Usage
|
19
|
+
=====
|
20
|
+
|
21
|
+
$> #{File.basename($0)} [options]
|
22
|
+
|
23
|
+
Options are:
|
24
|
+
BANNER
|
25
|
+
opts.separator ""
|
26
|
+
|
27
|
+
opts.on("-s", "--script=PATH", "The divvy script to run") do |script|
|
28
|
+
options.script = script
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-t", "--[no-]test", "Process but do not perform actions") do |t|
|
32
|
+
options.test = t
|
33
|
+
end
|
34
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
35
|
+
options.verbose = v
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
|
39
|
+
opts.parse!(ARGV)
|
40
|
+
|
41
|
+
unless options.script
|
42
|
+
puts "script is required"
|
43
|
+
puts opts
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
Divvy.init
|
50
|
+
|
51
|
+
Divvy.test = options.test
|
52
|
+
Divvy.verbose = options.verbose
|
53
|
+
|
54
|
+
Divvy.run(File.read(options.script), options.script)
|
data/divvy.gemspec
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{divvy}
|
5
|
+
s.version = "0.1.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Michael Dwan"]
|
9
|
+
s.date = %q{2009-05-31}
|
10
|
+
s.default_executable = %q{divvy}
|
11
|
+
s.email = %q{mpdwan@gmail.com}
|
12
|
+
s.executables = ["divvy"]
|
13
|
+
s.extra_rdoc_files = [
|
14
|
+
"LICENSE",
|
15
|
+
"README.rdoc"
|
16
|
+
]
|
17
|
+
s.files = [
|
18
|
+
".gitignore",
|
19
|
+
"LICENSE",
|
20
|
+
"README.rdoc",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"bin/divvy",
|
24
|
+
"divvy.gemspec",
|
25
|
+
"examples/packages/build_essential.rb",
|
26
|
+
"examples/packages/databases/mysql.rb",
|
27
|
+
"examples/packages/databases/sqlite3.rb",
|
28
|
+
"examples/packages/ruby/rails.rb",
|
29
|
+
"examples/packages/ruby/ruby.rb",
|
30
|
+
"examples/packages/ruby/ruby_gems.rb",
|
31
|
+
"examples/packages/scm/git.rb",
|
32
|
+
"examples/packages/utilities/screen.rb",
|
33
|
+
"lib/divvy.rb",
|
34
|
+
"lib/divvy/duplicate_package_error.rb",
|
35
|
+
"lib/divvy/package.rb",
|
36
|
+
"lib/divvy/package_runner.rb",
|
37
|
+
"lib/divvy/plugins/apt.rb",
|
38
|
+
"lib/divvy/plugins/file_utilities.rb",
|
39
|
+
"lib/divvy/plugins/gem.rb",
|
40
|
+
"lib/divvy/plugins/rails.rb",
|
41
|
+
"lib/divvy/plugins/source.rb",
|
42
|
+
"lib/divvy/provisioner.rb",
|
43
|
+
"lib/divvy/server.rb",
|
44
|
+
"lib/divvy/verification.rb",
|
45
|
+
"lib/divvy/verifiers.rb",
|
46
|
+
"test/divvy_test.rb",
|
47
|
+
"test/package_test.rb",
|
48
|
+
"test/test_helper.rb"
|
49
|
+
]
|
50
|
+
s.has_rdoc = true
|
51
|
+
s.homepage = %q{http://github.com/mdwan/divvy}
|
52
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
53
|
+
s.require_paths = ["lib"]
|
54
|
+
s.rubygems_version = %q{1.3.2}
|
55
|
+
s.summary = %q{Divvy is an easy to configure, easy to extend server provisioning framework written in Ruby.}
|
56
|
+
s.test_files = [
|
57
|
+
"test/divvy_test.rb",
|
58
|
+
"test/package_test.rb",
|
59
|
+
"test/test_helper.rb",
|
60
|
+
"examples/packages/build_essential.rb",
|
61
|
+
"examples/packages/databases/mysql.rb",
|
62
|
+
"examples/packages/databases/sqlite3.rb",
|
63
|
+
"examples/packages/ruby/rails.rb",
|
64
|
+
"examples/packages/ruby/ruby.rb",
|
65
|
+
"examples/packages/ruby/ruby_gems.rb",
|
66
|
+
"examples/packages/scm/git.rb",
|
67
|
+
"examples/packages/utilities/screen.rb"
|
68
|
+
]
|
69
|
+
|
70
|
+
if s.respond_to? :specification_version then
|
71
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
72
|
+
s.specification_version = 3
|
73
|
+
|
74
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
75
|
+
else
|
76
|
+
end
|
77
|
+
else
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Divvy.package :apt_update do
|
2
|
+
apply do
|
3
|
+
run('aptitude -y update')
|
4
|
+
run('aptitude -y safe-upgrade')
|
5
|
+
run('aptitude -y full-upgrade')
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Divvy.package :build_essential do
|
10
|
+
requires :apt_update
|
11
|
+
apply do
|
12
|
+
apt %w(build-essential libssl-dev libreadline5-dev zlib1g-dev)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Divvy.package :mysql do
|
2
|
+
verify do
|
3
|
+
has_executable 'mysql'
|
4
|
+
end
|
5
|
+
|
6
|
+
apply do
|
7
|
+
apt %q(mysql-server mysql-client libmysqlclient15-dev)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
Divvy.package :mysql_ruby_driver do
|
12
|
+
requires :mysql, :ruby, :ruby_gems
|
13
|
+
verify do
|
14
|
+
ruby_can_load 'mysql'
|
15
|
+
end
|
16
|
+
|
17
|
+
apply do
|
18
|
+
ruby_gem 'mysql'
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Divvy.package :sqlite3 do
|
2
|
+
verify do
|
3
|
+
has_executable 'sqlite3'
|
4
|
+
end
|
5
|
+
|
6
|
+
apply do
|
7
|
+
apt 'sqlite3'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
Divvy.package :sqlite3_ruby do
|
12
|
+
requires :sqlite3
|
13
|
+
|
14
|
+
verify do
|
15
|
+
ruby_can_load 'sqlite3'
|
16
|
+
end
|
17
|
+
|
18
|
+
apply do
|
19
|
+
apt 'libsqlite3-dev libsqlite3-ruby1.8'
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Divvy.package :ruby do
|
2
|
+
requires :build_essential
|
3
|
+
|
4
|
+
verify { has_file '/usr/bin/ruby1.8' }
|
5
|
+
verify { has_file '/usr/bin/ri1.8' }
|
6
|
+
verify { has_file '/usr/bin/rdoc1.8' }
|
7
|
+
verify { has_file '/usr/bin/irb1.8' }
|
8
|
+
|
9
|
+
|
10
|
+
apply do
|
11
|
+
apt %q(ruby1.8-dev ruby1.8 ri1.8 rdoc1.8 irb1.8 libreadline-ruby1.8 libruby1.8 libopenssl-ruby)
|
12
|
+
run('ln -s /usr/bin/ruby1.8 /usr/bin/ruby')
|
13
|
+
run('ln -s /usr/bin/ri1.8 /usr/bin/ri')
|
14
|
+
run('ln -s /usr/bin/rdoc1.8 /usr/bin/rdoc')
|
15
|
+
run('ln -s /usr/bin/irb1.8 /usr/bin/irb')
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Divvy.package :ruby_gems do
|
2
|
+
requires :build_essential, :ruby
|
3
|
+
|
4
|
+
verify do
|
5
|
+
has_executable 'gem'
|
6
|
+
end
|
7
|
+
|
8
|
+
apply do
|
9
|
+
source "http://rubyforge.org/frs/download.php/56227/rubygems-1.3.3.tgz" do
|
10
|
+
skip :configure, :build
|
11
|
+
stage :install do
|
12
|
+
run("cd #{build_dir} && ruby setup.rb")
|
13
|
+
run('ln -Fs /usr/bin/gem1.8 /usr/bin/gem')
|
14
|
+
run('gem update')
|
15
|
+
run('gem update --system')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Divvy
|
2
|
+
class Package
|
3
|
+
|
4
|
+
def initialize(name, options = {}, &block)
|
5
|
+
# raise ArgumentError.new('Name is required') unless name
|
6
|
+
@name = name.to_sym
|
7
|
+
@options = options
|
8
|
+
@dependencies = []
|
9
|
+
@verifications = []
|
10
|
+
self.instance_eval(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :name, :options, :verifications, :apply_block
|
14
|
+
|
15
|
+
def requires(*packages)
|
16
|
+
@dependencies << packages
|
17
|
+
@dependencies.flatten!
|
18
|
+
end
|
19
|
+
|
20
|
+
def dependencies
|
21
|
+
@dependencies.each { |package| raise "Package #{package} not found!" unless Divvy.packages.key?(package) }
|
22
|
+
@dependencies.map { |key| Divvy.packages[key] }
|
23
|
+
end
|
24
|
+
|
25
|
+
def apply(&block)
|
26
|
+
@apply_block = block
|
27
|
+
end
|
28
|
+
|
29
|
+
def verify(description = '', &block)
|
30
|
+
@verifications << Verification.new(self, description, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Divvy
|
2
|
+
class PackageRunner
|
3
|
+
def initialize(server, package)
|
4
|
+
@server, @package = server, package
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :server, :package
|
8
|
+
|
9
|
+
def run(command, options = {})
|
10
|
+
server.remote_command(command, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def scp(source, destination, options = {})
|
14
|
+
server.scp(source, destination, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def process
|
18
|
+
puts "==> Processing #{package.name}"
|
19
|
+
|
20
|
+
unless package.verifications.empty?
|
21
|
+
begin
|
22
|
+
process_verifications(true)
|
23
|
+
puts " --> #{package.name} already installed package: #{server.host}"
|
24
|
+
return
|
25
|
+
rescue Divvy::VerificationFailed => e
|
26
|
+
# Yaay package not installed yet
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
self.instance_eval(&package.apply_block) unless package.apply_block.nil?
|
31
|
+
|
32
|
+
process_verifications
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def process_verifications(pre = false)
|
37
|
+
return if package.verifications.empty?
|
38
|
+
|
39
|
+
if pre
|
40
|
+
puts " --> Checking if #{package.name} is already installed for server: #{server.host}"
|
41
|
+
else
|
42
|
+
puts " --> Verifying #{package.name} was properly installed for server: #{server.host}"
|
43
|
+
end
|
44
|
+
|
45
|
+
package.verifications.each do |v|
|
46
|
+
v.verify(server)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Divvy
|
2
|
+
module Plugins
|
3
|
+
module Apt
|
4
|
+
def apt(*packages)
|
5
|
+
packages.flatten!
|
6
|
+
|
7
|
+
options = { :dependencies_only => false }
|
8
|
+
options.update(packages.pop) if packages.last.is_a?(Hash)
|
9
|
+
|
10
|
+
command = [ "DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive" ]
|
11
|
+
command << 'apt-get -qyu'
|
12
|
+
command << (options[:dependencies_only] ? 'build-dep' : 'install')
|
13
|
+
command << packages
|
14
|
+
|
15
|
+
run(command.join(' '))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Divvy.register_plugin(Divvy::Plugins::Apt)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Divvy
|
2
|
+
module Plugins
|
3
|
+
module FileUtilities
|
4
|
+
|
5
|
+
# Makes a directory
|
6
|
+
# path is the diretory to create. This will not create the directory if it already exists
|
7
|
+
# mode is the file mode of the directory. If nil, no mode changes will be made
|
8
|
+
def mkdir(path, mode = nil)
|
9
|
+
run("[ -d #{path} ] || mkdir -p #{path}")
|
10
|
+
run("chmod #{mode} #{dir}") unless mode.nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
def push_text(path, text)
|
14
|
+
run("[ -f #{path} ] || touch #{path}")
|
15
|
+
run("echo '#{text}' | tee -a #{path}")
|
16
|
+
# "echo '#{text}' |#{'sudo' if option?(:sudo)} tee -a #{path}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Divvy.register_plugin(Divvy::Plugins::FileUtilities)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Divvy
|
2
|
+
module Plugins
|
3
|
+
module RubyGems
|
4
|
+
def ruby_gem(gem_name, options = {})
|
5
|
+
options = {
|
6
|
+
:no_doc => true,
|
7
|
+
}.merge(options)
|
8
|
+
|
9
|
+
cmd = "gem install #{gem_name}"
|
10
|
+
cmd << " --version '#{options[:version]}'" if options[:version]
|
11
|
+
cmd << " --source #{options[:source]}" if options[:source]
|
12
|
+
cmd << " --install-dir #{options[:install_dir]}" if options[:install_dir]
|
13
|
+
cmd << " --no-rdoc --no-ri" if options[:no_doc]
|
14
|
+
cmd << " -- #{build_flags}" if options[:build_flags]
|
15
|
+
run(cmd)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Divvy.register_plugin(Divvy::Plugins::RubyGems)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Divvy
|
2
|
+
module Plugins
|
3
|
+
module Rails
|
4
|
+
def set_rails_env(env, file = '~/.profile')
|
5
|
+
run("grep -q \"export RAILS_ENV=production\" #{file} || echo \"export RAILS_ENV=#{env}\" >> #{file}")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
Divvy.register_plugin(Divvy::Plugins::Rails)
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Divvy
|
2
|
+
module Plugins
|
3
|
+
module Source
|
4
|
+
|
5
|
+
def source(source, options = {}, &block)
|
6
|
+
SourceInstaller.new(self, source, options, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
class SourceInstaller
|
10
|
+
def initialize(runner, source, options = {}, &block)
|
11
|
+
@runner = runner
|
12
|
+
@source = source
|
13
|
+
@options = {
|
14
|
+
:prefix => '/usr/local',
|
15
|
+
:archives => '/usr/local/sources',
|
16
|
+
:builds => '/usr/local/build'
|
17
|
+
}.merge(options)
|
18
|
+
|
19
|
+
@befores = {}
|
20
|
+
@afters = {}
|
21
|
+
@stages = {
|
22
|
+
:prepare => Proc.new { prepare },
|
23
|
+
:download => Proc.new { download },
|
24
|
+
:extract => Proc.new { extract },
|
25
|
+
:configure => Proc.new { configure },
|
26
|
+
:build => Proc.new { build },
|
27
|
+
:install => Proc.new { install }
|
28
|
+
}
|
29
|
+
self.instance_eval(&block) unless block.nil?
|
30
|
+
|
31
|
+
[:prepare, :download, :extract, :configure, :build, :install].each do |stage|
|
32
|
+
puts "before #{stage}" if Divvy.verbose
|
33
|
+
@befores[stage].call if @befores[stage]
|
34
|
+
puts "stage #{stage}" if Divvy.verbose
|
35
|
+
@stages[stage].call if @stages[stage]
|
36
|
+
puts "after #{stage}" if Divvy.verbose
|
37
|
+
@afters[stage].call if @afters[stage]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :options, :source, :runner
|
42
|
+
|
43
|
+
def before(stage, &block)
|
44
|
+
@befores[stage] = block
|
45
|
+
end
|
46
|
+
|
47
|
+
def after(stage, &block)
|
48
|
+
@afters[stage] = block
|
49
|
+
end
|
50
|
+
|
51
|
+
def stage(stage, &block)
|
52
|
+
@stages[stage] = block
|
53
|
+
end
|
54
|
+
|
55
|
+
def skip(*stages)
|
56
|
+
stages.each { |stage| @stages.delete(stage) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_dir #:nodoc:
|
60
|
+
"#{options[:builds]}/#{options[:custom_dir] || base_dir}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def base_dir #:nodoc:
|
64
|
+
if source.split('/').last =~ /(.*)\.(tar\.gz|tgz|tar\.bz2|tb2)/
|
65
|
+
return $1
|
66
|
+
end
|
67
|
+
raise "Unknown base path for source archive: #{ssource}, please update code knowledge"
|
68
|
+
end
|
69
|
+
|
70
|
+
def archive_name
|
71
|
+
name = @source.split('/').last
|
72
|
+
raise "Unable to determine archive name for source: #{source}, please update code knowledge" unless name
|
73
|
+
name
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def prepare
|
78
|
+
runner.mkdir(options[:prefix])
|
79
|
+
runner.mkdir(options[:archives])
|
80
|
+
runner.mkdir(options[:builds])
|
81
|
+
end
|
82
|
+
|
83
|
+
def download
|
84
|
+
run("wget -cq --directory-prefix='#{options[:archives]}' #{source}")
|
85
|
+
end
|
86
|
+
|
87
|
+
def extract
|
88
|
+
extract_command = case source
|
89
|
+
when /(tar.gz)|(tgz)$/
|
90
|
+
'tar xzf'
|
91
|
+
when /(tar.bz2)|(tb2)$/
|
92
|
+
'tar xjf'
|
93
|
+
when /tar$/
|
94
|
+
'tar xf'
|
95
|
+
when /zip$/
|
96
|
+
'unzip'
|
97
|
+
else
|
98
|
+
raise "Unknown source archive format: #{source}"
|
99
|
+
end
|
100
|
+
|
101
|
+
run("bash -c 'cd #{options[:builds]} && #{extract_command} #{options[:archives]}/#{archive_name}'")
|
102
|
+
end
|
103
|
+
|
104
|
+
def configure
|
105
|
+
# command = "bash -c 'cd #{build_dir} && ./configure --prefix=#{@options[:prefix]} "
|
106
|
+
command = "cd #{build_dir} && ./configure --prefix=#{@options[:prefix]} "
|
107
|
+
extras = {
|
108
|
+
:enable => '--enable', :disable => '--disable',
|
109
|
+
:with => '--with', :without => '--without'
|
110
|
+
}
|
111
|
+
extras.inject(command) { |m, (k, v)| m << create_options(k, v) if options[k]; m }
|
112
|
+
|
113
|
+
# command << " > #{archive_name}-configure.log 2>&1'"
|
114
|
+
|
115
|
+
run(command)
|
116
|
+
end
|
117
|
+
|
118
|
+
def build
|
119
|
+
# run("bash -c 'cd #{build_dir} && make > #{archive_name}-build.log 2>&1'")
|
120
|
+
run("cd #{build_dir} && make")
|
121
|
+
end
|
122
|
+
|
123
|
+
def install
|
124
|
+
# run("bash -c 'cd #{build_dir} && make install > #{archive_name}-install.log 2>&1'")
|
125
|
+
run("cd #{build_dir} && make install")
|
126
|
+
end
|
127
|
+
|
128
|
+
def create_options(key, prefix) #:nodoc:
|
129
|
+
options[key].inject(' ') { |m, option| m << "#{prefix}-#{option} "; m }
|
130
|
+
end
|
131
|
+
|
132
|
+
def method_missing(symbol, *args)
|
133
|
+
runner.send(symbol, args)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
Divvy.register_plugin(Divvy::Plugins::Source)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Divvy
|
2
|
+
class Provisioner
|
3
|
+
def initialize(host, target_package, server_options)
|
4
|
+
@target_package = Divvy.packages[target_package]
|
5
|
+
raise "Package #{target_package} not found!" unless @target_package
|
6
|
+
@server = Server.new(host, server_options)
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :target_package, :server
|
10
|
+
|
11
|
+
def run
|
12
|
+
start_time = Time.now
|
13
|
+
print_package(target_package)
|
14
|
+
install_plan = normalize(target_package)
|
15
|
+
puts "Normalized install order: #{install_plan.map { |package| package.name }.join(', ')}"
|
16
|
+
install_plan.each do |package|
|
17
|
+
PackageRunner.new(server, package).process
|
18
|
+
end
|
19
|
+
puts "Took #{Time.now - start_time} seconds"
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
# TODO: this does not prevent circular dependencies yet
|
24
|
+
def normalize(package)
|
25
|
+
packages = []
|
26
|
+
package.dependencies.each do |dependent|
|
27
|
+
packages << normalize(dependent)
|
28
|
+
# packages << package
|
29
|
+
end
|
30
|
+
packages << package
|
31
|
+
packages.flatten.uniq
|
32
|
+
end
|
33
|
+
|
34
|
+
def print_package(package, depth = 1)
|
35
|
+
puts "#{" " * depth}#{package.name}"
|
36
|
+
package.dependencies.each do |dependent|
|
37
|
+
print_package(dependent, depth + 1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/divvy/server.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'net/scp'
|
3
|
+
|
4
|
+
module Divvy
|
5
|
+
class Server
|
6
|
+
def initialize(host, options = {})
|
7
|
+
@host = host
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :host, :options
|
12
|
+
|
13
|
+
def remote_command(command, options = {})
|
14
|
+
options = {
|
15
|
+
:verbose => Divvy.verbose,
|
16
|
+
:raise_on_exit_code => true,
|
17
|
+
:raise_errors => true
|
18
|
+
}.merge(options)
|
19
|
+
puts command if options[:verbose]
|
20
|
+
response_data = ''
|
21
|
+
begin
|
22
|
+
key_path = File.expand_path(self.options[:key]) if self.options[:key]
|
23
|
+
Net::SSH.start(host, self.options[:user], :password => self.options[:password], :keys => [key_path]) do |ssh|
|
24
|
+
ssh.open_channel do |channel|
|
25
|
+
channel.exec(command) do |ch, success|
|
26
|
+
raise "FAILED: couldn't execute command (ssh.channel.exec failure)" unless success
|
27
|
+
|
28
|
+
channel.on_data do |ch, data| # stdout
|
29
|
+
print data if options[:verbose]
|
30
|
+
STDOUT.flush
|
31
|
+
response_data << data
|
32
|
+
end
|
33
|
+
|
34
|
+
channel.on_extended_data do |ch, type, data|
|
35
|
+
next unless type == 1 # only handle stderr
|
36
|
+
$stderr.print data
|
37
|
+
end
|
38
|
+
|
39
|
+
channel.on_request("exit-status") do |ch, data|
|
40
|
+
exit_code = data.read_long
|
41
|
+
raise NonZeroExitCode.new(command, exit_code) if exit_code > 0 && options[:raise_on_exit_code]
|
42
|
+
end
|
43
|
+
|
44
|
+
channel.on_request("exit-signal") do |ch, data|
|
45
|
+
puts "SIGNAL: #{data.read_long}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
ssh.loop
|
50
|
+
end
|
51
|
+
rescue Exception => err
|
52
|
+
return false unless options[:raise_errors]
|
53
|
+
raise
|
54
|
+
end
|
55
|
+
response_data
|
56
|
+
end
|
57
|
+
|
58
|
+
def scp(source, target, options = {})
|
59
|
+
key_path = File.expand_path(self.options[:key]) if self.options[:key]
|
60
|
+
Net::SCP.start(host, self.options[:user], :password => self.options[:password], :keys => [key_path]) do |scp|
|
61
|
+
scp.upload! source, target do |ch, name, sent, total|
|
62
|
+
puts "#{name}: #{sent}/#{total}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class NonZeroExitCode < Exception
|
69
|
+
def initialize(command, exit_code)
|
70
|
+
super("Non-zero exit code: #{exit_code} for #{command}")
|
71
|
+
@command = command
|
72
|
+
@exit_code = exit_code
|
73
|
+
end
|
74
|
+
|
75
|
+
attr_accessor :command, :exit_code
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Divvy
|
2
|
+
class Verification
|
3
|
+
|
4
|
+
attr_accessor :package, :description, :commands #:nodoc:
|
5
|
+
|
6
|
+
def initialize(package, description = '', &block) #:nodoc:
|
7
|
+
raise 'Verify requires a block.' unless block
|
8
|
+
|
9
|
+
@package = package
|
10
|
+
@description = description.empty? ? package.name : description
|
11
|
+
@commands = []
|
12
|
+
|
13
|
+
self.instance_eval(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def verify(server)
|
17
|
+
@commands.each do |command|
|
18
|
+
begin
|
19
|
+
server.remote_command(command)
|
20
|
+
rescue Divvy::NonZeroExitCode => ex
|
21
|
+
raise Divvy::VerificationFailed.new(@package, description)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class VerificationFailed < Exception #:nodoc:
|
28
|
+
attr_accessor :package, :description
|
29
|
+
|
30
|
+
def initialize(package, description)
|
31
|
+
super("Verifying #{package.name}#{description} failed.")
|
32
|
+
|
33
|
+
@package = package
|
34
|
+
@description = description
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Divvy
|
2
|
+
module Verifiers
|
3
|
+
# Checks to make sure <tt>path</tt> is a file on the remote server.
|
4
|
+
def has_file(path)
|
5
|
+
@commands << "test -f #{path}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def file_contains(path, text)
|
9
|
+
@commands << "grep '#{text}' #{path}"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Tests that the directory <tt>dir</tt> exists.
|
13
|
+
def has_directory(dir)
|
14
|
+
@commands << "test -d #{dir}"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Checks if <tt>path</tt> is an executable script. This verifier is "smart" because
|
18
|
+
# if the path contains a forward slash '/' then it assumes you're checking an
|
19
|
+
# absolute path to an executable. If no '/' is in the path, it assumes you're
|
20
|
+
# checking for a global executable that would be available anywhere on the command line.
|
21
|
+
def has_executable(path)
|
22
|
+
# Be smart: If the path includes a forward slash, we're checking
|
23
|
+
# an absolute path. Otherwise, we're checking a global executable
|
24
|
+
if path.include?('/')
|
25
|
+
@commands << "test -x #{path}"
|
26
|
+
else
|
27
|
+
@commands << "[ -n \"`echo \\`which #{path}\\``\" ]"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Checks to make sure <tt>process</tt> is a process running
|
32
|
+
# on the remote server.
|
33
|
+
def has_process(process)
|
34
|
+
@commands << "ps aux | grep '#{process}' | grep -v grep"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Checks if ruby can require the <tt>files</tt> given. <tt>rubygems</tt>
|
38
|
+
# is always included first.
|
39
|
+
def ruby_can_load(*files)
|
40
|
+
# Always include rubygems first
|
41
|
+
files = files.unshift('rubygems').collect { |x| "require '#{x}'" }
|
42
|
+
|
43
|
+
@commands << "ruby -e \"#{files.join(';')}\""
|
44
|
+
end
|
45
|
+
|
46
|
+
# Checks if a gem exists by calling "sudo gem list" and grepping against it.
|
47
|
+
def has_gem(name, version=nil)
|
48
|
+
version = version.nil? ? '' : version.gsub('.', '\.')
|
49
|
+
@commands << "sudo gem list | grep -e '^#{name} (.*#{version}.*)$'"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Checks that <tt>symlink</tt> is a symbolic link. If <tt>file</tt> is
|
53
|
+
# given, it checks that <tt>symlink</tt> points to <tt>file</tt>
|
54
|
+
def has_symlink(symlink, file = nil)
|
55
|
+
if file.nil?
|
56
|
+
@commands << "test -L #{symlink}"
|
57
|
+
else
|
58
|
+
@commands << "test '#{file}' = `readlink #{symlink}`"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/divvy.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
Dir["#{File.dirname(__FILE__)}/divvy/*.rb"].each { |f| require f}
|
4
|
+
|
5
|
+
# class Object
|
6
|
+
# include Divvy::Script
|
7
|
+
# end
|
8
|
+
|
9
|
+
module Divvy
|
10
|
+
class << self
|
11
|
+
# user configurable
|
12
|
+
attr_accessor :test,
|
13
|
+
:verbose
|
14
|
+
# :port,
|
15
|
+
# :allow,
|
16
|
+
# :log_buffer_size,
|
17
|
+
# :pid_file_directory,
|
18
|
+
# :log_file,
|
19
|
+
# :log_level,
|
20
|
+
# :use_events
|
21
|
+
|
22
|
+
# Internal variables
|
23
|
+
attr_accessor :initialized,
|
24
|
+
# :running,
|
25
|
+
:packages
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.say(message)
|
29
|
+
puts message
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.init
|
33
|
+
# exit if divvy is already initialized
|
34
|
+
return if self.initialized
|
35
|
+
|
36
|
+
# variable init
|
37
|
+
self.packages = {}
|
38
|
+
|
39
|
+
# # set defaults
|
40
|
+
# self.log_buffer_size ||= LOG_BUFFER_SIZE_DEFAULT
|
41
|
+
# self.port ||= DRB_PORT_DEFAULT
|
42
|
+
# self.allow ||= DRB_ALLOW_DEFAULT
|
43
|
+
# self.log_level ||= LOG_LEVEL_DEFAULT
|
44
|
+
|
45
|
+
# # log level
|
46
|
+
# log_level_map = {:debug => Logger::DEBUG,
|
47
|
+
# :info => Logger::INFO,
|
48
|
+
# :warn => Logger::WARN,
|
49
|
+
# :error => Logger::ERROR,
|
50
|
+
# :fatal => Logger::FATAL}
|
51
|
+
# LOG.level = log_level_map[self.log_level]
|
52
|
+
|
53
|
+
# flag as initialized
|
54
|
+
self.initialized = true
|
55
|
+
|
56
|
+
# not yet running
|
57
|
+
# self.running = false
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.register_verifier(verifier)
|
61
|
+
Divvy::Verification.class_eval { include verifier }
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.register_plugin(plugin)
|
65
|
+
Divvy::PackageRunner.class_eval { include plugin }
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.package(name, options = {}, &block)
|
69
|
+
package = Package.new(name, options, &block)
|
70
|
+
raise DuplicatePackageError.new(package.name) if packages.key?(package.name)
|
71
|
+
packages[package.name] = package
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.provision(host, package, server_options)
|
75
|
+
provision = Provisioner.new(host, package, server_options)
|
76
|
+
provision.run
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_package(key)
|
80
|
+
package = PACKAGES[key]
|
81
|
+
raise "Package #{key} not found!" unless package
|
82
|
+
package
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.run(script, filename = '__SCRIPT__')
|
86
|
+
Divvy.init
|
87
|
+
Object.new.instance_eval(script, filename)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
Divvy.register_verifier(Divvy::Verifiers)
|
93
|
+
|
94
|
+
Dir["#{File.dirname(__FILE__)}/divvy/plugins/*"].each { |f| require f }
|
data/test/divvy_test.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class DivvyTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context 'internal_init' do
|
6
|
+
should 'flag itself as initialized' do
|
7
|
+
Divvy.init
|
8
|
+
assert Divvy.initialized
|
9
|
+
end
|
10
|
+
|
11
|
+
should 'setup empty packages' do
|
12
|
+
Divvy.init
|
13
|
+
assert_equal Hash.new, Divvy.packages
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'run' do
|
18
|
+
should 'call initialize' do
|
19
|
+
Divvy.expects(:init)
|
20
|
+
Divvy.run('')
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'eval the script on Object' do
|
24
|
+
Object.any_instance.expects(:instance_eval).with('foobar', 'michael.rb')
|
25
|
+
Divvy.run('foobar', 'michael.rb')
|
26
|
+
end
|
27
|
+
|
28
|
+
should 'set the filename to __SCRIPT__ if no filename specified' do
|
29
|
+
Object.any_instance.expects(:instance_eval).with('foobar', '__SCRIPT__')
|
30
|
+
Divvy.run('foobar')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'packages' do
|
35
|
+
should 'add a package to the internal packages' do
|
36
|
+
code = <<-EOF
|
37
|
+
Divvy.package :foobar do
|
38
|
+
end
|
39
|
+
EOF
|
40
|
+
|
41
|
+
Divvy.run(code)
|
42
|
+
|
43
|
+
assert_equal 1, Divvy.packages.size
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'raise DuplicatePackageError when adding duplicate package names' do
|
47
|
+
code = <<-EOF
|
48
|
+
Divvy.package :foobar do
|
49
|
+
end
|
50
|
+
Divvy.package :foobar do
|
51
|
+
end
|
52
|
+
EOF
|
53
|
+
|
54
|
+
assert_raises Divvy::DuplicatePackageError do
|
55
|
+
Divvy.run(code)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PackageTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context 'initialize' do
|
6
|
+
should 'set name to name accessor' do
|
7
|
+
package = Divvy::Package.new :michael do
|
8
|
+
end
|
9
|
+
assert_equal :michael, package.name
|
10
|
+
end
|
11
|
+
|
12
|
+
should 'convert name strings into symbols' do
|
13
|
+
package = Divvy::Package.new 'michael' do
|
14
|
+
end
|
15
|
+
assert_equal :michael, package.name
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'set dependencies to an empty array' do
|
19
|
+
package = Divvy::Package.new :name do
|
20
|
+
end
|
21
|
+
assert_equal [], package.dependencies
|
22
|
+
end
|
23
|
+
|
24
|
+
should 'set verifications to an empty array' do
|
25
|
+
package = Divvy::Package.new :name do
|
26
|
+
end
|
27
|
+
assert_equal [], package.verifications
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'assign options to options accessor' do
|
31
|
+
options = { :peanut => 'butter jelly time' }
|
32
|
+
package = Divvy::Package.new :name, options do
|
33
|
+
end
|
34
|
+
assert_equal options, package.options
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'assign options an empty hash if no options present' do
|
38
|
+
package = Divvy::Package.new :name do
|
39
|
+
end
|
40
|
+
assert_equal Hash.new, package.options
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: michaeldwan-divvy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Dwan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-31 00:00:00 -07:00
|
13
|
+
default_executable: divvy
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: mpdwan@gmail.com
|
18
|
+
executables:
|
19
|
+
- divvy
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- LICENSE
|
28
|
+
- README.rdoc
|
29
|
+
- Rakefile
|
30
|
+
- VERSION
|
31
|
+
- bin/divvy
|
32
|
+
- divvy.gemspec
|
33
|
+
- examples/packages/build_essential.rb
|
34
|
+
- examples/packages/databases/mysql.rb
|
35
|
+
- examples/packages/databases/sqlite3.rb
|
36
|
+
- examples/packages/ruby/rails.rb
|
37
|
+
- examples/packages/ruby/ruby.rb
|
38
|
+
- examples/packages/ruby/ruby_gems.rb
|
39
|
+
- examples/packages/scm/git.rb
|
40
|
+
- examples/packages/utilities/screen.rb
|
41
|
+
- lib/divvy.rb
|
42
|
+
- lib/divvy/duplicate_package_error.rb
|
43
|
+
- lib/divvy/package.rb
|
44
|
+
- lib/divvy/package_runner.rb
|
45
|
+
- lib/divvy/plugins/apt.rb
|
46
|
+
- lib/divvy/plugins/file_utilities.rb
|
47
|
+
- lib/divvy/plugins/gem.rb
|
48
|
+
- lib/divvy/plugins/rails.rb
|
49
|
+
- lib/divvy/plugins/source.rb
|
50
|
+
- lib/divvy/provisioner.rb
|
51
|
+
- lib/divvy/server.rb
|
52
|
+
- lib/divvy/verification.rb
|
53
|
+
- lib/divvy/verifiers.rb
|
54
|
+
- test/divvy_test.rb
|
55
|
+
- test/package_test.rb
|
56
|
+
- test/test_helper.rb
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/mdwan/divvy
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options:
|
61
|
+
- --charset=UTF-8
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.2.0
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Divvy is an easy to configure, easy to extend server provisioning framework written in Ruby.
|
83
|
+
test_files:
|
84
|
+
- test/divvy_test.rb
|
85
|
+
- test/package_test.rb
|
86
|
+
- test/test_helper.rb
|
87
|
+
- examples/packages/build_essential.rb
|
88
|
+
- examples/packages/databases/mysql.rb
|
89
|
+
- examples/packages/databases/sqlite3.rb
|
90
|
+
- examples/packages/ruby/rails.rb
|
91
|
+
- examples/packages/ruby/ruby.rb
|
92
|
+
- examples/packages/ruby/ruby_gems.rb
|
93
|
+
- examples/packages/scm/git.rb
|
94
|
+
- examples/packages/utilities/screen.rb
|