bozo 0.3.3 → 0.3.4

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/README.md CHANGED
@@ -1,408 +1,420 @@
1
- # Bozo
2
-
3
- Bozo is a build system written in Ruby. It is designed to be rigid yet
4
- extensible.
5
-
6
- ## Steps
7
-
8
- There is a fixed set of steps, they are currently:
9
-
10
- 1. Clean
11
- 2. Resolve dependencies
12
- 3. Prepare
13
- 4. Compile
14
- 5. Test
15
- 6. Package
16
- 7. Publish
17
-
18
- The steps are sequential but you can run up to any of them. For example `bozo
19
- compile` executes the `clean`, `dependencies`, `prepare` and `compile` steps
20
- whereas `bozo dependencies` only executes the `clean` and `dependencies`
21
- steps.
22
-
23
- Bozo is a framework that provides a skeleton which can be populated by custom
24
- runners and hooks. Bozo itself provides no runners or hooks a reference
25
- project for runners and hooks can be found in the [bozo-scripts
26
- project](https://github.com/zopaUK/bozo-scripts/).
27
-
28
- Each step allows several runners to execute, for example you may run RSpec
29
- unit tests followed by Cucumber integration tests within the scope of the
30
- `test` step. Each step, along with the entire build, also expose pre- and
31
- post-step hooks.
32
-
33
- ## Configuration
34
-
35
- A bozo build is configured through a single Ruby file, by convention this
36
- should be `bozorc.rb` located at the root of your project.
37
-
38
- Bozo makes use of a VERSION file in the root directory of the project. Versions can be specified in whatever format
39
- is required but a string in the format [major].[minor].[point] is generally expected.
40
-
41
- ### Conventions
42
-
43
- Dependency resolvers must be defined within the `Bozo::DependencyResolvers`
44
- module, project preparers must be defined within the `Bozo::Preparers`
45
- module, compilers must be defined within the `Bozo::Compilers` module, test
46
- runners must be defined within the `Bozo::TestRunners` module, packagers must
47
- be defined within the `Bozo::Packagers`, publishers must be defined within
48
- the `Bozo::Publishers` module and hooks, regardless of the steps they relate
49
- to, must be defined within the `Bozo::Hooks` module.
50
-
51
- Runners are specified by convention with the relevant module being inspected
52
- for a matching class definition. For example, the configuration `compile_with
53
- :msbuild` will resolve to the class definition `Bozo::Compilers::Msbuild`.
54
- The symbol provided will be converted to Pascal Case prior to resolution. For
55
- example, the configuration `pre_compile :common_assembly_info` will resolve
56
- to the class definition `Bozo::Hooks::CommonAssemblyInfo`.
57
-
58
- Each runner and hook must provide a parameterless constructor and Bozo will
59
- invoke that constructor before registering and passing the instance to any
60
- block provided as part of the configuration. A runner or hook should be able
61
- to run with the default configuration whenever possible with customizations
62
- being provided through the block:
63
-
64
- ```ruby
65
- test_with :nunit do |n| # Creates and registers a new Bozo::TestRunners::Nunit instance
66
- n.project 'Project.Tests' # Adds additonal configuration to the instance
67
- end
68
- ```
69
-
70
- If there are several runners for the same step then they will be executed in
71
- the order the are specified within the configuration.
72
-
73
- As soon as one runner or hook raises an error through either failing to
74
- execute a command successfully or some custom condition then the build is
75
- aborted.
76
-
77
- ### Configuration example
78
-
79
- The exact syntax is still a work in progress though the concepts will remain
80
- the same.
81
-
82
- ```ruby
83
- require 'bozo_scripts' # Makes custom runners and hooks available
84
-
85
- prepare :common_assembly_info # Defines that the common assembly info should be prepared for the project
86
-
87
- compile_with :msbuild # Defines that the project should be compiled with the `msbuild` compiler
88
-
89
- test_with :nunit do |n| # Defines that the project should be tested with the `nunit` test runner
90
- n.project 'Project.Tests' # Runner specific configuration - in this case defining the assemblies to run
91
- end
92
-
93
- package_with :nuget do |p| # Defines that the project should be packaged with `nuget`
94
- p.project 'Project' # Runner specific configuration - in this case the projects to package
95
- p.project 'Project.Testing'
96
- end
97
-
98
- resolve_dependencies_with :nuget # Defines that project dependencies should be resolved with `nuget`
99
-
100
- with_hook :git_commit_hashes # Defines that the `git_commit_hashes` hook should be executed with the build
101
- with_hook :timing # Defines that the `timing` hook should be executed with the build
102
-
103
- build_tools_location '//SERVER/network/path' # Defines the location build tools can be copied from
104
- ```
105
-
106
- ## Creating step runners and hooks
107
-
108
- Both step runners and hooks have their nuances which are covered in their
109
- dedicated sections. However, both are extended by the `Bozo::Runner` module
110
- that makes a collection of methods available to them.
111
-
112
- ### build_configuration
113
-
114
- Returns the `Bozo::Configuration` of the build.
115
-
116
- ### build_server?
117
-
118
- Returns `true` when the build is being run with the `--build-server` switch,
119
- otherwise `false`.
120
-
121
- This is a shortcut for `global_params[:build_server]`.
122
-
123
- ### env
124
-
125
- Returns the hash of environment variables. Initially populated by calling
126
- `ENV.to_hash` this may be added to by runners and hooks to enable lightweight
127
- communication and to cache the result of expensive calls.
128
-
129
- ### environment
130
-
131
- Returns the name of the environment that the build is running in, eg.
132
- `'development'`.
133
-
134
- This is a shortcut for `global_params[:environment]`.
135
-
136
- ### execute_command(tool, args)
137
-
138
- Executes a command line tool.
139
-
140
- Raises a `Bozo::ExecutionError` if the command responds with a non-zero exit
141
- code.
142
-
143
- #### Parameters
144
-
145
- * __tool [Symbol]__ A friendly identifier for the tool
146
- * __args [Array]__ An array of arguments making up the command to execute
147
-
148
- ### global_params
149
-
150
- Returns the hash of global parameters passed to bozo. All key symbols are
151
- converted from the CLI style of `:'multi-word'` to `:multi_word` to be more
152
- idiomatic for Ruby.
153
-
154
- ### log_debug(msg)
155
-
156
- Records an `debug` log message.
157
-
158
- #### Parameters
159
-
160
- * __msg [String]__ The message to log
161
-
162
- ### log_fatal(msg)
163
-
164
- Records an `fatal` log message.
165
-
166
- #### Parameters
167
-
168
- * __msg [String]__ The message to log
169
-
170
- ### log_info(msg)
171
-
172
- Records an `info` log message.
173
-
174
- #### Parameters
175
-
176
- * __msg [String]__ The message to log
177
-
178
- ### log_warn(msg)
179
-
180
- Records an `warn` log message.
181
-
182
- #### Parameters
183
-
184
- * __msg [String]__ The message to log
185
-
186
- ### params
187
-
188
- Returns the hash of command parameters passed to bozo. All key symbols are
189
- converted from the CLI style of `:'multi-word'` to `:multi_word` to be more
190
- idiomatic for Ruby.
191
-
192
- ### version
193
-
194
- Returns the version of the build.
195
-
196
- This is a shortcut for `build_configuration.version`.
197
-
198
- ## Creating step runners
199
-
200
- The structure of all runners is the same. They must be defined within the
201
- appropriate module, dependency resolvers in the `Bozo::DependencyResolvers`
202
- module, project preparers must be defined within the `Bozo::Preparers`
203
- module, compilers in the`Bozo::Compilers` module, test runners in the
204
- `Bozo::TestRunners` module, packagers in the `Bozo::Packagers` module and
205
- publishers in the `Bozo::Publishers` module. They must have a parameterless
206
- constructor and they must expose an `#execute` method which will be invoked
207
- when they should execute whatever task they are meant to perform. They can
208
- optionally define a `#required_tools` method which returns the name of any
209
- build tools it requires that cannot be retrieved through dependency
210
- resolvers, for example a dependency resolving executable such as `nuget.exe`.
211
-
212
- When executing a command line executable they should use the
213
- `execute_command(tool, args)` method so that the command will be logged in if
214
- the correct format and if executable completes with an error exit code the
215
- build will be aborted. They should also use the `log_info(msg)` and
216
- `log_debug(msg)` methods to ensure their output is formatted correctly and
217
- the verbosity of the messages can be controlled centrally.
218
-
219
- The runner will be passed back to the configuration code via an optional
220
- block so if further configuration of the runner is possible, or required,
221
- this should be exposed through public methods on the runner. If required
222
- configuration is omitted then a `Bozo::ConfigurationError` with a message
223
- explaining the problem and how to rectify it should be raised when the
224
- `#execute` method of the runner is called.
225
-
226
- ### Registration
227
-
228
- Runners are registered through step-specific methods:
229
-
230
- * `dependency_resolver(identifier, &block)` registers dependency resolvers
231
- * `prepare(identifier, &block)` registers project preparers
232
- * `compile_with(identifier, &block)` registers compilers
233
- * `test_with(identifier, &block)` registers test runners
234
- * `package_with(identifier, &block)` registers packagers
235
- * `publish_with(identifier, &block)` registers publishers
236
-
237
- ### Example
238
-
239
- Here is an example of a 'compiler' that logs `"Hello, <name>!"` where name is
240
- configured from the optional block and a `Bozo::ConfigurationError` is raised
241
- if no name has been configured:
242
-
243
- ```ruby
244
- module Bozo::Compilers
245
-
246
- class HelloWorld
247
-
248
- def name(name)
249
- @name = name
250
- end
251
-
252
- def execute
253
- raise Bozo::ConfigurationError.new('You must specify a name to say "Hello" to') if @name.nil?
254
- log_info "Hello, #{@name}!"
255
- end
256
-
257
- end
258
-
259
- end
260
- ```
261
-
262
- This compiler would be added to your build via the configuration:
263
-
264
- ```ruby
265
- compile_with :hello_world do |hw|
266
- hw.name 'Bozo'
267
- end
268
- ```
269
-
270
- ## Creating hooks
271
-
272
- The structure of all hooks is the same. The must be defined within the
273
- `Bozo::Hooks` module and they must have a parameterless constructor. They can
274
- optionally define a `#required_tools` method which returns the name of any
275
- build tools it requires that cannot be retrieved through dependency resolvers,
276
- for example a dependency resolving executable such as `nuget.exe`.
277
-
278
- When executing a command line executable they should use the
279
- `execute_command(tool, args)` method so that the command will be logged in if
280
- the correct format and if executable completes with an error exit code the
281
- build will be aborted. They should also use the `log_info(msg)` and
282
- `log_debug(msg)` methods to ensure their output is formatted correctly and the
283
- verbosity of the messages can be controlled centrally.
284
-
285
- The hook will be passed back to the configuration code via an optional block
286
- so if further configuration of the hook is possible, or required, this should
287
- be exposed through public methods on the hook. If required configuration is
288
- omitted then a `Bozo::ConfigurationError` with a message explaining the
289
- problem and how to rectify it should be raised when a hook method is called.
290
-
291
- A hook can be called several times. In order to hook around a step all that is
292
- required is that an appropriately named method is defined within the class.
293
- For example, this hook logs a message both before and after the compile step
294
- is run:
295
-
296
- ```ruby
297
- module Bozo::Hooks
298
-
299
- class CompilingMessages
300
-
301
- def pre_compile
302
- log_info 'About to compile'
303
- end
304
-
305
- def post_compile
306
- log_info 'Finished compiling'
307
- end
308
-
309
- end
310
-
311
- end
312
- ```
313
-
314
- Which steps the hook wants to execute on is determined by checking the
315
- response to the `#respond_to?` method so if you wish to use `#method_missing`
316
- to add functionality you need to ensure that the response to `#respond_to?`
317
- reflects that.
318
-
319
- ### Registration
320
-
321
- As hook instances can listen to one or more pre- or post-stage hooks there are
322
- multiple ways to register a hook. However, they are all functionally identical
323
- and are just aliases to the same method so that your configuration can read
324
- more clearly.
325
-
326
- The registration methods are:
327
-
328
- * `with_hook(identifier, &block)` (recommended when hooking several stages)
329
- * `pre_build(identifier, &block)`
330
- * `post_build(identifier, &block)`
331
- * `pre_clean(identifier, &block)`
332
- * `post_clean(identifier, &block)`
333
- * `pre_dependencies(identifier, &block)`
334
- * `post_dependencies(identifier, &block)`
335
- * `pre_prepare(identifier, &block)`
336
- * `post_prepare(identifier, &block)`
337
- * `pre_compile(identifier, &block)`
338
- * `post_compile(identifier, &block)`
339
- * `pre_test(identifier, &block)`
340
- * `post_test(identifier, &block)`
341
- * `pre_package(identifier, &block)`
342
- * `post_package(identifier, &block)`
343
- * `pre_publish(identifier, &block)`
344
- * `post_publish(identifier, &block)`
345
-
346
- ## Build tools
347
-
348
- Build tools are usually executables that you need to perform a task that are
349
- not available via some other means.
350
-
351
- For example, at Zopa we use in Nuget to resolve our .NET dependencies. This is
352
- a chicken and egg situation in that you can't use a dependency management
353
- system like Nuget until you've got a copy of the Nuget executable you can
354
- call. The build tools function aims to resolve this loop of cyclical dependency.
355
-
356
- Your build tools are resolve as the first part of the "resolve dependencies"
357
- step. When possible you should use real package management systems to retrieve
358
- dependencies rather than using the build tools function.
359
-
360
- ### Specifying required build tools
361
-
362
- All the runners and hooks you create can optionally specify a `required_tools`
363
- method which returns the name of one or more required build tools:
364
-
365
- ```ruby
366
- module Bozo::DependencyResolvers
367
-
368
- class Nuget
369
-
370
- def required_tools
371
- :nuget # or for many [:nuget, :open_wrap]
372
- end
373
-
374
- end
375
-
376
- end
377
- ```
378
-
379
- ### How it works
380
-
381
- Within the example configuration there is a single line:
382
-
383
- ```ruby
384
- build_tools_location '//SERVER/network/path' # Defines the location build tools can be copied from
385
- ```
386
-
387
- This specifies the location that build tools should be retrieved from. This
388
- location is then joined with the name of the build tool to find the directory
389
- that must be copied into the `./build/tools` directory. For example with a
390
- `build_tools_location` of `//SERVER/network/path` along with a required
391
- build tool called `:nuget` will result in the directory
392
- `//SERVER/network/path/nuget` being copied to `./build/tools/nuget` directory.
393
- By knowing the contents of this directory you can then invoke the executables
394
- contained within it:
395
-
396
- ```ruby
397
- module Bozo::DependencyResolvers
398
-
399
- class Nuget
400
-
401
- def execute
402
- execute_command :nuget, File.join('build', 'tools', 'nuget', 'NuGet.exe')
403
- end
404
-
405
- end
406
-
407
- end
1
+ # Bozo
2
+
3
+ Bozo is a build system written in Ruby. It is designed to be rigid yet
4
+ extensible.
5
+
6
+ ## Steps
7
+
8
+ There is a fixed set of steps, they are currently:
9
+
10
+ 1. Clean
11
+ 2. Resolve dependencies
12
+ 3. Prepare
13
+ 4. Compile
14
+ 5. Test
15
+ 6. Package
16
+ 7. Publish
17
+
18
+ The steps are sequential but you can run up to any of them. For example `bozo
19
+ compile` executes the `clean`, `dependencies`, `prepare` and `compile` steps
20
+ whereas `bozo dependencies` only executes the `clean` and `dependencies`
21
+ steps.
22
+
23
+ Bozo is a framework that provides a skeleton which can be populated by custom
24
+ runners and hooks. Bozo itself provides no runners or hooks a reference
25
+ project for runners and hooks can be found in the [bozo-scripts
26
+ project](https://github.com/zopaUK/bozo-scripts/).
27
+
28
+ Each step allows several runners to execute, for example you may run RSpec
29
+ unit tests followed by Cucumber integration tests within the scope of the
30
+ `test` step. Each step, along with the entire build, also expose pre- and
31
+ post-step hooks.
32
+
33
+ ## Configuration
34
+
35
+ A bozo build is configured through a single Ruby file, by convention this
36
+ should be `bozorc.rb` located at the root of your project.
37
+
38
+ Bozo makes use of a VERSION file in the root directory of the project. Versions can be specified in whatever format
39
+ is required but a string in the format [major].[minor].[point] is generally expected.
40
+
41
+ ### Conventions
42
+
43
+ Dependency resolvers must be defined within the `Bozo::DependencyResolvers`
44
+ module, project preparers must be defined within the `Bozo::Preparers`
45
+ module, compilers must be defined within the `Bozo::Compilers` module, test
46
+ runners must be defined within the `Bozo::TestRunners` module, packagers must
47
+ be defined within the `Bozo::Packagers`, publishers must be defined within
48
+ the `Bozo::Publishers` module and hooks, regardless of the steps they relate
49
+ to, must be defined within the `Bozo::Hooks` module.
50
+
51
+ Runners are specified by convention with the relevant module being inspected
52
+ for a matching class definition. For example, the configuration `compile_with
53
+ :msbuild` will resolve to the class definition `Bozo::Compilers::Msbuild`.
54
+ The symbol provided will be converted to Pascal Case prior to resolution. For
55
+ example, the configuration `pre_compile :common_assembly_info` will resolve
56
+ to the class definition `Bozo::Hooks::CommonAssemblyInfo`.
57
+
58
+ Each runner and hook must provide a parameterless constructor and Bozo will
59
+ invoke that constructor before registering and passing the instance to any
60
+ block provided as part of the configuration. A runner or hook should be able
61
+ to run with the default configuration whenever possible with customizations
62
+ being provided through the block:
63
+
64
+ ```ruby
65
+ test_with :nunit do |n| # Creates and registers a new Bozo::TestRunners::Nunit instance
66
+ n.project 'Project.Tests' # Adds additonal configuration to the instance
67
+ end
68
+ ```
69
+
70
+ If there are several runners for the same step then they will be executed in
71
+ the order the are specified within the configuration.
72
+
73
+ As soon as one runner or hook raises an error through either failing to
74
+ execute a command successfully or some custom condition then the build is
75
+ aborted.
76
+
77
+ ### Configuration example
78
+
79
+ The exact syntax is still a work in progress though the concepts will remain
80
+ the same.
81
+
82
+ ```ruby
83
+ require 'bozo_scripts' # Makes custom runners and hooks available
84
+
85
+ prepare :common_assembly_info # Defines that the common assembly info should be prepared for the project
86
+
87
+ compile_with :msbuild # Defines that the project should be compiled with the `msbuild` compiler
88
+
89
+ test_with :nunit do |n| # Defines that the project should be tested with the `nunit` test runner
90
+ n.project 'Project.Tests' # Runner specific configuration - in this case defining the assemblies to run
91
+ end
92
+
93
+ package_with :nuget do |p| # Defines that the project should be packaged with `nuget`
94
+ p.project 'Project' # Runner specific configuration - in this case the projects to package
95
+ p.project 'Project.Testing'
96
+ end
97
+
98
+ resolve_dependencies_with :nuget # Defines that project dependencies should be resolved with `nuget`
99
+
100
+ with_hook :git_commit_hashes # Defines that the `git_commit_hashes` hook should be executed with the build
101
+ with_hook :timing # Defines that the `timing` hook should be executed with the build
102
+
103
+ build_tools_location '//SERVER/network/path' # Defines the location build tools can be copied from
104
+ ```
105
+
106
+ ## Creating step runners and hooks
107
+
108
+ Both step runners and hooks have their nuances which are covered in their
109
+ dedicated sections. However, both are extended by the `Bozo::Runner` module
110
+ that makes a collection of methods available to them.
111
+
112
+ ### build_configuration
113
+
114
+ Returns the `Bozo::Configuration` of the build.
115
+
116
+ ### build_server?
117
+
118
+ Returns `true` when the build is being run with the `--build-server` switch,
119
+ otherwise `false`.
120
+
121
+ This is a shortcut for `global_params[:build_server]`.
122
+
123
+ ### env
124
+
125
+ Returns the hash of environment variables. Initially populated by calling
126
+ `ENV.to_hash` this may be added to by runners and hooks to enable lightweight
127
+ communication and to cache the result of expensive calls.
128
+
129
+ ### environment
130
+
131
+ Returns the name of the environment that the build is running in, eg.
132
+ `'development'`.
133
+
134
+ This is a shortcut for `global_params[:environment]`.
135
+
136
+ ### execute_command(tool, args)
137
+
138
+ Executes a command line tool.
139
+
140
+ Raises a `Bozo::ExecutionError` if the command responds with a non-zero exit
141
+ code.
142
+
143
+ #### Parameters
144
+
145
+ * __tool [Symbol]__ A friendly identifier for the tool
146
+ * __args [Array]__ An array of arguments making up the command to execute
147
+
148
+ ### global_params
149
+
150
+ Returns the hash of global parameters passed to bozo. All key symbols are
151
+ converted from the CLI style of `:'multi-word'` to `:multi_word` to be more
152
+ idiomatic for Ruby.
153
+
154
+ ### log_debug(msg)
155
+
156
+ Records an `debug` log message.
157
+
158
+ #### Parameters
159
+
160
+ * __msg [String]__ The message to log
161
+
162
+ ### log_fatal(msg)
163
+
164
+ Records an `fatal` log message.
165
+
166
+ #### Parameters
167
+
168
+ * __msg [String]__ The message to log
169
+
170
+ ### log_info(msg)
171
+
172
+ Records an `info` log message.
173
+
174
+ #### Parameters
175
+
176
+ * __msg [String]__ The message to log
177
+
178
+ ### log_warn(msg)
179
+
180
+ Records an `warn` log message.
181
+
182
+ #### Parameters
183
+
184
+ * __msg [String]__ The message to log
185
+
186
+ ### params
187
+
188
+ Returns the hash of command parameters passed to bozo. All key symbols are
189
+ converted from the CLI style of `:'multi-word'` to `:multi_word` to be more
190
+ idiomatic for Ruby.
191
+
192
+ ### version
193
+
194
+ Returns the version of the build.
195
+
196
+ This is a shortcut for `build_configuration.version`.
197
+
198
+ ## Creating step runners
199
+
200
+ The structure of all runners is the same. They must be defined within the
201
+ appropriate module, dependency resolvers in the `Bozo::DependencyResolvers`
202
+ module, project preparers must be defined within the `Bozo::Preparers`
203
+ module, compilers in the`Bozo::Compilers` module, test runners in the
204
+ `Bozo::TestRunners` module, packagers in the `Bozo::Packagers` module and
205
+ publishers in the `Bozo::Publishers` module. They must have a parameterless
206
+ constructor and they must expose an `#execute` method which will be invoked
207
+ when they should execute whatever task they are meant to perform. They can
208
+ optionally define a `#required_tools` method which returns the name of any
209
+ build tools it requires that cannot be retrieved through dependency
210
+ resolvers, for example a dependency resolving executable such as `nuget.exe`.
211
+
212
+ When executing a command line executable they should use the
213
+ `execute_command(tool, args)` method so that the command will be logged in if
214
+ the correct format and if executable completes with an error exit code the
215
+ build will be aborted. They should also use the `log_info(msg)` and
216
+ `log_debug(msg)` methods to ensure their output is formatted correctly and
217
+ the verbosity of the messages can be controlled centrally.
218
+
219
+ The runner will be passed back to the configuration code via an optional
220
+ block so if further configuration of the runner is possible, or required,
221
+ this should be exposed through public methods on the runner. If required
222
+ configuration is omitted then a `Bozo::ConfigurationError` with a message
223
+ explaining the problem and how to rectify it should be raised when the
224
+ `#execute` method of the runner is called.
225
+
226
+ ### Registration
227
+
228
+ Runners are registered through step-specific methods:
229
+
230
+ * `dependency_resolver(identifier, &block)` registers dependency resolvers
231
+ * `prepare(identifier, &block)` registers project preparers
232
+ * `compile_with(identifier, &block)` registers compilers
233
+ * `test_with(identifier, &block)` registers test runners
234
+ * `package_with(identifier, &block)` registers packagers
235
+ * `publish_with(identifier, &block)` registers publishers
236
+
237
+ ### Example
238
+
239
+ Here is an example of a 'compiler' that logs `"Hello, <name>!"` where name is
240
+ configured from the optional block and a `Bozo::ConfigurationError` is raised
241
+ if no name has been configured:
242
+
243
+ ```ruby
244
+ module Bozo::Compilers
245
+
246
+ class HelloWorld
247
+
248
+ def name(name)
249
+ @name = name
250
+ end
251
+
252
+ def execute
253
+ raise Bozo::ConfigurationError.new('You must specify a name to say "Hello" to') if @name.nil?
254
+ log_info "Hello, #{@name}!"
255
+ end
256
+
257
+ end
258
+
259
+ end
260
+ ```
261
+
262
+ This compiler would be added to your build via the configuration:
263
+
264
+ ```ruby
265
+ compile_with :hello_world do |hw|
266
+ hw.name 'Bozo'
267
+ end
268
+ ```
269
+
270
+ ## Creating hooks
271
+
272
+ The structure of all hooks is the same. The must be defined within the
273
+ `Bozo::Hooks` module and they must have a parameterless constructor. They can
274
+ optionally define a `#required_tools` method which returns the name of any
275
+ build tools it requires that cannot be retrieved through dependency resolvers,
276
+ for example a dependency resolving executable such as `nuget.exe`.
277
+
278
+ When executing a command line executable they should use the
279
+ `execute_command(tool, args)` method so that the command will be logged in if
280
+ the correct format and if executable completes with an error exit code the
281
+ build will be aborted. They should also use the `log_info(msg)` and
282
+ `log_debug(msg)` methods to ensure their output is formatted correctly and the
283
+ verbosity of the messages can be controlled centrally.
284
+
285
+ The hook will be passed back to the configuration code via an optional block
286
+ so if further configuration of the hook is possible, or required, this should
287
+ be exposed through public methods on the hook. If required configuration is
288
+ omitted then a `Bozo::ConfigurationError` with a message explaining the
289
+ problem and how to rectify it should be raised when a hook method is called.
290
+
291
+ A hook can be called several times. In order to hook around a step all that is
292
+ required is that an appropriately named method is defined within the class.
293
+ For example, this hook logs a message both before and after the compile step
294
+ is run:
295
+
296
+ ```ruby
297
+ module Bozo::Hooks
298
+
299
+ class CompilingMessages
300
+
301
+ def pre_compile
302
+ log_info 'About to compile'
303
+ end
304
+
305
+ def post_compile
306
+ log_info 'Finished compiling'
307
+ end
308
+
309
+ end
310
+
311
+ end
312
+ ```
313
+
314
+ Which steps the hook wants to execute on is determined by checking the
315
+ response to the `#respond_to?` method so if you wish to use `#method_missing`
316
+ to add functionality you need to ensure that the response to `#respond_to?`
317
+ reflects that.
318
+
319
+ ### Registration
320
+
321
+ As hook instances can listen to one or more pre- or post-stage hooks there are
322
+ multiple ways to register a hook. However, they are all functionally identical
323
+ and are just aliases to the same method so that your configuration can read
324
+ more clearly.
325
+
326
+ The registration methods are:
327
+
328
+ * `with_hook(identifier, &block)` (recommended when hooking several stages)
329
+ * `pre_build(identifier, &block)`
330
+ * `post_build(identifier, &block)`
331
+ * `pre_clean(identifier, &block)`
332
+ * `post_clean(identifier, &block)`
333
+ * `pre_dependencies(identifier, &block)`
334
+ * `post_dependencies(identifier, &block)`
335
+ * `pre_prepare(identifier, &block)`
336
+ * `post_prepare(identifier, &block)`
337
+ * `pre_compile(identifier, &block)`
338
+ * `post_compile(identifier, &block)`
339
+ * `pre_test(identifier, &block)`
340
+ * `post_test(identifier, &block)`
341
+ * `pre_package(identifier, &block)`
342
+ * `post_package(identifier, &block)`
343
+ * `pre_publish(identifier, &block)`
344
+ * `post_publish(identifier, &block)`
345
+
346
+ Failed hooks exist that are called when a stage fails, in these cases the
347
+ relevant `post` hook is not called.
348
+
349
+ * `failed_build(identifier, &block)`
350
+ * `failed_clean(identifier, &block)`
351
+ * `failed_dependencies(identifier, &block)`
352
+ * `failed_prepare(identifier, &block)`
353
+ * `failed_compile(identifier, &block)`
354
+ * `failed_test(identifier, &block)`
355
+ * `failed_package(identifier, &block)`
356
+ * `failed_publish(identifier, &block)`
357
+
358
+ ## Build tools
359
+
360
+ Build tools are usually executables that you need to perform a task that are
361
+ not available via some other means.
362
+
363
+ For example, at Zopa we use in Nuget to resolve our .NET dependencies. This is
364
+ a chicken and egg situation in that you can't use a dependency management
365
+ system like Nuget until you've got a copy of the Nuget executable you can
366
+ call. The build tools function aims to resolve this loop of cyclical dependency.
367
+
368
+ Your build tools are resolve as the first part of the "resolve dependencies"
369
+ step. When possible you should use real package management systems to retrieve
370
+ dependencies rather than using the build tools function.
371
+
372
+ ### Specifying required build tools
373
+
374
+ All the runners and hooks you create can optionally specify a `required_tools`
375
+ method which returns the name of one or more required build tools:
376
+
377
+ ```ruby
378
+ module Bozo::DependencyResolvers
379
+
380
+ class Nuget
381
+
382
+ def required_tools
383
+ :nuget # or for many [:nuget, :open_wrap]
384
+ end
385
+
386
+ end
387
+
388
+ end
389
+ ```
390
+
391
+ ### How it works
392
+
393
+ Within the example configuration there is a single line:
394
+
395
+ ```ruby
396
+ build_tools_location '//SERVER/network/path' # Defines the location build tools can be copied from
397
+ ```
398
+
399
+ This specifies the location that build tools should be retrieved from. This
400
+ location is then joined with the name of the build tool to find the directory
401
+ that must be copied into the `./build/tools` directory. For example with a
402
+ `build_tools_location` of `//SERVER/network/path` along with a required
403
+ build tool called `:nuget` will result in the directory
404
+ `//SERVER/network/path/nuget` being copied to `./build/tools/nuget` directory.
405
+ By knowing the contents of this directory you can then invoke the executables
406
+ contained within it:
407
+
408
+ ```ruby
409
+ module Bozo::DependencyResolvers
410
+
411
+ class Nuget
412
+
413
+ def execute
414
+ execute_command :nuget, File.join('build', 'tools', 'nuget', 'NuGet.exe')
415
+ end
416
+
417
+ end
418
+
419
+ end
408
420
  ```