doo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ ## Just Doo It
2
+
3
+ Doo is an experiment in taking a relentlessly polymorphic approach to deployment scripting. Like most deployment tools, doo lets you define executable blocks of deployment code independent of the configuration particulars that get bound into them at runtime. Where doo is different is in its succinctness (its core is just shy of 100 LoC), and in its use of polymorphism to realize stacking contexts. This lets you do useful things like defining configuration parameters on a project-wide, server-specific, and even task-specific level without having to rewrite (or doctor the hell out of) your deployment code.
4
+
5
+ Because executable blocks are also polymorphic, you can mix and match provider blocks into your recipes. Want to have one deployment recipe for local and remote targets? No problem. Want to abstract away the particulars of your server's OS? We can do that, too.
6
+
7
+ Doo layers a thin DSL on top of bare Ruby that facilitates polymorphic behaviour, and leans on a small number of built-in (and optional) deployment recipes to provide useful functionality.
8
+
9
+ ## Play With It
10
+
11
+ gem install doo
12
+
13
+ Check out examples/sample.rb to get started.
14
+
15
+ ## LICENSE
16
+
17
+ (The MIT License)
18
+
19
+ Copyright (c) 2010 Mat Trudel
20
+
21
+ Permission is hereby granted, free of charge, to any person obtaining
22
+ a copy of this software and associated documentation files (the
23
+ 'Software'), to deal in the Software without restriction, including
24
+ without limitation the rights to use, copy, modify, merge, publish,
25
+ distribute, sublicense, and/or sell copies of the Software, and to
26
+ permit persons to whom the Software is furnished to do so, subject to
27
+ the following conditions:
28
+
29
+ The above copyright notice and this permission notice shall be
30
+ included in all copies or substantial portions of the Software.
31
+
32
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
33
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
35
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
36
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
37
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
38
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.version
5
+ gem.name = "doo"
6
+ gem.summary = %Q{Doo - an stacked-cotnext approach to deployment scripting }
7
+ gem.description = %Q{Doo is a deployment scripting tool in the vein of capistrano and sprinkle that uses stacked contexts and a aspect-ish data model}
8
+ gem.homepage = "http://github.com/mtrudel/doo"
9
+ gem.authors = [ "Mat Trudel" ]
10
+ gem.email = [ "mat@geeky.net" ]
11
+ gem.executables = %W(doo)
12
+ gem.files = FileList["[A-Z]*", "{bin,examples,lib,spec}/**/*", 'lib/jeweler/templates/.gitignore']
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
16
+ end
17
+
18
+ require 'spec/rake/spectask'
19
+ desc "Run all tests"
20
+ Spec::Rake::SpecTask.new('spec') do |t|
21
+ t.spec_files = FileList['spec/**/*.rb']
22
+ end
23
+
24
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/doo ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'doo/cli'
4
+
5
+ Doo::CLI.start
@@ -0,0 +1,37 @@
1
+ # You can set options like so
2
+ set :web, { :server => "example.com", :production => true }
3
+ set :arg, "foo"
4
+
5
+ # options can also be blocks, in which case they become callable
6
+ set :show_uptime do
7
+ run "uptime"
8
+ end
9
+
10
+ # You can bind a variable's content into the current context like so
11
+ using web # Now 'server' and 'production' will be top-level variables
12
+
13
+ # You can run local commands inside a block
14
+ run_locally do
15
+ run "echo #{arg}" # Will output 'foo'
16
+ end
17
+
18
+ # optionally giving the blocks overriding arguments
19
+ run_locally :arg => "bar" do
20
+ run "echo #{arg}"# Will output 'bar'
21
+ end
22
+
23
+ # You can run a set of commands on a remote server like so
24
+ run_on_server web do
25
+ run "whoami"
26
+ end
27
+ # You can define servers as hashes or bare hostnames...
28
+ run_on_server [web, "otherhost.com"] do
29
+ run "hostname"
30
+ # This runs the show_uptime block with the current set of variables
31
+ show_uptime
32
+ end
33
+
34
+ # You can run a block into a file like so
35
+ render_to_file 'sample.out' do
36
+ run "echo #{arg}" # Will ouput 'echo foo' to sample.out
37
+ end
data/lib/doo/base.rb ADDED
@@ -0,0 +1,41 @@
1
+ module Doo
2
+ class Base
3
+ def load(filename)
4
+ instance_eval(File.read(filename), filename)
5
+ end
6
+
7
+ def initialize(variables = {})
8
+ using variables
9
+ end
10
+
11
+ def using(variables = {})
12
+ variables.each { |k,v| set k, v }
13
+ end
14
+
15
+ def set(k, v=nil, &block)
16
+ singleton_class.class_eval do
17
+ define_method k, (block_given?)? Proc.new { |*args|
18
+ if args.empty?
19
+ instance_eval &block
20
+ else
21
+ with_clone(args[0], &block)
22
+ end
23
+ } : lambda {v}
24
+ end
25
+ end
26
+
27
+ def with_clone(variables = {}, &block)
28
+ obj = clone
29
+ obj.using variables
30
+ obj.instance_eval &block if block_given?
31
+ obj
32
+ end
33
+
34
+ private
35
+ def singleton_class
36
+ class << self
37
+ self
38
+ end
39
+ end
40
+ end
41
+ end
data/lib/doo/cli.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'optparse'
2
+ require 'doo'
3
+
4
+ module Doo
5
+ module CLI
6
+ module Options
7
+ def self.parse!(args)
8
+ options = {}
9
+ optparse = OptionParser.new do |opts|
10
+ opts.banner = "Usage: doo [options] file1 file2 ..."
11
+
12
+ options[:verbose] = false
13
+ opts.on( '-v', '--verbose', 'Output more information' ) do
14
+ options[:verbose] = true
15
+ end
16
+
17
+ opts.on( '-s', '--set key=value', 'Set runtime values' ) do |arg|
18
+ options[arg.split('=')[0]] = arg.split('=')[1]
19
+ end
20
+
21
+ options[:dry_run] = false
22
+ opts.on( '-d', '--dry-run', 'Do a dry-run' ) do
23
+ options[:dry_run] = true
24
+ end
25
+
26
+ opts.on( '-h', '--help', 'Display this screen' ) do
27
+ puts opts
28
+ exit
29
+ end
30
+ end
31
+ begin
32
+ optparse.parse!(args)
33
+ raise "You need to specify one or more files to process" if args.empty?
34
+ rescue
35
+ puts $!
36
+ puts optparse
37
+ exit
38
+ end
39
+
40
+ options
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ module Doo
47
+ module CLI
48
+ def self.start
49
+ inst = Doo::Base.new(Options.parse!(ARGV))
50
+ ARGV.each do |filename|
51
+ inst.load(filename)
52
+ end
53
+ end
54
+ end
55
+ end
File without changes
@@ -0,0 +1,13 @@
1
+ Doo::Base.class_eval do
2
+ def prereq(cmd)
3
+ raise PrerequisiteFailure unless run cmd == true
4
+ end
5
+
6
+ def check_prereqs
7
+ begin
8
+ yield
9
+ rescue PrerequisiteFailure
10
+ puts "Pre-requisite test failed: ##{$!}"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ Doo::Base.class_eval do
2
+ def render_to_file(filename, variables = {}, &block)
3
+ File.open(filename, 'w') do |file|
4
+ with_clone(variables) do
5
+ @file = file
6
+ def run(cmd)
7
+ puts "Rendering #{cmd}" if verbose
8
+ @file.puts cmd
9
+ end
10
+ check_prereqs do
11
+ instance_eval &block if block_given?
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ Doo::Base.class_eval do
2
+ def run_locally(variables = {}, &block)
3
+ with_clone(variables) do
4
+ def run(cmd)
5
+ puts "Running #{cmd}" if verbose
6
+ system cmd unless dry_run
7
+ $?
8
+ end
9
+ instance_eval &block if block_given?
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ SshCommand = "ssh -S '~/.ssh/master-%l-%r@%h:%p'"
2
+ Doo::Base.class_eval do
3
+ def run_on_server(servers, variables = {}, &block)
4
+ [servers].flatten.each do |server|
5
+ with_clone(server.is_a?(Hash)? variables.merge(server) : variables) do
6
+ @host = server.is_a?(Hash)? server[:server] : server
7
+ def run(cmd, opt = {})
8
+ puts "Running ssh #{(!opt.include? :pty || opt[:pty])? '-t' : ''} #{@host} \"#{cmd}\"" if verbose
9
+ system("#{SshCommand} #{(!opt.include? :pty || opt[:pty])? '-t' : ''} #{@host} \"#{cmd}\"") || raise("SSH Error") unless dry_run
10
+ $?
11
+ end
12
+
13
+ def put(local, remote)
14
+ puts "scp -r \"#{local}\" \"#{@host}:#{remote}\"" if verbose
15
+ system("scp -r \"#{local}\" \"#{@host}:#{remote}\"") || raise("SSH Error") unless dry_run
16
+ $?
17
+ end
18
+
19
+ begin
20
+ system("#{SshCommand} -MNf #{@host}") || raise("SSH Error") unless dry_run
21
+ instance_eval &block if block_given?
22
+ ensure
23
+ system("#{SshCommand} -Oexit #{@host}") || raise("SSH Error") unless dry_run
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ Array.class_eval do
31
+ def with_role(role)
32
+ select { |obj| [obj[:role]].flatten.member? role }
33
+ end
34
+
35
+ def except_role(role)
36
+ reject { |obj| [obj[:role]].flatten.member? role }
37
+ end
38
+ end
data/lib/doo.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "doo/base"
2
+
3
+ require 'doo/stock/common'
4
+ require 'doo/stock/prereqs'
5
+ require 'doo/stock/render_to_file'
6
+ require 'doo/stock/run_locally'
7
+ require 'doo/stock/run_on_server'
@@ -0,0 +1,73 @@
1
+ require 'tempfile'
2
+ require 'doo/base'
3
+
4
+ describe Doo::Base do
5
+
6
+ before :each do
7
+ @inst = Doo::Base.new
8
+ end
9
+
10
+ it "can read in other files" do
11
+ @inst.should_receive("spamcan").once
12
+ Tempfile.open(nil) do |tmpfile|
13
+ tmpfile.puts("spamcan")
14
+ tmpfile.rewind
15
+ @inst.load(tmpfile.path)
16
+ end
17
+ end
18
+
19
+ it "sets itself up properly" do
20
+ inst = Doo::Base.new(:extra => "cheeps")
21
+ inst.should respond_to :extra
22
+ inst.extra.should == "cheeps"
23
+ end
24
+
25
+ it "knows how to add context to itself" do
26
+ @inst.using(:extra => "ham")
27
+ @inst.should respond_to :extra
28
+ @inst.extra.should == "ham"
29
+ end
30
+
31
+ it "knows how to set a variable" do
32
+ @inst.set :extra, "bologna"
33
+ @inst.should respond_to :extra
34
+ @inst.extra.should == "bologna"
35
+ end
36
+
37
+ it "knows how to set a block" do
38
+ @inst.set :extra do
39
+ its_not_real_meat
40
+ end
41
+ @inst.should respond_to :extra
42
+ @inst.should_receive(:its_not_real_meat).exactly(1).times
43
+ @inst.extra
44
+ end
45
+
46
+ it "returns a clone of itself" do
47
+ @inst.instance_eval do
48
+ def something_extra
49
+ "bacon"
50
+ end
51
+ end
52
+ @inst.with_clone.something_extra.should == "bacon"
53
+ end
54
+
55
+ it "runs code in a clone" do
56
+ @inst.instance_eval do
57
+ def something_extra
58
+ "bacon"
59
+ end
60
+ end
61
+ @inst.with_clone do
62
+ something_extra.should == "bacon"
63
+ end
64
+ @inst.should_receive(:something_extra).exactly(0).times
65
+ end
66
+
67
+ it "sets variables in the clone but not in itself" do
68
+ @inst.with_clone(:extra => "fries") do
69
+ extra.should == "fries"
70
+ end
71
+ @inst.should_not respond_to :extra
72
+ end
73
+ end
@@ -0,0 +1,38 @@
1
+ require 'doo/cli'
2
+
3
+ describe Doo::CLI::Options do
4
+ it "should message and error on no args" do
5
+ lambda { Doo::CLI::Options.parse!([]) }.should raise_error SystemExit
6
+ end
7
+
8
+ it "should message and error on --help" do
9
+ lambda { Doo::CLI::Options.parse!(["--help"]) }.should raise_error SystemExit
10
+ end
11
+
12
+ it "should set on --dry-run" do
13
+ Doo::CLI::Options.parse!(["-d", "foo"]).member?(:dry_run).should == true
14
+ end
15
+
16
+ it "should set on --verbose" do
17
+ Doo::CLI::Options.parse!(["-v", "foo"]).member?(:verbose).should == true
18
+ end
19
+
20
+ it "should set variables" do
21
+ Doo::CLI::Options.parse!(["-swoz=bar", "foo"])["woz"].should == "bar"
22
+ end
23
+
24
+ it "should pull off varialbes and leave files" do
25
+ args = ["-d", "foo", "bar"]
26
+ Doo::CLI::Options.parse!(args)
27
+ args.should == ["foo", "bar"]
28
+ end
29
+
30
+ it "should run all files" do
31
+ ARGV = ["-d", "foo", "bar"]
32
+ inst = Doo::Base.new
33
+ Doo::Base.should_receive(:new).and_return inst
34
+ inst.should_receive(:load).with("foo")
35
+ inst.should_receive(:load).with("bar")
36
+ Doo::CLI.start
37
+ end
38
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: doo
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Mat Trudel
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-15 00:00:00 -04:00
19
+ default_executable: doo
20
+ dependencies: []
21
+
22
+ description: Doo is a deployment scripting tool in the vein of capistrano and sprinkle that uses stacked contexts and a aspect-ish data model
23
+ email:
24
+ - mat@geeky.net
25
+ executables:
26
+ - doo
27
+ extensions: []
28
+
29
+ extra_rdoc_files:
30
+ - README.md
31
+ files:
32
+ - README.md
33
+ - Rakefile
34
+ - VERSION
35
+ - bin/doo
36
+ - examples/sample.rb
37
+ - lib/doo.rb
38
+ - lib/doo/base.rb
39
+ - lib/doo/cli.rb
40
+ - lib/doo/stock/common.rb
41
+ - lib/doo/stock/prereqs.rb
42
+ - lib/doo/stock/render_to_file.rb
43
+ - lib/doo/stock/run_locally.rb
44
+ - lib/doo/stock/run_on_server.rb
45
+ - spec/doo/base_spec.rb
46
+ - spec/doo/cli_spec.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/mtrudel/doo
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options:
53
+ - --charset=UTF-8
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.7
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Doo - an stacked-cotnext approach to deployment scripting
81
+ test_files:
82
+ - spec/doo/base_spec.rb
83
+ - spec/doo/cli_spec.rb
84
+ - examples/sample.rb