Commit 6c8fde40 authored by Sarah White's avatar Sarah White

merge !35

resolves #47 improve playbook architecture guidebook
parents 938b928a f92827c3
Pipeline #14637467 passed with stages
in 3 minutes and 56 seconds
= Playbook Component Guidebook
== Context
The documentation pipeline needs to be able to accept user input in the form of configuration options.
This configuration, which we call a [.term]_playbook_, tells the documentation pipeline what input to use, how the input should be processed, how the site should be generated, and where to publish the output.
== Functional Overview
The playbook needs to be loaded as the first step in the pipeline.
Numerous components in the documentation pipeline need to access configuration options that are provided by the playbook.
Therefore, the object that holds the data in the playbook needs to be transmittable.
The main configuration will be read from a file, which we call a [.term]_playbook spec_.
The user should be able to apply [.term]_audibles_, which are substitute options passed at usage time.
Audibles may be passed using either process arguments (i.e., command-line flags and switches), environment variables, or a combination of both.
(Future idea: Plugins should be able to participate in the process of building the playbook, either to modify the schema or modify the configuration data.)
== Software Architecture
The playbook functionality is provided by the playbook component.
The playbook component is composed of several object prototypes, including a playbook builder and the playbook data model.
All the details of loading the configuration, including the schema, the validation, the precedence of input methods, and the conversion to a transmittable model should be encapsulated in this component.
The playbook builder is the main coordinator, though it may delegate work to subordinate objects.
The playbook builder should accept two parameters, an array of arguments (populated from or simulating process arguments) and a hash of variables (populated from or simulating environment variables).
By accepting these two parameters, the playbook builder can be used and tested independently of the CLI runtime environment.
Within those parameters, a playbook spec file may be specified, which is a third (and the bulk) method of user input.
The playbook spec file may be composed in either YAML or CSON format.
(Future idea: The playbook builder should fire one event after the configuration schema is loaded and one event after the configuration data is populated.
This means that the playbook component has an implicit dependency on an event bus infrastructure.)
The playbook builder should return a well-defined, read-only data model called a playbook.
This model should not be coupled to any configuration-related technology used within the component.
Instead, it should be a pure model that can be easily reproduced by another means, such as an alternate implementation of the playbook component.
== Code
The playbook component is implemented as a dedicated node package (i.e., module).
It's APIs should be exported so that they can be required using the `require` keyword in the documentation pipeline.
Here's a rough sketch of how this component will be used in the pipeline:
[source,js]
----
const PlaybookBuilder = require('./packages/playbook/builder')
const playbook = PlaybookBuilder.load(process.argv, process.env)
----
The playbook builder should use https://github.com/mozilla/node-convict[convict] to process the user input.
If the `playbook` option is specified, it should be interpretted as a relative path to a playbook spec file (i.e., configuration file).
The playbook spec can be composed in either the YAML or CSON data format.
The format of the playbook spec file is determined by the file's extension.
If the path does not have a file extension, the builder should attempt to locate a file with the `.yml` extension or `.cson` extension, in that order.
If the playbook spec file is found, the configuration should be loaded again, this time loading the playbook spec file first followed by the audibles (environment variables then process arguments).
(Future idea: This component should use the global event emitter to fire events into the event bus.)
== Data
The playbook object produced by this component should have a well-defined data model.
Each section of configuration (site, content, ui) should be represented by a dedicated model type whose properties (name, location, and type) can be easily converted into API documentation (for example, using a tool like https://github.com/documentationjs/documentation[documentationjs]).
== Consequences
By introducing a dedicated playbook component that produces a pure data model, the configuration step is decoupled from the runtime environment (e.g., CLI environment).
This design will have an immediate impact on development by making the component easier to test in isolation.
This component also reserves room in the future for the documentation pipeline to accept configuration from other types of input, such as a database or web service.
(Future idea: By raising events at strategic points, the playbook component allows plugins to introduce flags and switches to the main application interface.)
= Playbook Builder Component Architecture
== Context
The documentation pipeline needs to be able to accept user input in the form of configuration options.
This configuration, which we call a [.term]_playbook_, tells the documentation pipeline what content to use, how the content should be processed, how the site should be generated, and where to publish the output.
== Functional Overview
The playbook needs to be loaded as the first step in the Antora pipeline.
The playbook component is responsible for accessing, validating, and processing a user provided playbook.
Numerous components in the Antora pipeline will need to access the configuration options provided by the playbook.
Therefore, the object that holds the data in the playbook needs to be transmittable.
The playbook component takes a configuration file, which we call a [.term]_playbook spec_, as input.
A playbook spec can be created and used by anyone operating an Antora pipeline.
While the playbook spec is the primary means of configuration, audibles may be passed using either process arguments (i.e., command-line flags and switches), environment variables, or a combination of both.
Audibles override equivalent option values in the playbook spec.
Using a playbook spec as an input, the component should carry out the following operations:
* Read the playbook spec file
* Accept and apply any audibles (i.e., override options) passed by the user at runtime
* Validate the playbook spec
* Convert the playbook spec file into a transmittable model that the other pipeline components can use
== Software Architecture
The playbook component functionality is provided by the playbook module.
All the details of loading the configuration, including the schema, the validation, the precedence of input methods, and the conversion to a transmittable model should be encapsulated in this component.
The playbook component should:
* Load the built-in playbook schema and assign default option values
* Accept two parameters, an array of arguments (populated from or simulating process arguments) and a hash of variables (populated from or simulating environment variables)
** By accepting these two parameters, the playbook builder can be used and tested independently of the CLI runtime environment.
** Within those parameters, a playbook spec file may be specified, which is a third (and the bulk) method of user input.
* Look for the specified playbook spec file composed in YAML, JSON, or CSON format
** When the data in these formats are read in, they become plain JavaScript objects.
* Apply audibles in the form of process arguments and environment variables
** The order of precedence for an option value is as follows (highest to lowest): process argument, environment variable, spec, default.
* Validate the aggregate playbook spec values
** The validation step checks that the option value matches the expected data type and enforces required options.
* Convert a valid playbook spec into a transmittable, well-defined, read-only data model that other pipeline components can use
.Inputs
* Playbook spec (`site.yml`, `site.json`, or `site.cson`)
* Process arguments (i.e., commandline flags and switches)
* Environment variables
.Output
* Playbook (`playbook`)
== Code
The playbook component is implemented as a dedicated node package (i.e., module).
Its API exports the `buildPlaybook()` function, which reads environment variables, commandline flags and switches, and a playbook spec file to produce the playbook data model.
#FIXME# the buildPlaybook function should accept an array of process arguments and a hash of environment variables.
The playbook builder should:
* Be the main coordinator, though it may delegate work to subordinate objects
* Use https://github.com/mozilla/node-convict[convict] to process the user input
* Interpret the `playbook` option as a relative path to a playbook spec file
* Load a playbook spec composed in YAML, JSON, or CSON data format
** The format of the playbook spec file is determined by the file's extension
** If the path does not have a file extension, the builder should attempt to locate a file with the `.yml`, `.json`, or `.cson` extension, in that order
** Select and use the appropriate parser based on the data format; the resulting object should be the same regardless of data format
* Load any environment variables, then process arguments
The API for the playbook should be used as follows:
[source,js]
----
const buildPlaybook = require ('../packages/playbook/lib/playbook-builder')
const playbook = buildPlaybook()
----
The properties of the playbook can be accessed as follows:
[source,js]
----
const sources = playbook.content.sources
----
== Data
The schema for the playbook is managed in the file https://gitlab.com/antora/antora/blob/master/packages/playbook/lib/config/schema.js[schema.js].
The schema defines the hierarchy of configuration properties as well as the names of environment variables and process arguments that can be used to override the default values or the values specified in the playbook spec.
It may be possible in the future for extensions to contribute entries to the schema.
The playbook object (`playbook`) produced by this component should be a well-defined, read-only data model.
This model should allow the playbook properties to be referenced using path navigation (e.g., `playbook.content.sources`).
This model should be easy to reproduce by another means, such as an alternate implementation of the playbook component.
Each section of configuration (site, content, ui) should be represented by a dedicated model type whose properties (name, location, and type) can be easily converted into API documentation (for example, using a tool like https://github.com/documentationjs/documentation[documentationjs]).
== Consequences
Once the playbook is built, all user input has been captured and the pipeline can proceed to generate the site based on these parameters.
Almost all subsequent components will be configured in some way based on information provided in the playbook.
No other component should look for user input for the site other than in the playbook.
By introducing a dedicated playbook component to handle user configuration, the configuration step is decoupled from the rest of the pipeline and the runtime environment.
This design will have an immediate impact on development by making the component easier to test in isolation.
This component also reserves room in the future for Antora to accept configuration from other input types, such as a database or web service.
////
== Future Ideas
* Plugins should be able to participate in the process of building the playbook, either to modify the schema or modify the configuration data.
* The playbook builder should fire one event after the configuration schema is loaded and one event after the configuration data is populated.
* This means that the playbook component has an implicit dependency on an event bus infrastructure.
* This component should use the global event emitter to fire events into the event bus.
* By raising events at strategic points, the playbook component allows plugins to introduce flags and switches to the main application interface.
////
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment