automateit 0.70923
Sign up to get free protection for your applications and to get access to all the features.
- 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
|