automateit 0.70923

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. data.tar.gz.sig +1 -0
  2. data/CHANGES.txt +100 -0
  3. data/Hoe.rake +35 -0
  4. data/Manifest.txt +111 -0
  5. data/README.txt +44 -0
  6. data/Rakefile +284 -0
  7. data/TESTING.txt +57 -0
  8. data/TODO.txt +26 -0
  9. data/TUTORIAL.txt +390 -0
  10. data/bin/ai +3 -0
  11. data/bin/aifield +82 -0
  12. data/bin/aitag +128 -0
  13. data/bin/automateit +117 -0
  14. data/docs/friendly_errors.txt +50 -0
  15. data/docs/previews.txt +86 -0
  16. data/env.sh +4 -0
  17. data/examples/basic/Rakefile +26 -0
  18. data/examples/basic/config/automateit_env.rb +16 -0
  19. data/examples/basic/config/fields.yml +3 -0
  20. data/examples/basic/config/tags.yml +13 -0
  21. data/examples/basic/dist/README.txt +9 -0
  22. data/examples/basic/dist/myapp_server.erb +30 -0
  23. data/examples/basic/install.log +15 -0
  24. data/examples/basic/lib/README.txt +10 -0
  25. data/examples/basic/recipes/README.txt +4 -0
  26. data/examples/basic/recipes/install.rb +53 -0
  27. data/examples/basic/recipes/uninstall.rb +6 -0
  28. data/gpl.txt +674 -0
  29. data/lib/automateit.rb +66 -0
  30. data/lib/automateit/account_manager.rb +106 -0
  31. data/lib/automateit/account_manager/linux.rb +171 -0
  32. data/lib/automateit/account_manager/passwd.rb +69 -0
  33. data/lib/automateit/account_manager/portable.rb +136 -0
  34. data/lib/automateit/address_manager.rb +165 -0
  35. data/lib/automateit/address_manager/linux.rb +80 -0
  36. data/lib/automateit/address_manager/portable.rb +37 -0
  37. data/lib/automateit/cli.rb +80 -0
  38. data/lib/automateit/common.rb +65 -0
  39. data/lib/automateit/constants.rb +33 -0
  40. data/lib/automateit/edit_manager.rb +292 -0
  41. data/lib/automateit/error.rb +10 -0
  42. data/lib/automateit/field_manager.rb +103 -0
  43. data/lib/automateit/interpreter.rb +641 -0
  44. data/lib/automateit/package_manager.rb +242 -0
  45. data/lib/automateit/package_manager/apt.rb +63 -0
  46. data/lib/automateit/package_manager/egg.rb +64 -0
  47. data/lib/automateit/package_manager/gem.rb +179 -0
  48. data/lib/automateit/package_manager/portage.rb +69 -0
  49. data/lib/automateit/package_manager/yum.rb +65 -0
  50. data/lib/automateit/platform_manager.rb +47 -0
  51. data/lib/automateit/platform_manager/darwin.rb +30 -0
  52. data/lib/automateit/platform_manager/debian.rb +26 -0
  53. data/lib/automateit/platform_manager/freebsd.rb +25 -0
  54. data/lib/automateit/platform_manager/gentoo.rb +26 -0
  55. data/lib/automateit/platform_manager/lsb.rb +40 -0
  56. data/lib/automateit/platform_manager/struct.rb +78 -0
  57. data/lib/automateit/platform_manager/uname.rb +29 -0
  58. data/lib/automateit/platform_manager/windows.rb +33 -0
  59. data/lib/automateit/plugin.rb +7 -0
  60. data/lib/automateit/plugin/base.rb +32 -0
  61. data/lib/automateit/plugin/driver.rb +218 -0
  62. data/lib/automateit/plugin/manager.rb +232 -0
  63. data/lib/automateit/project.rb +460 -0
  64. data/lib/automateit/root.rb +14 -0
  65. data/lib/automateit/service_manager.rb +79 -0
  66. data/lib/automateit/service_manager/chkconfig.rb +39 -0
  67. data/lib/automateit/service_manager/rc_update.rb +37 -0
  68. data/lib/automateit/service_manager/sysv.rb +126 -0
  69. data/lib/automateit/service_manager/update_rcd.rb +35 -0
  70. data/lib/automateit/shell_manager.rb +261 -0
  71. data/lib/automateit/shell_manager/base_link.rb +67 -0
  72. data/lib/automateit/shell_manager/link.rb +24 -0
  73. data/lib/automateit/shell_manager/portable.rb +421 -0
  74. data/lib/automateit/shell_manager/symlink.rb +32 -0
  75. data/lib/automateit/shell_manager/which.rb +25 -0
  76. data/lib/automateit/tag_manager.rb +63 -0
  77. data/lib/automateit/tag_manager/struct.rb +101 -0
  78. data/lib/automateit/tag_manager/tag_parser.rb +91 -0
  79. data/lib/automateit/tag_manager/yaml.rb +29 -0
  80. data/lib/automateit/template_manager.rb +55 -0
  81. data/lib/automateit/template_manager/base.rb +172 -0
  82. data/lib/automateit/template_manager/erb.rb +17 -0
  83. data/lib/ext/metaclass.rb +17 -0
  84. data/lib/ext/object.rb +18 -0
  85. data/lib/hashcache.rb +22 -0
  86. data/lib/helpful_erb.rb +63 -0
  87. data/lib/nested_error.rb +33 -0
  88. data/lib/queued_logger.rb +68 -0
  89. data/lib/tempster.rb +239 -0
  90. data/misc/index_gem_repository.rb +303 -0
  91. data/misc/setup_egg.rb +12 -0
  92. data/misc/setup_gem_dependencies.sh +7 -0
  93. data/misc/setup_rubygems.sh +21 -0
  94. data/misc/which.cmd +6 -0
  95. data/spec/extras/automateit_service_sysv_test +50 -0
  96. data/spec/extras/scratch.rb +15 -0
  97. data/spec/extras/simple_recipe.rb +8 -0
  98. data/spec/integration/account_manager_spec.rb +218 -0
  99. data/spec/integration/address_manager_linux_spec.rb +119 -0
  100. data/spec/integration/address_manager_portable_spec.rb +30 -0
  101. data/spec/integration/cli_spec.rb +215 -0
  102. data/spec/integration/examples_spec.rb +54 -0
  103. data/spec/integration/examples_spec_editor.rb +71 -0
  104. data/spec/integration/package_manager_spec.rb +104 -0
  105. data/spec/integration/platform_manager_spec.rb +69 -0
  106. data/spec/integration/service_manager_sysv_spec.rb +115 -0
  107. data/spec/integration/shell_manager_spec.rb +471 -0
  108. data/spec/integration/template_manager_erb_spec.rb +31 -0
  109. data/spec/spec_helper.rb +23 -0
  110. data/spec/unit/edit_manager_spec.rb +162 -0
  111. data/spec/unit/field_manager_spec.rb +79 -0
  112. data/spec/unit/hashcache_spec.rb +28 -0
  113. data/spec/unit/interpreter_spec.rb +98 -0
  114. data/spec/unit/platform_manager_spec.rb +44 -0
  115. data/spec/unit/plugins_spec.rb +253 -0
  116. data/spec/unit/tag_manager_spec.rb +189 -0
  117. data/spec/unit/template_manager_erb_spec.rb +137 -0
  118. metadata +249 -0
  119. 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