automate-it 0.9.0
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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.hgignore +10 -0
- data/.loadpath +5 -0
- data/.project +17 -0
- data/CHANGES.txt +314 -0
- data/Hoe.rake +40 -0
- data/Manifest.txt +164 -0
- data/README.txt +40 -0
- data/Rakefile +256 -0
- data/TESTING.txt +57 -0
- data/TODO.txt +50 -0
- data/TUTORIAL.txt +391 -0
- data/automate-it.gemspec +25 -0
- data/bin/ai +3 -0
- data/bin/aifield +75 -0
- data/bin/aissh +93 -0
- data/bin/aitag +134 -0
- data/bin/automateit +133 -0
- data/docs/friendly_errors.txt +50 -0
- data/docs/previews.txt +86 -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 +7 -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 +61 -0
- data/examples/basic/recipes/uninstall.rb +6 -0
- data/gpl.txt +674 -0
- data/helpers/cpan_wrapper.pl +220 -0
- data/helpers/which.cmd +7 -0
- data/lib/automateit.rb +55 -0
- data/lib/automateit/account_manager.rb +114 -0
- data/lib/automateit/account_manager/base.rb +138 -0
- data/lib/automateit/account_manager/etc.rb +128 -0
- data/lib/automateit/account_manager/nscd.rb +33 -0
- data/lib/automateit/account_manager/passwd_expect.rb +40 -0
- data/lib/automateit/account_manager/passwd_pty.rb +69 -0
- data/lib/automateit/account_manager/posix.rb +138 -0
- data/lib/automateit/address_manager.rb +88 -0
- data/lib/automateit/address_manager/base.rb +171 -0
- data/lib/automateit/address_manager/bsd.rb +28 -0
- data/lib/automateit/address_manager/freebsd.rb +59 -0
- data/lib/automateit/address_manager/linux.rb +42 -0
- data/lib/automateit/address_manager/openbsd.rb +66 -0
- data/lib/automateit/address_manager/portable.rb +37 -0
- data/lib/automateit/address_manager/sunos.rb +34 -0
- data/lib/automateit/cli.rb +85 -0
- data/lib/automateit/common.rb +65 -0
- data/lib/automateit/constants.rb +35 -0
- data/lib/automateit/download_manager.rb +48 -0
- data/lib/automateit/edit_manager.rb +321 -0
- data/lib/automateit/error.rb +10 -0
- data/lib/automateit/field_manager.rb +103 -0
- data/lib/automateit/interpreter.rb +631 -0
- data/lib/automateit/package_manager.rb +257 -0
- data/lib/automateit/package_manager/apt.rb +27 -0
- data/lib/automateit/package_manager/cpan.rb +101 -0
- data/lib/automateit/package_manager/dpkg.rb +54 -0
- data/lib/automateit/package_manager/egg.rb +64 -0
- data/lib/automateit/package_manager/gem.rb +201 -0
- data/lib/automateit/package_manager/pear.rb +95 -0
- data/lib/automateit/package_manager/pecl.rb +80 -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 +49 -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 +29 -0
- data/lib/automateit/platform_manager/gentoo.rb +26 -0
- data/lib/automateit/platform_manager/lsb.rb +44 -0
- data/lib/automateit/platform_manager/openbsd.rb +28 -0
- data/lib/automateit/platform_manager/struct.rb +80 -0
- data/lib/automateit/platform_manager/sunos.rb +39 -0
- data/lib/automateit/platform_manager/uname.rb +29 -0
- data/lib/automateit/platform_manager/windows.rb +40 -0
- data/lib/automateit/plugin.rb +7 -0
- data/lib/automateit/plugin/base.rb +32 -0
- data/lib/automateit/plugin/driver.rb +256 -0
- data/lib/automateit/plugin/manager.rb +224 -0
- data/lib/automateit/project.rb +493 -0
- data/lib/automateit/root.rb +17 -0
- data/lib/automateit/service_manager.rb +93 -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 +139 -0
- data/lib/automateit/service_manager/update_rcd.rb +35 -0
- data/lib/automateit/shell_manager.rb +316 -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 +523 -0
- data/lib/automateit/shell_manager/symlink.rb +32 -0
- data/lib/automateit/shell_manager/which_base.rb +30 -0
- data/lib/automateit/shell_manager/which_unix.rb +16 -0
- data/lib/automateit/shell_manager/which_windows.rb +20 -0
- data/lib/automateit/tag_manager.rb +127 -0
- data/lib/automateit/tag_manager/struct.rb +121 -0
- data/lib/automateit/tag_manager/tag_parser.rb +93 -0
- data/lib/automateit/tag_manager/yaml.rb +29 -0
- data/lib/automateit/template_manager.rb +56 -0
- data/lib/automateit/template_manager/base.rb +181 -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/ext/shell_escape.rb +7 -0
- data/lib/hashcache.rb +22 -0
- data/lib/helpful_erb.rb +63 -0
- data/lib/inactive_support.rb +53 -0
- data/lib/inactive_support/basic_object.rb +6 -0
- data/lib/inactive_support/clean_logger.rb +127 -0
- data/lib/inactive_support/core_ext/array/extract_options.rb +19 -0
- data/lib/inactive_support/core_ext/blank.rb +50 -0
- data/lib/inactive_support/core_ext/class/attribute_accessors.rb +48 -0
- data/lib/inactive_support/core_ext/class/inheritable_attributes.rb +140 -0
- data/lib/inactive_support/core_ext/enumerable.rb +63 -0
- data/lib/inactive_support/core_ext/hash/keys.rb +54 -0
- data/lib/inactive_support/core_ext/module/aliasing.rb +70 -0
- data/lib/inactive_support/core_ext/numeric/time.rb +91 -0
- data/lib/inactive_support/core_ext/string/inflections.rb +153 -0
- data/lib/inactive_support/core_ext/symbol.rb +14 -0
- data/lib/inactive_support/core_ext/time/conversions.rb +96 -0
- data/lib/inactive_support/duration.rb +96 -0
- data/lib/inactive_support/inflections.rb +53 -0
- data/lib/inactive_support/inflector.rb +282 -0
- data/lib/nested_error.rb +33 -0
- data/lib/nitpick.rb +33 -0
- data/lib/queued_logger.rb +68 -0
- data/lib/tempster.rb +250 -0
- data/misc/index_gem_repository.rb +304 -0
- data/misc/setup_egg.rb +12 -0
- data/misc/setup_gem_dependencies.sh +6 -0
- data/misc/setup_rubygems.sh +21 -0
- metadata +279 -0
data/lib/nitpick.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Nitpick
|
2
|
+
module ClassMethods
|
3
|
+
# Use to manage nitpick message for debugging AutomateIt internals.
|
4
|
+
#
|
5
|
+
# Arguments:
|
6
|
+
# * nil -- Returns boolean of whether nitpick messages will be displayed.
|
7
|
+
# * Boolean -- Sets nitpick state.
|
8
|
+
# * String or Symbol -- Displays nitpick message if state is on.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
# nitpick true
|
12
|
+
# nitpick "I'm nitpicking"
|
13
|
+
def nitpick(value=nil)
|
14
|
+
case value
|
15
|
+
when NilClass
|
16
|
+
@nitpick
|
17
|
+
when TrueClass, FalseClass
|
18
|
+
@nitpick = value
|
19
|
+
when String, Symbol
|
20
|
+
puts "%% #{value}" if @nitpick
|
21
|
+
else
|
22
|
+
raise TypeError.new("Unknown nitpick type: #{value.class}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.included(receiver)
|
28
|
+
receiver.extend(ClassMethods)
|
29
|
+
end
|
30
|
+
|
31
|
+
include ClassMethods
|
32
|
+
extend ClassMethods
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
class QueuedLogger < Logger
|
4
|
+
def initialize(*args)
|
5
|
+
super(*args)
|
6
|
+
@queue = []
|
7
|
+
@emitted = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def process_queue
|
11
|
+
if queued?
|
12
|
+
while true
|
13
|
+
entry = @queue.shift or break
|
14
|
+
severity, message = entry
|
15
|
+
raw_method = "#{severity}_without_queue"
|
16
|
+
severity_i = self.class.send(:const_get, severity.to_s.upcase)
|
17
|
+
send(raw_method, message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def enqueue(level, message)
|
23
|
+
@queue << [level, message]
|
24
|
+
@emitted = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def dequeue(level=nil, message=nil)
|
28
|
+
if queued?
|
29
|
+
@queue.clear
|
30
|
+
elsif level and message
|
31
|
+
send("%s_without_queue"%level, message) if @emitted
|
32
|
+
end
|
33
|
+
@emitted = false
|
34
|
+
end
|
35
|
+
|
36
|
+
def queued?
|
37
|
+
! @queue.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
for level in %w(debug info warn error fatal) do
|
41
|
+
class_eval <<-EOB
|
42
|
+
def #{level}_with_queue(*args)
|
43
|
+
severity = self.class.send(:const_get, "#{level.upcase}")
|
44
|
+
process_queue if severity >= self.level
|
45
|
+
@emitted = true
|
46
|
+
#{level}_without_queue(*args)
|
47
|
+
end
|
48
|
+
alias_method_chain :#{level}, :queue
|
49
|
+
EOB
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
if __FILE__ == $0
|
55
|
+
# TODO QueuedLogger -- write a spec
|
56
|
+
|
57
|
+
q = QueuedLogger.new($stdout)
|
58
|
+
q.level = Logger::INFO
|
59
|
+
q.info("first message")
|
60
|
+
q.enqueue(:info, "queued message that'll be pushed")
|
61
|
+
q.info("second message, which pushed out queued message")
|
62
|
+
q.enqueue(:error, "ERROR: queued message that'll be discarded")
|
63
|
+
q.debug("ERROR: message that'll be discarded and won't push queue")
|
64
|
+
q.dequeue(:error, "ERROR: dequeue message that'll be discarded")
|
65
|
+
q.enqueue(:info, "queued message that'll be pushed")
|
66
|
+
q.info("message that'll push out the queue and dequeue messages")
|
67
|
+
q.dequeue(:info, "dequeue message that'll be pushed")
|
68
|
+
end
|
data/lib/tempster.rb
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# == Tempster
|
5
|
+
#
|
6
|
+
# Tempster is a pure-Ruby library for creating temporary files and directories.
|
7
|
+
# Unlike other tools, it can create both temporary files and directories, and
|
8
|
+
# is designed to be secure, thread-safe, easy-to-use, powerful thanks to
|
9
|
+
# user-configurable options, and user-friendly thanks to good defaults so you
|
10
|
+
# don't need to provide any arguments.
|
11
|
+
#
|
12
|
+
# Why write another library for this? The Tempfile standard library provides no
|
13
|
+
# way to create temporary directories and always deletes the files it creates,
|
14
|
+
# even if you want to keep them. The MkTemp gem is insecure and fails on
|
15
|
+
# collisions. Linux "mktemp" works fine but is platform-specific. Therefore, I
|
16
|
+
# had to write something.
|
17
|
+
#
|
18
|
+
# === WARNING: Using 'cd' and :noop together can be dangerous!
|
19
|
+
#
|
20
|
+
# Tempster will only *pretend* to make directories in :noop (no-operation)
|
21
|
+
# mode. In :noop mode, it will also only *pretend* to change into the directory
|
22
|
+
# when using :cd or +mktempdircd+.
|
23
|
+
#
|
24
|
+
# This can be *disastrous* if you're executing non-AutomateIt commands (e.g.
|
25
|
+
# +system+) that use *relative* *paths* and expect to be run inside the
|
26
|
+
# newly-created temporary directory because the +chdir+ didn't actually happen.
|
27
|
+
#
|
28
|
+
# Read previews.txt[link:files/docs/previews_txt.html] for instructions on how
|
29
|
+
# to write code that can be safely previewed.
|
30
|
+
#
|
31
|
+
# == Credits
|
32
|
+
# * Random string generator taken from
|
33
|
+
# http://pleac.sourceforge.net/pleac_ruby/numbers.html
|
34
|
+
class Tempster
|
35
|
+
DEFAULT_PREFIX = "tempster"
|
36
|
+
DEFAULT_FILE_MODE = 0600
|
37
|
+
DEFAULT_DIRECTORY_MODE = 0700
|
38
|
+
DEFAULT_ARMOR_LENGTH = 10
|
39
|
+
ARMOR_CHARACTERS = ["A".."Z","a".."z","0".."9"].collect{|r| r.to_a}.join
|
40
|
+
|
41
|
+
# Alias for ::tempster.
|
42
|
+
def self._tempster(opts={}, &block) # :nodoc:
|
43
|
+
tempster(opts, &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Options:
|
47
|
+
# * :kind -- Create a :file or :directory, required.
|
48
|
+
# * :prefix -- String to prefix the temporary name with, defaults to "tempster".
|
49
|
+
# * :suffix -- String to suffix the temporary name with, defaults is no suffix.
|
50
|
+
# * :name -- Same as :prefix
|
51
|
+
# * :dir -- Base directory to create temporary entries in, uses system-wide temporary directory (e.g., <tt>/tmp</tt>) by default.
|
52
|
+
# * :cd -- Change into the newly directory created using +ch+ within the block, and then switch back to the previous directory. Only used when a block is given and the :kind is :directory. Default is false. See WARNING at the top of this class's documentation!
|
53
|
+
# * :noop -- no-operation mode, pretends to do actions without actually creating or deleting temporary entries. Default is false. WARNING: See WARNING at the top of this class's documentation!
|
54
|
+
# * :verbose -- Print status messages about creating and deleting the temporary entries. Default is false.
|
55
|
+
# * :delete -- Delete the temporary entries when exiting block. Default is true when given a block, false otherwise. If you don't use a block, you're responsible for deleting the entries yourself.
|
56
|
+
# * :tries -- Number of tries to create a temporary entry, usually it'll succeed on the first try. Default is 10.
|
57
|
+
# * :armor -- Length of armor to append to the prefix. These are random characters padding out the temporary entry names to prevent them from using existing files. If you have a very short armor, you're likely to get a collision and the algorithm will have to try again for the specified number of +tries+.
|
58
|
+
# * :message_callback -- +lambda+ called when there's a message, e.g., <tt>lambda{|message| puts message}</tt>, regardless of :verbose state. By default :messaging is nil and messages are printed to STDOUT only when :verbose is true.
|
59
|
+
# * :message_prefix -- String to put in front of messages, e.g., "# "
|
60
|
+
def self.tempster(opts={}, &block)
|
61
|
+
kind = opts.delete(:kind) or raise ArgumentError.new("'kind' option not specified")
|
62
|
+
prefix = opts.delete(:prefix) || opts.delete(:name) || DEFAULT_PREFIX
|
63
|
+
suffix = opts.delete(:suffix) || nil
|
64
|
+
dir = opts.delete(:dir) || Dir.tmpdir
|
65
|
+
cd = opts.delete(:cd) || false
|
66
|
+
noop = opts.delete(:noop) || false
|
67
|
+
verbose = opts.delete(:verbose) || false
|
68
|
+
delete = opts.delete(:delete) || block ? true : false
|
69
|
+
tries = opts.delete(:tries) || 10
|
70
|
+
armor = opts.delete(:armor) || DEFAULT_ARMOR_LENGTH
|
71
|
+
message_callback = opts.delete(:message_callback) || nil
|
72
|
+
message_prefix = opts.delete(:message_prefix) || ""
|
73
|
+
mode = opts.delete(:mode) || \
|
74
|
+
case kind
|
75
|
+
when :file
|
76
|
+
DEFAULT_FILE_MODE
|
77
|
+
when :directory
|
78
|
+
DEFAULT_DIRECTORY_MODE
|
79
|
+
else
|
80
|
+
raise ArgumentError.new("unknown kind: #{kind}")
|
81
|
+
end
|
82
|
+
|
83
|
+
raise ArgumentError.new("can only use 'delete' option with block") if delete and not block
|
84
|
+
raise ArgumentError.new("can only use 'cd' with directories and blocks") if cd and (not dir or not block)
|
85
|
+
raise ArgumentError.new("unknown extra options: #{opts.inspect}") unless opts.empty?
|
86
|
+
|
87
|
+
messager = Messager.new(verbose, message_callback, message_prefix)
|
88
|
+
|
89
|
+
path = nil
|
90
|
+
success = false
|
91
|
+
for i in 1..tries
|
92
|
+
begin
|
93
|
+
name = prefix+"_"+_armor_string(armor)
|
94
|
+
name << suffix if suffix
|
95
|
+
path = File.join(dir, name)
|
96
|
+
|
97
|
+
unless noop
|
98
|
+
case kind
|
99
|
+
when :file
|
100
|
+
File.open(path, File::RDWR|File::CREAT|File::EXCL).close
|
101
|
+
File.chmod(mode, path)
|
102
|
+
when :directory
|
103
|
+
Dir.mkdir(path, mode)
|
104
|
+
else
|
105
|
+
raise ArgumentError.new("unknown kind: #{kind}")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
message = "mktempster --mode=0%o --kind=%s --dir=%s --prefix=%s" % [mode, kind, dir, prefix]
|
109
|
+
message << (" --suffix=%s" % suffix) if suffix
|
110
|
+
message << (" # => %s" % path) if block
|
111
|
+
success = true
|
112
|
+
break
|
113
|
+
rescue Errno::EEXIST
|
114
|
+
# Try again
|
115
|
+
end
|
116
|
+
end
|
117
|
+
raise IOError.new("couldn't create temporary #{kind}, ") unless success
|
118
|
+
if block
|
119
|
+
previous = Dir.pwd if cd
|
120
|
+
begin
|
121
|
+
if cd
|
122
|
+
Dir.chdir(path) unless noop
|
123
|
+
messager.puts("pushd #{path}")
|
124
|
+
end
|
125
|
+
block.call(path)
|
126
|
+
rescue Exception => e
|
127
|
+
# Re-throw exception after cleaning up
|
128
|
+
raise e
|
129
|
+
ensure
|
130
|
+
if cd
|
131
|
+
Dir.chdir(previous) unless noop
|
132
|
+
messager.puts("popd # => #{previous}")
|
133
|
+
end
|
134
|
+
if delete
|
135
|
+
FileUtils.rm_rf(path) unless noop
|
136
|
+
messager.puts("rm -rf #{path}")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
return true
|
140
|
+
else
|
141
|
+
return path
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns a string of random characters.
|
146
|
+
def self._armor_string(length=DEFAULT_ARMOR_LENGTH)
|
147
|
+
(1..length).collect{ARMOR_CHARACTERS[rand(ARMOR_CHARACTERS.size)]}.pack("C*")
|
148
|
+
end
|
149
|
+
|
150
|
+
# Creates a temporary file.
|
151
|
+
def self.mktemp(opts={}, &block)
|
152
|
+
tempster({:kind => :file}.merge(opts), &block)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Creates a temporary directory.
|
156
|
+
#
|
157
|
+
# *WARNING*: See WARNING text at the top of this class's documentation!
|
158
|
+
def self.mktempdir(opts={}, &block)
|
159
|
+
tempster({:kind => :directory}.merge(opts), &block)
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
# Creates a temporary directory and changes into it using +chdir+. This is a
|
164
|
+
# shortcut for using +mktempdir+ with the <tt>:cd => true</tt> option.
|
165
|
+
#
|
166
|
+
# *WARNING*: See WARNING text at the top of this class's documentation!
|
167
|
+
def self.mktempdircd(opts={}, &block)
|
168
|
+
tempster({:kind => :directory, :cd => true}.merge(opts), &block)
|
169
|
+
end
|
170
|
+
|
171
|
+
class Messager
|
172
|
+
def initialize(verbose, callback=nil, prefix="")
|
173
|
+
@verbose = verbose
|
174
|
+
@callback = callback
|
175
|
+
@prefix = prefix
|
176
|
+
end
|
177
|
+
|
178
|
+
def puts(message)
|
179
|
+
if @callback
|
180
|
+
@callback.call(@prefix+message)
|
181
|
+
else
|
182
|
+
if @verbose
|
183
|
+
STDOUT.puts @prefix+message
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
=begin
|
191
|
+
# Mostly recreated this in spec/integration/tempster_spec.rb
|
192
|
+
|
193
|
+
if __FILE__ == $0
|
194
|
+
# TODO Tempster -- write a spec
|
195
|
+
|
196
|
+
# Show a temp file created
|
197
|
+
x = Tempster.mktemp(:verbose => true)
|
198
|
+
File.stat(x)
|
199
|
+
File.unlink(x)
|
200
|
+
|
201
|
+
# Show a temp directory created
|
202
|
+
x = Tempster.mktempdir(:verbose => true)
|
203
|
+
File.directory?(x)
|
204
|
+
FileUtils.rm_r(x)
|
205
|
+
|
206
|
+
# Show a temp file created and removed with block
|
207
|
+
path = nil
|
208
|
+
Tempster.mktemp(:verbose => true) do |file|
|
209
|
+
path = file
|
210
|
+
puts file
|
211
|
+
end
|
212
|
+
begin
|
213
|
+
File.unlink(path)
|
214
|
+
raise "temporary file wasn't deleted when block ended: #{path}"
|
215
|
+
rescue Errno::ENOENT
|
216
|
+
# Expect the error
|
217
|
+
end
|
218
|
+
|
219
|
+
# Show temp directory created and removed with block
|
220
|
+
path = nil
|
221
|
+
Tempster.mktempdir(:verbose => true) do |dir|
|
222
|
+
puts dir
|
223
|
+
path = dir
|
224
|
+
puts File.directory?(path)
|
225
|
+
end
|
226
|
+
puts File.directory?(path)
|
227
|
+
|
228
|
+
# Show temp directory created and removed with block and cd
|
229
|
+
path = nil
|
230
|
+
Tempster.mktempdircd(:verbose => true) do |dir|
|
231
|
+
path = dir
|
232
|
+
puts "block's arg: "+dir
|
233
|
+
puts "block's pwd: "+Dir.pwd
|
234
|
+
puts "block's dir exists?: %s" % File.directory?(path)
|
235
|
+
end
|
236
|
+
puts "after dir exists?: %s" % File.directory?(path)
|
237
|
+
puts "after pwd: "+Dir.pwd
|
238
|
+
|
239
|
+
# Same with message callback
|
240
|
+
path = nil
|
241
|
+
Tempster.mktempdircd(:message_callback => lambda{|message| puts "$$$ "+message}) do |dir|
|
242
|
+
path = dir
|
243
|
+
puts "block's arg: "+dir
|
244
|
+
puts "block's pwd: "+Dir.pwd
|
245
|
+
puts "block's dir exists?: %s" % File.directory?(path)
|
246
|
+
end
|
247
|
+
puts "after dir exists?: %s" % File.directory?(path)
|
248
|
+
puts "after pwd: "+Dir.pwd
|
249
|
+
end
|
250
|
+
=end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
# http://svn.codehaus.org/jruby/trunk/jruby/bin/index_gem_repository.rb
|
3
|
+
#--
|
4
|
+
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
5
|
+
# All rights reserved.
|
6
|
+
# See LICENSE.txt for permissions.
|
7
|
+
#++
|
8
|
+
|
9
|
+
|
10
|
+
# Generate the yaml/yaml.Z index files for a gem server directory.
|
11
|
+
#
|
12
|
+
# Usage: generate_yaml_index.rb --dir DIR [--verbose]
|
13
|
+
|
14
|
+
$:.unshift '~/rubygems' if File.exist? "~/rubygems"
|
15
|
+
|
16
|
+
require 'optparse'
|
17
|
+
require 'rubygems'
|
18
|
+
require 'rubygems/format' #IK# Fix for RubyGems 0.9.5
|
19
|
+
require 'zlib'
|
20
|
+
require 'digest/sha2'
|
21
|
+
begin
|
22
|
+
require 'builder/xchar'
|
23
|
+
rescue LoadError
|
24
|
+
fail "index_gem_repository requires that the XML Builder library be installed"
|
25
|
+
end
|
26
|
+
|
27
|
+
Gem.manage_gems
|
28
|
+
|
29
|
+
######################################################################
|
30
|
+
# Mixin that provides a +compress+ method for compressing files on
|
31
|
+
# disk.
|
32
|
+
#
|
33
|
+
module Compressor
|
34
|
+
# Compress the given file.
|
35
|
+
def compress(filename, ext="rz")
|
36
|
+
File.open(filename + ".#{ext}", "w") do |file|
|
37
|
+
file.write(zip(File.read(filename)))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return a compressed version of the given string.
|
42
|
+
def zip(string)
|
43
|
+
Zlib::Deflate.deflate(string)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return an uncompressed version of a compressed string.
|
47
|
+
def unzip(string)
|
48
|
+
Zlib::Inflate.inflate(string)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
######################################################################
|
53
|
+
# Announcer provides a way of announcing activities to the user.
|
54
|
+
#
|
55
|
+
module Announcer
|
56
|
+
# Announce +msg+ to the user.
|
57
|
+
def announce(msg)
|
58
|
+
puts msg if @options[:verbose]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
######################################################################
|
63
|
+
# Abstract base class for building gem indicies. Uses the template
|
64
|
+
# pattern with subclass specialization in the +begin_index+,
|
65
|
+
# +end_index+ and +cleanup+ methods.
|
66
|
+
#
|
67
|
+
class AbstractIndexBuilder
|
68
|
+
include Compressor
|
69
|
+
include Announcer
|
70
|
+
|
71
|
+
# Build a Gem index. Yields to block to handle the details of the
|
72
|
+
# actual building. Calls +begin_index+, # +end_index+ and +cleanup+
|
73
|
+
# at appropriate times to customize basic operations.
|
74
|
+
def build
|
75
|
+
if ! @enabled
|
76
|
+
yield
|
77
|
+
else
|
78
|
+
unless File.exist?(@directory)
|
79
|
+
FileUtils.mkdir_p(@directory)
|
80
|
+
end
|
81
|
+
fail "not a directory: #{@directory}" unless File.directory?(@directory)
|
82
|
+
File.open(File.join(@directory, @filename), "w") do |file|
|
83
|
+
@file = file
|
84
|
+
start_index
|
85
|
+
yield
|
86
|
+
end_index
|
87
|
+
end
|
88
|
+
cleanup
|
89
|
+
end
|
90
|
+
ensure
|
91
|
+
@file = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
# Called immediately before the yield in build. The index file is
|
95
|
+
# open and availabe as @file.
|
96
|
+
def start_index
|
97
|
+
end
|
98
|
+
|
99
|
+
# Called immediately after the yield in build. The index file is
|
100
|
+
# still open and available as @file.
|
101
|
+
def end_index
|
102
|
+
end
|
103
|
+
|
104
|
+
# Called from within builder after the index file has been closed.
|
105
|
+
def cleanup
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
######################################################################
|
110
|
+
# Construct the master Gem index file.
|
111
|
+
#
|
112
|
+
class MasterIndexBuilder < AbstractIndexBuilder
|
113
|
+
def initialize(filename, options)
|
114
|
+
@filename = filename
|
115
|
+
@options = options
|
116
|
+
@directory = options[:directory]
|
117
|
+
@enabled = true
|
118
|
+
end
|
119
|
+
|
120
|
+
def start_index
|
121
|
+
super
|
122
|
+
@file.puts "--- !ruby/object:Gem::Cache"
|
123
|
+
@file.puts "gems:"
|
124
|
+
end
|
125
|
+
|
126
|
+
def cleanup
|
127
|
+
super
|
128
|
+
index_file_name = File.join(@directory, @filename)
|
129
|
+
compress(index_file_name, "Z")
|
130
|
+
paranoid(index_file_name, "#{index_file_name}.Z")
|
131
|
+
end
|
132
|
+
|
133
|
+
def add(spec)
|
134
|
+
@file.puts " #{spec.full_name}: #{nest(spec.to_yaml)}"
|
135
|
+
end
|
136
|
+
|
137
|
+
def nest(yaml_string)
|
138
|
+
yaml_string[4..-1].gsub(/\n/, "\n ")
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def paranoid(fn, compressed_fn)
|
144
|
+
data = File.read(fn)
|
145
|
+
compressed_data = File.read(compressed_fn)
|
146
|
+
if data != unzip(compressed_data)
|
147
|
+
fail "Compressed file #{compressed_fn} does not match uncompressed file #{fn}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
######################################################################
|
153
|
+
# Construct a quick index file and all of the individual specs to
|
154
|
+
# support incremental loading.
|
155
|
+
#
|
156
|
+
class QuickIndexBuilder < AbstractIndexBuilder
|
157
|
+
def initialize(filename, options)
|
158
|
+
@filename = filename
|
159
|
+
@options = options
|
160
|
+
@directory = options[:quick_directory]
|
161
|
+
@enabled = options[:quick]
|
162
|
+
end
|
163
|
+
|
164
|
+
def cleanup
|
165
|
+
compress(File.join(@directory, @filename))
|
166
|
+
end
|
167
|
+
|
168
|
+
def add(spec)
|
169
|
+
return unless @enabled
|
170
|
+
@file.puts spec.full_name
|
171
|
+
fn = File.join(@directory, "#{spec.full_name}.gemspec.rz")
|
172
|
+
File.open(fn, "w") do |gsfile|
|
173
|
+
gsfile.write(zip(spec.to_yaml))
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
######################################################################
|
179
|
+
# Top level class for building the repository index. Initialize with
|
180
|
+
# an options hash and call +build_index+.
|
181
|
+
#
|
182
|
+
class Indexer
|
183
|
+
include Compressor
|
184
|
+
include Announcer
|
185
|
+
|
186
|
+
# Create an indexer with the options specified by the options hash.
|
187
|
+
def initialize(options)
|
188
|
+
@options = options.dup
|
189
|
+
@directory = @options[:directory]
|
190
|
+
@options[:quick_directory] = File.join(@directory, "quick")
|
191
|
+
@master_index = MasterIndexBuilder.new("yaml", @options)
|
192
|
+
@quick_index = QuickIndexBuilder.new("index", @options)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Build the index.
|
196
|
+
def build_index
|
197
|
+
announce "Building Server Index"
|
198
|
+
FileUtils.rm_r(@options[:quick_directory]) rescue nil
|
199
|
+
@master_index.build do
|
200
|
+
@quick_index.build do
|
201
|
+
gem_file_list.each do |gemfile|
|
202
|
+
spec = Gem::Format.from_file_by_path(gemfile).spec
|
203
|
+
abbreviate(spec)
|
204
|
+
sanitize(spec)
|
205
|
+
announce " ... adding #{spec.full_name}"
|
206
|
+
@master_index.add(spec)
|
207
|
+
@quick_index.add(spec)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# List of gem file names to index.
|
214
|
+
def gem_file_list
|
215
|
+
Dir.glob(File.join(@directory, "gems", "*.gem"))
|
216
|
+
end
|
217
|
+
|
218
|
+
# Abbreviate the spec for downloading. Abbreviated specs are only
|
219
|
+
# used for searching, downloading and related activities and do not
|
220
|
+
# need deployment specific information (e.g. list of files). So we
|
221
|
+
# abbreviate the spec, making it much smaller for quicker downloads.
|
222
|
+
def abbreviate(spec)
|
223
|
+
spec.files = []
|
224
|
+
spec.test_files = []
|
225
|
+
spec.rdoc_options = []
|
226
|
+
spec.extra_rdoc_files = []
|
227
|
+
spec.cert_chain = []
|
228
|
+
spec
|
229
|
+
end
|
230
|
+
|
231
|
+
# Sanitize the descriptive fields in the spec. Sometimes non-ASCII
|
232
|
+
# characters will garble the site index. Non-ASCII characters will
|
233
|
+
# be replaced by their XML entity equivalent.
|
234
|
+
def sanitize(spec)
|
235
|
+
spec.summary = sanitize_string(spec.summary)
|
236
|
+
spec.description = sanitize_string(spec.description)
|
237
|
+
spec.post_install_message = sanitize_string(spec.post_install_message)
|
238
|
+
spec.authors = spec.authors.collect { |a| sanitize_string(a) }
|
239
|
+
spec
|
240
|
+
end
|
241
|
+
|
242
|
+
# Sanitize a single string.
|
243
|
+
def sanitize_string(string)
|
244
|
+
string ? string.to_xs : string
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
######################################################################
|
249
|
+
# Top Level Functions
|
250
|
+
######################################################################
|
251
|
+
|
252
|
+
def handle_options(args)
|
253
|
+
# default options
|
254
|
+
options = {
|
255
|
+
:directory => '.',
|
256
|
+
:verbose => false,
|
257
|
+
:quick => true,
|
258
|
+
}
|
259
|
+
|
260
|
+
args.options do |opts|
|
261
|
+
opts.on_tail("--help", "show this message") do
|
262
|
+
puts opts
|
263
|
+
exit
|
264
|
+
end
|
265
|
+
opts.on(
|
266
|
+
'-d', '--dir=DIRNAME', '--directory=DIRNAME',
|
267
|
+
"repository base dir containing gems subdir",
|
268
|
+
String) do |value|
|
269
|
+
options[:directory] = value
|
270
|
+
end
|
271
|
+
opts.on('--[no-]quick', "include quick index") do |value|
|
272
|
+
options[:quick] = value
|
273
|
+
end
|
274
|
+
opts.on('-v', '--verbose', "show verbose output") do |value|
|
275
|
+
options[:verbose] = value
|
276
|
+
end
|
277
|
+
opts.on('-V', '--version',
|
278
|
+
"show version") do |value|
|
279
|
+
puts Gem::RubyGemsVersion
|
280
|
+
exit
|
281
|
+
end
|
282
|
+
opts.parse!
|
283
|
+
end
|
284
|
+
|
285
|
+
if options[:directory].nil?
|
286
|
+
puts "Error, must specify directory name. Use --help"
|
287
|
+
exit
|
288
|
+
elsif ! File.exist?(options[:directory]) ||
|
289
|
+
! File.directory?(options[:directory])
|
290
|
+
puts "Error, unknown directory name #{directory}."
|
291
|
+
exit
|
292
|
+
end
|
293
|
+
options
|
294
|
+
end
|
295
|
+
|
296
|
+
# Main program.
|
297
|
+
def main_index(args)
|
298
|
+
options = handle_options(args)
|
299
|
+
Indexer.new(options).build_index
|
300
|
+
end
|
301
|
+
|
302
|
+
if __FILE__ == $0 then
|
303
|
+
main_index(ARGV)
|
304
|
+
end
|