crafterm-sprinkle 0.1.5 → 0.1.6

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.
Files changed (45) hide show
  1. data/CREDITS +2 -1
  2. data/Manifest.txt +11 -2
  3. data/{README.rdoc → README.txt} +25 -5
  4. data/bin/sprinkle +7 -0
  5. data/config/hoe.rb +2 -2
  6. data/examples/rails/rails.rb +4 -2
  7. data/lib/sprinkle.rb +8 -2
  8. data/lib/sprinkle/actors/actors.rb +17 -0
  9. data/lib/sprinkle/actors/capistrano.rb +41 -4
  10. data/lib/sprinkle/actors/vlad.rb +39 -4
  11. data/lib/sprinkle/configurable.rb +31 -0
  12. data/lib/sprinkle/deployment.rb +46 -6
  13. data/lib/sprinkle/extensions/arbitrary_options.rb +1 -1
  14. data/lib/sprinkle/extensions/array.rb +1 -3
  15. data/lib/sprinkle/extensions/blank_slate.rb +1 -1
  16. data/lib/sprinkle/extensions/dsl_accessor.rb +1 -1
  17. data/lib/sprinkle/extensions/string.rb +1 -1
  18. data/lib/sprinkle/extensions/symbol.rb +1 -1
  19. data/lib/sprinkle/installers/apt.rb +33 -6
  20. data/lib/sprinkle/installers/gem.rb +34 -6
  21. data/lib/sprinkle/installers/installer.rb +64 -29
  22. data/lib/sprinkle/installers/rake.rb +15 -2
  23. data/lib/sprinkle/installers/rpm.rb +20 -3
  24. data/lib/sprinkle/installers/source.rb +74 -16
  25. data/lib/sprinkle/package.rb +127 -2
  26. data/lib/sprinkle/policy.rb +42 -2
  27. data/lib/sprinkle/script.rb +11 -1
  28. data/lib/sprinkle/verifiers/directory.rb +16 -0
  29. data/lib/sprinkle/verifiers/executable.rb +36 -0
  30. data/lib/sprinkle/verifiers/file.rb +20 -0
  31. data/lib/sprinkle/verifiers/process.rb +21 -0
  32. data/lib/sprinkle/verifiers/ruby.rb +25 -0
  33. data/lib/sprinkle/verifiers/symlink.rb +30 -0
  34. data/lib/sprinkle/verify.rb +114 -0
  35. data/lib/sprinkle/version.rb +1 -1
  36. data/spec/sprinkle/actors/capistrano_spec.rb +21 -1
  37. data/spec/sprinkle/configurable_spec.rb +46 -0
  38. data/spec/sprinkle/installers/apt_spec.rb +1 -5
  39. data/spec/sprinkle/installers/gem_spec.rb +1 -1
  40. data/spec/sprinkle/installers/installer_spec.rb +55 -29
  41. data/spec/sprinkle/package_spec.rb +137 -0
  42. data/spec/sprinkle/verify_spec.rb +160 -0
  43. data/sprinkle.gemspec +6 -6
  44. metadata +17 -4
  45. data/examples/merb/deploy.rb +0 -5
@@ -1,4 +1,4 @@
1
- module ArbitraryOptions
1
+ module ArbitraryOptions #:nodoc:
2
2
  def self.included(base)
3
3
  base.alias_method_chain :method_missing, :arbitrary_options
4
4
  end
@@ -1,7 +1,5 @@
1
- class Array
2
-
1
+ class Array #:nodoc:
3
2
  def to_task_name
4
3
  collect(&:to_task_name).join('_')
5
4
  end
6
-
7
5
  end
@@ -1,4 +1,4 @@
1
- class BlankSlate
1
+ class BlankSlate #:nodoc:
2
2
  instance_methods.each do |m|
3
3
  undef_method(m) unless %w( __send__ __id__ send class inspect instance_eval instance_variables ).include?(m)
4
4
  end
@@ -1,4 +1,4 @@
1
- class Module
1
+ class Module #:nodoc:
2
2
  def dsl_accessor(*symbols)
3
3
  symbols.each do |sym|
4
4
  class_eval %{
@@ -1,4 +1,4 @@
1
- class String
1
+ class String #:nodoc:
2
2
 
3
3
  # REVISIT: what chars shall we allow in task names?
4
4
  def to_task_name
@@ -1,4 +1,4 @@
1
- class Symbol
1
+ class Symbol #:nodoc:
2
2
 
3
3
  def to_task_name
4
4
  to_s.to_task_name
@@ -1,23 +1,50 @@
1
1
  module Sprinkle
2
2
  module Installers
3
+ # = Apt Package Installer
4
+ #
5
+ # The Apt package installer uses the +apt-get+ command to install
6
+ # packages. The apt installer has only one option which can be
7
+ # modified which is the +dependencies_only+ option. When this is
8
+ # set to true, the installer uses +build-dep+ instead of +install+
9
+ # to only build the dependencies.
10
+ #
11
+ # == Example Usage
12
+ #
13
+ # First, a simple installation of the magic_beans package:
14
+ #
15
+ # package :magic_beans do
16
+ # description "Beans beans they're good for your heart..."
17
+ # apt 'magic_beans_package'
18
+ # end
19
+ #
20
+ # Second, only build the magic_beans dependencies:
21
+ #
22
+ # package :magic_beans_depends do
23
+ # apt 'magic_beans_package' { dependencies_only true }
24
+ # end
25
+ #
26
+ # As you can see, setting options is as simple as creating a
27
+ # block and calling the option as a method with the value as
28
+ # its parameter.
3
29
  class Apt < Installer
4
- attr_accessor :packages
30
+ attr_accessor :packages #:nodoc:
5
31
 
6
- def initialize(parent, *packages, &block)
7
- super parent, &block
32
+ def initialize(parent, *packages, &block) #:nodoc:
8
33
  packages.flatten!
9
34
 
10
35
  options = { :dependencies_only => false }
11
36
  options.update(packages.pop) if packages.last.is_a?(Hash)
12
37
 
13
- @command = options[:dependencies_only] ? 'build-dep' : 'install'
38
+ super parent, options, &block
39
+
14
40
  @packages = packages
15
41
  end
16
42
 
17
43
  protected
18
44
 
19
- def install_commands
20
- "DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get -qyu #{@command} #{@packages.join(' ')}"
45
+ def install_commands #:nodoc:
46
+ command = @options[:dependencies_only] ? 'build-dep' : 'install'
47
+ "DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get -qyu #{command} #{@packages.join(' ')}"
21
48
  end
22
49
 
23
50
  end
@@ -1,14 +1,42 @@
1
1
  module Sprinkle
2
2
  module Installers
3
+ # = Ruby Gem Package Installer
4
+ #
5
+ # The gem package installer installs ruby gems.
6
+ #
7
+ # The installer has a single optional configuration: source.
8
+ # By changing source you can specify a given ruby gems
9
+ # repository from which to install.
10
+ #
11
+ # == Example Usage
12
+ #
13
+ # First, a simple installation of the magic_beans gem:
14
+ #
15
+ # package :magic_beans do
16
+ # description "Beans beans they're good for your heart..."
17
+ # gem 'magic_beans'
18
+ # end
19
+ #
20
+ # Second, install magic_beans gem from github:
21
+ #
22
+ # package :magic_beans do
23
+ # gem 'magic_beans_package' do
24
+ # source 'http://gems.github.com'
25
+ # end
26
+ # end
27
+ #
28
+ # As you can see, setting options is as simple as creating a
29
+ # block and calling the option as a method with the value as
30
+ # its parameter.
3
31
  class Gem < Installer
4
- attr_accessor :gem
32
+ attr_accessor :gem #:nodoc:
5
33
 
6
- def initialize(parent, gem, options = {}, &block)
34
+ def initialize(parent, gem, options = {}, &block) #:nodoc:
7
35
  super parent, options, &block
8
36
  @gem = gem
9
37
  end
10
38
 
11
- def source(location = nil)
39
+ def source(location = nil) #:nodoc:
12
40
  # package defines an installer called source so here we specify a method directly
13
41
  # rather than rely on the automatic options processing since packages' method missing
14
42
  # won't be run
@@ -20,14 +48,14 @@ module Sprinkle
20
48
 
21
49
  # rubygems 0.9.5+ installs dependencies by default, and does platform selection
22
50
 
23
- def install_commands
51
+ def install_commands #:nodoc:
24
52
  cmd = "gem install #{gem}"
25
53
  cmd << " --version '#{version}'" if version
26
54
  cmd << " --source #{source}" if source
27
- cmd << " --install-dir #{repository}" if repository
55
+ cmd << " --install-dir #{repository}" if option?(:repository)
28
56
  cmd
29
57
  end
30
-
58
+
31
59
  end
32
60
  end
33
61
  end
@@ -1,9 +1,49 @@
1
1
  module Sprinkle
2
2
  module Installers
3
+ # The base class which all installers must subclass, this class makes
4
+ # sure all installers share some general features, which are outlined
5
+ # below.
6
+ #
7
+ # = Pre/Post Installation Hooks
8
+ #
9
+ # With all intallation methods you have the ability to specify multiple
10
+ # pre/post installation hooks. This gives you the ability to specify
11
+ # commands to run before and after an installation takes place. All
12
+ # commands by default are sudo'd so there is no need to include "sudo"
13
+ # in the command itself. There are three ways to specify a pre/post hook.
14
+ #
15
+ # First, a single command:
16
+ #
17
+ # pre :install, 'echo "Hello, World!"'
18
+ # post :install, 'rm -rf /'
19
+ #
20
+ # Second, an array of commands:
21
+ #
22
+ # commands = ['echo "First"', 'echo "Then Another"']
23
+ # pre :install, commands
24
+ # post :install, commands
25
+ #
26
+ # Third, a block which returns either a single or multiple commands:
27
+ #
28
+ # pre :install do
29
+ # amount = 7 * 3
30
+ # "echo 'Before we install, lets plant #{amount} magic beans...'"
31
+ # end
32
+ # post :install do
33
+ # ['echo "Now... let's hope they sprout!", 'echo "Indeed they have!"']
34
+ # end
35
+ #
36
+ # = Other Pre/Post Hooks
37
+ #
38
+ # Some installation methods actually grant you more fine grained
39
+ # control of when commands are run rather than a blanket pre :install
40
+ # or post :install. If this is the case, it will be documented on
41
+ # the installation method's corresponding documentation page.
3
42
  class Installer
4
- attr_accessor :delivery, :package, :options, :pre, :post
43
+ include Sprinkle::Configurable
44
+ attr_accessor :delivery, :package, :options, :pre, :post #:nodoc:
5
45
 
6
- def initialize(package, options = {}, &block)
46
+ def initialize(package, options = {}, &block) #:nodoc:
7
47
  @package = package
8
48
  @options = options
9
49
  @pre = {}; @post = {}
@@ -13,21 +53,17 @@ module Sprinkle
13
53
  def pre(stage, *commands)
14
54
  @pre[stage] ||= []
15
55
  @pre[stage] += commands
56
+ @pre[stage] += [yield] if block_given?
16
57
  end
17
58
 
18
59
  def post(stage, *commands)
19
60
  @post[stage] ||= []
20
61
  @post[stage] += commands
62
+ @post[stage] += [yield] if block_given?
21
63
  end
22
64
 
23
- def defaults(deployment)
24
- defaults = deployment.defaults[self.class.name.split(/::/).last.downcase.to_sym]
25
- self.instance_eval(&defaults) if defaults
26
- @delivery = deployment.style
27
- end
28
-
29
- def process(roles)
30
- raise 'Unknown command delivery target' unless @delivery
65
+ def process(roles) #:nodoc:
66
+ assert_delivery
31
67
 
32
68
  if logger.debug?
33
69
  sequence = install_sequence; sequence = sequence.join('; ') if sequence.is_a? Array
@@ -40,44 +76,43 @@ module Sprinkle
40
76
  end
41
77
  end
42
78
 
43
- def method_missing(sym, *args, &block)
44
- unless args.empty? # mutate if not set
45
- @options[sym] = *args unless @options[sym]
46
- end
47
-
48
- @options[sym] || @package.send(sym, *args, &block) # try the parents options if unknown
49
- end
50
-
51
79
  protected
52
-
53
- # Installation is separated into two styles that concrete derivative installer classes
54
- # can implement.
55
- #
56
- # Simple installers that issue a single or set of commands can overwride
57
- # install_commands (eg. apt, gem, rpm). Pre/post install commands are included in this
58
- # style for free.
59
- #
60
80
  # More complicated installers that have different stages, and require pre/post commands
61
81
  # within stages can override install_sequence and take complete control of the install
62
82
  # command sequence construction (eg. source based installer).
63
-
64
83
  def install_sequence
65
84
  commands = pre_commands(:install) + [ install_commands ] + post_commands(:install)
66
85
  commands.flatten
67
86
  end
68
87
 
88
+ # A concrete installer (subclass of this virtual class) must override this method
89
+ # and return the commands it needs to run as either a string or an array.
90
+ #
91
+ # <b>Overriding this method is required.</b>
69
92
  def install_commands
70
93
  raise 'Concrete installers implement this to specify commands to run to install their respective packages'
71
94
  end
72
95
 
73
- def pre_commands(stage)
96
+ def pre_commands(stage) #:nodoc:
74
97
  dress @pre[stage] || [], :pre
75
98
  end
76
99
 
77
- def post_commands(stage)
100
+ def post_commands(stage) #:nodoc:
78
101
  dress @post[stage] || [], :post
79
102
  end
80
103
 
104
+ # Concrete installers (subclasses of this virtual class) can override this method to
105
+ # specify stage-specific (pre-installation, post-installation, etc.) modifications
106
+ # of commands.
107
+ #
108
+ # An example usage of overriding this would be to prefix all commands for a
109
+ # certain stage to change to a certain directory. An example is given below:
110
+ #
111
+ # def dress(commands, stage)
112
+ # commands.collect { |x| "cd #{magic_beans_path} && #{x}" }
113
+ # end
114
+ #
115
+ # By default, no modifications are made to the commands.
81
116
  def dress(commands, stage); commands; end
82
117
 
83
118
  end
@@ -1,14 +1,27 @@
1
1
  module Sprinkle
2
2
  module Installers
3
+ # = Rake Installer
4
+ #
5
+ # This installer runs a rake command.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # The following example runs the command "rake spec" on
10
+ # the remote server.
11
+ #
12
+ # package :spec do
13
+ # rake 'spec'
14
+ # end
15
+ #
3
16
  class Rake < Installer
4
- def initialize(parent, commands = [], &block)
17
+ def initialize(parent, commands = [], &block) #:nodoc:
5
18
  super parent, &block
6
19
  @commands = commands
7
20
  end
8
21
 
9
22
  protected
10
23
 
11
- def install_commands
24
+ def install_commands #:nodoc:
12
25
  "rake #{@commands.join(' ')}"
13
26
  end
14
27
 
@@ -1,9 +1,26 @@
1
1
  module Sprinkle
2
2
  module Installers
3
+ # = RPM Package Installer
4
+ #
5
+ # The RPM package installer installs RPM packages.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # Installing the magic_beans RPM. Its all the craze these days.
10
+ #
11
+ # package :magic_beans do
12
+ # rpm 'magic_beans'
13
+ # end
14
+ #
15
+ # You may also specify multiple rpms as an array:
16
+ #
17
+ # package :magic_beans do
18
+ # rpm %w(magic_beans magic_sauce)
19
+ # end
3
20
  class Rpm < Installer
4
- attr_accessor :packages
21
+ attr_accessor :packages #:nodoc:
5
22
 
6
- def initialize(parent, packages, &block)
23
+ def initialize(parent, packages, &block) #:nodoc:
7
24
  super parent, &block
8
25
  packages = [packages] unless packages.is_a? Array
9
26
  @packages = packages
@@ -11,7 +28,7 @@ module Sprinkle
11
28
 
12
29
  protected
13
30
 
14
- def install_commands
31
+ def install_commands #:nodoc:
15
32
  "rpm -Uvh #{@packages.join(' ')}"
16
33
  end
17
34
 
@@ -1,16 +1,72 @@
1
1
  module Sprinkle
2
2
  module Installers
3
+ # = Source Package Installer
4
+ #
5
+ # The source package installer installs software from source.
6
+ # It handles downloading, extracting, configuring, building,
7
+ # and installing software.
8
+ #
9
+ # == Configuration Options
10
+ #
11
+ # The source installer has many configuration options:
12
+ # * <b>prefix</b> - The prefix directory that is configured to.
13
+ # * <b>archives</b> - The location all the files are downloaded to.
14
+ # * <b>builds</b> - The directory the package is extracted to to configure and install
15
+ #
16
+ # == Pre/Post Hooks
17
+ #
18
+ # The source installer defines a myriad of new stages which can be hooked into:
19
+ # * <b>prepare</b> - Prepare is the stage which all the prefix, archives, and build directories are made.
20
+ # * <b>download</b> - Download is the stage which the software package is downloaded.
21
+ # * <b>extract</b> - Extract is the stage which the software package is extracted.
22
+ # * <b>configure</b> - Configure is the stage which the ./configure script is run.
23
+ # * <b>build</b> - Build is the stage in which `make` is called.
24
+ # * <b>install</b> - Install is the stage which `make install` is called.
25
+ #
26
+ # == Example Usage
27
+ #
28
+ # First, a simple package, no configuration:
29
+ #
30
+ # package :magic_beans do
31
+ # source 'http://magicbeansland.com/latest-1.1.1.tar.gz'
32
+ # end
33
+ #
34
+ # Second, specifying exactly where I want my files:
35
+ #
36
+ # package :magic_beans do
37
+ # source 'http://magicbeansland.com/latest-1.1.1.tar.gz' do
38
+ # prefix '/usr/local'
39
+ # archives '/tmp'
40
+ # builds '/tmp/builds'
41
+ # end
42
+ # end
43
+ #
44
+ # Third, specifying some hooks:
45
+ #
46
+ # package :magic_beans do
47
+ # source 'http://magicbeansland.com/latest-1.1.1.tar.gz' do
48
+ # prefix '/usr/local'
49
+ #
50
+ # pre :prepare { 'echo "Here we go folks."' }
51
+ # post :extract { 'echo "I believe..."' }
52
+ # pre :build { 'echo "Cross your fingers!"' }
53
+ # end
54
+ # end
55
+ #
56
+ # As you can see, setting options is as simple as creating a
57
+ # block and calling the option as a method with the value as
58
+ # its parameter.
3
59
  class Source < Installer
4
- attr_accessor :source
60
+ attr_accessor :source #:nodoc:
5
61
 
6
- def initialize(parent, source, options = {}, &block)
62
+ def initialize(parent, source, options = {}, &block) #:nodoc:
7
63
  @source = source
8
64
  super parent, options, &block
9
65
  end
10
66
 
11
67
  protected
12
68
 
13
- def install_sequence
69
+ def install_sequence #:nodoc:
14
70
  prepare + download + extract + configure + build + install
15
71
  end
16
72
 
@@ -20,7 +76,7 @@ module Sprinkle
20
76
  end
21
77
  end
22
78
 
23
- def prepare_commands
79
+ def prepare_commands #:nodoc:
24
80
  raise 'No installation area defined' unless @options[:prefix]
25
81
  raise 'No build area defined' unless @options[:builds]
26
82
  raise 'No source download area defined' unless @options[:archives]
@@ -30,15 +86,15 @@ module Sprinkle
30
86
  "mkdir -p #{@options[:archives]}" ]
31
87
  end
32
88
 
33
- def download_commands
89
+ def download_commands #:nodoc:
34
90
  [ "wget -cq --directory-prefix='#{@options[:archives]}' #{@source}" ]
35
91
  end
36
92
 
37
- def extract_commands
93
+ def extract_commands #:nodoc:
38
94
  [ "bash -c 'cd #{@options[:builds]} && #{extract_command} #{@options[:archives]}/#{archive_name}'" ]
39
95
  end
40
96
 
41
- def configure_commands
97
+ def configure_commands #:nodoc:
42
98
  return [] if custom_install?
43
99
 
44
100
  command = "bash -c 'cd #{build_dir} && ./configure --prefix=#{@options[:prefix]} "
@@ -53,38 +109,40 @@ module Sprinkle
53
109
  [ command << " > #{@package.name}-configure.log 2>&1'" ]
54
110
  end
55
111
 
56
- def build_commands
112
+ def build_commands #:nodoc:
57
113
  return [] if custom_install?
58
114
  [ "bash -c 'cd #{build_dir} && make > #{@package.name}-build.log 2>&1'" ]
59
115
  end
60
116
 
61
- def install_commands
117
+ def install_commands #:nodoc:
62
118
  return custom_install_commands if custom_install?
63
119
  [ "bash -c 'cd #{build_dir} && make install > #{@package.name}-install.log 2>&1'" ]
64
120
  end
65
121
 
66
- def custom_install?
122
+ def custom_install? #:nodoc:
67
123
  !! @options[:custom_install]
68
124
  end
69
125
 
70
126
  # REVISIT: must be better processing of custom install commands somehow? use splat operator?
71
- def custom_install_commands
127
+ def custom_install_commands #:nodoc:
72
128
  dress @options[:custom_install], :install
73
129
  end
74
130
 
75
131
  protected
76
132
 
133
+ # dress is overriden from the base Sprinkle::Installers::Installer class so that the command changes
134
+ # directory to the build directory first. Also, the result of the command is logged.
77
135
  def dress(commands, stage)
78
136
  commands.collect { |command| "bash -c 'cd #{build_dir} && #{command} >> #{@package.name}-#{stage}.log 2>&1'" }
79
137
  end
80
138
 
81
139
  private
82
140
 
83
- def create_options(key, prefix)
141
+ def create_options(key, prefix) #:nodoc:
84
142
  @options[key].inject(' ') { |m, option| m << "#{prefix}-#{option} "; m }
85
143
  end
86
144
 
87
- def extract_command
145
+ def extract_command #:nodoc:
88
146
  case @source
89
147
  when /(tar.gz)|(tgz)$/
90
148
  'tar xzf'
@@ -99,17 +157,17 @@ module Sprinkle
99
157
  end
100
158
  end
101
159
 
102
- def archive_name
160
+ def archive_name #:nodoc:
103
161
  name = @source.split('/').last
104
162
  raise "Unable to determine archive name for source: #{source}, please update code knowledge" unless name
105
163
  name
106
164
  end
107
165
 
108
- def build_dir
166
+ def build_dir #:nodoc:
109
167
  "#{@options[:builds]}/#{options[:custom_dir] || base_dir}"
110
168
  end
111
169
 
112
- def base_dir
170
+ def base_dir #:nodoc:
113
171
  if @source.split('/').last =~ /(.*)\.(tar\.gz|tgz|tar\.bz2|tb2)/
114
172
  return $1
115
173
  end