Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
76bcfc4
feat: override environment variables based on priority and export all…
magentaqin Jun 12, 2025
c62be75
draft: override environment varibales of run_activation function in r…
magentaqin Jun 18, 2025
30929bd
feat: [activation.env] should override activation scripts
magentaqin Jun 19, 2025
ab6dacc
chore: remove unecessary blank line
magentaqin Jun 20, 2025
97c5921
fix: remove unnecessary binding
magentaqin Jun 20, 2025
10b0607
test: update test for 'get_export_specific_task_env' and 'as_script'
magentaqin Jun 20, 2025
42535ac
test: update unit tests
magentaqin Jun 25, 2025
9be1dc2
fix: exclude certain keys
magentaqin Jun 25, 2025
bcf0ada
style: fix lint issues
magentaqin Jun 25, 2025
418ef0f
test: add integration test for environment variable priority'
magentaqin Jun 26, 2025
edee77f
test: compare priority of activation.env and activation.script
magentaqin Jun 26, 2025
237d5f5
fix: Platform-specific export format
magentaqin Jun 26, 2025
98ff4c4
fix: Platform-specific export format with proper escaping
magentaqin Jun 26, 2025
8b0e9e6
fix: escape opening parenthesis
magentaqin Jun 26, 2025
289f389
fix: Early return if task.env() is empty
magentaqin Jun 26, 2025
dbdc7bd
fix: If task.env() and command_env don't have duplicated keys, simpl…
magentaqin Jun 26, 2025
8784f5c
test: add test for activation.script > activation scripts from depen…
magentaqin Jun 26, 2025
14325f2
test: update unit test
magentaqin Jun 26, 2025
7b9d674
test: update test 'test_task_with_env'
magentaqin Jun 26, 2025
9b690ca
fix: fix proper quotes
magentaqin Jun 26, 2025
04d2edd
fix: fix esacpe parenthis
magentaqin Jun 26, 2025
ef68cc9
test: update env key
magentaqin Jun 26, 2025
66701f5
fix: fix issues mentioned in PR comments
magentaqin Jul 3, 2025
7302460
fix: fix windows test errors
magentaqin Jul 3, 2025
a3b8187
fix: Skip command env to avoid exporting system variables
magentaqin Jul 7, 2025
28d78f2
fix: fix compatible issues in 'test_task_with_env'
magentaqin Jul 7, 2025
3a31933
fix: fix compatible issues in 'test_task_with_env'
magentaqin Jul 7, 2025
db3b7c7
fix: fix compatible issues in 'test_run_with_environment_variable_pri…
magentaqin Jul 7, 2025
80c6053
fix: fix compatible issues
magentaqin Jul 7, 2025
5893e29
fix: fix compatible issues
magentaqin Jul 7, 2025
7b6c081
fix: fix compatible issues
magentaqin Jul 7, 2025
f623552
fix: fix compatible issues
magentaqin Jul 7, 2025
42c7707
fix: add single quote
magentaqin Jul 7, 2025
d3f8317
chore: remove spaces in test_task_with_env
magentaqin Jul 7, 2025
ace5ee3
fix: in windows, child shell should inherit the parent's environemnt …
magentaqin Jul 7, 2025
7d295ca
fix: in windows, child shell should inherit the parent's environemnt …
magentaqin Jul 7, 2025
cd0c430
chore: add debugger helper for 'test_task_with_env'
magentaqin Jul 7, 2025
6df2457
fix: revert changes
magentaqin Jul 9, 2025
3a6b998
fix: remove set
magentaqin Jul 9, 2025
a1215ea
fix: fix powershell issues
magentaqin Jul 9, 2025
bc36791
fix: fix powershell compatible issues
magentaqin Jul 9, 2025
a64be50
fix: fix powershell compatible issues
magentaqin Jul 9, 2025
5d12d82
fix: fix escaping issues
magentaqin Jul 9, 2025
3c052ea
chore: print err for test_task_with_env
magentaqin Jul 9, 2025
6bc06aa
fix: Use PowerShell environment variable syntax
magentaqin Jul 10, 2025
38e066d
chore: add println for 'expected_prefix'
magentaqin Jul 10, 2025
b8dd9f3
fix: replace .bat with ps1
magentaqin Jul 10, 2025
f21a07d
chore: print echo_cmd
magentaqin Jul 10, 2025
42b74df
fix: remove unecessary windows code
magentaqin Jul 11, 2025
073b638
fix: fix linting error
magentaqin Jul 11, 2025
9125d7c
fix: Only the keys that are in TASK_SPECIFIC_ENVS map would be exported.
magentaqin Jul 14, 2025
1863e96
fix: use '. bat' for windows
magentaqin Jul 14, 2025
aa7f150
chore: print env set in windows
magentaqin Jul 14, 2025
8f8c2c1
fix: specify 'target.win-64.activation' in pixi.toml
magentaqin Jul 14, 2025
b008b67
chore: remove unecessary echo
magentaqin Jul 14, 2025
a869e18
chore: update test order
magentaqin Jul 17, 2025
c2d5ec4
test: add tests for 'activation scripts from dependencies > outside e…
magentaqin Jul 17, 2025
766d013
doc: add 'test' section to CONTRIBUTING.md
magentaqin Jul 17, 2025
1b050d6
Update src/activation.rs
magentaqin Jul 24, 2025
2c54b1e
Update src/task/executable_task.rs
magentaqin Jul 24, 2025
506c853
Update tests/integration_python/test_run_cli.py
magentaqin Jul 24, 2025
ae1b505
chore: remove unecessary comments in test
magentaqin Jul 24, 2025
aff1145
doc: add doc to explain priority rules of environment variables defin…
magentaqin Jul 24, 2025
969d2f7
refactor: refactor get_export_specific_task_env with EnvMap struct
magentaqin Jul 25, 2025
ab821a2
test: update tests-test unecessary exports
magentaqin Jul 25, 2025
86456f6
Merge branch 'main' into bugfix/environment-variable-priority
lucascolley Jul 28, 2025
82b9bfd
Merge branch 'main' into bugfix/environment-variable-priority
Hofer-Julian Jul 28, 2025
7e03bd9
Apply suggestions from code review
lucascolley Jul 28, 2025
c17b427
move docs, add warning
lucascolley Jul 28, 2025
347d019
fix syntax
lucascolley Jul 28, 2025
f429903
remove old docs
lucascolley Jul 28, 2025
473c47d
fix syntax
lucascolley Jul 28, 2025
a1aa9f3
fix example syntax
lucascolley Jul 28, 2025
0950cd4
add file names to docs
lucascolley Jul 28, 2025
6c2096c
run CI
lucascolley Jul 28, 2025
5ff1ed8
chore: merge main branch and solve conflicts
magentaqin Aug 1, 2025
c0b8849
feat: resolve variable references
magentaqin Aug 4, 2025
5b82d73
docs: split docs between tasks and env vars pages
lucascolley Aug 5, 2025
210a47d
Merge branch 'main' into bugfix/environment-variable-priority
lucascolley Aug 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,28 @@ pub enum SortBy {
}
```

## Tests

To run all tests, use:
```bash
pixi run test
```
But if you have modified recipe data under the tests/data/channels directory, you need to update the test channel before running tests:
```bash
pixi run update-test-channel <channel_name>
```
> [!NOTE]
> This task currently only works on unix systems. If you are on Windows, it is recommended to use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install).

For example, if you modified data for dummy_channel_1:
```bash
pixi run update-test-channel dummy_channel_1
```
After updating the test channel, run the tests again:
```
pixi run test
```

## CLI documentation
The CLI reference is automatically generated from the code documentation of CLI commands under `src/cli`.

Expand All @@ -136,4 +158,4 @@ and `pixi run -e docs mkdocs serve` to check out the web docs locally.
You must commit the generated files as well as part of your PR.

The `*_extender` files under `docs/reference/cli` are meant as extensions of the autogenerated documentation.
Those are meant to be updated manually.
Those are meant to be updated manually.
114 changes: 114 additions & 0 deletions docs/reference/environment_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,117 @@ The following environment variables are set by Pixi, when using the `pixi run`,

!!! note
Even though the variables are environment variables these cannot be overridden. E.g. you can not change the root of the project by setting `PIXI_PROJECT_ROOT` in the environment.

## Environment Variable Priority

The following priority rule applies for environment variables: `task.env` > `activation.env` > `activation.scripts` > activation scripts of dependencies > outside environment variables.
Variables defined at a higher priority will override those defined at a lower priority.

!!! warning

In older versions of Pixi, this priority was not well-defined, and there are a number of known
deviations from the current priority which exist in some older versions.
Please see the warning in [the advanced tasks documentation](../workspace/advanced_tasks.md#environment-variables)
for further details and migration guidance.

##### Example 1: `task.env` > `activation.env`

In `pixi.toml`, we defined an environment variable `HELLO_WORLD` in both `tasks.hello` and `activation.env`.

When we run `echo $HELLO_WORLD`, it will output:
```
Hello world!
```

```toml title="pixi.toml"
[tasks.hello]
cmd = "echo $HELLO_WORLD"
env = { HELLO_WORLD = "Hello world!" }
[activation.env]
HELLO_WORLD = "Activate!"
```

##### Example 2: `activation.env` > `activation.scripts`

In `pixi.toml`, we defined the same environment variable `DEBUG_MODE` in both `activation.env` and in the activation script file `setup.sh`.
When we run `echo Debug mode: $DEBUG_MODE`, it will output:
```bash
Debug mode: enabled
```

```toml title="pixi.toml"
[activation.env]
DEBUG_MODE = "enabled"

[activation]
scripts = ["setup.sh"]
```

```bash title="setup.sh"
export DEBUG_MODE="disabled"
```

##### Example 3: `activation.scripts` > activation scripts of dependencies

In `pixi.toml`, we have our local activation script and a dependency `my-package` that also sets environment variables through its activation scripts.
When we run `echo Library path: $LIB_PATH`, it will output:
```
Library path: /my/lib
```

```toml title="pixi.toml"
[activation]
scripts = ["local_setup.sh"]

[dependencies]
my-package = "*" # This package has its own activation scripts that set LIB_PATH="/dep/lib"
```
```bash title="local_setup.sh"
export LIB_PATH="/my/lib"
```

##### Example 4: activation scripts of dependencies > outside environment variable

If we have a dependency that sets `PYTHON_PATH` and the same variable is already set in the outside environment.
When we run `echo Python path: $PYTHON_PATH`, it will output:
```bash
Python path: /pixi/python
```
```
# Outside environment
export PYTHON_PATH="/system/python"
```
```toml title="pixi.toml"
[dependencies]
python-utils = "*" # This package sets PYTHON_PATH="/pixi/python" in its activation scripts
```

##### Example 5: Complex Example - All priorities combined
In `pixi.toml`, we define the same variable `APP_CONFIG` across multiple levels:
```toml title="pixi.toml"
[tasks.start]
cmd = "echo Config: $APP_CONFIG"
env = { APP_CONFIG = "task-specific" }

[activation.env]
APP_CONFIG = "activation-env"

[activation]
scripts = ["app_setup.sh"]

[dependencies]
config-loader = "*" # Sets APP_CONFIG="dependency-config"
```
```bash title="app_setup.sh"
export APP_CONFIG="activation-script"
```
```bash
# Outside environment
export APP_CONFIG="system-config"
```

Since `task.env` has the highest priority, when we run `pixi run start` it will output:

```
Config: task-specific
```
58 changes: 38 additions & 20 deletions docs/workspace/advanced_tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,33 +324,51 @@ 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.
## Environment Variables

```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.
You can set environment variables directly for a task, as well as by other means.
See [the environment variable priority documentation](../reference/environment_variables.md#environment-variable-priority) for full details of ways to set environment variables,
and how those ways interact with each other.

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

These variables are not shared over tasks, so you need to define these for every task you want to use them in.
In older versions of Pixi, this priority was not well-defined, and there are a number of known
deviations from the current priority which exist in some older versions:

- `activation.scripts` used to take priority over `activation.env`
- activation scripts of dependencies used to take priority over `activation.env`
- outside environment variables used to override variables set in `task.env`

If you previously relied on a certain priority which no longer applies, you may need to change your
task definitions.

For the specific case of overriding `task.env` with outside environment variables, this behaviour can
now be recreated using [task arguments](#task-arguments). For example, if you were previously using
a setup like:

!!! 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" } }
echo = { cmd = "echo $ARGUMENT", env = { ARGUMENT = "hello" } }
```

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

you can now recreate this behaviour like:

```toml title="pixi.toml"
[tasks]
echo = { cmd = "echo {{ ARGUMENT }}", args = [{"arg" = "ARGUMENT", "default" = "hello" }]}
```

```shell
pixi run echo world
✨ Pixi task (echo): echo world
world
```
This will output `/tmp/path:/usr/bin:/bin` instead of the original `/usr/bin:/bin`.

## Clean environment
You can make sure the environment of a task is "Pixi only".
Expand Down
39 changes: 37 additions & 2 deletions src/activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,19 @@ async fn try_get_valid_activation_cache(
}
}

// Extract variable name
fn extract_variable_name(value: &str) -> Option<&str> {
if let Some(inner) = value.strip_prefix('$') {
Some(inner)
} else if let Some(inner) = value.strip_prefix('%').and_then(|s| s.strip_suffix('%')) {
Some(inner)
} else if let Some(inner) = value.strip_prefix("${").and_then(|s| s.strip_suffix('}')) {
Some(inner)
} else {
None
}
}
Comment on lines +239 to +250
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Hofer-Julian do you know if we already have something like this elsewhere?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least after a quick look at our code base, I wouldn't be able to find it


/// Runs and caches the activation script.
pub async fn run_activation(
environment: &Environment<'_>,
Expand Down Expand Up @@ -286,7 +299,7 @@ pub async fn run_activation(
let current_env = std::env::vars().collect::<HashMap<_, _>>();

// Run and cache the activation script
activator.run_activation(
let new_activator = activator.run_activation(
ActivationVariables {
// Get the current PATH variable
path: Default::default(),
Expand All @@ -301,7 +314,29 @@ pub async fn run_activation(
current_env,
},
None,
)
);

// `activator.env_vars` should override `activator_result` for duplicate keys
new_activator.map(|mut map: HashMap<String, String>| {
// First pass: Add all variables from activator.env_vars(as map is unordered, we need to update the referenced value in the second loop)
for (k, v) in &activator.env_vars {
map.insert(k.clone(), v.clone());
}

// Second pass: Loop through the map and resolve variable references
let keys_to_update: Vec<String> = map.keys().cloned().collect();
for key in keys_to_update {
if let Some(value) = map.get(&key).cloned() {
if let Some(referenced_var) = extract_variable_name(&value) {
if let Some(actual_value) = map.get(referenced_var) {
map.insert(key, actual_value.clone());
}
}
}
}

map
})
})
.await
.into_diagnostic()?
Expand Down
2 changes: 1 addition & 1 deletion src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ async fn execute_task(
command_env: &HashMap<OsString, OsString>,
kill_signal: KillSignal,
) -> Result<(), TaskExecutionError> {
let Some(script) = task.as_deno_script()? else {
let Some(script) = task.as_deno_script(command_env)? else {
return Ok(());
};
let cwd = task.working_directory()?;
Expand Down
Loading
Loading