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 (42) hide show
  1. data/CREDITS +2 -1
  2. data/Manifest.txt +11 -2
  3. data/{README.rdoc → README.txt} +7 -1
  4. data/bin/sprinkle +7 -0
  5. data/config/hoe.rb +2 -2
  6. data/lib/sprinkle/actors/actors.rb +17 -0
  7. data/lib/sprinkle/actors/capistrano.rb +41 -4
  8. data/lib/sprinkle/actors/vlad.rb +39 -4
  9. data/lib/sprinkle/configurable.rb +31 -0
  10. data/lib/sprinkle/deployment.rb +46 -6
  11. data/lib/sprinkle/extensions/arbitrary_options.rb +1 -1
  12. data/lib/sprinkle/extensions/array.rb +1 -3
  13. data/lib/sprinkle/extensions/blank_slate.rb +1 -1
  14. data/lib/sprinkle/extensions/dsl_accessor.rb +1 -1
  15. data/lib/sprinkle/extensions/string.rb +1 -1
  16. data/lib/sprinkle/extensions/symbol.rb +1 -1
  17. data/lib/sprinkle/installers/apt.rb +29 -3
  18. data/lib/sprinkle/installers/gem.rb +34 -6
  19. data/lib/sprinkle/installers/installer.rb +64 -29
  20. data/lib/sprinkle/installers/rake.rb +15 -2
  21. data/lib/sprinkle/installers/rpm.rb +20 -3
  22. data/lib/sprinkle/installers/source.rb +74 -16
  23. data/lib/sprinkle/package.rb +127 -2
  24. data/lib/sprinkle/policy.rb +42 -2
  25. data/lib/sprinkle/script.rb +11 -1
  26. data/lib/sprinkle/verifiers/directory.rb +16 -0
  27. data/lib/sprinkle/verifiers/executable.rb +36 -0
  28. data/lib/sprinkle/verifiers/file.rb +20 -0
  29. data/lib/sprinkle/verifiers/process.rb +21 -0
  30. data/lib/sprinkle/verifiers/ruby.rb +25 -0
  31. data/lib/sprinkle/verifiers/symlink.rb +30 -0
  32. data/lib/sprinkle/verify.rb +114 -0
  33. data/lib/sprinkle/version.rb +1 -1
  34. data/lib/sprinkle.rb +8 -2
  35. data/spec/sprinkle/actors/capistrano_spec.rb +21 -1
  36. data/spec/sprinkle/installers/gem_spec.rb +1 -1
  37. data/spec/sprinkle/installers/installer_spec.rb +55 -29
  38. data/spec/sprinkle/package_spec.rb +137 -0
  39. data/spec/sprinkle/verify_spec.rb +160 -0
  40. data/sprinkle.gemspec +6 -6
  41. metadata +14 -4
  42. data/examples/merb/deploy.rb +0 -5
@@ -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
@@ -1,4 +1,96 @@
1
1
  module Sprinkle
2
+ # = Packages
3
+ #
4
+ # A package defines one or more things to provision onto the server.
5
+ # There is a lot of flexibility in a way a package is defined but
6
+ # let me give you a basic example:
7
+ #
8
+ # package :ruby do
9
+ # description 'Ruby MRI'
10
+ # version '1.8.6'
11
+ # apt 'ruby'
12
+ #
13
+ # verify { has_executable 'ruby' }
14
+ # end
15
+ #
16
+ # The above would define a package named 'ruby' and give it a description
17
+ # and explicitly say its version. It is installed via apt and to verify
18
+ # the installation was successful sprinkle will check for the executable
19
+ # 'ruby' being availble. Pretty simple, right?
20
+ #
21
+ # <b>Note:</b> Defining a package does not INSTALL it. To install a
22
+ # package, you must require it in a Sprinkle::Policy block.
23
+ #
24
+ # == Pre-Requirements
25
+ #
26
+ # Most packages have some sort of pre-requisites in order to be installed.
27
+ # Sprinkle allows you to define the requirements of the package, which
28
+ # will be installed before the package itself. An example below:
29
+ #
30
+ # package :rubygems do
31
+ # source 'http://rubyforge.org/rubygems.tgz'
32
+ # requires :ruby
33
+ # end
34
+ #
35
+ # In this case, when rubygems is being installed, Sprinkle will first
36
+ # provision the server with Ruby to make sure the requirements are met.
37
+ # In turn, if ruby has requirements, it installs those first, and so on.
38
+ #
39
+ # == Verifications
40
+ #
41
+ # Most of the time its important to know whether the software you're
42
+ # attempting to install was installed successfully or not. For this,
43
+ # Sprinkle provides verifications. Verifications are one or more blocks
44
+ # which define rules with which Sprinkle can check if it installed
45
+ # the package successfully. If these verification blocks fail, then
46
+ # Sprinkle will gracefully stop the entire process. An example below:
47
+ #
48
+ # package :rubygems do
49
+ # source 'http://rubyforge.org/rubygems.tgz'
50
+ # requires :ruby
51
+ #
52
+ # verify { has_executable 'gem' }
53
+ # end
54
+ #
55
+ # In addition to verifying an installation was successfully, by default
56
+ # Sprinkle runs these verifications <em>before</em> the installation to
57
+ # check if the package is already installed. If the verifications pass
58
+ # before installing the package, it skips the package. To override this
59
+ # behavior, set the -f flag on the sprinkle script or set the
60
+ # :force option to true in Sprinkle::OPTIONS
61
+ #
62
+ # For more information on verifications and to see all the available
63
+ # verifications, see Sprinkle::Verify
64
+ #
65
+ # == Virtual Packages
66
+ #
67
+ # Sometimes, there are multiple packages available for a single task. An
68
+ # example is a database package. It can contain mySQL, postgres, or sqlite!
69
+ # This is where virtual packages come in handy. They are defined as follows:
70
+ #
71
+ # package :sqlite3, :provides => :database do
72
+ # apt 'sqlite3'
73
+ # end
74
+ #
75
+ # The :provides option allows you to reference this package either by :sqlite3
76
+ # or by :database. But whereas the package name is unique, multiple packages may
77
+ # share the same provision. If this is the case, when running Sprinkle, the
78
+ # script will ask you which provision you want to install. At this time, you
79
+ # can only install one.
80
+ #
81
+ # == Meta-Packages
82
+ #
83
+ # A package doesn't require an installer. If you want to define a package which
84
+ # merely encompasses other packages, that is fine too. Example:
85
+ #
86
+ # package :meta do
87
+ # requires :magic_beans
88
+ # requires :magic_sauce
89
+ # end
90
+ #
91
+ #--
92
+ # FIXME: Should probably document recommendations.
93
+ #++
2
94
  module Package
3
95
  PACKAGES = {}
4
96
 
@@ -13,9 +105,9 @@ module Sprinkle
13
105
  package
14
106
  end
15
107
 
16
- class Package
108
+ class Package #:nodoc:
17
109
  include ArbitraryOptions
18
- attr_accessor :name, :provides, :installer, :dependencies, :recommends
110
+ attr_accessor :name, :provides, :installer, :dependencies, :recommends, :verifications
19
111
 
20
112
  def initialize(name, metadata = {}, &block)
21
113
  raise 'No package name supplied' unless name
@@ -24,6 +116,7 @@ module Sprinkle
24
116
  @provides = metadata[:provides]
25
117
  @dependencies = []
26
118
  @recommends = []
119
+ @verifications = []
27
120
  self.instance_eval &block
28
121
  end
29
122
 
@@ -44,12 +137,44 @@ module Sprinkle
44
137
  @recommends << :build_essential # Ubuntu/Debian
45
138
  @installer = Sprinkle::Installers::Source.new(self, source, options, &block)
46
139
  end
140
+
141
+ def verify(description = '', &block)
142
+ @verifications << Sprinkle::Verify.new(self, description, &block)
143
+ end
47
144
 
48
145
  def process(deployment, roles)
49
146
  return if meta_package?
147
+
148
+ # Run a pre-test to see if the software is already installed. If so,
149
+ # we can skip it, unless we have the force option turned on!
150
+ unless @verifications.empty? || Sprinkle::OPTIONS[:force]
151
+ begin
152
+ process_verifications(deployment, roles, true)
153
+
154
+ logger.info "--> #{self.name} already installed for roles: #{roles}"
155
+ return
156
+ rescue Sprinkle::VerificationFailed => e
157
+ # Continue
158
+ end
159
+ end
50
160
 
51
161
  @installer.defaults(deployment)
52
162
  @installer.process(roles)
163
+
164
+ process_verifications(deployment, roles)
165
+ end
166
+
167
+ def process_verifications(deployment, roles, pre = false)
168
+ if pre
169
+ logger.info "--> Checking if #{self.name} is already installed for roles: #{roles}"
170
+ else
171
+ logger.info "--> Verifying #{self.name} was properly installed for roles: #{roles}"
172
+ end
173
+
174
+ @verifications.each do |v|
175
+ v.defaults(deployment)
176
+ v.process(roles)
177
+ end
53
178
  end
54
179
 
55
180
  def requires(*packages)
@@ -1,16 +1,56 @@
1
1
  require 'highline/import'
2
2
 
3
3
  module Sprinkle
4
+ # = Policies
5
+ #
6
+ # A policy defines a set of packages which are required for a certain
7
+ # role (app, database, etc.). All policies defined will be run and all
8
+ # packages required by the policy will be installed. So whereas defining
9
+ # a Sprinkle::Package merely defines it, defining a Sprinkle::Policy
10
+ # actually causes those packages to install.
11
+ #
12
+ # == A Basic Example
13
+ #
14
+ # policy :blog, :roles => :app do
15
+ # require :webserver
16
+ # require :database
17
+ # require :rails
18
+ # end
19
+ #
20
+ # This says that for the blog on the app role, it requires certain
21
+ # packages. The :roles option is <em>exactly</em> the same as a capistrano
22
+ # or vlad role. A role merely defines what server the commands are run
23
+ # on. This way, a single Sprinkle script can provision an entire group
24
+ # of servers.
25
+ #
26
+ # To define a role, put in your actor specific configuration file (recipe or
27
+ # script file):
28
+ #
29
+ # role :app, "208.28.38.44"
30
+ #
31
+ # The capistrano and vlad syntax is the same for that. If you're using a
32
+ # custom actor, you may have to do it differently.
33
+ #
34
+ # == Multiple Policies
35
+ #
36
+ # You may specify as many policies as you'd like. If the packages you're
37
+ # requiring are properly defined with verification blocks, then
38
+ # no software will be installed twice, so you may require a webserver on
39
+ # multiple packages within the same role without having to wait for
40
+ # that package to install repeatedly.
4
41
  module Policy
5
- POLICIES = []
42
+ POLICIES = [] #:nodoc:
6
43
 
44
+ # Defines a single policy. Currently the only option, which is also
45
+ # required, is :roles, which defines which servers a policy is
46
+ # used on.
7
47
  def policy(name, options = {}, &block)
8
48
  p = Policy.new(name, options, &block)
9
49
  POLICIES << p
10
50
  p
11
51
  end
12
52
 
13
- class Policy
53
+ class Policy #:nodoc:
14
54
  attr_reader :name, :packages
15
55
 
16
56
  def initialize(name, metadata = {}, &block)
@@ -1,12 +1,22 @@
1
1
  module Sprinkle
2
+ # = Programmatically Run Sprinkle
3
+ #
4
+ # Sprinkle::Script gives you a way to programatically run a given
5
+ # sprinkle script.
2
6
  class Script
7
+ # Run a given sprinkle script. This method is <b>blocking</b> so
8
+ # it will not return until the sprinkling is complete or fails.
9
+ #--
10
+ # FIXME: Improve documentation, possibly notify user how to tell
11
+ # if a sprinkling failed.
12
+ #++
3
13
  def self.sprinkle(script, filename = '__SCRIPT__')
4
14
  powder = new
5
15
  powder.instance_eval script, filename
6
16
  powder.sprinkle
7
17
  end
8
18
 
9
- def sprinkle
19
+ def sprinkle #:nodoc:
10
20
  @deployment.process if @deployment
11
21
  end
12
22
  end
@@ -0,0 +1,16 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ # = Directory Verifier
4
+ #
5
+ # Defines a verify which can be used to test the existence of a
6
+ # directory.
7
+ module Directory
8
+ Sprinkle::Verify.register(Sprinkle::Verifiers::Directory)
9
+
10
+ # Tests that the directory <tt>dir</tt> exists.
11
+ def has_directory(dir)
12
+ @commands << "test -d #{dir}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ # = Executable Verifier
4
+ #
5
+ # Contains a verifier to check the existance of an executable
6
+ # script on your server.
7
+ #
8
+ # == Example Usage
9
+ #
10
+ # First, absolute path to an executable:
11
+ #
12
+ # verify { has_executable '/usr/special/secret/bin/scipt' }
13
+ #
14
+ # Second, a global executable which would be available anywhere on the
15
+ # command line:
16
+ #
17
+ # verify { has_executable 'grep' }
18
+ module Executable
19
+ Sprinkle::Verify.register(Sprinkle::Verifiers::Executable)
20
+
21
+ # Checks if <tt>path</tt> is an executable script. This verifier is "smart" because
22
+ # if the path contains a forward slash '/' then it assumes you're checking an
23
+ # absolute path to an executable. If no '/' is in the path, it assumes you're
24
+ # checking for a global executable that would be available anywhere on the command line.
25
+ def has_executable(path)
26
+ # Be smart: If the path includes a forward slash, we're checking
27
+ # an absolute path. Otherwise, we're checking a global executable
28
+ if path.include?('/')
29
+ @commands << "test -x #{path}"
30
+ else
31
+ @commands << "[ -n \"`echo \\`which #{path}\\``\" ]"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ # = File Verifier
4
+ #
5
+ # Contains a verifier to check the existance of a file.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # verify { has_file '/etc/apache2/apache2.conf' }
10
+ #
11
+ module File
12
+ Sprinkle::Verify.register(Sprinkle::Verifiers::File)
13
+
14
+ # Checks to make sure <tt>path</tt> is a file on the remote server.
15
+ def has_file(path)
16
+ @commands << "test -f #{path}"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ # = Process Verifier
4
+ #
5
+ # Contains a verifier to check that a process is running.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # verify { has_process 'httpd' }
10
+ #
11
+ module Process
12
+ Sprinkle::Verify.register(Sprinkle::Verifiers::Process)
13
+
14
+ # Checks to make sure <tt>process</tt> is a process running
15
+ # on the remote server.
16
+ def has_process(process)
17
+ @commands << "ps aux | grep '#{process}' | grep -v grep"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ # = Ruby Verifiers
4
+ #
5
+ # The verifiers in this module are ruby specific.
6
+ module Ruby
7
+ Sprinkle::Verify.register(Sprinkle::Verifiers::Ruby)
8
+
9
+ # Checks if ruby can require the <tt>files</tt> given. <tt>rubygems</tt>
10
+ # is always included first.
11
+ def ruby_can_load(*files)
12
+ # Always include rubygems first
13
+ files = files.unshift('rubygems').collect { |x| "require '#{x}'" }
14
+
15
+ @commands << "ruby -e \"#{files.join(';')}\""
16
+ end
17
+
18
+ # Checks if a gem exists by calling "sudo gem list" and grepping against it.
19
+ def has_gem(name, version=nil)
20
+ version = version.nil? ? '' : version.gsub('.', '\.')
21
+ @commands << "sudo gem list | grep -e '^#{name} (.*#{version}.*)$'"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ # = Symlink Verifier
4
+ #
5
+ # Contains a verifier to check the existance of a symbolic link.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # First, checking for the existence of a symlink:
10
+ #
11
+ # verify { has_symlink '/usr/special/secret/pointer' }
12
+ #
13
+ # Second, checking that the symlink points to a specific place:
14
+ #
15
+ # verify { has_symlink '/usr/special/secret/pointer', '/usr/local/realfile' }
16
+ module Symlink
17
+ Sprinkle::Verify.register(Sprinkle::Verifiers::Symlink)
18
+
19
+ # Checks that <tt>symlink</tt> is a symbolic link. If <tt>file</tt> is
20
+ # given, it checks that <tt>symlink</tt> points to <tt>file</tt>
21
+ def has_symlink(symlink, file = nil)
22
+ if file.nil?
23
+ @commands << "test -L #{symlink}"
24
+ else
25
+ @commands << "test '#{file}' = `readlink #{symlink}`"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end