Enhance the Pixi task system to support more composable and flexible tasks.
- Arguments: Allow the user to inject arguments into tasks when using
pixi run
. #502 - Variables: Enable the use of previously defined variables in tasks.
- Dependencies: Introduce the ability to set variables for the tasks you depend on to make them more composable. #957 #1152
- Extensibility: Ensure that the task system is extensible using a custom syntax to also support jinja functions.
- Environment specification: Depend on a task in a specific environment.
This document is heavily inspired by just and task. They are both great tools that have a lot of features that we can learn from. Because of their success, some of the decisions are based on their design choices.
- Phase 1: Implement a variable system that allows the user to define variables in the manifest file and use them in the tasks.
- Phase 2: Implement a jinja function system that allows the user to define functions in the manifest file and use them in the tasks.
[vars]
test-type = "unit"
[tasks.test]
cmd = "pytest tests/{{ test-type }}" # Use the variable in the command
[tasks.test-integration]
depends-on = [{task = "test", vars = {test-type = "integration"}}] # Use the variable in the depends-on task
[tasks.build]
cmd = "cargo build {{ FLAGS }} --target {{ target }}" # Use the variable in the command
# Use * to pass all trailing args to the task (* = zero or more args, + = one or more args), target is a required variable
vars = ["*FLAGS", "target"]
[tasks.build-release]
depends-on = [{task = "build", vars = {target = "x86_64-unknown-linux-gnu", FLAGS = "--release"}}]
$ pixi run test
Runs: pytest tests/unit
$ pixi run test test-type=downstream
Runs: pytest tests/downstream
$ pixi run test-integration
Runs: pytest tests/integration
$ pixi run build
Error: Missing required variable 'target'
$ pixi run build target=x86_64-unknown-linux-gnu
Runs : cargo build --target x86_64-unknown-linux-gnu
$ pixi run build-release
Runs: cargo build --release --target x86_64-unknown-linux-gnu
- Simple string variable, like any toml variable
"string"
- A variable build from other variables
var2 = "{{ test_path }}/path/to/file"
- Definable per
feature
andtarget
similar toactivation
,dependencies
, etc. - Undefined variables evaluate to an empty string, but throws a warning.
- If no
vars
are defined, but the{{ var }}
is used in the command, the variable will be seen as an optional variable. - The variable in
vars
is a required variable, and if not defined, the task will not run. - The variable in
vars
with a*
prefix contains all trailing arguments and is optional. - The variable in
vars
with a+
prefix contains all trailing arguments and is requires atleast one value, thus making it a required variable. - The definition can also be a full dictionary, like
vars = [{name = "threads", required = true, multiple = "false", type = "number", default = "4"}]
.
[tasks.test]
cmd = "pytest tests/{{ test-type }}"
[tasks]
test-all = {task = "test", vars = {test-type = "all"}}
# Equlivalent to: test-all = { depends-on = [{task = "test", vars = {test-type = "all"}}]}
current-platform
: the platform used to run the taskcurrent-environment_name
: the name of the environment used to run the taskprefix
: the prefix pathtask
: the name of the task used to run the taskcwd
: the current working directorypixi-version
: the version of pixiversion
: the version of the workspace
If a value type would be defined it could be used to early out.
[tasks.test]
cmd = "pytest tests/{{ test-type }} -n {{ threads }}"
vars = [{name = "threads", type = "number"}]
$ pixi run test threads=four
Error: Invalid value for 'threads': four is not a valid number
bool
: a boolean variable, that can be used in the command (true/false/yes/no/1/0)number
: an integer variable, that can be used in the command (1/1.2)string
: a string variable, that can be used in the command (any string)
The variables become really powerful when we can use jinja functions to manipulate them.
This can be copied for a large part from rattler-build
Or from minijinja
[vars]
# Normal string variable
hello = "world"
# Use the jinja function to get the current platform
platform = "{{ current_platform() }}"
# Use the jinja function to run a command (using our runner) and use the output as a variable
commit = "{{ cmd('git log -n 1 --format=%h') }}"
# Use the jinja function to get an environment variable in a cross-platform way
my_env_var = "{{ env.get('MY_ENV_VAR') }}"
[tasks.build]
cmd = "cargo build --target {{ platform }} | echo build commit {{ commit }}" # Use the global variables in the command
[tasks]
hello = "echo hello {{ hello }}"
# Use the jinja function to check if the platform variable contains linux
hello-you = "echo hello {{ 'tux' if target.contains('linux') else 'you' }}"
- Should we use
{{ }}
or${{ }}
for the syntax?
@ruben-arts personally, I'd prefer the more verbose one. For the other, one it isn't immediately obvious what it does, and the use case isn't common enough that everyone will just get used to it.