Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add env to the tasks to specify tasks specific environment variables #972

Merged
merged 10 commits into from
Apr 18, 2024
28 changes: 28 additions & 0 deletions docs/features/advanced_tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,34 @@ Note: if you want to debug the globs you can use the `--verbose` flag to see whi
pixi run -v start
```

## Environment variables
You can set environment variables for a task.
These are seen as "default" values for the variables as you can overwrite them from the shell.

```toml title="pixi.toml"
[tasks]
echo = { cmd = "echo $ARGUMENT", env = { ARGUMENT = "hello" } }
```
If you run `pixi run echo` it will output `hello`.
When you set the environment variable `ARGUMENT` before running the task, it will use that value instead.

```shell
ARGUMENT=world pixi run echo
✨ Pixi task (echo in default): echo $ARGUMENT
world
```

These variables are not shared over tasks, so you need to define these for every task you want to use them in.

!!! note "Extend instead of overwrite"
If you use the same environment variable in the value as in the key of the map you will also overwrite the variable.
For example overwriting a `PATH`
```toml title="pixi.toml"
[tasks]
echo = { cmd = "echo $PATH", env = { PATH = "/tmp/path:$PATH" } }
wolfv marked this conversation as resolved.
Show resolved Hide resolved
```
This will output `/tmp/path:/usr/bin:/bin` instead of the original `/usr/bin:/bin`.

## Our task runner: deno_task_shell

To support the different OS's (Windows, OSX and Linux), pixi integrates a shell that can run on all of them.
Expand Down
3 changes: 3 additions & 0 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ simple = "echo This is a simple task"
cmd = { cmd="echo Same as a simple task but now more verbose"}
depending = { cmd="echo run after simple", depends_on="simple"}
alias = { depends_on=["depending"]}
download = { cmd="curl -o file.txt https://example.com/file.txt" , outputs=["file.txt"]}
build = { cmd="npm build", cwd="frontend", inputs=["frontend/package.json", "frontend/*.js"]}
run = { cmd="python run.py $ARGUMENT", env={ ARGUMENT="value" }}
```

You can modify this table using [`pixi task`](cli.md#task).
Expand Down
5 changes: 3 additions & 2 deletions examples/ros2-nav2/pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ authors = ["Ruben Arts <ruben@prefix.dev>"]
channels = ["conda-forge", "robostack-staging"]
platforms = ["linux-64", "osx-arm64", "osx-64"]

[tasks]
start = {cmd="export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:$PIXI_PROJECT_ROOT/.pixi/env/share/turtlebot3_gazebo/models && export TURTLEBOT3_MODEL=waffle && ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False"}
[tasks.start]
env = { GAZEBO_MODEL_PATH = "$GAZEBO_MODEL_PATH:$PIXI_PROJECT_ROOT/.pixi/env/share/turtlebot3_gazebo/models", TURTLEBOT3_MODEL = "waffle" }
cmd="ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False"

[dependencies]
ros-humble-desktop = "*"
Expand Down
1 change: 1 addition & 0 deletions schema/examples/valid/full.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ test3 = { cmd = "pytest", depends_on = ["test2"] }
test4 = { cmd = "pytest", cwd = "tests", depends_on = ["test2"] }
test5 = { cmd = "pytest" }
test6 = { depends_on = ["test5"] }
test7 = { cmd = "pytest", cwd = "tests", depends_on = ["test5"], env = {PYTHONPATH = "bla", "WEIRD_STRING" = "blu"}}

[system-requirements]
linux = "5.10"
Expand Down
3 changes: 3 additions & 0 deletions schema/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ class TaskInlineTable(StrictBaseModel):
outputs: list[Glob] | None = Field(
None, description="A list of glob patterns that are generated by this command"
)
env: dict[NonEmptyStr, NonEmptyStr] | None = Field(
None, description="A map of environment variables to values, used in the task, these will be overwritten by the shell." , examples = [{"key": "value"}, {"ARGUMENT": "value"}]
)


#######################
Expand Down
25 changes: 25 additions & 0 deletions schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,31 @@
"default": null,
"description": "A list of glob patterns that are generated by this command",
"title": "Outputs"
},
"env": {
"anyOf": [
{
"additionalProperties": {
"minLength": 1,
"type": "string"
},
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "A map of environment variables to values, used in the task, these will be overwritten by the shell.",
"examples": [
{
"key": "value"
},
{
"ARGUMENT": "value"
}
],
"title": "Env"
}
},
"title": "TaskInlineTable",
Expand Down
1 change: 1 addition & 0 deletions src/cli/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ impl From<AddArgs> for Task {
inputs: None,
outputs: None,
cwd: value.cwd,
env: Default::default(),
})
}
}
Expand Down
16 changes: 15 additions & 1 deletion src/task/executable_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,23 @@ impl<'p> ExecutableTask<'p> {
return Ok(None);
};

// Append the environment variables if they don't exist
let mut export = String::new();
if let Some(env) = self.task.env() {
for (key, value) in env {
if value.contains(format!("${}", key).as_str())
|| std::env::var(key.as_str()).is_err()
{
tracing::info!("Setting environment variable: {}={}", key, value);
export.push_str(&format!("export {}={};\n", key, value));
}
tracing::info!("Environment variable {} already set", key);
}
}

// Append the command line arguments
let cli_args = quote_arguments(self.additional_args.iter().map(|arg| arg.as_str()));
let full_script = format!("{task} {cli_args}");
let full_script = format!("{export}\n{task} {cli_args}");

// Parse the shell command
deno_task_shell::parser::parse(full_script.trim())
Expand Down
14 changes: 14 additions & 0 deletions src/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use itertools::Itertools;
use serde::{Deserialize, Serialize};
use serde_with::{formats::PreferMany, serde_as, OneOrMany};
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf};

Expand Down Expand Up @@ -129,6 +130,16 @@ impl Task {
}
}

/// Returns the environment variables for the task to run in.
pub fn env(&self) -> Option<&HashMap<String, String>> {
match self {
Task::Plain(_) => None,
Task::Custom(_) => None,
Task::Execute(exe) => exe.env.as_ref(),
Task::Alias(_) => None,
}
}

/// Returns the working directory for the task to run in.
pub fn working_directory(&self) -> Option<&Path> {
match self {
Expand Down Expand Up @@ -167,6 +178,9 @@ pub struct Execute {

/// The working directory for the command relative to the root of the project.
pub cwd: Option<PathBuf>,

/// A list of environment variables to set before running the command
pub env: Option<HashMap<String, String>>,
wolfv marked this conversation as resolved.
Show resolved Hide resolved
}

impl From<Execute> for Task {
Expand Down
Loading