automateit 0.70923
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.tar.gz.sig +1 -0
- data/CHANGES.txt +100 -0
- data/Hoe.rake +35 -0
- data/Manifest.txt +111 -0
- data/README.txt +44 -0
- data/Rakefile +284 -0
- data/TESTING.txt +57 -0
- data/TODO.txt +26 -0
- data/TUTORIAL.txt +390 -0
- data/bin/ai +3 -0
- data/bin/aifield +82 -0
- data/bin/aitag +128 -0
- data/bin/automateit +117 -0
- data/docs/friendly_errors.txt +50 -0
- data/docs/previews.txt +86 -0
- data/env.sh +4 -0
- data/examples/basic/Rakefile +26 -0
- data/examples/basic/config/automateit_env.rb +16 -0
- data/examples/basic/config/fields.yml +3 -0
- data/examples/basic/config/tags.yml +13 -0
- data/examples/basic/dist/README.txt +9 -0
- data/examples/basic/dist/myapp_server.erb +30 -0
- data/examples/basic/install.log +15 -0
- data/examples/basic/lib/README.txt +10 -0
- data/examples/basic/recipes/README.txt +4 -0
- data/examples/basic/recipes/install.rb +53 -0
- data/examples/basic/recipes/uninstall.rb +6 -0
- data/gpl.txt +674 -0
- data/lib/automateit.rb +66 -0
- data/lib/automateit/account_manager.rb +106 -0
- data/lib/automateit/account_manager/linux.rb +171 -0
- data/lib/automateit/account_manager/passwd.rb +69 -0
- data/lib/automateit/account_manager/portable.rb +136 -0
- data/lib/automateit/address_manager.rb +165 -0
- data/lib/automateit/address_manager/linux.rb +80 -0
- data/lib/automateit/address_manager/portable.rb +37 -0
- data/lib/automateit/cli.rb +80 -0
- data/lib/automateit/common.rb +65 -0
- data/lib/automateit/constants.rb +33 -0
- data/lib/automateit/edit_manager.rb +292 -0
- data/lib/automateit/error.rb +10 -0
- data/lib/automateit/field_manager.rb +103 -0
- data/lib/automateit/interpreter.rb +641 -0
- data/lib/automateit/package_manager.rb +242 -0
- data/lib/automateit/package_manager/apt.rb +63 -0
- data/lib/automateit/package_manager/egg.rb +64 -0
- data/lib/automateit/package_manager/gem.rb +179 -0
- data/lib/automateit/package_manager/portage.rb +69 -0
- data/lib/automateit/package_manager/yum.rb +65 -0
- data/lib/automateit/platform_manager.rb +47 -0
- data/lib/automateit/platform_manager/darwin.rb +30 -0
- data/lib/automateit/platform_manager/debian.rb +26 -0
- data/lib/automateit/platform_manager/freebsd.rb +25 -0
- data/lib/automateit/platform_manager/gentoo.rb +26 -0
- data/lib/automateit/platform_manager/lsb.rb +40 -0
- data/lib/automateit/platform_manager/struct.rb +78 -0
- data/lib/automateit/platform_manager/uname.rb +29 -0
- data/lib/automateit/platform_manager/windows.rb +33 -0
- data/lib/automateit/plugin.rb +7 -0
- data/lib/automateit/plugin/base.rb +32 -0
- data/lib/automateit/plugin/driver.rb +218 -0
- data/lib/automateit/plugin/manager.rb +232 -0
- data/lib/automateit/project.rb +460 -0
- data/lib/automateit/root.rb +14 -0
- data/lib/automateit/service_manager.rb +79 -0
- data/lib/automateit/service_manager/chkconfig.rb +39 -0
- data/lib/automateit/service_manager/rc_update.rb +37 -0
- data/lib/automateit/service_manager/sysv.rb +126 -0
- data/lib/automateit/service_manager/update_rcd.rb +35 -0
- data/lib/automateit/shell_manager.rb +261 -0
- data/lib/automateit/shell_manager/base_link.rb +67 -0
- data/lib/automateit/shell_manager/link.rb +24 -0
- data/lib/automateit/shell_manager/portable.rb +421 -0
- data/lib/automateit/shell_manager/symlink.rb +32 -0
- data/lib/automateit/shell_manager/which.rb +25 -0
- data/lib/automateit/tag_manager.rb +63 -0
- data/lib/automateit/tag_manager/struct.rb +101 -0
- data/lib/automateit/tag_manager/tag_parser.rb +91 -0
- data/lib/automateit/tag_manager/yaml.rb +29 -0
- data/lib/automateit/template_manager.rb +55 -0
- data/lib/automateit/template_manager/base.rb +172 -0
- data/lib/automateit/template_manager/erb.rb +17 -0
- data/lib/ext/metaclass.rb +17 -0
- data/lib/ext/object.rb +18 -0
- data/lib/hashcache.rb +22 -0
- data/lib/helpful_erb.rb +63 -0
- data/lib/nested_error.rb +33 -0
- data/lib/queued_logger.rb +68 -0
- data/lib/tempster.rb +239 -0
- data/misc/index_gem_repository.rb +303 -0
- data/misc/setup_egg.rb +12 -0
- data/misc/setup_gem_dependencies.sh +7 -0
- data/misc/setup_rubygems.sh +21 -0
- data/misc/which.cmd +6 -0
- data/spec/extras/automateit_service_sysv_test +50 -0
- data/spec/extras/scratch.rb +15 -0
- data/spec/extras/simple_recipe.rb +8 -0
- data/spec/integration/account_manager_spec.rb +218 -0
- data/spec/integration/address_manager_linux_spec.rb +119 -0
- data/spec/integration/address_manager_portable_spec.rb +30 -0
- data/spec/integration/cli_spec.rb +215 -0
- data/spec/integration/examples_spec.rb +54 -0
- data/spec/integration/examples_spec_editor.rb +71 -0
- data/spec/integration/package_manager_spec.rb +104 -0
- data/spec/integration/platform_manager_spec.rb +69 -0
- data/spec/integration/service_manager_sysv_spec.rb +115 -0
- data/spec/integration/shell_manager_spec.rb +471 -0
- data/spec/integration/template_manager_erb_spec.rb +31 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/unit/edit_manager_spec.rb +162 -0
- data/spec/unit/field_manager_spec.rb +79 -0
- data/spec/unit/hashcache_spec.rb +28 -0
- data/spec/unit/interpreter_spec.rb +98 -0
- data/spec/unit/platform_manager_spec.rb +44 -0
- data/spec/unit/plugins_spec.rb +253 -0
- data/spec/unit/tag_manager_spec.rb +189 -0
- data/spec/unit/template_manager_erb_spec.rb +137 -0
- metadata +249 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# == PackageManager
|
|
2
|
+
#
|
|
3
|
+
# The PackageManager provides a way to manage packages, e.g., install,
|
|
4
|
+
# uninstall and query if the Apache package is installed with APT.
|
|
5
|
+
#
|
|
6
|
+
# Examples:
|
|
7
|
+
#
|
|
8
|
+
# package_manager.installed?("apache2") # => false
|
|
9
|
+
# package_manager.install("apache2") # => true
|
|
10
|
+
# package_manager.installed?("apache2") # => true
|
|
11
|
+
# package_manager.uninstall("apache2") # => true
|
|
12
|
+
# package_manager.not_installed("apache2") # => true
|
|
13
|
+
#
|
|
14
|
+
# Commands can accept arrays:
|
|
15
|
+
#
|
|
16
|
+
# package_manager.install("apache2", "bash")
|
|
17
|
+
# package_manager.installed? %w(apache2 bash)
|
|
18
|
+
#
|
|
19
|
+
# Commands can also accept a single, annotated string as a manifest -- useful
|
|
20
|
+
# for installing large numbers of packages at once:
|
|
21
|
+
#
|
|
22
|
+
# package_manager.install <<HERE, :with => :apt
|
|
23
|
+
# # One per line
|
|
24
|
+
# apache
|
|
25
|
+
# bash
|
|
26
|
+
#
|
|
27
|
+
# # Or many on the same line
|
|
28
|
+
# sysvconfig sysv-rc-conf
|
|
29
|
+
# HERE
|
|
30
|
+
#
|
|
31
|
+
# Commands can also accept a hash of names to paths -- necessary for installing
|
|
32
|
+
# packages stored on the filesystem:
|
|
33
|
+
#
|
|
34
|
+
# # Is the package called "TracTags" installed? If not, run the installer
|
|
35
|
+
# # with the "/tmp/tractags_latest" path as an argument:
|
|
36
|
+
# package.manager.install({"TracTags" => "/tmp/tractags_latest"}, :with => :egg)
|
|
37
|
+
class AutomateIt::PackageManager < AutomateIt::Plugin::Manager
|
|
38
|
+
# Alias for #install
|
|
39
|
+
def add(*packages) dispatch_to(:install, *packages) end
|
|
40
|
+
|
|
41
|
+
# Alias for #uninstall
|
|
42
|
+
def remove(*packages) dispatch_to(:uninstall, *packages) end
|
|
43
|
+
|
|
44
|
+
# Are these +packages+ installed?
|
|
45
|
+
#
|
|
46
|
+
# Options:
|
|
47
|
+
# * :details -- Returns an array containing the boolean result value and an
|
|
48
|
+
# array with a subset of installed +packages+. Boolean, defaults to false.
|
|
49
|
+
def installed?(*packages) dispatch(*packages) end
|
|
50
|
+
|
|
51
|
+
# Are these +packages+ not installed?
|
|
52
|
+
#
|
|
53
|
+
# Options:
|
|
54
|
+
# * :details -- Returns an array containing the boolean result value and an
|
|
55
|
+
# array with a subset of +packages+ not installed. Boolean, defaults to false.
|
|
56
|
+
def not_installed?(*packages) dispatch(*packages) end
|
|
57
|
+
|
|
58
|
+
# Install these +packages+. Returns +true+ if any packages are installed
|
|
59
|
+
# successfully; or +false+ if all packages were already installed.
|
|
60
|
+
def install(*packages) dispatch(*packages) end
|
|
61
|
+
|
|
62
|
+
# Uninstall these +packages+. Returns +true+ if any packages are uninstalled
|
|
63
|
+
# successfully; or +false+ if none of the packages are installed.
|
|
64
|
+
def uninstall(*packages) dispatch(*packages) end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# == PackageManager::BaseDriver
|
|
68
|
+
#
|
|
69
|
+
# Base class for all PackageManager drivers.
|
|
70
|
+
class AutomateIt::PackageManager::BaseDriver < AutomateIt::Plugin::Driver
|
|
71
|
+
protected
|
|
72
|
+
|
|
73
|
+
# Are these +packages+ installed? Works like PackageManager#installed?
|
|
74
|
+
# but calls a block that actually checks whether the packages are
|
|
75
|
+
# installed and returns an array of packages installed.
|
|
76
|
+
#
|
|
77
|
+
# For example:
|
|
78
|
+
# _installed_helper?("package1", "package2", :details => true) do |packages, opts|
|
|
79
|
+
# # Dummy code which reports that these packages are installed:
|
|
80
|
+
# ["package1]
|
|
81
|
+
# end
|
|
82
|
+
def _installed_helper?(*packages, &block) # :yields: filtered_packages, opts
|
|
83
|
+
_raise_unless_available
|
|
84
|
+
|
|
85
|
+
packages, opts = args_and_opts(*packages)
|
|
86
|
+
packages = _list_normalizer(packages)
|
|
87
|
+
packages = packages.keys if Hash === packages
|
|
88
|
+
|
|
89
|
+
available = block.call(packages, opts)
|
|
90
|
+
truth = (packages - available).empty?
|
|
91
|
+
result = opts[:details] ? [truth, available] : truth
|
|
92
|
+
log.debug(PNOTE+"installed?(#{packages.inspect}) => #{truth}: #{available.inspect}")
|
|
93
|
+
return result
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Are these +packages+ not installed?
|
|
97
|
+
def _not_installed_helper?(*packages)
|
|
98
|
+
_raise_unless_available
|
|
99
|
+
|
|
100
|
+
# Requires that your PackageManager#installed? method is implemented.
|
|
101
|
+
packages, opts = args_and_opts(*packages)
|
|
102
|
+
packages = _list_normalizer(packages)
|
|
103
|
+
packages = packages.keys if Hash === packages
|
|
104
|
+
|
|
105
|
+
available = [installed?(packages, :details => true)].flatten
|
|
106
|
+
missing = packages - available
|
|
107
|
+
truth = (packages - missing).empty?
|
|
108
|
+
result = opts[:details] ? [truth, missing] : truth
|
|
109
|
+
log.debug(PNOTE+"not_installed?(#{packages.inspect}) => #{truth}: #{missing.inspect}")
|
|
110
|
+
return result
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Install these +packages+. Works like PackageManager#install but calls a
|
|
114
|
+
# block that's responsible for actually installing the packages and
|
|
115
|
+
# returning true if the installation succeeded. This block is only called
|
|
116
|
+
# if packages need to be installed and receives a filtered list of
|
|
117
|
+
# packages that are guaranteed not to be installed on the system already.
|
|
118
|
+
#
|
|
119
|
+
# For example:
|
|
120
|
+
# _install_helper("package1", "package2", :quiet => true) do |packages, opts|
|
|
121
|
+
# # Dummy code that installs packages here, e.g:
|
|
122
|
+
# system("apt-get", "install", "-y", packages)
|
|
123
|
+
# end
|
|
124
|
+
def _install_helper(*packages, &block) # :yields: filtered_packages, opts
|
|
125
|
+
_raise_unless_available
|
|
126
|
+
|
|
127
|
+
packages, opts = args_and_opts(*packages)
|
|
128
|
+
packages = _list_normalizer(packages)
|
|
129
|
+
|
|
130
|
+
check_packages = \
|
|
131
|
+
case packages
|
|
132
|
+
when Hash: packages.keys
|
|
133
|
+
else packages
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
missing = not_installed?(check_packages, :details => true)[1]
|
|
137
|
+
return false if missing.blank?
|
|
138
|
+
|
|
139
|
+
install_packages = \
|
|
140
|
+
case packages
|
|
141
|
+
when Hash: missing.map{|t| packages[t]}
|
|
142
|
+
else missing
|
|
143
|
+
end
|
|
144
|
+
block.call(install_packages, opts)
|
|
145
|
+
|
|
146
|
+
return true if preview?
|
|
147
|
+
unless (failed = not_installed?(check_packages, :details => true)[1]).empty?
|
|
148
|
+
raise ArgumentError.new("Couldn't install: #{failed.join(' ')}")
|
|
149
|
+
else
|
|
150
|
+
return true
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Uninstall these +packages+. Works like PackageManager#uninstall but calls a
|
|
155
|
+
# block that's responsible for actually uninstalling the packages and
|
|
156
|
+
# returning true if the uninstall succeeded. This block is only called
|
|
157
|
+
# if packages need to be uninstalled and receives a filtered list of
|
|
158
|
+
# packages that are guaranteed to be installed on the system.
|
|
159
|
+
#
|
|
160
|
+
# For example:
|
|
161
|
+
# _uninstall_helper("package1", "package2", :quiet => true) do |packages, opts|
|
|
162
|
+
# # Dummy code that removes packages here, e.g:
|
|
163
|
+
# system("apt-get", "remove", "-y", packages)
|
|
164
|
+
# end
|
|
165
|
+
def _uninstall_helper(*packages, &block) # :yields: filtered_packages, opts
|
|
166
|
+
_raise_unless_available
|
|
167
|
+
|
|
168
|
+
packages, opts = args_and_opts(*packages)
|
|
169
|
+
packages = _list_normalizer(packages)
|
|
170
|
+
|
|
171
|
+
check_packages = \
|
|
172
|
+
case packages
|
|
173
|
+
when Hash: packages.keys
|
|
174
|
+
else packages
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
present = installed?(check_packages, :details => true)[1]
|
|
178
|
+
return false if present.blank?
|
|
179
|
+
|
|
180
|
+
uninstall_packages = \
|
|
181
|
+
case packages
|
|
182
|
+
when Hash: present.map{|t| packages[t]}
|
|
183
|
+
else present
|
|
184
|
+
end
|
|
185
|
+
block.call(uninstall_packages, opts)
|
|
186
|
+
|
|
187
|
+
return true if preview?
|
|
188
|
+
unless (failed = installed?(check_packages, :details => true)[1]).empty?
|
|
189
|
+
raise ArgumentError.new("Couldn't uninstall: #{failed.join(' ')}")
|
|
190
|
+
else
|
|
191
|
+
return true
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Returns a normalized array of packages. Transforms manifest string into
|
|
196
|
+
# packages. Turns symbols into string, strips blank lines and comments.
|
|
197
|
+
def _list_normalizer(*packages)
|
|
198
|
+
packages = [packages].flatten
|
|
199
|
+
if packages.size == 1
|
|
200
|
+
packages = packages.first
|
|
201
|
+
nitpick "LN SI %s" % packages.inspect
|
|
202
|
+
nitpick "LN Sc %s" % packages.class
|
|
203
|
+
case packages
|
|
204
|
+
when Symbol
|
|
205
|
+
nitpick "LN Sy"
|
|
206
|
+
packages = packages.to_s
|
|
207
|
+
when String
|
|
208
|
+
nitpick "LN Ss"
|
|
209
|
+
packages = packages.grep(LIST_NORMALIZER_RE).join(" ").split
|
|
210
|
+
when Hash
|
|
211
|
+
# Don't do anything
|
|
212
|
+
nitpick "LN Sh"
|
|
213
|
+
else
|
|
214
|
+
nitpick "LN S?"
|
|
215
|
+
raise TypeError.new("Unknown input type: #{packages.class}")
|
|
216
|
+
end
|
|
217
|
+
nitpick "LN SO %s" % packages.inspect
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
case packages
|
|
221
|
+
when Array
|
|
222
|
+
result = packages.map(&:to_s).grep(LIST_NORMALIZER_RE)
|
|
223
|
+
when Hash
|
|
224
|
+
result = packages.stringify_keys
|
|
225
|
+
else
|
|
226
|
+
raise TypeError.new("Unknown input type: #{packages.class}")
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
nitpick "LN RR %s" % result.inspect
|
|
230
|
+
return result
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Expression for matching packages in arguments
|
|
234
|
+
LIST_NORMALIZER_RE = /^\s*([^\s#]+)/
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Drivers
|
|
238
|
+
require 'automateit/package_manager/apt'
|
|
239
|
+
require 'automateit/package_manager/yum'
|
|
240
|
+
require 'automateit/package_manager/gem'
|
|
241
|
+
require 'automateit/package_manager/egg'
|
|
242
|
+
require 'automateit/package_manager/portage'
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# == PackageManager::APT
|
|
2
|
+
#
|
|
3
|
+
# The APT driver for the PackageManager provides a way to manage software
|
|
4
|
+
# packages on Debian-style systems using <tt>apt-get</tt> and <tt>dpkg</tt>.
|
|
5
|
+
class AutomateIt::PackageManager::APT < AutomateIt::PackageManager::BaseDriver
|
|
6
|
+
depends_on :programs => %w(apt-get dpkg)
|
|
7
|
+
|
|
8
|
+
def suitability(method, *args) # :nodoc:
|
|
9
|
+
return available? ? 1 : 0
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# See AutomateIt::PackageManager#installed?
|
|
13
|
+
def installed?(*packages)
|
|
14
|
+
return _installed_helper?(*packages) do |list, opts|
|
|
15
|
+
### data = `dpkg --status nomarch apache2 not_a_real_package 2>&1`
|
|
16
|
+
cmd = "dpkg --status "+list.join(" ")+" 2>&1"
|
|
17
|
+
|
|
18
|
+
log.debug(PEXEC+cmd)
|
|
19
|
+
data = `#{cmd}`
|
|
20
|
+
matches = data.scan(/^Package: (.+)$\s*^Status: (.+)$/)
|
|
21
|
+
available = matches.inject([]) do |sum, match|
|
|
22
|
+
package, status = match
|
|
23
|
+
sum << package if status.match(/(?:^|\s)installed\b/)
|
|
24
|
+
sum
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
available
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# See AutomateIt::PackageManager#not_installed?
|
|
32
|
+
def not_installed?(*packages)
|
|
33
|
+
return _not_installed_helper?(*packages)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# See AutomateIt::PackageManager#install
|
|
37
|
+
def install(*packages)
|
|
38
|
+
return _install_helper(*packages) do |list, opts|
|
|
39
|
+
# apt-get options:
|
|
40
|
+
# -y : yes to all queries
|
|
41
|
+
# -q : no interactive progress bars
|
|
42
|
+
cmd = "export DEBIAN_FRONTEND=noninteractive; apt-get install -y -q "+list.join(" ")+" < /dev/null"
|
|
43
|
+
cmd << " > /dev/null" if opts[:quiet]
|
|
44
|
+
cmd << " 2>&1"
|
|
45
|
+
|
|
46
|
+
interpreter.sh(cmd)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# See AutomateIt::PackageManager#uninstall
|
|
51
|
+
def uninstall(*packages)
|
|
52
|
+
return _uninstall_helper(*packages) do |list, opts|
|
|
53
|
+
# apt-get options:
|
|
54
|
+
# -y : yes to all queries
|
|
55
|
+
# -q : no interactive progress bars
|
|
56
|
+
cmd = "export DEBIAN_FRONTEND=noninteractive; apt-get remove -y -q "+list.join(" ")+" < /dev/null"
|
|
57
|
+
cmd << " > /dev/null" if opts[:quiet]
|
|
58
|
+
cmd << " 2>&1"
|
|
59
|
+
|
|
60
|
+
interpreter.sh(cmd)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# == PackageManager::Egg
|
|
2
|
+
#
|
|
3
|
+
# The Egg driver for the PackageManager provides a way to manage
|
|
4
|
+
# Python software packages with the PEAK +easy_install+ tool.
|
|
5
|
+
class AutomateIt::PackageManager::Egg < AutomateIt::PackageManager::BaseDriver
|
|
6
|
+
depends_on :programs => %w(python easy_install)
|
|
7
|
+
|
|
8
|
+
def suitability(method, *args) # :nodoc:
|
|
9
|
+
# Never select as default driver
|
|
10
|
+
return 0
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# See AutomateIt::PackageManager#installed?
|
|
14
|
+
def installed?(*packages)
|
|
15
|
+
return _installed_helper?(*packages) do |list, opts|
|
|
16
|
+
cmd = "python -c 'import sys; print(sys.path)' 2>&1"
|
|
17
|
+
|
|
18
|
+
log.debug(PEXEC+cmd)
|
|
19
|
+
data = `#{cmd}`
|
|
20
|
+
# Extract array elements, turn them into basenames, and then split on
|
|
21
|
+
# '-' because that's the separator for the name and version.
|
|
22
|
+
found = data.scan(/'([^']+\.egg)'/).flatten.map{|t| File.basename(t).split('-', 2)[0]}
|
|
23
|
+
available = found & list
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# See AutomateIt::PackageManager#not_installed?
|
|
28
|
+
def not_installed?(*packages)
|
|
29
|
+
return _not_installed_helper?(*packages)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# See AutomateIt::PackageManager#install
|
|
33
|
+
def install(*packages)
|
|
34
|
+
return _install_helper(*packages) do |list, opts|
|
|
35
|
+
# easy_install options:
|
|
36
|
+
# -Z : install into a direcory rather than a file
|
|
37
|
+
cmd = "easy_install -Z "+list.join(" ")+" < /dev/null"
|
|
38
|
+
cmd << " > /dev/null" if opts[:quiet]
|
|
39
|
+
cmd << " 2>&1"
|
|
40
|
+
|
|
41
|
+
interpreter.sh(cmd)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# See AutomateIt::PackageManager#uninstall
|
|
46
|
+
def uninstall(*packages)
|
|
47
|
+
return _uninstall_helper(*packages) do |list, opts|
|
|
48
|
+
# easy_install options:
|
|
49
|
+
# -m : removes package from the easy-install.pth
|
|
50
|
+
cmd = "easy_install -m "+list.join(" ")+" < /dev/null"
|
|
51
|
+
cmd << " > /dev/null" if opts[:quiet]
|
|
52
|
+
cmd << " 2>&1"
|
|
53
|
+
|
|
54
|
+
# Parse output for paths and remove the orphaned entries
|
|
55
|
+
log.info(PEXEC+cmd)
|
|
56
|
+
return packages if preview?
|
|
57
|
+
data = `#{cmd}`
|
|
58
|
+
paths = data.scan(/^Using ([^\n]+\.egg)$/m).flatten
|
|
59
|
+
for path in paths
|
|
60
|
+
interpreter.rm_rf(path)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# == PackageManager::Gem
|
|
2
|
+
#
|
|
3
|
+
# The Gem driver for the PackageManager provides a way to manage software
|
|
4
|
+
# packages for RubyGems using the +gem+ command.
|
|
5
|
+
class AutomateIt::PackageManager::Gem < AutomateIt::PackageManager::BaseDriver
|
|
6
|
+
depends_on \
|
|
7
|
+
:programs => %w(gem),
|
|
8
|
+
:callbacks => [lambda{
|
|
9
|
+
# TODO PackageManager::Gem -- Misleading #depends_on makes entire driver unavailable, although only #install requires PTY.
|
|
10
|
+
begin
|
|
11
|
+
require 'expect'
|
|
12
|
+
require 'pty'
|
|
13
|
+
return true
|
|
14
|
+
rescue LoadError
|
|
15
|
+
return false
|
|
16
|
+
end
|
|
17
|
+
}]
|
|
18
|
+
|
|
19
|
+
def suitability(method, *args) # :nodoc:
|
|
20
|
+
# Never select GEM as the default driver
|
|
21
|
+
return 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# See PackageManager#installed?
|
|
25
|
+
def installed?(*packages)
|
|
26
|
+
return _installed_helper?(*packages) do |list, opts|
|
|
27
|
+
cmd = "gem list --local 2>&1"
|
|
28
|
+
|
|
29
|
+
log.debug(PEXEC+cmd)
|
|
30
|
+
data = `#{cmd}`
|
|
31
|
+
|
|
32
|
+
# Gem lists packages out of order, which screws up the
|
|
33
|
+
# install/uninstall sequence, so we need to put them back in the
|
|
34
|
+
# order that the user specified.
|
|
35
|
+
present = data.scan(/^([^\s\(]+)\s+\([^\)]+\)\s*$/).flatten
|
|
36
|
+
available = []
|
|
37
|
+
for package in list
|
|
38
|
+
available << package if present.include?(package)
|
|
39
|
+
end
|
|
40
|
+
available
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# See PackageManager#not_installed?
|
|
45
|
+
def not_installed?(*packages)
|
|
46
|
+
_not_installed_helper?(*packages)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Special options:
|
|
50
|
+
# * :docs -- If set to false, won't install rdoc or ri.
|
|
51
|
+
# * :source -- URL source to retrieve Gems from.
|
|
52
|
+
#
|
|
53
|
+
# See PackageManager#install
|
|
54
|
+
def install(*packages)
|
|
55
|
+
return _install_helper(*packages) do |list, opts|
|
|
56
|
+
# Why is the "gem" utility such a steaming pile of offal? Lameness include:
|
|
57
|
+
# - Requires interactive input to install a package, with no way to prevent this
|
|
58
|
+
# - Repeatedly updates indexes even when there's no reason to, and can't be told to stop
|
|
59
|
+
# - Doesn't cache packages, insists on downloading them again
|
|
60
|
+
# - Installs broken packages, often without giving any indication of failure
|
|
61
|
+
# - Installs broken packages and leaves you to deal with the jagged pieces
|
|
62
|
+
# - Sometimes fails through exit status, sometimes through output, but not both and not consistently
|
|
63
|
+
# - Lacks a proper "is this package installed?" feature
|
|
64
|
+
# - A nightmare to deal with if you want to install your own GEMHOME/GEMPATH
|
|
65
|
+
|
|
66
|
+
# Example of an invalid gem that'll cause the failure I'm trying to avoid below:
|
|
67
|
+
# package_manager.install("sys-cpu", :with => :gem)
|
|
68
|
+
|
|
69
|
+
# gem options:
|
|
70
|
+
# -y : Include dependencies,
|
|
71
|
+
# -E : use /usr/bin/env for installed executables; but only with >= 0.9.4
|
|
72
|
+
cmd = "gem install -y"
|
|
73
|
+
cmd << " --no-ri" if opts[:ri] == false or opts[:docs] == false
|
|
74
|
+
cmd << " --no-rdoc" if opts[:rdoc] == false or opts[:docs] == false
|
|
75
|
+
cmd << " --source #{opts[:source]}" if opts[:source]
|
|
76
|
+
cmd << " "+list.join(" ")
|
|
77
|
+
cmd << " " << opts[:args] if opts[:args]
|
|
78
|
+
cmd << " 2>&1"
|
|
79
|
+
|
|
80
|
+
# XXX Try to warn the user that they won't see any output for a while
|
|
81
|
+
log.info(PNOTE+"Installing Gems, this will take a while...") if writing? and not opts[:quiet]
|
|
82
|
+
log.info(PEXEC+cmd)
|
|
83
|
+
return true if preview?
|
|
84
|
+
|
|
85
|
+
uninstall_needed = false
|
|
86
|
+
begin
|
|
87
|
+
require 'expect'
|
|
88
|
+
exitstruct = Open4::popen4(cmd) do |pid, sin, sout, serr|
|
|
89
|
+
$expect_verbose = opts[:quiet] ? false : true
|
|
90
|
+
|
|
91
|
+
re_missing=/Could not find.+in any repository/m
|
|
92
|
+
re_select=/Select which gem to install.+>/m
|
|
93
|
+
re_failed=/Gem files will remain.+for inspection/m
|
|
94
|
+
re_refused=/Errno::ECONNREFUSED reading .+?\.gem/m
|
|
95
|
+
re_all=/#{re_missing}|#{re_select}|#{re_failed}|#{re_refused}/m
|
|
96
|
+
|
|
97
|
+
while true
|
|
98
|
+
begin
|
|
99
|
+
captureded = sout.expect(re_all)
|
|
100
|
+
rescue NoMethodError
|
|
101
|
+
log.debug(PNOTE+"Gem seems to be done")
|
|
102
|
+
break
|
|
103
|
+
end
|
|
104
|
+
### puts "Captureded %s" % captureded.inspect
|
|
105
|
+
captured = captureded.first
|
|
106
|
+
if captured.match(re_failed)
|
|
107
|
+
log.warn(PERROR+"Gem install failed mid-process")
|
|
108
|
+
uninstall_needed = true
|
|
109
|
+
break
|
|
110
|
+
elsif captured.match(re_refused)
|
|
111
|
+
log.warn(PERROR+"Gem install refused by server!\n#{captured}")
|
|
112
|
+
break
|
|
113
|
+
elsif captured.match(re_select)
|
|
114
|
+
choice = captured.match(/^ (\d+)\. .+?\(ruby\)\s*$/)[1]
|
|
115
|
+
log.info(PNOTE+"Guessing: #{choice}")
|
|
116
|
+
sin.puts(choice)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
rescue Errno::ENOENT => e
|
|
121
|
+
raise NotImplementedError.new("can't find gem command: #{e}")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if uninstall_needed or not exitstruct.exitstatus.zero?
|
|
125
|
+
log.error(PERROR+"Gem install failed, trying to uninstall broken pieces: #{list.inspect}")
|
|
126
|
+
uninstall(list, opts)
|
|
127
|
+
|
|
128
|
+
raise ArgumentError.new("Gem install failed because it's invalid, missing a dependency, or can't talk with Gem server: #{list.inspect}")
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# See PackageManager#uninstall
|
|
134
|
+
def uninstall(*packages)
|
|
135
|
+
return _uninstall_helper(*packages) do |list, opts|
|
|
136
|
+
# TODO PackageManager::gem#uninstall -- add logic to handle prompts during removal
|
|
137
|
+
=begin
|
|
138
|
+
# idiotic program MAY prompt you like this on uninstall:
|
|
139
|
+
|
|
140
|
+
Gem 0.9.4 generates prompts like this:
|
|
141
|
+
** gem uninstall -x mongrel < /dev/null 2>&1
|
|
142
|
+
|
|
143
|
+
You have requested to uninstall the gem:
|
|
144
|
+
mongrel-1.0.1
|
|
145
|
+
mongrel_cluster-1.0.2 depends on [mongrel (>= 1.0.1)]
|
|
146
|
+
If you remove this gems, one or more dependencies will not be met.
|
|
147
|
+
Continue with Uninstall? [Yn] Successfully uninstalled mongrel version 1.0.1
|
|
148
|
+
Removing mongrel_rails
|
|
149
|
+
|
|
150
|
+
#-----------------------------------------------------------------------
|
|
151
|
+
Gem 0.9.0 generates prompts like this:
|
|
152
|
+
** gem uninstall -x mongrel < /dev/null 2>&1
|
|
153
|
+
|
|
154
|
+
Select RubyGem to uninstall:
|
|
155
|
+
1. mongrel-1.0.1
|
|
156
|
+
2. mongrel_cluster-1.0.2
|
|
157
|
+
3. All versions
|
|
158
|
+
> 3
|
|
159
|
+
|
|
160
|
+
You have requested to uninstall the gem:
|
|
161
|
+
mongrel-1.0.1
|
|
162
|
+
mongrel_cluster-1.0.2 depends on [mongrel (>= 1.0.1)]
|
|
163
|
+
If you remove this gems, one or more dependencies will not be met.
|
|
164
|
+
Continue with Uninstall? [Yn] y
|
|
165
|
+
Successfully uninstalled mongrel version 1.0.1
|
|
166
|
+
Successfully uninstalled mongrel_cluster version 1.0.2
|
|
167
|
+
root@ubuntu:/mnt/satori/svnwork/automateit/src/examples/myapp_rails#
|
|
168
|
+
=end
|
|
169
|
+
for package in list
|
|
170
|
+
# gem options:
|
|
171
|
+
# -x : remove installed executables
|
|
172
|
+
cmd = "gem uninstall -x #{package} < /dev/null"
|
|
173
|
+
cmd << " > /dev/null" if opts[:quiet]
|
|
174
|
+
cmd << " 2>&1"
|
|
175
|
+
interpreter.sh(cmd)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|