rubycut-babushka 0.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +8 -0
- data/Gemfile.lock +31 -0
- data/README.markdown +246 -0
- data/Rakefile +26 -0
- data/bin/babushka +11 -0
- data/deps/babushka.rb +101 -0
- data/deps/dev.rb +12 -0
- data/deps/fhs.rb +31 -0
- data/deps/git.rb +29 -0
- data/deps/homebrew.rb +30 -0
- data/deps/os_x.rb +33 -0
- data/deps/packages.rb +22 -0
- data/deps/pkg_managers.rb +110 -0
- data/deps/ruby.rb +23 -0
- data/deps/rubygems.rb +24 -0
- data/deps/system.rb +10 -0
- data/deps/templates/app.rb +68 -0
- data/deps/templates/external.rb +12 -0
- data/deps/templates/installer.rb +31 -0
- data/deps/templates/managed.rb +105 -0
- data/deps/templates/ppa.rb +24 -0
- data/deps/templates/src.rb +42 -0
- data/deps/templates/tmbundle.rb +15 -0
- data/lib/babushka.rb +28 -0
- data/lib/babushka/accepts_block_for.rb +72 -0
- data/lib/babushka/accepts_list_for.rb +49 -0
- data/lib/babushka/accepts_value_for.rb +24 -0
- data/lib/babushka/base.rb +78 -0
- data/lib/babushka/bug_reporter.rb +55 -0
- data/lib/babushka/cmdline.rb +133 -0
- data/lib/babushka/cmdline/handler.rb +41 -0
- data/lib/babushka/cmdline/helpers.rb +127 -0
- data/lib/babushka/cmdline/parser.rb +69 -0
- data/lib/babushka/colorizer.rb +59 -0
- data/lib/babushka/core_patches/array.rb +171 -0
- data/lib/babushka/core_patches/blank.rb +22 -0
- data/lib/babushka/core_patches/bytes.rb +52 -0
- data/lib/babushka/core_patches/hash.rb +107 -0
- data/lib/babushka/core_patches/hashish.rb +14 -0
- data/lib/babushka/core_patches/integer.rb +25 -0
- data/lib/babushka/core_patches/io.rb +8 -0
- data/lib/babushka/core_patches/numeric.rb +16 -0
- data/lib/babushka/core_patches/object.rb +27 -0
- data/lib/babushka/core_patches/string.rb +116 -0
- data/lib/babushka/core_patches/symbol.rb +12 -0
- data/lib/babushka/core_patches/try.rb +15 -0
- data/lib/babushka/core_patches/uri.rb +24 -0
- data/lib/babushka/dep.rb +470 -0
- data/lib/babushka/dep_context.rb +18 -0
- data/lib/babushka/dep_definer.rb +115 -0
- data/lib/babushka/dep_pool.rb +49 -0
- data/lib/babushka/dep_runner.rb +85 -0
- data/lib/babushka/dsl.rb +26 -0
- data/lib/babushka/git_repo.rb +185 -0
- data/lib/babushka/helpers/git_helpers.rb +32 -0
- data/lib/babushka/helpers/log_helpers.rb +176 -0
- data/lib/babushka/helpers/path_helpers.rb +34 -0
- data/lib/babushka/helpers/run_helpers.rb +145 -0
- data/lib/babushka/helpers/shell_helpers.rb +229 -0
- data/lib/babushka/helpers/suggest_helpers.rb +16 -0
- data/lib/babushka/helpers/uri_helpers.rb +36 -0
- data/lib/babushka/ip.rb +160 -0
- data/lib/babushka/lambda_chooser.rb +40 -0
- data/lib/babushka/levenshtein.rb +125 -0
- data/lib/babushka/meta_dep.rb +65 -0
- data/lib/babushka/meta_dep_context.rb +15 -0
- data/lib/babushka/parameter.rb +143 -0
- data/lib/babushka/pkg_helper.rb +81 -0
- data/lib/babushka/pkg_helpers/apt_helper.rb +61 -0
- data/lib/babushka/pkg_helpers/base_helper.rb +19 -0
- data/lib/babushka/pkg_helpers/binpkgsrc_helper.rb +48 -0
- data/lib/babushka/pkg_helpers/binports_helper.rb +34 -0
- data/lib/babushka/pkg_helpers/brew_helper.rb +110 -0
- data/lib/babushka/pkg_helpers/gem_helper.rb +120 -0
- data/lib/babushka/pkg_helpers/macports_helper.rb +22 -0
- data/lib/babushka/pkg_helpers/npm_helper.rb +45 -0
- data/lib/babushka/pkg_helpers/pacman_helper.rb +27 -0
- data/lib/babushka/pkg_helpers/pip_helper.rb +45 -0
- data/lib/babushka/pkg_helpers/src_helper.rb +16 -0
- data/lib/babushka/pkg_helpers/yum_helper.rb +25 -0
- data/lib/babushka/popen.rb +40 -0
- data/lib/babushka/prompt.rb +176 -0
- data/lib/babushka/renderable.rb +67 -0
- data/lib/babushka/resource.rb +215 -0
- data/lib/babushka/run_reporter.rb +60 -0
- data/lib/babushka/shell.rb +108 -0
- data/lib/babushka/source.rb +216 -0
- data/lib/babushka/source_pool.rb +146 -0
- data/lib/babushka/system_definitions.rb +97 -0
- data/lib/babushka/system_profile.rb +210 -0
- data/lib/babushka/task.rb +142 -0
- data/lib/babushka/vars.rb +108 -0
- data/lib/babushka/version_of.rb +65 -0
- data/lib/babushka/version_str.rb +57 -0
- data/lib/babushka/xml_string.rb +28 -0
- data/lib/components.rb +82 -0
- data/lib/fancypath/fancypath.rb +200 -0
- data/lib/inkan/inkan.rb +76 -0
- data/spec/acceptance/acceptance.rb +43 -0
- data/spec/acceptance_helper.rb +113 -0
- data/spec/archives/Blah.app.zip +0 -0
- data/spec/archives/archive.tar +0 -0
- data/spec/archives/archive.tar.bz2 +0 -0
- data/spec/archives/archive.tar.gz +0 -0
- data/spec/archives/archive.tbz2 +0 -0
- data/spec/archives/archive.tgz +0 -0
- data/spec/archives/archive.zip +0 -0
- data/spec/archives/content.txt +5 -0
- data/spec/archives/invalid_archive +5 -0
- data/spec/archives/nested archive/content.txt +5 -0
- data/spec/archives/nested_archive.tar +0 -0
- data/spec/archives/really_a_gzip.zip +0 -0
- data/spec/archives/test-0.3.1.tgz +0 -0
- data/spec/archives/tgz_archive +0 -0
- data/spec/archives/zip_without_extension +0 -0
- data/spec/babushka/accepts_for_spec.rb +174 -0
- data/spec/babushka/accepts_for_support.rb +72 -0
- data/spec/babushka/cmdline/console_spec.rb +11 -0
- data/spec/babushka/cmdline/help_spec.rb +61 -0
- data/spec/babushka/cmdline/version_spec.rb +10 -0
- data/spec/babushka/core_patches_spec.rb +171 -0
- data/spec/babushka/dep_context_spec.rb +58 -0
- data/spec/babushka/dep_definer_spec.rb +152 -0
- data/spec/babushka/dep_definer_support.rb +36 -0
- data/spec/babushka/dep_spec.rb +567 -0
- data/spec/babushka/dep_support.rb +29 -0
- data/spec/babushka/deps_spec.rb +113 -0
- data/spec/babushka/gem_helper_spec.rb +90 -0
- data/spec/babushka/git_repo_spec.rb +396 -0
- data/spec/babushka/ip_spec.rb +131 -0
- data/spec/babushka/lambda_chooser_spec.rb +115 -0
- data/spec/babushka/meta_dep_definer_spec.rb +127 -0
- data/spec/babushka/meta_dep_wrapper_spec.rb +32 -0
- data/spec/babushka/parameter_spec.rb +135 -0
- data/spec/babushka/path_helpers_spec.rb +102 -0
- data/spec/babushka/prompt_spec.rb +188 -0
- data/spec/babushka/renderable_spec.rb +100 -0
- data/spec/babushka/resource_spec.rb +141 -0
- data/spec/babushka/run_helpers_spec.rb +26 -0
- data/spec/babushka/shell_helpers_spec.rb +244 -0
- data/spec/babushka/shell_spec.rb +19 -0
- data/spec/babushka/source_pool_spec.rb +320 -0
- data/spec/babushka/source_pool_support.rb +31 -0
- data/spec/babushka/source_spec.rb +382 -0
- data/spec/babushka/source_support.rb +17 -0
- data/spec/babushka/system_profile_spec.rb +61 -0
- data/spec/babushka/task_spec.rb +141 -0
- data/spec/babushka/uri_spec.rb +13 -0
- data/spec/babushka/vars_spec.rb +59 -0
- data/spec/babushka/version_of_spec.rb +110 -0
- data/spec/babushka/version_str_spec.rb +130 -0
- data/spec/babushka/version_str_support.rb +37 -0
- data/spec/babushka/xml_string_spec.rb +98 -0
- data/spec/deps/bad/broken.rb +7 -0
- data/spec/deps/bad/working.rb +3 -0
- data/spec/deps/good/meta.rb +14 -0
- data/spec/deps/good/test.rb +11 -0
- data/spec/deps/outer/deps.rb +19 -0
- data/spec/deps/outer/more deps.rb +11 -0
- data/spec/deps/params/params.rb +10 -0
- data/spec/fancypath/fancypath_spec.rb +272 -0
- data/spec/fancypath_support.rb +10 -0
- data/spec/inkan/inkan_spec.rb +217 -0
- data/spec/renderable/different_example.conf.erb +4 -0
- data/spec/renderable/example.conf.erb +3 -0
- data/spec/renderable/example.sh +6 -0
- data/spec/renderable/with_binding.conf.erb +4 -0
- data/spec/renderable/xml_example.conf.erb +8 -0
- data/spec/repos/remote.git.tgz +0 -0
- data/spec/spec_helper.rb +87 -0
- metadata +238 -0
data/lib/babushka/dep.rb
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
module Babushka
|
|
2
|
+
|
|
3
|
+
class UnmeetableDep < RuntimeError
|
|
4
|
+
end
|
|
5
|
+
class DepDefinitionError < ArgumentError
|
|
6
|
+
end
|
|
7
|
+
class InvalidDepName < DepDefinitionError
|
|
8
|
+
end
|
|
9
|
+
class TemplateNotFound < DepDefinitionError
|
|
10
|
+
end
|
|
11
|
+
class DepParameterError < DepDefinitionError
|
|
12
|
+
end
|
|
13
|
+
class DepArgumentError < DepDefinitionError
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Dep
|
|
17
|
+
include LogHelpers
|
|
18
|
+
extend LogHelpers
|
|
19
|
+
include PathHelpers
|
|
20
|
+
extend SuggestHelpers
|
|
21
|
+
|
|
22
|
+
# This class is used for deps that aren't defined against a meta dep. Using
|
|
23
|
+
# this class with the default values it contains means that the code below
|
|
24
|
+
# can be simpler, because at the code level everything is defined against
|
|
25
|
+
# a 'template' of some sort; some are just BaseTemplate, and some are
|
|
26
|
+
# actual meta deps.
|
|
27
|
+
class BaseTemplate
|
|
28
|
+
def self.contextual_name; name end
|
|
29
|
+
def self.suffixed?; false end
|
|
30
|
+
def self.context_class; DepContext end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# A Requirement is a representation of a dep being called - its name, along
|
|
34
|
+
# with the arguments that will be passed to it.
|
|
35
|
+
#
|
|
36
|
+
# Requirement is used internally by babushka when deps are required with
|
|
37
|
+
# arguments using "name".with(args). This allows babushka to delay loading
|
|
38
|
+
# the dep in question until the moment it's called.
|
|
39
|
+
class Requirement < Struct.new(:name, :args)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
attr_reader :name, :params, :args, :opts, :vars, :dep_source, :load_path
|
|
43
|
+
attr_accessor :result_message
|
|
44
|
+
|
|
45
|
+
# Create a new dep named +name+ within +source+, whose implementation is
|
|
46
|
+
# found in +block+. To define deps yourself, you should call +dep+ (which
|
|
47
|
+
# is +Dep::Helpers#dep+).
|
|
48
|
+
def initialize name, source, params, opts, block
|
|
49
|
+
if name.empty?
|
|
50
|
+
raise InvalidDepName, "Deps can't have empty names."
|
|
51
|
+
elsif /\A[[:print:]]+\z/i !~ name
|
|
52
|
+
raise InvalidDepName, "The dep name '#{name}' contains nonprintable characters."
|
|
53
|
+
elsif /\// =~ name
|
|
54
|
+
raise InvalidDepName, "The dep name '#{name}' contains '/', which isn't allowed (logs are named after deps, and filenames can't contain '/')."
|
|
55
|
+
elsif /\:/ =~ name
|
|
56
|
+
raise InvalidDepName, "The dep name '#{name}' contains ':', which isn't allowed (colons separate dep and template names from source prefixes)."
|
|
57
|
+
elsif !params.all? {|param| param.is_a?(Symbol) }
|
|
58
|
+
non_symbol_params = params.reject {|p| p.is_a?(Symbol) }
|
|
59
|
+
raise DepParameterError, "The dep '#{name}' has #{'a ' if non_symbol_params.length == 1}non-symbol param#{'s' if non_symbol_params.length > 1} #{non_symbol_params.map(&:inspect).to_list}, which #{non_symbol_params.length == 1 ? "isn't" : "aren't"} allowed."
|
|
60
|
+
else
|
|
61
|
+
@name = name.to_s
|
|
62
|
+
@params = params
|
|
63
|
+
@args = {}
|
|
64
|
+
@opts = Base.sources.current_load_opts.merge(opts)
|
|
65
|
+
@block = block
|
|
66
|
+
@dep_source = source
|
|
67
|
+
@load_path = Base.sources.current_load_path
|
|
68
|
+
@dep_source.deps.register self
|
|
69
|
+
assign_template if Base.sources.current_real_load_source.nil?
|
|
70
|
+
@dep_defined = @_cached_process = nil # false represents failure for these two.
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def context
|
|
75
|
+
define! if @context.nil?
|
|
76
|
+
@context
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def template
|
|
80
|
+
assign_template if @template.nil?
|
|
81
|
+
@template
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns true if +#define!+ has aready successfully run on this dep.
|
|
85
|
+
def dep_defined?
|
|
86
|
+
@dep_defined
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Look up the dep specified by +dep_name+, yielding it to the block if it
|
|
90
|
+
# was found.
|
|
91
|
+
#
|
|
92
|
+
# If no such dep exists, search for other similarly spelt deps and re-call
|
|
93
|
+
# this same method on the one chosen by the user, if any.
|
|
94
|
+
def self.find_or_suggest dep_name, opts = {}, &block
|
|
95
|
+
if (dep = Dep(dep_name, opts)).nil?
|
|
96
|
+
log "#{dep_name.to_s.colorize 'grey'} #{"<- this dep isn't defined!".colorize('red')}"
|
|
97
|
+
suggestion = suggest_value_for(dep_name, Base.sources.current_names)
|
|
98
|
+
Dep.find_or_suggest suggestion, opts, &block unless suggestion.nil?
|
|
99
|
+
elsif block.nil?
|
|
100
|
+
dep
|
|
101
|
+
else
|
|
102
|
+
block.call dep
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Returns this dep's name, including the source name as a prefix if this
|
|
107
|
+
# dep is in a cloneable source.
|
|
108
|
+
#
|
|
109
|
+
# A cloneable source is one that babushka knows how to automatically
|
|
110
|
+
# update; i.e. a source that babushka could have installed itself.
|
|
111
|
+
#
|
|
112
|
+
# In effect, a cloneable source is one whose deps you prefix when you run
|
|
113
|
+
# them, so this method returns the dep's name in the same form as you would
|
|
114
|
+
# refer to it on the commandline or within a +require+ call in another dep.
|
|
115
|
+
def contextual_name
|
|
116
|
+
dep_source.cloneable? ? "#{dep_source.name}:#{name}" : name
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Return this dep's name, first removing the template suffix if one is
|
|
120
|
+
# present.
|
|
121
|
+
#
|
|
122
|
+
# Note that this only removes the suffix when it was used to define the
|
|
123
|
+
# dep. Dep names that end in something that looks like a template suffix,
|
|
124
|
+
# but didn't match a template and result in a templated dep, won't be
|
|
125
|
+
# touched.
|
|
126
|
+
#
|
|
127
|
+
# Some examples:
|
|
128
|
+
# Dep('benhoskings:Chromium.app').basename #=> 'Chromium'
|
|
129
|
+
# Dep('generated report.pdf').basename #=> "generated report.pdf"
|
|
130
|
+
def basename
|
|
131
|
+
suffixed? ? name.sub(/\.#{Regexp.escape(template.name)}$/, '') : name
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Returns the portion of the end of the dep name that looks like a template
|
|
135
|
+
# suffix, if any. Unlike +#basename+, this method will return anything that
|
|
136
|
+
# looks like a template suffix, even if it doesn't match a template.
|
|
137
|
+
def suffix
|
|
138
|
+
name.scan(MetaDep::TEMPLATE_NAME_MATCH).flatten.first
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def with *args
|
|
142
|
+
@args = if args.map(&:class) == [Hash]
|
|
143
|
+
parse_named_arguments(args.first)
|
|
144
|
+
else
|
|
145
|
+
parse_positional_arguments(args)
|
|
146
|
+
end.map_values {|k,v|
|
|
147
|
+
Parameter.for(k, v)
|
|
148
|
+
}
|
|
149
|
+
self
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Entry point for a dry +#process+ run, where only +met?+ blocks will be
|
|
153
|
+
# evaluated.
|
|
154
|
+
#
|
|
155
|
+
# This is useful to inspect the current state of a dep tree, without
|
|
156
|
+
# altering the system. It can cause failures, though, because some deps
|
|
157
|
+
# have requirements that need to be met before the dep can perform its
|
|
158
|
+
# +met?+ check.
|
|
159
|
+
#
|
|
160
|
+
# TODO: In future, there will be support for specifying that in the DSL.
|
|
161
|
+
def met? *args
|
|
162
|
+
with(*args).process :dry_run => true, :top_level => true
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Entry point for a full met?/meet +#process+ run.
|
|
166
|
+
def meet *args
|
|
167
|
+
with(*args).process :dry_run => false, :top_level => true
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Trigger a dep run with this dep at the top of the tree.
|
|
171
|
+
#
|
|
172
|
+
# Running the dep involves the following:
|
|
173
|
+
# - First, the +setup+ block is run.
|
|
174
|
+
# - Next, the dep's dependencies (i.e. the contents of +requires+) are
|
|
175
|
+
# run recursively by calling +#process+ on each; this dep's +#process+
|
|
176
|
+
# early-exits if any of the subdeps fail.
|
|
177
|
+
# - Next, the +met?+ block is run. If +met?+ returns +true+, or any
|
|
178
|
+
# true-like value, the dep is already met and there is nothing to do.
|
|
179
|
+
# Otherwise, the dep is unmet, and the following happens:
|
|
180
|
+
# - The +prepare+ task is run
|
|
181
|
+
# - The +before+ task is run
|
|
182
|
+
# - If +before+ returned a true-like value, the +meet+ task is run.
|
|
183
|
+
# This is where the actual work of achieving the dep's aim is done.
|
|
184
|
+
# - If +meet+ returned a true-like value, the +after+ task is run.
|
|
185
|
+
# - Finally, the +met?+ task is run again, to check whether running
|
|
186
|
+
# +meet+ has achieved the dep's goal.
|
|
187
|
+
#
|
|
188
|
+
# The final step is important to understand. The +meet+ block is run
|
|
189
|
+
# unconditionally, and its return value is ignored, apart from it
|
|
190
|
+
# determining whether to run the +after+ block. The result of a dep is
|
|
191
|
+
# always taken from its +met?+ block, whether it was already met,
|
|
192
|
+
# unmeetable, or met during the run.
|
|
193
|
+
#
|
|
194
|
+
# Sometimes there are conditions under which a dep can't be met. For
|
|
195
|
+
# example, if a dep detects that the existing version of a package is
|
|
196
|
+
# broken in some way that requires manual intervention, then there's no
|
|
197
|
+
# use running the +meet+ block. In this circumstance, you can call
|
|
198
|
+
# +#unmeetable+, which raises an +UnmeetableDep+ exception. Babushka will
|
|
199
|
+
# rescue it and consider the dep unmeetable (that is, it will just allow
|
|
200
|
+
# the dep to fail without attempting to meet it).
|
|
201
|
+
#
|
|
202
|
+
# The following describes the return values of a few components, and of
|
|
203
|
+
# the dep itself.
|
|
204
|
+
# - A '-' means the corresponding block wouldn't be run at all.
|
|
205
|
+
# - An 'X' means the corresponding return value doesn't matter, and is
|
|
206
|
+
# discarded.
|
|
207
|
+
#
|
|
208
|
+
# Initial state | initial met? | meet | subsequent met? | dep returns
|
|
209
|
+
# ----------------+----------------------+-------+-----------------+------------
|
|
210
|
+
# already met | true | - | - | true
|
|
211
|
+
# unmeetable | UnmeetableDep raised | - | - | false
|
|
212
|
+
# couldn't be met | false | X | false | false
|
|
213
|
+
# met during run | false | X | true | true
|
|
214
|
+
#
|
|
215
|
+
# Wherever possible, the +met?+ test shouldn't directly test that the
|
|
216
|
+
# +meet+ block performed specific tasks; only that its overall purpose has
|
|
217
|
+
# been achieved. For example, if the purpose of a given dep is to make sure
|
|
218
|
+
# the webserver is running, the contents of the +meet+ block would probably
|
|
219
|
+
# involve `/etc/init.d/nginx start` or similar, on a Linux system at least.
|
|
220
|
+
# In this case, the +met?+ block shouldn't test anything involving
|
|
221
|
+
# `/etc/init.d` directly; instead, it should separately test that the
|
|
222
|
+
# webserver is running, for example by using `netstat` to check that
|
|
223
|
+
# something is listening on port 80.
|
|
224
|
+
def process with_opts = {}
|
|
225
|
+
task.opts.update with_opts
|
|
226
|
+
(cached? ? cached_result : process_and_cache).tap {
|
|
227
|
+
Base.sources.uncache! if with_opts[:top_level]
|
|
228
|
+
}
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
private
|
|
232
|
+
|
|
233
|
+
def define_or_complain!
|
|
234
|
+
@dep_defined = begin
|
|
235
|
+
define!
|
|
236
|
+
rescue StandardError => e
|
|
237
|
+
log_exception_in_dep(e)
|
|
238
|
+
false
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Attempt to look up the template this dep was defined against (or if no
|
|
243
|
+
# template was specified, BaseTemplate), and then define the dep against
|
|
244
|
+
# it. If an error occurs, the backtrace point within the dep from which the
|
|
245
|
+
# exception was triggered is logged, as well as the actual exception point.
|
|
246
|
+
def define!
|
|
247
|
+
if dep_defined?
|
|
248
|
+
debug "#{name}: already defined."
|
|
249
|
+
elsif dep_defined? == false
|
|
250
|
+
debug "#{name}: defining already failed."
|
|
251
|
+
elsif template
|
|
252
|
+
debug "(defining #{name} against #{template.contextual_name})"
|
|
253
|
+
define_dep!
|
|
254
|
+
end
|
|
255
|
+
dep_defined?
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Create a context for this dep from its template, and then process the
|
|
259
|
+
# dep's outer block in that context.
|
|
260
|
+
#
|
|
261
|
+
# This results in the details of the dep being stored, like the
|
|
262
|
+
# implementation of +met?+ and +meet+, as well as its +requires+ list and
|
|
263
|
+
# any other items defined at the top level.
|
|
264
|
+
def define_dep!
|
|
265
|
+
@context = template.context_class.new self, &@block
|
|
266
|
+
context.define!
|
|
267
|
+
@dep_defined = true
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Attempt to retrieve the template specified in +opts[:template]+. If the
|
|
271
|
+
# template name includes a source prefix, it is searched for within the
|
|
272
|
+
# corresponding source. Otherwise, it is searched for in the current source
|
|
273
|
+
# and the core sources.
|
|
274
|
+
def assign_template
|
|
275
|
+
@template = if opts[:template]
|
|
276
|
+
Base.sources.template_for(opts[:template], :from => dep_source).tap {|t|
|
|
277
|
+
raise TemplateNotFound, "There is no template named '#{opts[:template]}' to define '#{name}' against." if t.nil?
|
|
278
|
+
}
|
|
279
|
+
else
|
|
280
|
+
Base.sources.template_for(suffix, :from => dep_source) || self.class.base_template
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def self.base_template
|
|
285
|
+
BaseTemplate
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def parse_named_arguments args
|
|
289
|
+
if (non_symbol = args.keys.reject {|key| key.is_a?(Symbol) }).any?
|
|
290
|
+
# We sort here so we can spec the exception message across different rubies.
|
|
291
|
+
non_symbol = non_symbol.sort_by(&:to_s)
|
|
292
|
+
raise DepArgumentError, "The dep '#{name}' received #{'a ' if non_symbol.length == 1}non-symbol argument#{'s' if non_symbol.length > 1} #{non_symbol.map(&:inspect).to_list}."
|
|
293
|
+
elsif (unexpected = args.keys - params).any?
|
|
294
|
+
unexpected = unexpected.sort_by(&:to_s)
|
|
295
|
+
raise DepArgumentError, "The dep '#{name}' received #{'an ' if unexpected.length == 1}unexpected argument#{'s' if unexpected.length > 1} #{unexpected.map(&:inspect).to_list}."
|
|
296
|
+
end
|
|
297
|
+
args
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def parse_positional_arguments args
|
|
301
|
+
if !args.empty? && args.length != params.length
|
|
302
|
+
raise DepArgumentError, "The dep '#{name}' accepts #{params.length} argument#{'s' unless params.length == 1}, but #{args.length} #{args.length == 1 ? 'was' : 'were'} passed."
|
|
303
|
+
end
|
|
304
|
+
params.inject({}) {|hsh,param| hsh[param] = args.shift; hsh }
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def process_and_cache
|
|
308
|
+
log logging_name, :closing_status => (task.opt(:dry_run) ? :dry_run : true) do
|
|
309
|
+
if dep_defined? == false
|
|
310
|
+
# Only log about define errors if the define previously failed...
|
|
311
|
+
log_error "This dep isn't defined. Perhaps there was a load error?"
|
|
312
|
+
elsif !define_or_complain!
|
|
313
|
+
# ... not if it failed as part of this process, since that should log anyway.
|
|
314
|
+
elsif task.callstack.include? self
|
|
315
|
+
log_error "Oh crap, endless loop! (#{task.callstack.push(self).drop_while {|dep| dep != self }.map(&:name).join(' -> ')})"
|
|
316
|
+
elsif !opts[:for].nil? && !Base.host.matches?(opts[:for])
|
|
317
|
+
log_ok "Not required on #{Base.host.differentiator_for opts[:for]}."
|
|
318
|
+
else
|
|
319
|
+
task.callstack.push self
|
|
320
|
+
process_this_dep.tap {
|
|
321
|
+
task.callstack.pop
|
|
322
|
+
}
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def process_this_dep
|
|
328
|
+
process_task(:setup)
|
|
329
|
+
process_deps and process_self
|
|
330
|
+
rescue UnmeetableDep => e
|
|
331
|
+
log_error e.message
|
|
332
|
+
log "I don't know how to fix that, so it's up to you. :)"
|
|
333
|
+
nil
|
|
334
|
+
rescue StandardError => e
|
|
335
|
+
log_exception_in_dep e
|
|
336
|
+
Base.task.reportable = e.is_a?(DepDefinitionError)
|
|
337
|
+
nil
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def process_deps accessor = :requires
|
|
341
|
+
requirements_for(accessor).send(task.opt(:dry_run) ? :each : :all?) do |requirement|
|
|
342
|
+
Dep.find_or_suggest requirement.name, :from => dep_source do |dep|
|
|
343
|
+
dep.with(*requirement.args).process
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def process_self
|
|
349
|
+
cd context.run_in do
|
|
350
|
+
process_met_task(:initial => true) {
|
|
351
|
+
if task.opt(:dry_run)
|
|
352
|
+
false # unmet
|
|
353
|
+
else
|
|
354
|
+
process_task(:prepare)
|
|
355
|
+
if !process_deps(:requires_when_unmet)
|
|
356
|
+
false # install-time deps unmet
|
|
357
|
+
else
|
|
358
|
+
log 'meet' do
|
|
359
|
+
process_task(:before) and process_task(:meet) and process_task(:after)
|
|
360
|
+
end
|
|
361
|
+
process_met_task
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
}
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def process_met_task task_opts = {}, &block
|
|
369
|
+
# Explicitly return false to distinguish unmet deps from failed
|
|
370
|
+
# ones -- those return nil.
|
|
371
|
+
run_met_task(task_opts) || block.try(:call) || false
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def run_met_task task_opts = {}
|
|
375
|
+
cache_process(process_task(:met?)).tap {|result|
|
|
376
|
+
log result_message, :as => (:error unless result || task_opts[:initial]) unless result_message.nil?
|
|
377
|
+
self.result_message = nil
|
|
378
|
+
}
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def process_task task_name
|
|
382
|
+
# log "calling #{name} / #{task_name}"
|
|
383
|
+
track_block_for(task_name) if Base.task.opt(:track_blocks)
|
|
384
|
+
context.instance_eval(&context.send(task_name))
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def requirements_for list_name
|
|
388
|
+
context.send(list_name).map {|dep_or_requirement|
|
|
389
|
+
if dep_or_requirement.is_a?(Requirement)
|
|
390
|
+
dep_or_requirement
|
|
391
|
+
else
|
|
392
|
+
Requirement.new(dep_or_requirement, [])
|
|
393
|
+
end
|
|
394
|
+
}
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def logging_name
|
|
398
|
+
if Base.task.opt(:show_args) || Base.task.opt(:debug)
|
|
399
|
+
"#{contextual_name}(#{args.values.map(&:description).join(', ')})"
|
|
400
|
+
else
|
|
401
|
+
contextual_name
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def log_exception_in_dep e
|
|
406
|
+
log_error e.message
|
|
407
|
+
advice = e.is_a?(DepDefinitionError) ? "Looks like a problem with '#{name}' - check" : "Check"
|
|
408
|
+
log "#{advice} #{(e.backtrace.detect {|l| l[load_path.to_s] } || load_path).sub(/\:in [^:]+$/, '')}." unless load_path.nil?
|
|
409
|
+
debug e.backtrace * "\n"
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def track_block_for task_name
|
|
413
|
+
if context.has_block? task_name
|
|
414
|
+
file, line = *context.file_and_line_for(task_name)
|
|
415
|
+
shell "mate '#{file}' -l #{line}" unless file.nil? || line.nil?
|
|
416
|
+
sleep 2
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def cached_result
|
|
421
|
+
cached_process.tap {|result|
|
|
422
|
+
if result
|
|
423
|
+
log "#{Logging::TickChar} #{name} (cached)".colorize('green')
|
|
424
|
+
elsif task.opt(:dry_run)
|
|
425
|
+
log "~ #{name} (cached)".colorize('blue')
|
|
426
|
+
else
|
|
427
|
+
log "#{Logging::CrossChar} #{name} (cached)".colorize('red')
|
|
428
|
+
end
|
|
429
|
+
}
|
|
430
|
+
end
|
|
431
|
+
def cached?
|
|
432
|
+
!@_cached_process.nil?
|
|
433
|
+
end
|
|
434
|
+
def uncache!
|
|
435
|
+
@_cached_process = nil
|
|
436
|
+
end
|
|
437
|
+
def cached_process
|
|
438
|
+
@_cached_process
|
|
439
|
+
end
|
|
440
|
+
def cache_process value
|
|
441
|
+
@_cached_process = (value.nil? ? false : value)
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def suffixed?
|
|
445
|
+
!opts[:template] && template != BaseTemplate
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def payload
|
|
449
|
+
context.payload
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def task
|
|
453
|
+
Base.task
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
public
|
|
457
|
+
|
|
458
|
+
def inspect
|
|
459
|
+
"#<Dep:#{object_id} #{"#{dep_source.name}:" unless dep_source.nil?}'#{name}' #{defined_info}>"
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def defined_info
|
|
463
|
+
if dep_defined?
|
|
464
|
+
"#{"(#{'un' unless cached_process}met) " if cached?}<- [#{context.requires.join(', ')}]"
|
|
465
|
+
else
|
|
466
|
+
"(not defined yet)"
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
end
|