🌊 Workflows
A workflow is an orchestration of actions. It can be triggered by an event, or it can be invoked by another workflow. In this section, we'll walk you through all the building blocks of a workflow.
AutoPR comes with a few workflows, which you can find in the workflow catalogue.
If you'd like to contribute a workflow, see the tutorial.
🌱 The Hello World Example
hello:
outputs:
- name
- message
steps:
- set_vars:
message:
template: "Hello {{ name }}!"
hello_world:
outputs:
- message
steps:
- workflow: hello
inputs:
name:
const: "world"
outputs:
message: message
🗝️ Workflow Skeleton
A workflow definition has the following structure:
workflow_name:
inputs:
- input1
- input2
outputs:
- output1
- output2
steps:
- action: action1
- workflow: workflow1
- action: action2
- ...
This defines a workflow with the name workflow_name
,
which has two inputs named input1
and input2
, and two outputs named output1
and output2
.
In the steps
section, we operate with inputs to produce the desired outputs.
Both inputs
and outputs
are optional.
🏃 Steps
There are multiple different possible steps to use in a workflow. Each step has a different purpose and syntax.
set_vars
Workflows use the set_vars
step to declare and construct variables.
The same syntax can be used in action and workflow inputs, defaulting to template
.
- Constants
- Templates
- Lambdas
- Params
- Variables
In AutoPR workflows, you can define constants with the const
keyword.
These hold various types (strings, integers, lists, dictionaries, etc.)
set_constant_workflow:
outputs:
- int_const
- str_const
- list_const
- dict_const
steps:
- set_vars:
int_const:
const: 1
str_const:
const: "Hello world!"
list_const:
const:
- 1
- 2
- 3
dict_const:
const:
key1: value1
key2: value2
Here, we've defined a workflow named set_constant_workflow
that defines and outputs four constants:
int_const
with value1
str_const
with value"Hello world!"
list_const
with value[1, 2, 3]
dict_const
with value{"key1": "value1", "key2": "value2"}
You'll notice a similar pattern in the other examples as well.
template
uses Jinja2 templating to generate the variable's value. They can be useful when you already have a variable
that you want to use as a template for another variable.
set_template_workflow:
outputs:
- templated_var
steps:
- set_vars:
var_to_template:
const: "World"
templated_var:
template: 'Hello, {{ template }}!'
lambda
is a Python expression that computes the variable's value.
set_lambda_workflow:
outputs:
- lambda_var
steps:
- set_vars:
first_list:
const:
- 1
- 2
- 3
second_list:
const:
- "a"
- "b"
- "c"
lambda_var:
lambda: "dict(zip(first_list, second_list))"
param
is an optional field that is assigned the value of the variable if it is not defined in the trigger declaration.
set_params_workflow:
outputs:
- param_var
steps:
- set_vars:
param_var:
param:
name: IGNORE_FILES
default: []
If IGNORE_FILES
is defined in the trigger, param_var
will be assigned its value.
Otherwise, param_var
will be assigned an empty list.
In the triggers, such parameters are specified as follows:
triggers:
- name: trigger_name
workflow: set_params_workflow
params:
IGNORE_FILES:
- "file1"
- "file2"
If we've got already variable in scope and we want to assign its value to another variable, this is the way to do it:
set_variable_workflow:
outputs:
- second_variable
steps:
- set_vars:
first_variable:
const: "Hello world!"
second_variable:
var: first_variable
action
action
is a step that allows you to invoke an action from the action catalogue.
An action is a predefined operation or task that has inputs and outputs.
One of the actions that comes with AutoPR is the bash
action, which allows you to execute arbitrary bash commands,
so we'll use it as an example:
bash_workflow:
inputs:
- message
outputs:
- stdout
steps:
- action: bash
inputs:
command:
template: |
'echo "{{ message }}"'
outputs:
stdout: stdout
Note how the inputs use the same syntax as the set_vars
step.
When this workflow is triggered, it will execute the bash
action with the given command and return its output in the stdout
output.
workflow
If you build a workflow that performs a specific task, you can invoke it from another workflow with the workflow
step.
This is particularly useful if you either want to reduce complexity of the workflows or
reuse some of the workflows you've already defined.
The available workflows are listed in the workflow catalogue, or you can use one of your custom-defined workflows.
Like invoking an action, invoking a workflow also has inputs and outputs.
- Vars example
- Bash example
Here's an example workflow that combines two variables using the lambda
keyword:
set_vars_workflow:
inputs:
- var1
- var2
outputs:
- var3
steps:
- set_vars:
var3:
lambda: "var1 + var2"
Now, we can define another workflow, which will use the set_vars_workflow
we've just defined.
usage_of_set_vars_workflow:
outputs:
- var3
steps:
- workflow: set_vars_workflow
inputs:
var1:
const: 1
var2:
const: 2
outputs:
var3: var3
This workflow, when triggered, will return value 3
in its var3
output.
We could also invoke the workflow defined above in the action
section:
invocation_of_bash_workflow:
outputs:
- output
steps:
- workflow: bash_workflow
inputs:
message:
const: "Hello world!"
outputs:
stdout: output
Note how the inputs use the same syntax as the set_vars
step.
if_lambda
Conditionals are decision-making steps based on certain conditions, the first of which is the if_lambda
step.
Assuming we've got workflows summarize_file
and summarize_dir
defined, we can use them in a conditional:
summarize_entry:
inputs:
- path
outputs:
- summary
steps:
- if_lambda: not os.path.isdir(path)
then: summarize_file
else: summarize_dir
This workflow has a very important assumption that the summarize_file
and summarize_dir
workflows have variable path
as its only input and summary
as its only output. This is because the summarize_entry
workflow uses the path
input
and outputs.
iterate
If we have a variable that's a list, or we would like to iterate a number of times,
we can use the iterate
step and perform some action or workflow multiple times.
Workflows can iterate over lists or ranges, and combine with the action
and workflow
keyword.
Most notably, they add an as
keyword to define the name of the variable that will be used in the iteration,
and change the outputs
keyword to list_outputs
, to define the list of outputs that will be returned.
- Action iteration over a list
- Action iteration over a range
If iterate
's value is a variable of type list
, the workflow will use iteration over a list.
iter_var_action:
inputs:
- list_of_commands
outputs:
- outputs_list
- concatenated_outputs
steps:
- action: bash
iterate: list_of_commands
as: cmd
inputs:
command:
template: |
'{{ cmd }}'
list_outputs:
stdout: outputs_list
- action: bash
inputs:
command:
template: |
echo '{{ outputs_list | join("") }}'
outputs:
stdout: concatenated_outputs
This workflow will execute all the commands in the list_of_commands
input and return the list of outputs in the outputs_list
output.
If iterate
's value is an integer, it will iterate as many times as specified. Variable, listed in the as
field will
serve as an iterator variable, which will be incremented by one in each iteration.
iter_range_by_index_action:
inputs:
- list_of_commands
outputs:
- outputs_list
- concatenated_outputs
steps:
- action: bash
iterate: 2
as: cmd
inputs:
command:
lambda: '{{ list_of_commands[cmd] }}'
list_outputs:
stdout: outputs_list
- action: bash
inputs:
command:
template: |
echo '{{ outputs_list | join("") }}'
outputs:
stdout: concatenated_outputs
🚀 Let's go!
Great, now you know how to define workflows. The next step is to define your own workflow.