bozo 0.3.2 → 0.3.3

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.
@@ -0,0 +1,300 @@
1
+ module Bozo
2
+
3
+ # Class used for defining the configuration of a build.
4
+ class BozoConfiguration
5
+
6
+ include Bozo::ClassNameHelpers
7
+
8
+ # Creates a new instance
9
+ def initialize
10
+ @build_tools_location = nil
11
+ @compilers = []
12
+ @dependency_resolvers = []
13
+ @hooks = []
14
+ @packagers = []
15
+ @preparers = []
16
+ @publishers = []
17
+ @test_runners = []
18
+ @version = nil
19
+ end
20
+
21
+ # Loads the configuration from the specified file.
22
+ #
23
+ # @param [String] path
24
+ # The path to the configuration file.
25
+ def load(path)
26
+ instance_eval IO.read(path), path
27
+ end
28
+
29
+ # The version of the project to build.
30
+ def version
31
+ @version ||= Versioning::Version.load_from_file
32
+ end
33
+
34
+ # The location of the base build tools.
35
+ #
36
+ # Sets and returns the location if a value is provided, otherwise returns
37
+ # the current value.
38
+ #
39
+ # It is expected the build tools will be in sub-directory of this location
40
+ # according to the name of the tool and that the runner will be able to
41
+ # copy the contents of the sub-directory to a local directory via the
42
+ # `FileUtils.cp_r` method.
43
+ #
44
+ # @param [String] location
45
+ # The path to set as the build tools location.
46
+ def build_tools_location(location = nil)
47
+ @build_tools_location = location if location
48
+ @build_tools_location
49
+ end
50
+
51
+ # Returns the all the build tools required by the specified compilers,
52
+ # dependency resolvers, hooks, packagers, publishers and test runners.
53
+ def build_tools
54
+ build_tools = get_build_tools @compilers
55
+ build_tools |= get_build_tools @dependency_resolvers
56
+ build_tools |= get_build_tools @hooks
57
+ build_tools |= get_build_tools @packagers
58
+ build_tools |= get_build_tools @preparers
59
+ build_tools |= get_build_tools @publishers
60
+ build_tools | (get_build_tools @test_runners)
61
+ end
62
+
63
+ # Returns the configured dependency resolvers.
64
+ attr_reader :dependency_resolvers
65
+
66
+ # Adds an instance of the named preparer to the preparer collection and
67
+ # yields it to the configuration block when one is specified.
68
+ #
69
+ # @param [Symbol] type
70
+ # The name of the preparer.
71
+ # @param [Proc] block
72
+ # Optional block to refine the configuration of the preparer.
73
+ def prepare(type, &block) # :yields: preparer
74
+ add_instance @preparers, Bozo::Preparers, type, block
75
+ end
76
+
77
+ # Returns the configured preparers.
78
+ attr_reader :preparers
79
+
80
+ # Adds an instance of the named dependency resolver to the dependency
81
+ # resolver collection and yields it to the configuration block when one
82
+ # is specified.
83
+ #
84
+ # @param [Symbol] type
85
+ # The name of the dependency resolver.
86
+ # @param [Proc] block
87
+ # Optional block to refine the configuration of the dependency
88
+ # resolver.
89
+ def resolve_dependencies_with(type, &block) # :yields: dependency_resolver
90
+ add_instance @dependency_resolvers, Bozo::DependencyResolvers, type, block
91
+ end
92
+
93
+ # Returns the configured compilers.
94
+ attr_reader :compilers
95
+
96
+ # Adds an instance of the named compiler to the compiler collection and
97
+ # yields it to the configuration block when one is specified.
98
+ #
99
+ # @param [Symbol] type
100
+ # The name of the compiler.
101
+ # @param [Proc] block
102
+ # Optional block to refine the configuration of the compiler.
103
+ def compile_with(type, &block) # :yields: compiler
104
+ add_instance @compilers, Bozo::Compilers, type, block
105
+ end
106
+
107
+ # Returns the configured test runners.
108
+ attr_reader :test_runners
109
+
110
+ # Adds an instance of the named test runner to the test runner collection
111
+ # and yields it to the configuration block when one is specified.
112
+ #
113
+ # @param [Symbol] type
114
+ # The name of the test runner.
115
+ # @param [Proc] block
116
+ # Optional block to refine the configuration of the test runner.
117
+ def test_with(type, &block) # :yields: test_runner
118
+ add_instance @test_runners, Bozo::TestRunners, type, block
119
+ end
120
+
121
+ # Returns the configured packagers.
122
+ attr_reader :packagers
123
+
124
+ # Adds an instance of the named packager to the packager collection and
125
+ # yields it to the configuration block when one is specified.
126
+ #
127
+ # @param [Symbol] type
128
+ # The name of the packager.
129
+ # @param [Proc] block
130
+ # Optional block to refine the configuration of the packager.
131
+ def package_with(type, &block) # :yields: packager
132
+ add_instance @packagers, Bozo::Packagers, type, block
133
+ end
134
+
135
+ # Returns the configured publishers.
136
+ attr_reader :publishers
137
+
138
+ # Adds an instance of the named publisher to the publisher collection and
139
+ # yields it to the configuration block when one is specified.
140
+ #
141
+ # @param [Symbol] type
142
+ # The name of the publisher.
143
+ # @param [Proc] block
144
+ # Optional block to refine the configuration of the publisher.
145
+ def publish_with(type, &block) # :yields: publisher
146
+ add_instance @publishers, Bozo::Publishers, type, block
147
+ end
148
+
149
+ # Returns the configured hooks.
150
+ attr_reader :hooks
151
+
152
+ # Adds an instance of the named hook to the hook collection and yields it
153
+ # to the configuration block when one is specified.
154
+ #
155
+ # @param [Symbol] type
156
+ # The name of the hook.
157
+ # @param [Proc] block
158
+ # Optional block to refine the configuration of the hook.
159
+ def with_hook(type, &block) # :yields: hook
160
+ add_instance @hooks, Bozo::Hooks, type, block
161
+ end
162
+
163
+ # Adds an instance of the named hook to the hook collection and yields it
164
+ # to the configuration block when one is specified.
165
+ alias :pre_build :with_hook
166
+
167
+ # Adds an instance of the named hook to the hook collection and yields it
168
+ # to the configuration block when one is specified.
169
+ alias :pre_clean :with_hook
170
+
171
+ # Adds an instance of the named hook to the hook collection and yields it
172
+ # to the configuration block when one is specified.
173
+ alias :post_clean :with_hook
174
+
175
+ # Adds an instance of the named hook to the hook collection and yields it
176
+ # to the configuration block when one is specified.
177
+ alias :pre_uninstall :with_hook
178
+
179
+ # Adds an instance of the named hook to the hook collection and yields it
180
+ # to the configuration block when one is specified.
181
+ alias :post_uninstall :with_hook
182
+
183
+ # Adds an instance of the named hook to the hook collection and yields it
184
+ # to the configuration block when one is specified.
185
+ alias :pre_dependencies :with_hook
186
+
187
+ # Adds an instance of the named hook to the hook collection and yields it
188
+ # to the configuration block when one is specified.
189
+ alias :post_dependencies :with_hook
190
+
191
+ # Adds an instance of the named hook to the hook collection and yields it
192
+ # to the configuration block when one is specified.
193
+ alias :pre_prepare :with_hook
194
+
195
+ # Adds an instance of the named hook to the hook collection and yields it
196
+ # to the configuration block when one is specified.
197
+ alias :post_prepare :with_hook
198
+
199
+ # Adds an instance of the named hook to the hook collection and yields it
200
+ # to the configuration block when one is specified.
201
+ alias :pre_compile :with_hook
202
+
203
+ # Adds an instance of the named hook to the hook collection and yields it
204
+ # to the configuration block when one is specified.
205
+ alias :post_compile :with_hook
206
+
207
+ # Adds an instance of the named hook to the hook collection and yields it
208
+ # to the configuration block when one is specified.
209
+ alias :pre_test :with_hook
210
+
211
+ # Adds an instance of the named hook to the hook collection and yields it
212
+ # to the configuration block when one is specified.
213
+ alias :post_test :with_hook
214
+
215
+ # Adds an instance of the named hook to the hook collection and yields it
216
+ # to the configuration block when one is specified.
217
+ alias :pre_package :with_hook
218
+
219
+ # Adds an instance of the named hook to the hook collection and yields it
220
+ # to the configuration block when one is specified.
221
+ alias :post_package :with_hook
222
+
223
+ # Adds an instance of the named hook to the hook collection and yields it
224
+ # to the configuration block when one is specified.
225
+ alias :pre_publish :with_hook
226
+
227
+ # Adds an instance of the named hook to the hook collection and yields it
228
+ # to the configuration block when one is specified.
229
+ alias :post_publish :with_hook
230
+
231
+ # Adds an instance of the named hook to the hook collection and yields it
232
+ # to the configuration block when one is specified.
233
+ alias :post_build :with_hook
234
+
235
+ private
236
+
237
+ # Resolves the named class within the given namespace and then created an
238
+ # instance of the class before adding it to the given collection and
239
+ # yielding it to the configuration block when one is provided.
240
+ #
241
+ # @param [Array] collection
242
+ # The collection the step executor should be added to once created.
243
+ # @param [Module] namespace
244
+ # The module the named step executor should be found in.
245
+ # @param [Symbol] type
246
+ # The name of the step executor.
247
+ # @param [Proc] block
248
+ # Optional block to refine the configuration of the step executor.
249
+ def add_instance(collection, namespace, type, block)
250
+ instance = namespace.const_get(to_class_name(type)).new
251
+ instance.extend Bozo::Runner
252
+ collection << instance
253
+ block.call instance if block
254
+ end
255
+
256
+ # Returns the collection of build tools required for the configuration.
257
+ #
258
+ # Sourced from the required tools of compilers, dependency resolvers,
259
+ # hooks, publishers and test runners.
260
+ #
261
+ # @param [Array] providers
262
+ # Collection of providers that can define required tools.
263
+ def get_build_tools(providers)
264
+ providers.
265
+ select{|p| p.respond_to? :required_tools}.
266
+ map{|p| p.required_tools}.
267
+ flatten
268
+ end
269
+
270
+ end
271
+
272
+ # Module containing all dependency resolvers.
273
+ module DependencyResolvers
274
+ end
275
+
276
+ # Module containing all compilers.
277
+ module Compilers
278
+ end
279
+
280
+ # Module containing all test runners.
281
+ module TestRunners
282
+ end
283
+
284
+ # Module containing all packagers.
285
+ module Packagers
286
+ end
287
+
288
+ # Module containing all preparers.
289
+ module Preparers
290
+ end
291
+
292
+ # Module containing all publishers.
293
+ module Publishers
294
+ end
295
+
296
+ # Module containing all hooks.
297
+ module Hooks
298
+ end
299
+
300
+ end
@@ -0,0 +1,17 @@
1
+ module Bozo
2
+
3
+ module ClassNameHelpers
4
+
5
+ # Converts a symbol into a Pascal Case class name.
6
+ #
7
+ # eg. `:single` => `"Single"`, `:two_words` => `"TwoWords"`.
8
+ #
9
+ # @param [Symbol] type
10
+ # The name of a step executor.
11
+ def to_class_name(type)
12
+ type.to_s.split('_').map{|word| word.capitalize}.join
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,30 @@
1
+ module Bozo
2
+
3
+ # Error raised when a runner or hook finds the configuration or build in an
4
+ # unexpected state.
5
+ class ConfigurationError < StandardError
6
+
7
+ # The code the program should exit with upon handling this error.
8
+ attr_reader :exit_code
9
+
10
+ # A message explaining the corrective actions someone can take to avoid the
11
+ # error.
12
+ attr_reader :message
13
+
14
+ # Creates a new instance.
15
+ #
16
+ # @param [String] message
17
+ # A message explaining the corrective actions someone can take to avoid
18
+ # the error.
19
+ def initialize(message)
20
+ @message = message
21
+ @exit_code = -1
22
+ end
23
+
24
+ def inspect
25
+ "Configuration error: #{message}"
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,36 @@
1
+ module Bozo
2
+
3
+ # Error raised when a command line executable does not return an exit code of
4
+ # zero.
5
+ class ExecutionError < StandardError
6
+
7
+ # The array of parameters that made up the call.
8
+ attr_reader :command
9
+
10
+ # The exit code returned by the command.
11
+ attr_reader :exit_code
12
+
13
+ # The friendly name of the tool that failed.
14
+ attr_reader :tool
15
+
16
+ # Create a new instance.
17
+ #
18
+ # @param [Symbol] tool
19
+ # The friendly name of the tool that failed.
20
+ # @param [Array] command
21
+ # The array of parameters that made up the call.
22
+ # @param [Integer] exit_code
23
+ # The exit code returned by the command
24
+ def initialize(tool, command, exit_code)
25
+ @tool = tool
26
+ @command = command
27
+ @exit_code = exit_code
28
+ end
29
+
30
+ def inspect
31
+ "Execution error: Exit code of #{exit_code} returned from: #{tool} #{command[1..-1].join(' ')}"
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,282 @@
1
+ module Bozo
2
+
3
+ # Class defining the structure of the build and responsible for executing
4
+ # tasks and their dependencies.
5
+ class Executor
6
+ include Runner
7
+
8
+ # Create a new instance.
9
+ #
10
+ # @param [Configuration] build_configuration
11
+ # The build configuration.
12
+ # @param [Hash] global_params
13
+ # The global parameters
14
+ def initialize(build_configuration, global_params)
15
+ @build_configuration = build_configuration
16
+ @global_params = cli_keys_to_ruby global_params
17
+ end
18
+
19
+ # Executes the command using the given parameters and environment.
20
+ #
21
+ # @param [Symbol] command
22
+ # The name of the command to execute.
23
+ # @param [Hash] params
24
+ # The parameters for the command.
25
+ # @param [Hash] env
26
+ # The environment that the command is run within.
27
+ def execute(command, params, env)
28
+ with_hooks :build, params, env do
29
+ send command, params, env
30
+ end
31
+ end
32
+
33
+ # Runs the clean task.
34
+ #
35
+ # @param [Hash] params
36
+ # The parameters for the command.
37
+ # @param [Hash] env
38
+ # The environment that the command is run within.
39
+ def clean(params, env)
40
+ with_hooks :clean, params, env do
41
+ log_info 'Cleaning project'
42
+ clear_directory 'temp'
43
+ clear_directory 'dist'
44
+ end
45
+ end
46
+
47
+ # Runs the uninstall task.
48
+ #
49
+ # @param [Hash] params
50
+ # The parameters for the command.
51
+ # @param [Hash] env
52
+ # The environment that the command is run within.
53
+ def uninstall(params, env)
54
+ clean params, env
55
+ with_hooks :uninstall, params, env do
56
+ log_info 'Uninstalling project'
57
+ clear_directory 'packages'
58
+ clear_directory 'build'
59
+ end
60
+ end
61
+
62
+ # Runs the dependency retrieval task.
63
+ #
64
+ # @param [Hash] params
65
+ # The parameters for the command.
66
+ # @param [Hash] env
67
+ # The environment that the command is run within.
68
+ def dependencies(params, env)
69
+ clean params, env
70
+ retrieve_build_dependencies
71
+ execute_with_hooks :dependencies, params, env
72
+ end
73
+
74
+ # Runs the prepare task.
75
+ #
76
+ # @param [Hash] params
77
+ # The parameters for the command.
78
+ # @param [Hash] env
79
+ # The environment that the command is run within.
80
+ def prepare(params, env)
81
+ dependencies params, env
82
+ execute_with_hooks :prepare, params, env
83
+ end
84
+
85
+ # Runs the compile task.
86
+ #
87
+ # @param [Hash] params
88
+ # The parameters for the command.
89
+ # @param [Hash] env
90
+ # The environment that the command is run within.
91
+ def compile(params, env)
92
+ prepare params, env
93
+ execute_with_hooks :compile, params, env
94
+ end
95
+
96
+ # Runs the test task.
97
+ #
98
+ # @param [Hash] params
99
+ # The parameters for the command.
100
+ # @param [Hash] env
101
+ # The environment that the command is run within.
102
+ def test(params, env)
103
+ compile params, env
104
+ execute_with_hooks :test, params, env
105
+ end
106
+
107
+ # Runs the package task.
108
+ #
109
+ # @param [Hash] params
110
+ # The parameters for the command.
111
+ # @param [Hash] env
112
+ # The environment that the command is run within.
113
+ def package(params, env)
114
+ test params, env
115
+ execute_with_hooks :package, params, env
116
+ end
117
+
118
+ # Runs the publish task.
119
+ #
120
+ # @param [Hash] params
121
+ # The parameters for the command.
122
+ # @param [Hash] env
123
+ # The environment that the command is run within.
124
+ def publish(params, env)
125
+ if build_server?
126
+ package params, env
127
+ execute_with_hooks :publish, params, env
128
+ else
129
+ log_warn 'You can only publish a package from build server.'
130
+ log_warn ''
131
+ log_warn 'If you really need to publish the package then provide the --build-server switch but you should only be doing that as a last resort.'
132
+ end
133
+ end
134
+
135
+ private
136
+
137
+ # Executes the executors for the given stage logging all messages with the
138
+ # given action.
139
+ #
140
+ # @param [Symbol] stage
141
+ # The name of the stage to execute.
142
+ # @param [Hash] params
143
+ # The parameters for the command.
144
+ # @param [Hash] env
145
+ # The environment that the command is run within.
146
+ def execute_with_hooks(stage, params, env)
147
+ action, executors = stage_definition stage
148
+ with_hooks stage, params, env do
149
+ log_info '' # blank line for aesthetics
150
+ log_info action
151
+ log_debug 'No executors' if executors.empty?
152
+ executors.each do |executor|
153
+ log_debug "#{action} with #{executor.class}"
154
+ set_parameters executor, params, env
155
+ executor.execute
156
+ end
157
+ end
158
+ end
159
+
160
+ # Gets the description and runners associated with the given stage.
161
+ #
162
+ # @params [Symbol] stage
163
+ # The name of the stage.
164
+ def stage_definition(stage)
165
+ case stage
166
+ when :dependencies
167
+ return 'Resolving dependencies', @build_configuration.dependency_resolvers
168
+ when :compile
169
+ return 'Compiling', @build_configuration.compilers
170
+ when :test
171
+ return 'Running tests', @build_configuration.test_runners
172
+ when :package
173
+ return 'Packaging', @build_configuration.packagers
174
+ when :prepare
175
+ return 'Preparing', @build_configuration.preparers
176
+ when :publish
177
+ return 'Publishing', @build_configuration.publishers
178
+ else
179
+ raise "Unrecognized stage: #{stage}"
180
+ end
181
+ end
182
+
183
+ # Runs the given code block surrounding it with the hooks for the named
184
+ # stage.
185
+ #
186
+ # @param [Symbol] stage
187
+ # The name of the stage being run.
188
+ # @param [Hash] params
189
+ # The parameters for the command.
190
+ # @param [Hash] env
191
+ # The environment that the command is run within.
192
+ def with_hooks(stage, params, env)
193
+ call_receiving_hooks "pre_#{stage}".to_sym, params, env
194
+ yield
195
+ call_receiving_hooks "post_#{stage}".to_sym, params, env
196
+ end
197
+
198
+ # Invokes the method for the hook for all hook objects that define a
199
+ # method for it.
200
+ #
201
+ # @param [Symbol] stage
202
+ # The name of the hook to call the methods for.
203
+ # @param [Hash] params
204
+ # The parameters for the command.
205
+ # @param [Hash] env
206
+ # The environment that the command is run within.
207
+ def call_receiving_hooks(stage, params, env)
208
+ @build_configuration.hooks.each do |hook|
209
+ if hook.respond_to? stage
210
+ set_parameters hook, params, env
211
+ hook.send stage
212
+ end
213
+ end
214
+ end
215
+
216
+ # Assigns the state of the build system to the runner.
217
+ #
218
+ # @param [Runner] runner
219
+ # The runner to assign the state to.
220
+ # @param [Hash] params
221
+ # The parameters for the step.
222
+ # @param [Hash] env
223
+ # The environment that the step is run within.
224
+ def set_parameters(runner, params, env)
225
+ runner.build_configuration = @build_configuration
226
+ runner.global_params = @global_params
227
+ runner.params = cli_keys_to_ruby params
228
+ runner.env = env
229
+ end
230
+
231
+ # Removes the named directory from the root directory.
232
+ #
233
+ # @param [String] dir
234
+ # The name of the directory to remove.
235
+ def clear_directory(dir)
236
+ full_path = File.expand_path(dir)
237
+ log_debug "Clearing directory #{full_path}"
238
+ FileUtils.rm_rf full_path
239
+ end
240
+
241
+ # Retrieves the required build tools from the configured build tools
242
+ # location.
243
+ def retrieve_build_dependencies
244
+ tools_location = @build_configuration.build_tools_location
245
+ required_tools = @build_configuration.build_tools
246
+ tools_path = File.expand_path(File.join('build', 'tools'))
247
+
248
+ log_info 'Retrieving build tools'
249
+
250
+ required_tools.each do |tool|
251
+ tool_name = tool.to_s
252
+ tool_path = File.join(tools_location, tool_name)
253
+ tool_destination = File.join(tools_path, tool_name)
254
+
255
+ if File.exist? tool_destination
256
+ log_debug "#{tool_name.capitalize} already present"
257
+ else
258
+ log_debug "Retrieving #{tool_name.capitalize}"
259
+ FileUtils.mkdir_p tool_destination
260
+ FileUtils.cp_r tool_path, tools_path
261
+ log_debug "#{tool_name.capitalize} retrieved"
262
+ end
263
+ end
264
+ end
265
+
266
+ # Converts a hash containing CLI-style keys (:'multi-word') and converts
267
+ # them into Ruby-style symbols (:multi_word).
268
+ #
269
+ # @param [Hash] hash
270
+ # A hash containing CLI-style keys.
271
+ def cli_keys_to_ruby(hash)
272
+ hash.keys.to_a.each do |key|
273
+ new_key = key.to_s.gsub('-', '_').to_sym
274
+ hash[new_key] = hash[key] unless hash.has_key? new_key
275
+ end
276
+
277
+ hash
278
+ end
279
+
280
+ end
281
+
282
+ end
@@ -0,0 +1,40 @@
1
+ module Bozo
2
+
3
+ # Module that provides functionality for logging.
4
+ module Logging
5
+
6
+ # Records a `debug` log message.
7
+ #
8
+ # @param [String] msg
9
+ # The message to log.
10
+ def log_debug(msg)
11
+ puts " #{msg.bright.color(:black)}"
12
+ end
13
+
14
+ # Records a `fatal` log message.
15
+ #
16
+ # @param [String] msg
17
+ # The message to log.
18
+ def log_fatal(msg)
19
+ puts msg.color(:red).bright
20
+ end
21
+
22
+ # Records an `info` log message.
23
+ #
24
+ # @param [String] msg
25
+ # The message to log.
26
+ def log_info(msg)
27
+ puts msg.color(:cyan).bright
28
+ end
29
+
30
+ # Records a `warn` log message.
31
+ #
32
+ # @param [String] msg
33
+ # The message to log.
34
+ def log_warn(msg)
35
+ puts msg.color(:yellow).bright
36
+ end
37
+
38
+ end
39
+
40
+ end