# About FBL

Swiss knife to start automate any kind of routine work.

## Target users and use cases:

* DevOps, as fbl can make the flow control deployment an easy task. We already use FBL to make K8s deployments.
* Developers, who need to automate their routine tasks. We use FBL to create CLI wizards to share with other teams to execute one time tasks on environments we have no direct access.
* Managers. We use FBL to gather time sheet reports from multiple sources to create monthly reports on user activities.
* Anyone who just wants to automate his routine work a bit.

![Sample Flow](https://4228418674-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LRucx887DJv148ixYA1%2F-LRvdP4E6_1cnR2_Wdzl%2F-LRvdQLKnMRBsP0aJHgk%2Fsample_flow.png?generation=1542893977002927\&alt=media)

FBL allows to write both simple and really complex scenarios, where tasks can be spread into different streams. Streams can be executed in parallel and once all are done - another stream will start of proceed, depending on your needs.

## Plugin

FBL is not oriented to resolve just one particular thing, but to resolve any kind of automation task with reach set of plugins. By itself FBL exposes few handy plugins that can help you to start the automation flow. Plugins from FireBlink or any 3rd party developer can easily be integrated into your flows.

Found no suitable plugin? No problem - either create it yourself or contact us to create one for you. We also open to help with writing automation scenarios of any kind and complexity. We provide a handy template to start with plugin development - [fbl-plugin-template](https://github.com/FireBlinkLTD/fbl-plugin-template)

You can read more about the default flows by following this [link](/plugins).

### List of Official Plugins

#### General Purpose Plugins

* [@fbl-plugins/crypto](https://www.npmjs.com/package/@fbl-plugins/crypto) - Allows to encrypt/decrypt files. You may use this plugin to create secure vault inside your GIT repository with sensitive data, to help with that use [vault template](https://github.com/FireBlinkLTD/fbl-vault-flow-template)
* [@fbl-plugins/http](https://www.npmjs.com/package/@fbl-plugins/http) - Make any kind of HTTP request to integrate with REST APIs, upload and download files, etc.
* [@fbl-plugins/html-to-pdf](https://www.npmjs.com/package/@fbl-plugins/html-to-pdf) - Generate PDF file based on HTML template. You can use it to generate reports, invoices, etc.
* [@fbl-plugins/html-to-img](https://www.npmjs.com/package/@fbl-plugins/html-to-img) - Generate PNG/JPEG image based on HTML template.

#### K8s (Kubernetes) Plugins

* [@fbl-plugins/k8s-helm](https://www.npmjs.com/package/@fbl-plugins/k8s-helm) - Allows to manage helm releases.
* [@fbl-plugins/k8s-kubectl](https://www.npmjs.com/package/@fbl-plugins/k8s-kubectl) - Allows to execute kubectl commands (apply, delete, etc)

#### Others

* [@fbl-plugins/keycloak-admin-client](https://www.npmjs.com/package/@fbl-plugins/keycloak-admin-client) - Keycloak admin client integration to manage realm(s) from the FBL flow (clients, roles, etc).

Some of the plugins may not be listed here, but you can generally search NPM registry by following the [link](https://www.npmjs.com/search?q=fbl%20plugins).

## Packaging

Want to share your flow with others? No problem, just create a tarball (\*.tar.gz archive) with index.yml file inside and share with others. Packaged flows can be used directly inside other flows or invoked via CLI (remote URLs are supported).

Even more, you can wrap the flow into "[virtual](/plugins/flow#action-handler-virtual)" actions that allow to create plugin-like behavior with 0 coding.

## Requirements

FBL requires [Node.js](https://nodejs.org) to be installed on your system. We recommend to use latest LTS version.

## Installation

Just run following command in your terminal to install fbl:

```bash
npm i -g fbl
```

To get list of available CLI arguments run:

```bash
fbl --help
```

## Environment Variables

* FBL\_HOME - defines alternative `home` directory where `fbl` can store cached packages (downloaded tarballs), etc. Default value is `.fbl` inside user home directory, e.g. on unix systems: `~/.fbl`.

## Next steps

* Write your first flow. Check [this](/flows/hello-world) for more details.
* Review default [plugins](/plugins).


# Flows

## Format

FBL expecting flows to be presented as YAML files with following format:

```yaml
# [optional] flow version, used for informational purposes only.
version: 1.0.0

# [optional] flow description, used for informational purposes only. 
description: |-
  Plugn invoker.

# [optional] flow runtime dependencies
requires:
  # [optional] fbl version requirements
  fbl: '>=1.1.0'

  # [optional] required plugins and their versions
  plugins:
    '@fbl-plugins/http': '>=0.1.0'

  # [optional] required native applications presented in the PATH environment variable
  applications:
  - echo

# [required] entry point
pipeline:
  # Pipeline may only have one root action ID (or alias).
  # Value format is specific to the action. Each action may have its own format and requirements.  
  exec: 
    command: echo
    args: 
      - 'Hello World'

  # Metadata field.
  # Each action may have zero or more additional metadata fields associated with it.
  # All metadata fields should start with dollar sign ($). 
  $title: 'Say hello'
```

In most cases you will use [flow](https://github.com/FireBlinkLTD/fbl/tree/de8ffd0b86840bbfabb2bcfb879bcd09f9c9532c/docs/flows/flow.md) control action handlers as a starting point of your flow.


# Hello World

Create a file called `hello.yml` with following content:

```yaml
pipeline:
  exec:
    command: 'echo'
    args:
      - 'Hello World'
    options:
      verbose: true
```

Start the flow with fbl:

```bash
fbl hello.yml
```

You should now similar output in your console:

```
 -> Reading flow file: hello.yml
 -> [1] [exec] Processing.
 -> [1] [exec] stdout: Hello World

 <- [1] [exec] Completed successfully withing 0.037 second
```


# Templates

The most advanced feature of FBL is support of EJS templates inside the definitions.

The best way to understand how it works is to review following example in details:

```yaml
version: 1.0.0

pipeline:
  '--':
  - ctx:
      local:
        # use local template to pass local_var from "ctx"
        inline: <%- ctx.local_var %>

  # global template processing of array "arr"
  <$ ctx.arr.forEach(i => { $>
  - ctx:
      test_<$- i $>:
        # mix of global and local templates
        inline: <%- ctx.test + <$- i $> %>
  <$ }); $>
```

As you see in comments there are mentions of "global" and "local" templates.

## Local Templates

Default EJS Delimiter: %

Local templates are processed upon actual execution of a step. This allows to use up-to-date information in shared context that may be modified by previously executed steps.

However, the limitation to that is the template can not be used to generate YAML definition itself. You can bypass that limitation by using [Template Action Handler](/plugins/flow#action-handler-template) or global handler.

**Pros:**

* Allows to use up-to-date information from shared context

**Cons:**

* Can not be used to self generate YAML definition

## Global Templates

Default EJS Delimiter: $

Global templates are processed upon flow file loading. As a result they can be used to self generate YAML definition and that is actually the main usecase for them. However, as template processed at loading time shared context snapshot is used at that point.

**Pros:**

* Allows to self generate YAML definition.

**Cons:**

* Can not access up-to-date information from shared context, but only the one available upon file load time.

Note: if you're using [attached flow action handler](/plugins/flow#action-handler-attached-flow) that used global template - template will be resolved upon loading that file, e.g. when attached flow action gets executed (not upon main flow loading), causing template to use shared context snapshot available at that time (that might be modified by any previous action in main flow).

## Passing values by reference

In most cases passing values between actions with EJS template might be an overkill. Especially for complex structures (objects, array).

To avoid unnecessary overhead FBL allows to pass value directly by reference.

```yaml
# pass ctx.field value by reference
field: $ref:ctx.field
```

Reference syntax:

* reference should always start with `$ref:`
* following by `ctx`, `secrets` or `parameters`
* following by relative path starting with dot

E.g.

Let's say your context have a following state:

```yaml
ctx:
  l1:
    l2: 'value'
```

To pass `l2` value by reference you need to use: `$ref:ctx.l1.l2`

**Note:** FBL does not support referencing nested array elements with paths, only nested object fields.

E.g:

```yaml
ctx:
  l1:
    - l2: 'value'
```

You can't provide a path to reference `l2` field, but only `l1`, as `l1` is pointing to array.

## Escaped vs Raw output

EJS template allows to output expression either in raw (`<%-`) or escaped variants (`<%=`).

FBL overrides default EJS XML escape function with own one to prevent assign strings that may start with special chars: {, }, \[, ], |, -, >, @

This helps to prevent issue when unquoted scalar will get generated that may start with special chars and as a result break YAML parsing.


# Template Functions

FBL also provides few handy utility functions (JavaScript) you can use inside the template. They all assigned to '$' variable, so you can use them like this:

```yaml
value: <%- $.hash(ctx.string_field) %>
```

Template utilities just like action handlers are part of plugins. Some plugins may add their own handy functions.

## Default Functions

### File System

All the default action handlers are treating paths as relative to the directory where flow file is located. However in rare cases 3rd party plugins may not handle this right, to bypass that limitation just use following function to convert path:

```javascript
// get absolute path
$.fs.getAbsolutePath(path);
```

You may also want to create a relative path based on the directory from where `fbl` command was executed (might be handy for packaged flows):

```javascript
// get absolute path relative to the working directory of fbl command execution
$.fs.getAbsolutePath(path, cwd);
```

There are also 2 utility functions to read the file. Note: both are not recommended to use, as they block the flow for the time they read the file. In other words they are blocking entire process and any other actions that are running in parallel will wait till this functions will be resolved.

```javascript
// read text file
$.fs.read.text(path);

// read file into base64 encoded string
$.fs.read.base64(path);
```

### UUID Generation

Both UUID v4 and v5 are supported.

```javascript
// generate UUID (v4)
$.UUID.v4();

// generate UUID (v5) for given DNS namespace
$.UUID.v5.DNS('fireblink.com');

// generate UUID (v5) for given URL namespace
$.UUID.v5.URL('http://fireblink.com');

// generate UUID (v5) for custom namespace (should be UUID)
$.UUID.v5.custom('seed', $.UUID.v4());
```

### Hash

Generate hash (as string) for given string.

```javascript
// generate sha256 hash encoded as HEX
$.hash('test');

// generate hash for custom algorithm encoded as HEX
$.hash('test', 'md5');

// generate hash for custom algorithm as Base64
$.hash('test', 'md5', 'base64');
```

### Assign To

Assign value to context.

```javascript
// Assing 'value' string to ctx.test field
$.assignTo('$.ctx.test', 'value');

// Assign 'value' to both ctx.c1 and secrets.s2 fields
$.assignTo(
  {
    ctx: '$.c1',
    secrets: '$.s2',
  },
  'value',
);
```

**Note:** first parameter is using same syntax as [common assignTo syntax](/plugins/common#assign-to).

### Push To

Assign value to context.

```javascript
// Push 'value' string to ctx.test field
$.pushTo('$.ctx.test', 'value');

// Push 'value' to both ctx.c1 and secrets.s2 fields
$.pushTo(
  {
    ctx: '$.c1',
    secrets: '$.s2',
  },
  'value',
);
```

**Note:** first parameter is using same syntax as [common assignTo syntax](/plugins/common#push-to).

### Assign To / Push To - JSON Schema generation

It might be handy to use `$.assignTo()`/`$.pushTo()` in pair with `$.assignToSchema()`/`$.pushToSchema()` function inside virtual. To generate action like virtual.

```javascript
$.assignToSchema();
$.pushToSchema();
```

**Virtual Example:**

```yaml
pipeline:
  virtual:
    id: 'ftpo'
    # define parameters schema with generates assignTo and pushTo properties
    parametersSchema:
      type: object
      properties:
        assignTo: <$- JSON.stringify($.assignToSchema()) $>
        pushTo: <$- JSON.stringify($.pushToSchema()) $>
    action:
      fn: |-
        $.assignTo(parameters.assignTo, 'some value');
        $.pushTo(parameters.pushTo, 'some value');
```

### Include another template

Include another file as a template inside the flow.

```javascript
await $.include('relative/path.ejs');
await $.include('relative/path.ejs', {
  extraParameter: 'value',
});
```

**Example:**

Write to file based on the template that includes another file.

```yaml
pipeline:
  '->':
    path: /tmp/test.txt
    contentFromFile: /tmp/template.ejs
```

*/tmp/template.ejs:*

```
Include: <%- await $.include('fixture.ejs') %>
```

*/tmp/fixture.ejs:*

```
<%- ctx.test %>
```


# Metadata Fields

Metadata fields are used to provide additional information for FBL to change the action execution behavior.

Metadata fields are located on the same level as the action ID or alias and should always start with `$` character. E.g.:

```yaml
pipeline:
  $title: Assign 'value' to ctx.field
  ctx:
    '$.field':
      inline: 'value'
```

## $title

Title is used to make `--verbose` output more meaningful for the user. Instead of printing action ID or alias FBL will use the text inside the `$title` meta field.

## $parameters

Used to assign additional, provide initial or merge with existing parameters before running the action.

```yaml
pipeline:
  # provide parameters.test value right to the action
  $parameters: 
    test: true
  ctx:
    '$.field':
      # reference parameters field provided inside the metadata
      inline: $ref:parameters.test
```


# Plugins

Plugins is a key concept of fbl to extend its capabilities. FBL by itself distributed with handful [plugins](/plugins#default-plugins).

## Default Plugins

FBL distributed with few handful plugins:

* [Flow Execution Control](/plugins/flow)
* [Context Manipulation](/plugins/context)
* [Commands and Scripts Execution](/plugins/exec)
* [File System Operations](/plugins/fs)
* [Interactive User Prompts](/plugins/prompts)
* [Execution Reporters](/plugins/reporters)

## Official FBL Plugins

FBL offers additional open source plugins that can be found by following the (link)\[<https://www.npmjs.com/org/fbl-plugins>].

Additionally FireBlink is open to provide services to create specific closed source / open source plugins for your business needs.

## Plugin Development

Start with creating new plugin with [project generator](https://github.com/FireBlinkLTD/fbl-plugin-template).

You can also review the [source code](https://github.com/FireBlinkLTD/fbl/tree/develop/src/plugins) of the default plugins as a starting point.


# Flow Control

Flow order control.

Available actions:

* [sequence (sync)](/plugins/flow#action-handler-sequential-steps-execution) - execute sub-flows in a sequence
* [parallel (async)](/plugins/flow#action-handler-parallel-steps-execution) - execute sub-flows in parallel
* [attachment](/plugins/flow#action-handler-attached-flow) - execute flow described in different file / url
* [repeat](/plugins/flow#action-handler-repeat-flow) - repeat action multiple times
* [retry](/plugins/flow#action-handler-retry-flow) - repeat action till it succeeds or fails maximum number of times
* [for each of](/plugins/flow#action-handler-for-each) - execute action for each item in the array
* [while](/plugins/flow#action-handler-while) - execute action while condition satisfies
* [switch (conditional)](/plugins/flow#action-handler-switch-flow) - execute different actions based on the value
* [try-catch-finally](/plugins/flow#action-handler-try---catch---finally-flow) - "try / catch / finally"
* [sleep](/plugins/flow#action-handler-sleep) - sleep for given amount of second
* [template](/plugins/flow#action-handler-template) - execute action described in template string
* [virtual](/plugins/flow#action-handler-virtual) - create virtual action handler
* [error](/plugins/flow#action-handler-error) - rise error
* [echo](/plugins/flow#action-handler-echo) - print message to stdout
* [void](/plugins/flow#action-handler-void) - does nothing

## Action Handler: Sequential steps execution

Run steps one by one, if any of steps fail - chain of execution will stop on it.

Note: [parameters](/glossary#parameters) for each action will be cloned. Meaning each action will start its own `branch` unless `shareParameters` option is used.

**ID:** `com.fireblink.fbl.flow.sequence`

**Aliases:**

* `fbl.flow.sequence`
* `flow.sequence`
* `sequence`
* `sync`
* `--`

**Example 1: Base Declaration**

```yaml
# Run actions in a sequence
'--':
  - ctx:
      '.':
        inline:
          something: true
  - ctx:
      fromFile:
        files:
          - test.yml
```

**Example 2: Detailed Declaration**

```yaml
# Run actions in a sequence
'--':
  # [optional] whether to share parameters between actions instead of making a clone.
  # Default value: false, e.g. make cloned parameters for each action in a sequence.
  shareParameters: false

  # [required] list of actions to invoke in a sequence
  actions:
    - ctx:
        '.':
          inline:
            something: true
    - ctx:
        fromFile:
          files:
            - test.yml
```

**Warning:** `shareParameters` usage is considered an [anti-pattern](/pitfalls/sequence-share-parameters)

## Action Handler: Parallel steps execution

Run all steps in parallel. If any of steps will fail - it will not affect others. However parallel step itself will be marked as failed.

**ID:** `com.fireblink.fbl.flow.parallel`

**Aliases:**

* `fbl.flow.parallel`
* `flow.parallel`
* `parallel`
* `async`
* `||`

**Example 1: Base syntax**

```yaml
# Run steps in parallel
'||':
  - ctx:
      '.':
        inline:
          something: true
  - ctx:
      fromFile:
        files:
          - test.yml
```

**Example 2: Alternative syntax**

```yaml
'||':
  # [optional] whether to share parameters between actions instead of making a clone.
  # Default value: false, e.g. make cloned parameters for each action in a sequence.
  shareParameters: false

  # [required] actions to invoke in parallel
  actions:
    - ctx:
      '.':
        inline:
          something: true
    - ctx:
      fromFile:
        files:
          - test.yml
```

## Action Handler: Attached flow

Allows to reference external flow by its pass. Helps to logically split big flows for better organized structure.

**ID:** `com.fireblink.fbl.flow.attachment`

**Aliases:**

* `fbl.flow.attachment`
* `flow.attachment`
* `attachment`
* `@`

**Example 1: Specify flow file**

```yaml
# Run steps from external flow file or package (*.tar.gz)
'@': flow.yml
```

**Example 2: Specify directory**

```yaml
# Run steps from external flow file (index.yml) inside "flow" directory
# Note: slash in the end of path is not required
'@': flow/
```

**Example 3: Specify url to download a package**

```yaml
# Run steps from external flow file (index.yml) inside "flow" directory
# Note: slash in the end of path is not required
'@': http://some.host/flow.tar.gz
```

**Example 4: Specify target inside the package**

```yaml
'@':
  # [required] path or url to download the package
  path: flow.tar.gz
  # [optional] specify custom flow entry file name inside the package
  target: custom.yml
```

**Example 5: Custom HTTP headers**

```yaml
'@':
  # [required] path or url to download the package
  path: http://some.host/flow.tar.gz

  # [optional] specify custom flow entry file name inside the package
  target: custom.yml

  # [optional] http parameters
  http:
    # [optional] custom http headers
    headers:
      Authorization: Basic YWRtaW46YWRtaW4=

  # [optional] cache downloaded package inside $FBL_HOME/cache folder
  cache: true
```

## Action Handler: Repeat flow

Repeat action multiple times.

**ID:** `com.fireblink.fbl.flow.repeat`

**Aliases:**

* `fbl.flow.repeat`
* `flow.repeat`
* `repeat`

**Example:**

```yaml
repeat:
  # [optional] whether to share parameters between actions instead of making a clone.
  # Default value: false, e.g. make cloned parameters for each action in a sequence.
  shareParameters: false

  # [required] number of iterations
  times: 2

  # [optional] whether each iteration should wait previous to complete or run in parallel
  # Default value: false
  async: false

  # [required] action to run
  action:
    # run flow_0.yml and flow_1.yml flows
    @: flow_<%- iteration.index %>.yml
```

## Action Handler: Retry flow

Retry action multiple times till it passes or reach max failure attempts.

**ID:** `com.fireblink.fbl.flow.retry`

**Aliases:**

* `fbl.flow.retry`
* `flow.retry`
* `retry`

**Example:**

```yaml
retry:
  # [required] number of attempts to run the action
  attempts: 2

  # [required] action to run
  action:
    sleep: 1

  # [optional] allows to record last error code from last failed attempt
  # Note: will only be executed if all attempts are failed
  errorCode:
    # [optional] assign error code to context field
    # Follows common assignment logic practicies https://fbl.fireblink.com/plugins/common#assign-to
    assignTo: '$.ctx.errorCode'

    # [optional] push error code to context field
    # Follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to
    pushTo: '$.ctx.errorCode'
```

## Action Handler: For Each

Allows to execute action for every item in the array or key of an object.

**ID:** `com.fireblink.fbl.flow.foreach`

**Aliases:**

* `fbl.flow.foreach`
* `flow.foreach`
* `foreach`
* `each`

**Example: Array**

```yaml
each:
  # [optional] whether to share parameters between actions instead of making a clone of parameters.
  # Default value: false, e.g. make cloned parameters for each action in a sequence.
  shareParameters: false

  # [required] array to iterate over
  of: [1, 2, 3]

  # [required] action to invoke on every iteration
  action:
    ctx:
      test_<%- iteration.index %>:
        # assign 1,2,3 to test_0, test_1, test_3
        inline: $ref:iteration.value
```

**Example: Object**

```yaml
each:
  # [optional] whether to share parameters between actions instead of making a clone of parameters.
  # Default value: false, e.g. make cloned parameters for each action in a sequence.
  shareParameters: false

  # [required] object to iterate over
  of:
    a: 1
    b: 2

  # [required] action to invoke for each iteration
  action:
    ctx:
      test_<%- iteration.index %>:
        # assign 1a to test_0 and b2 to test_1 values
        inline: <%- iteration.value %><%- iteration.name %>
```

## Action Handler: Switch flow

Allows to run action based on some condition

**ID:** `com.fireblink.fbl.flow.switch`

**Aliases:**

* `fbl.flow.switch`
* `flow.switch`
* `switch`
* `if`
* `?`

**Example:**

```yaml
'?':
  # [required] value to check
  value: <% ctx.test %>

  # [requied] actions to run on specific value
  is:
    # execute "foo.yml" if "foo"
    foo:
      @: foo.yml

    # execute "bar.yml" if "bar"
    bar:
      @: bar.yml

  # [optional] if no match found "else" handler will get executed
  else:
    @: else.yml
```

## Action Handler: While

Runs action till condition is successful or not (based on configuration).

**ID:** `com.fireblink.fbl.flow.while`

**Aliases:**

* `fbl.flow.while`
* `flow.while`
* `while`

**Example: Positive condition check**

```yaml
while:
  # [optional] whether to share parameters between actions instead of making a clone of parameters.
  # Default value: false, e.g. make cloned parameters for each action in a sequence.
  shareParameters: false

  # [required] value to check
  value: <%- ctx.something %>

  # [required] if value IS equal to provided one - action will get executed
  is: true

  # [required] action to run
  action:
    '@': something.yml
```

**Example: Negative condition check**

```yaml
while:
  # [optional] whether to share parameters between actions instead of making a clone of parameters.
  # Default value: false, e.g. make cloned parameters for each action in a sequence.
  shareParameters: false

  # [required] value to check
  value: <%- ctx.something %>

  # [required] if value IS NOT equal to provided one - action will get executed
  not: true

  # [required] action to run
  action:
    '@': something.yml
```

## Action Handler: Sleep

Sleep for a given amount of seconds.

**ID:** `com.fireblink.fbl.flow.sleep`

**Aliases:**

* `fbl.flow.sleep`
* `flow.sleep`
* `sleep`

**Example:**

```yaml
# sleep for a minute
sleep: 60
```

## Action Handler: Try - Catch - Finally Flow

Allows to run sub-step in isolation causing its failure to be ignored by parent step. Optionally catch and finally steps can be invoked.

If catch or finally step block will be failed - this step will also be marked as failed even try block passes successfully.

**ID:** `com.fireblink.fbl.flow.try`

**Aliases:**

* `fbl.flow.try`
* `flow.try`
* `try`

**Example:**

```yaml
try:
  # [required] action to run
  action:
    @: foo.yml

  # [optional] call error.yml if foo.yml failed
  catch:
    @: error.yml

  # [optional] error code assignment, can be used to better handle error processing inside catch/final actions
  errorCode:
    # [optional] assign error code to context field
    # Follows common assignment logic practicies https://fbl.fireblink.com/plugins/common#assign-to
    assignTo: '$.ctx.errorCode'

    # [optional] push error code to context field
    # Follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to
    pushTo: '$.ctx.errorCode'

  # [optional] call cleanup.yml either after successful execution of foo.yml or error.yml
  finally:
    @: cleanup.yml
```

## Action Handler: Template

Run action based on dynamically constructed template. This is handy as you generally can not dynamically construct YAML with EJS template inside most of the actions.

E.g: following is invalid:

```yaml
ctx:
  something: <% [1, 2, 3].forEach(item => { %>
    - <%- item %>
    <% }) %>
```

It will fail as upon processing everything that goes after something will be treated as string, causing to produce following action:

```yaml
ctx:
  something: '-1\n -2\n -3'
```

But there is a template handler that can help you with that.

**ID:** `com.fireblink.fbl.flow.template`

**Aliases:**

* `fbl.flow.template`
* `flow.template`
* `template`
* `tpl`

**Example:**

```yaml
tpl: |-
  ctx:
    something: 
      <% [1, 2, 3].forEach(item => { %>
      - <%- item %>
      <% }) %>
```

that will generally produce:

```yaml
ctx:
  something: [1, 2, 3]
```

## Action Handler: Virtual

Allows to create virtual action handler for another action (that can be represented as one of the flows).

**ID:** `com.fireblink.fbl.flow.virtual`

**Aliases:**

* `fbl.flow.virtual`
* `flow.virtual`
* `virtual`

**Example:**

```yaml
virtual:
  # [required] virtual handler ID
  id: handler.id

  # [optional] aliases for the handler to reference
  aliases:
    - handler.alias

  # [optional] JSON Schema of options that can/should be passed to the generated handler
  parametersSchema:
    type: object
    properties:
      test:
        type: string

  # [optional] default parameters and merge function
  # Note: if no mergeFunction or modifiers is provided defaults with parameters will be deeply merged.
  # Upon merge arrays will be concatenated.
  defaults:
    # [required] default values
    values:
      test: yes

    # [optional] merge modification functions for given paths
    # This is a recommended way of overriding merge behaviour.
    # Use "mergeFunction" only when you need to do something really unique.
    # "parameters" - represents field state by given path
    # "defaults" - its default value if any
    modifiers:
      $.test: |-
        return parameters + defaults

    # [optional] custom merge function
    # Use it only when "modifiers" functionality isn't enough
    # "parameters" - represents provided parameters
    # "defaults" - defaults by itself
    mergeFunction: |-
      return parameters.test + defaults.test

  # [optional] Change working directory location based on the place of virtual execution
  #  instead of declaration.
  #
  # By default all relative paths are resolved based on the place where virtual action is declared.
  # If you need to change the behavior - set this property to "true".
  #
  # Parameters:
  #   $.parameters.wd  - will be dynamically set to either working directory where virtual
  #                      is declared or place where it will be invoked.
  #
  #   $.parameters.pwd - always points to the working directory where virtual was declared.
  dynamicWorkDir: true

  # [required] action to invoke
  action:
    ctx:
      some_field:
        # Note: you may use "parameters" to reference passed options to the virtual handler
        # These options are first pre-validated with provided validationSchema (if any)
        inline: <%- parameters.test %>
```

Then you can reference your generated handler like any other:

```yaml
handler.id:
  test: some_field_value
```

## Action Handler: Invoke

Allows to execute action passed as parameter. Can be used in pair with `virtual` to pass flow actions as parameters and create reach execution flow patterns.

**ID:** `com.fireblink.fbl.flow.invoke`

**Aliases:**

* `fbl.flow.invoke`
* `flow.invoke`
* `invoke`

**Example:**

```yaml
# Invoke action described in parameters `action` field
invoke: $ref:parameters.action
```

## Action Handler: Error

Throw error upon execution with given message.

**ID:** `com.fireblink.fbl.flow.error`

**Aliases:**

* `fbl.flow.error`
* `flow.error`
* `error`

**Example:**

```yaml
error: 'message'
```

## Action Handler: Echo

Print message to console

**ID:** `com.fireblink.fbl.flow.echo`

**Aliases:**

* `fbl.flow.echo`
* `flow.echo`
* `echo`

**Example:**

```yaml
echo: 'message'
```

## Action Handler: Void

Action handler that does nothing. Main usecase to bypass limitations of other action handlers that require action handler to be provided as an option, but you might not want to do that for some reason.

**ID:** `com.fireblink.fbl.flow.void`

**Aliases:**

* `fbl.flow.void`
* `flow.void`
* `void`

**Example:**

```yaml
pipeline:
  '--':
    - void
```


# Context State

Upon flow execution each action handler gets access to shared [context](/glossary#context).

[EJS](http://ejs.co/) template can be used inside options to pass values from shared context. Please refer to plugin documentation if this feature supported and what options are required.

Example:

```yaml
version: 1.0.0
pipeline:
  plugin:
    # Pass "something" from "ctx"
    contextValue: <%- ctx.something  %>
    # Pass "password" from "secrets"
    secretValue: <%- secrets.password %>
```

Available actions:

* [ctx](/plugins/context#action-handler-context-values-assignment) - assign values to "ctx"
* [secret](/plugins/context#action-handler-secret-values-assignment) - assign values to "secret"
* [summary](/plugins/context#action-handler-summary) - report in the end of fbl execution

## Action Handler: Context Values Assignment

Assign non-secret values to context `ctx` field or its child properties by [path](/glossary#path). General use case: register shared non-sensitive options that later will be used by actions.

**ID:** `com.fireblink.fbl.context.values`

**Aliases:**

* `fbl.context.values`
* `context.values`
* `context`
* `ctx`

**Example 1: Assign values to context root directly:**

```yaml
ctx:
  # assign to "ctx" directly
  '$':
    inline:
      something: true
      else: false
```

**Example 2: Assign values from file "vars.yml" to field "vars -> files":**

```yaml
ctx:
  # create hierarchy of objects: "files" inside "vars" that is inside "ctx"
  $.vars:
    files:
      - vars.yml
```

**Example 3: Assign values from file "vars.yml" after inline ones:**

```yaml
ctx:
  '$':
    inline:
      test: true
    files:
      - vars.yml
    # specify that files have a priority over inline vars
    # if not provided inline vars will have priority over files
    priority: 'files'
```

**Example 4: Override instead of assigning**

```yaml
ctx:
  '$.test':
    inline:
      test: true
    # [optional] override everything tha tis inside "test" object with { test: true }
    # use with caution
    override: true
```

**Example 5: Push to array**

```yaml
ctx:
  '$.test':
    inline: 1
    # [optional] override everything tha tis inside "test" object with { test: true }
    # use with caution
    override: true
    # [required] if you want to push inline or value(s) from file(s) to targets array
    push: true
    # [optional] if enambled and value is array its child items will be pushed instead of array itself
    children: false
```

**Example 6: Find files by mask:**

```yaml
ctx:
  '$':
    files:
      - vars/*.yml
```

## Action Handler: Secret Values Assignment

Same as above, but for secrets. All the options will me masked in report to prevent any security leakage.

**ID:** `com.fireblink.fbl.secret.values`

**Aliases:**

* `fbl.secret.values`
* `secret.values`
* `secrets`
* `secret`

**Example 1: Assign values to secrets root directly:**

```yaml
secrets:
  '$':
    inline:
      something: true
      else: false
```

**Example 2: Assign values from file "vars.yml" to field "vars -> files":**

```yaml
secrets:
  $.vars:
    files:
      - vars.yml
```

**Example 3: Assign values from file "vars.yml" after inline ones:**

```yaml
secrets:
  '.':
    inline:
      test: true
    files:
      - vars.yml

    # [optional] specify that files have a priority over inline vars
    # if not provided inline vars will have priority over files
    priority: 'files'
```

**Example 4: Override instead of assigning**

```yaml
secrets:
  '$.test':
    inline:
      test: true
    # [optional] override everything tha tis inside "test" object with { test: true }
    # use with caution
    override: true
```

**Example 5: Push to array**

```yaml
secrets:
  '$.test':
    inline: 1
    # [optional] override everything tha tis inside "test" object with { test: true }
    # use with caution
    override: true
    # [required] if you want to push inline or value(s) from file(s) to target's array
    push: true
    # [optional] if enambled and value is array its child items will be pushed instead of array itself
    children: false
```

**Example 6: Find files by mask:**

```yaml
secrets:
  '$':
    files:
      - vars/*.yml
```

## Action Handler: Summary

Add summary record. All summary records will be printed once the main flow ends.

**ID:** `com.fireblink.fbl.context.summary`

**Aliases:**

* `fbl.context.summary`
* `context.summary`
* `summary`

**Example:**

```yaml
summary:
  # summary record title
  title: Step Title
  # summary record status
  # statuses (ignoring case):
  # - 'created', 'updated', 'passed', 'success', 'ok', 'yes' will be colored in green
  # - 'deleted', 'failed', 'failure', 'error', 'no' - in red
  # - 'ignored', 'skipped', 'none' - in yellow
  # others will have default text color
  status: Failed
  # [optional] human readable duration (string)
  duration: <%- $.duration(1000) %>
  # [optional] additional data associated with record. Not presented in printed table.
  payload: anything
```


# Execute (Shell & JavaScript)

Plugin allows to invoke any shell command.

Available actions:

* [exec](/plugins/exec#action-handler-shell-command) - execute single command
* [shell](/plugins/exec#action-handler-shell-script) - execute shell script
* [fn](/plugins/exec#action-handler-function) - execute JS function

## Action Handler: Shell Command

Invoice single shell command.

**ID:** `com.fireblink.fbl.exec`

**Aliases:**

* `fbl.exec`
* `exec`

**Example:**

```yaml
exec:
  # executable to invoke
  command: 'echo'

  # additional executable arguments, note "command" cannot have arguments in its value, just executable alias or path to it
  args:
    - 'test'

  # [optional] working directory to run command from.
  # Default value: flow's folder
  wd: <%- cwd %>

  # [optional] options
  options:
    # [optional] if provided "stdout" will be included inside assigned object to proviced "ctx" and/or "secrets" name
    stdout: true
    # [optional]  if provided "stderr" will be included inside assigned object to proviced "ctx" and/or "secrets" name
    stderr: true
    # [optional] if provided - stdout and stderr will be logged in report and printed to console
    verbose: false

  # [optional] assign execution result {code: 0-255, stdout?: string, stderr?: string }
  assignResultTo: # follows common assignment logic practicies https://fbl.fireblink.com/plugins/common#assign-to

  # [optional] push execution result {code: 0-255, stdout?: string, stderr?: string }
  pushResultTo: # follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to
```

## Action Handler: Shell Script

Invoke shell script. While it gives a more convinient way to integrate shell scripts into the flow it also makes your flow less platform agnostic.

**ID:** `com.fireblink.fbl.shell`

**Aliases:**

* `fbl.shell`
* `shell`

**Example:**

```yaml
shell:
  # shell executable
  executable: '/bin/bash'

  # script to be invoked
  script: |-
    cd /tmp
    touch test.txt

  # [optional] working directory to run script from.
  # Default value: flow's folder
  wd: <%- cwd %>

  # [optional] options
  options:
    # [optional] if provided "stdout" will be included inside assigned object to proviced "ctx" and/or "secrets" name
    stdout: true
    # [optional] if provided "stderr" will be included inside assigned object to proviced "ctx" and/or "secrets" name
    stderr: true
    # [optional] if provided - stdout and stderr will be logged in report and printed to console
    verbose: false

  # [optional] assign execution result {code: 0-255, stdout?: string, stderr?: string }
  assignResultTo: # follows common assignment logic practicies https://fbl.fireblink.com/plugins/common#assign-to

  # [optional] push execution result {code: 0-255, stdout?: string, stderr?: string }
  ushResultTo: # follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to
```

## Action Handler: Function

Allows to invoke custom JavaScript script (ES6). Script has access to all context variables:

* cwd
* ctx
* secrets
* parameters
* iteration

But also to Node.js `require` function and can interact with awailable node modules. Though, it is recomended to use fbl plugins instead.

**ID:** `com.fireblink.fbl.function`

**Aliases:**

* `fbl.function`
* `function`
* `function()`
* `fn`
* `fn()`

**Example: Direct Changes**

```yaml
# Action handler expects valid JS function content string.
#
# Note: script is wrapped into async function, so you can "await" promises inside it
# if you need to do some long running operations.
fn: |-
  ctx.isWindows = require('os').platform() === 'win32';
```

**Example: Overrides**

Alternativelly function can return object that will be used to override entire state of context and parameters fields.

```yaml
fn: |-
  return {
    cwd: '/tmp',
    ctx: {
      test: true
    }      
  }
```

```yaml
fn: |-
  return {
    secrets: {
      test: true
    },
    parameters: {
      p1: ctx.p2
    }
  }
```


# File System

File System plugin.

Available actions:

* [->](/plugins/fs#action-handler-write-to-file) - write to file.
* [mkdir](/plugins/fs#action-handler-create-directories) - create directory
* [rm](/plugins/fs#action-handler-remove-file-or-directory) - remove file or directory
* [mv](/plugins/fs#action-handler-move-file-or-directory) - move file or directory
* [cp](/plugins/fs#action-handler-copy-file-or-directory) - copy file or directory
* [find](/plugins/fs#action-handler-find-files) - find files

## Action Handler: Write to file

**ID:** `com.fireblink.fbl.fs.file.write`

**Aliases:**

* `fbl.fs.file.write`
* `fs.file.write`
* `file.write`
* `->`

**Example 1: Define File Content Inline**

```yaml
->:
  # [optional] file path, if not provided content will be written to temporary location directory.
  # Note: all missing parent directories will be automatically created
  path: /tmp/test.txt

  # [optional]
  # Note: required if "path" (above) and "assignPathTo" (below) is not provided.
  assignPathTo: # follows common assign logic practicies https://fbl.fireblink.com/plugins/common#assign-to

  # [optional]
  # Note: required if "path" and "assignPathTo" (above) is not provided
  pushPathTo: # follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to

  # [required] content of the file
  content: |-
    test content
```

**Example 2: Define File Content From Other File**

```yaml
->:
  # [optional] file path, if not provided content will be written to temporary location directory.
  # Note: all missing parent directories will be automatically created
  path: /tmp/test.txt

  # [optional]
  # Note: required if "path" (above) and "assignPathTo" (below) is not provided.
  assignPathTo: # follows common assign logic practicies https://fbl.fireblink.com/plugins/common#push-to

  # [optional]
  # Note: required if "path" and "assignPathTo" (above) is not provided
  pushPathTo: # follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to

  # [required] template file
  # Note: global and then local EJS template processing will be applied to the template before writing
  contentFromFile: /tmp/template.ejs
```

## Action Handler: Create Directories

Create directory (and all parent ones if missing).

**ID:** `com.fireblink.fbl.fs.dir.create`

**Aliases:**

* `fbl.fs.dir.create`
* `fs.dir.create`
* `dir.create`
* `mkdir -p`
* `mkdir`

**Example:**

```yaml
# create "child" directory
# if "parent" is missing it will be also created
mkdir: /tmp/parent/child
```

## Action Handler: Remove File or Directory

Removes file or directory for given path.

**ID:** `com.fireblink.fbl.fs.remove`

**Aliases:**

* `fbl.fs.remove`
* `fbl.fs.rm`
* `fs.remove`
* `fs.rm`
* `rm -rf`
* `remove`
* `rm`

```yaml
# remove "child"
rm: /tmp/parent/child
```

## Action Handler: Move File or Directory

Allows to move/rename file or entire directory.

**ID:** `com.fireblink.fbl.fs.move`

**Aliases:**

* `fbl.fs.move`
* `fbl.fs.mv`
* `fs.move`
* `fs.mv`
* `move`
* `mv`

**Example 1: Move file to other folder**

```yaml
mv:
  # move file.txt
  from: /tmp/source/file.txt

  # to "target" folder
  # note: slash in the end is required if you want to specify a target folder
  to: /tmp/target/
```

**Example 2: Move file to other folder and rename it**

```yaml
mv:
  # move file.txt
  from: /tmp/source/file.txt

  # to "target" folder and rename it to "renamed.txt"
  to: /tmp/target/renamed.txt
```

**Example 3: Move folder contents to other folder**

```yaml
mv:
  # move everything from "source" folder
  # note: slash in the end is required if you want to move folder contents rather then the folder itself
  from: /tmp/source/

  # to "target"
  to: /tmp/target
```

## Action Handler: Copy File or Directory

Allows to copy file or entire directory.

**ID:** `com.fireblink.fbl.fs.copy`

**Aliases:**

* `fbl.fs.copy`
* `fbl.fs.cp`
* `fs.copy`
* `fs.cp`
* `copy`
* `cp`

**Example 1: Copy file to other folder**

```yaml
cp:
  # copy file.txt
  from: /tmp/source/file.txt

  # to "target" folder
  # note: slash in the end is required if you want to specify a target folder
  to: /tmp/target/
```

**Example 2: Copy file to other folder with different name**

```yaml
cp:
  # copy file.txt
  from: /tmp/source/file.txt

  # to "target" folder and name it "renamed.txt"
  to: /tmp/target/renamed.txt
```

**Example 3: Copy folder contents to other folder**

```yaml
cp:
  # copy everything from "source" folder
  # note: slash in the end is required if you want to copy folder contents rather then the folder itself
  from: /tmp/source/

  # to "target"
  to: /tmp/target
```

## Action Handler: Find Files

Find files by mask.

**ID:** `com.fireblink.fbl.fs.find`

**Aliases:**

* `fbl.fs.find`
* `fs.find`
* `find`

**Example:**

```yaml
find:
  # [required] list of masks to find files
  include:
    - /tmp/**/*.txt

  # [optional] list of masks to exclude
  exclude:
    - /tmp/**/*.tmp.txt

  # [required] result storage configuration
  result:
    # [optional] if provided all paths will be relative to give base directory
    baseDir: /tmp

    # [optional]
    # Note: either `assignTo` or `pushTo` is requred
    assignTo: # follows common assign logic practicies https://fbl.fireblink.com/plugins/common#push-to

    # [optional]
    # Note: either `assignTo` or `pushTo` is requred
    pushTo: # follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to
```


# CLI Prompts

Prompt user over CLI.

Available actions:

* [confirm](/plugins/prompts#action-handler-confirm) - request confirmation
* [prompt](/plugins/prompts#action-handler-prompt) - prompt to type the value
* [select](/plugins/prompts#action-handler-select) - prompt to select one options from the list
* [multiselect](/plugins/prompts#action-handler-multiselect) - prompt to select multiple options from the list

## Action Handler: Confirm

Ask user to confirm something.

**ID:** `com.fireblink.fbl.cli.prompts.confirm`

**Aliases:**

* `fbl.cli.prompts.confirm`
* `cli.prompts.confirm`
* `prompts.confirm`
* `confirm`

**Example:**

```yaml
confirm:
  # [required] confirm message
  message: 'Are you sure you want to proceed?'

  # [optional] default response value
  default: false

  # [optional] either "assignResponseTo" or "pushResponseTo" should exist
  assignResponseTo: # follows common assignment logic practicies https://fbl.fireblink.com/plugins/common#assign-to

  # [optional] either "assignResponseTo" or "pushResponseTo" should exist
  pushResponseTo: # follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to
```

## Action Handler: Prompt

Ask user to type the value.

**ID:** `com.fireblink.fbl.cli.prompts.prompt`

**Aliases:**

* `fbl.cli.prompts.prompt`
* `cli.prompts.prompt`
* `prompts.prompt`
* `prompt`

**Example:**

```yaml
prompt:
  # [required] message to print for user
  message: 'What is your name?'

  # [optional] whether answer should be masked
  password: false

  # [optional] default response
  default: 'anonymous'

  # [optional] json schema validation schema
  # note: only "string", "integer" and "number" types are supported
  schema:
    type: string

  # [optional] either "assignResponseTo" or "pushResponseTo" should exist
  assignResponseTo: # follows common assignment logic practicies https://fbl.fireblink.com/plugins/common#assign-to

  # [optional] either "assignResponseTo" or "pushResponseTo" should exist
  pushResponseTo: # follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to
```

## Action Handler: Select

Ask user to select one given answer from provided options.

**ID:** `com.fireblink.fbl.cli.prompts.select`

**Aliases:**

* `fbl.cli.prompts.select`
* `cli.prompts.select`
* `prompts.select`
* `select`

**Example:**

```yaml
select:
  # [required] message to print for user
  message: 'Pick your age:'

  # [optional] default selected option
  default: "I don't want to answer"

  # [required] list of options user needs to pick answer from
  options:
    - I don't want to answer
    - under 21
    - 21 - 59
    - 60+
    # there is also a way to specify different values and titles
    - title: Infinity
      value: 0

  # [optional] either "assignResponseTo" or "pushResponseTo" should exist
  assignResponseTo: # follows common assignment logic practicies https://fbl.fireblink.com/plugins/common#assign-to

  # [optional] either "assignResponseTo" or "pushResponseTo" should exist
  pushResponseTo: # follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to
```

## Action Handler: Multi Select

Aks user to pick one or more options.

**ID:** `com.fireblink.fbl.cli.prompts.multiselect`

**Aliases:**

* `fbl.cli.prompts.multiselect`
* `cli.prompts.multiselect`
* `prompts.multiselect`
* `multiselect`

**Example:**

```yaml
multiselect:
  # [required] message to print for user
  message: 'Select tags:'

  # [optional] default selected options
  default: ['music']

  # [required] list of options user needs to pick answer from
  options:
    - music
    - art
    # there is also a way to specify different values and titles
    - title: Original Tags
      value: '*'

  # [optional] either "assignResponseTo" or "pushResponseTo" should exist
  assignResponseTo: # follows common assignment logic practicies https://fbl.fireblink.com/plugins/common#assign-to

  # [optional] either "assignResponseTo" or "pushResponseTo" should exist
  pushResponseTo: # follows common push logic practicies https://fbl.fireblink.com/plugins/common#push-to
```


# Execution Reporters

Execution flow can be presented in different output formats. FBL supports only 2 generic ones: json and yaml. All other reporters should be provided as 3rd-party plugins.

**CLI examples:**

```bash
fbl \
    -o /tmp/fbl.json \
    -r json \
    sample.flow.yml
```

```bash
fbl \
    -o /tmp/fbl.yml \
    -r yaml \
    sample.flow.yml
```

**3rd Party Reporter options:**

```bash
fbl \
    -o /tmp/fbl.3rd \
    -r 3rd.party \
    --report-option key=value \
    sample.flow.yml
```


# Common Options

This section contains common option definitions for action handlers.

## Assign To

Commonly used to assign action results to context fields.

**Example 1: Direct assignment**

Allow to assign value directly to

```yaml
# assign value to context's `ctx` field `target`
# <assignTo> is action specific name and may generally can be either simply `assignTo` or more descrete `assignSomethingTo` 
'<assignTo>': $.ctx.target
```

Short name syntax accepts a [path](/glossary#path) to context field.

When path assignment targets [context](/plugins/context) following rules are applied: Value assignment is only possible to `ctx`, `secrets` or `parameters`.

**Example 2: Multi assignment**

Then example above can also be written in the following way:

```yaml
'<assignTo>': 
  ctx: $.target
```

The main advantage of this alternative syntax is the ability to assign to multiple values and override the entire target object.

Let's say we want to save value into both `ctx` and `parameters`, we can leverage that with:

```yaml
'<assignTo>': 
  ctx: $.target
  parameters: $.paramsTarget
```

Another example is with field override functionality.

Let assume we currently have the following context state:

```yaml
ctx:
  something:
    a: 1
    b: 2
```

and we want to assign to `ctx.something` entirell new object:

```yaml
'<assignTo>': 
  ctx: '$.something'
  # if override is not provided assigned value and existing will be merged
  override: true
```

## Push To

Another common usecase is to push some value into array.

```yaml
# push some value to `ctx.something` field array
# if field is missing - it will be created with array and one pushed element in it 
'<pushTo>': '$.ctx.something'
```

Similar to `assignTo` functionality `pushTo` can push values into multiple targets, override original value.

But additionally to that there is one more option that works only when pushed value is array - pushing its items to the target array instead of the array itself:

```yaml
'<pushTo>':
  ctx: '$.target'
  # if children option is passed value's array items will be pushed to `target` array instead of pushing value itself
  children: true
```


# Anti-Patterns & Pitfalls

Due to flexible nature of FBL lots of the things can be handled in multiple way. This documentation will help to avoid usage of some patterns that may cause problems in future.

* [EJS Escape Syntax](https://github.com/FireBlinkLTD/fbl/tree/f9756916eccf77649a7bd0a3405c8400b0f68f42/docs/pitfalls/ejs-escape.md)
* [Share parameters in sequence](/pitfalls/sequence-share-parameters)


# Sequence Action Handler - "shareParameters" option

`shareParameters` allow to share parameters across child actions instead of creating cloned version to split branches.

Usage example:

```yaml
# set default parameters
$parameters:
    test: false

'--':
    - fn: |-
        parameters.test = true

    - ctx:
        '$.test': 
            inline: $ref:parameters.test
```

As a result of execution `ctx.test` will have `false` value, ad `--` clones parameters for each of the actions. Meaning `fn` function that modifies the `parameters` will have no affect on `ctx`.

But, if we add `$shareParameters` metadata, things will change:

```yaml
# set default parameters
$parameters:
    test: false

'--':
  # tell action to share parameters
  shareParameters: true
  actions:
    - fn: |-
        parameters.test = true

    - ctx:
        '$.test': 
            inline: $ref:parameters.test
```

As a result of execution `ctx.test` will now have `true` value.

**Note:** this is actually an anti-pattern: `parameters` modification is shadowed and might not be obvious. Make sure to use `shareParameters` only when necessary.


# Packages

Sharing the flow is one of the critical aspects of complex automation tasks. Imagine you created a deployment flow for one of your services. But now you need to deploy it to another environment. Of course, you can just copy and paste your flow, but any fixes or changes will be hard to sync.

Package it. Any flow can be easily packaged and imported in other flow or invoked directly via local path or even URL.

Requirements:

* File extension should be either `.tar.gz` or `.tgz`.
* `index.yml` file should be located in root of the tarball or in any child directory if each parent dir has only one child.

## Package usage

You can reference tarball as any other flow by just providing path to it, e.g.:

`fbl path/to/flow.tar.gz`

or inside the flow:

```yaml
version: 1.0.0
pipeline:
  '--':
    # reference the flow stored locally
    - '@': path/to/flow.tar.gz
    # reference the flow stored remotely
    - '@': http://storage.com/another_flow.tar.gz
```

## Packaging - Tarball Creation

### Unix

Let's assume your flow is located in folder called `~/flows/sample`

To create a tarball just navigate to flow's parent directory (`flows` in this example) and execute following command:

```bash
# create a package named sample.tar.gz with all the files inside sample folder 
tar czf sample.tar.gz sample
```


# Glossary

FBL specific terms and their definitions.

## Action handler

Any action that one can use in his/her flow is served by Node.js plugin with one or more associated ActionHandler classes.

In the flow file action handler can be referenced via its ID or one of the specified aliases.

## Context

Every action has access to the shared object that is called `context`.

FBL Context has a following format:

```yaml
# directory path from where fbl command was executed
cwd: string

# non-secret values storage
ctx:
  key: value

# secret values storage
secrets:
  key: secret

# Summary records that will be printed in the end of execution in a nice ASCII table for user
# Generally used to present tests results or simple tracing reports for user
summary:
  - title: Count to 5
    status: SUCCESS
    duration: 5ms
    payload:
      additional: information
```

## Flow

One or more actions combined into together with defined order of execution.

## Template

Dynamic value processing and assignment inside the flow is handled by EJS template.

## Path

FBL uses its own concept of referencing paths to read from and write to context fields. And generally have a following look `$.parent.child`.

Rules:

* Path should always start with `$`.
* `$` defines root field. It can be either context or some other target based on the context of usage.
* `.` is used to define relationship between parent and child fields. Where parent is always goes first.
* Path should be read from left to right. Where left represent parent fields and right - their nested ones.
* Path can not reference array elements. Path traveling is only possible to the deepest nested object or first found field with basic type value (number, boolean, string) or array.

## Parameters

Parameters is additional shared object like [context](/glossary#context), but it is not shared across all actions, but only with ones where parameters are defined and all nested ones (if any). Generally parameters are used to isolate specific values from global scope.

## Plugin

Embedded or external Node.js module that has specific structure and exposes action handlers, template utility functions or custom reporters.


