Tasks#
Tasks enable the organized execution of commands and scripts. After
execution, a report is generated, containing the status and embedded
documentation of the task and scripts in a self-contained format. To run the
task produced by cijoe --example, use the following command:
# Run the task
cijoe cijoe-example-core.default/cijoe-task.yaml \
--config cijoe-example-core.default/cijoe-config.toml
The command will execute all the
steps in the task. To run a subset of
steps, you can specify the step name(s) as arguments to the cijoe tool,
similar to how targets are specified in a Makefile:
# Run a subset of the steps in the task
cijoe cijoe-example-core.default/cijoe-task.yaml \
--config cijoe-example-core.default/cijoe-config.toml \
inline_commands
With the above, only the step named inline_commands will be executed. This becomes even more useful when utilizing cijoe bash completions.
There are a couple of task-specific options. See the Task Configuration section for reference.
Content Overview#
Let’s take a look at what the task file produced by cijoe --example core.default
looks like:
---
doc: |
This is a task file, it serves as an example on how to run commands and
scripts, the structure intentionally mimics that of GitHub actions, however,
the keys you see here are all there is.
Running commands, as you can see below, looks just like running commands in a
GitHub Workflow
* Add the 'run' key with a value of multi-line string
Using scripts, it is similar to that of a GitHub action
* Add the 'uses' key with the name of the script
- packaged scripts have a namespaced name e.g. "my_pkg.my_script"
- non-packaged do not e.g. "my_script"
* Add the 'with' key providing arguments to the script
The commands and the scripts are passed an instance of cijoe which they can
use to call run() / get() / put(), with an output-directory matching the
current step. This is it, end of story.
steps:
- name: inline_commands
run: |
cat /proc/cpuinfo
hostname
- name: script_with_args
uses: core.cmdrunner
with:
commands:
- cat /proc/cpuinfo
- hostname
- name: example_script
uses: core.example_script_default
with:
repeat: 2
#
# Uncomment the lines below to include the non-packaged script
# 'cijoe-script.py', from the directory where you ran 'cijoe --example'
#
#- name: custom_script
# uses: cijoe-script
#
# Any step can name the target it runs on with 'target:'. Without it, a step runs
# on the default target: 'cijoe.default_target', else the first target the
# configuration describes ('initiator' if none are).
#
#- name: inline_commands_on_remote
# target: ssh
# run: |
# hostname
# uname -a
At a first glance, then it might feel a bit similar to GitHub Actions workflow, but dramatically simpler since:
There are no logic operators
There is simple variable substitution using
Values from configuration file
Values from environment variables on initiator
Facts that earlier steps wrote, as
{{ target.kernel.release }}, substituted when the step starts rather than when the task is loaded; see Facts
Minimal amount of “magic” keys
doc: Describe what the task does using multi-line plain-textsteps: Ordered list of scripts, to inline-commands, to run
Descriptions of the content are provided in the following subsections.
Steps#
Although cijoe aims to be simple, with minimal “magic” and a low learning curve, there is some yaml-magic involved in the task steps. A step can take one of two forms: either as Inline Commands or as Steps with Scripts.
Both forms require that a step must have a name. This allows subsets
of steps to be executed via the cijoe command-line tool. When naming steps,
follow these conventions:
Letters: a-z
Numbers: 0-9
Special characters: - and _
Must be lowercase
Must not start with a number
In short, use the typical lowercase identifier convention.
Inline Commands#
A step with inline commands take the form:
- name: inline_commands
run: |
cat /proc/cpuinfo
hostname
Each line in a multi-line string is executed. It is implemented as a call to
cijoe.run(command). Thus, the above notation for inline commands turns
into execution of functions in the cijoe Python module:
cijoe.run("cat /proc/cpuinfo")
cijoe.run("hostname")
Note
This is implemented in cijoe as “syntactic-sugar” for running the built-in script core.cmdrunner. Thus, have a look at Steps with Scripts to see what this unfolds as.
Because run: is sugar for core.cmdrunner, you can pass any of its
arguments alongside run: using a with: block. See
core.cmdrunner for the full set of
arguments.
Choosing the target of a step#
A step runs on the default target, see
Target Configuration. To run a step elsewhere, name the target
with target:, beside run: or uses::
- name: nvme_facts
target: storage_node
uses: core.facts_gather
with:
sections: "nvme"
- name: storage
target: storage_node
run: |
echo "{{ target.nvme | length }} NVMe controller(s)"
lsblk -f
- name: tools
target: storage_node
uses: core.tools_check
with:
tools: "fio,nvme"
Here storage reads the NVMe controllers that nvme_facts found on
storage_node, and lists its block devices; its tools are checked too.
target: works for any script: everything the step does on “the target”, its
commands, transfers, tool checks and its {{ target.* }} references to facts,
is about the one it names. The step after it is back on the default target. A
name the configuration does not describe is an error when the task loads, and
for cijoe -i.
Before target:, scripts took arguments of their own for this, such as
with: transport: for core.cmdrunner. Those still work, and warn.
Steps with Scripts#
When a step runs a script, you give it a name and tell it which script to run, like so:
- name: script_with_args
uses: core.cmdrunner
with:
commands:
- cat /proc/cpuinfo
- hostname
Take note of the “magic” keys:
- uses
Name of the script to run, without the
.pyextension. Packaged scripts include a prefix, such ascore.orlinux.. As in the example above where the scriptcmdrunner.pyfrom thecorepackage is used (core.cmdrunner).- with
Everything under this key is passed to the script’s entry function:
main(args, cijoe)in theargsargument.
Linting#
When you write a task yourself it can be nice to check whether it is valid without running it. You can do so by running:
# Check format of task and verify existance of the scripts used
cijoe --integrity-check \
--config cijoe-example-core.default/cijoe-config.toml \
cijoe-example-core.default/cijoe-task.yaml