-
Notifications
You must be signed in to change notification settings - Fork 421
feat(manifest): allow inline environment configuration #5403
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
base: main
Are you sure you want to change the base?
feat(manifest): allow inline environment configuration #5403
Conversation
Allow users to define dependencies, tasks, and other feature configuration directly on environments using `[environments.dev.dependencies]` syntax instead of requiring separate feature definitions. When inline config is detected, a synthetic feature is created with the same name as the environment and prepended to the environment's feature list. Example usage: ```toml [environments.dev.dependencies] pytest = "*" [environments.dev.tasks] test = "pytest" ``` This is equivalent to: ```toml [feature.dev.dependencies] pytest = "*" [feature.dev.tasks] test = "pytest" [environments] dev = ["dev"] ``` Closes prefix-dev#5317
|
Thank you @lllangWV, this is a big change to the manifest. I would like to test run this for a little bit. One question, that I know @Hofer-Julian has an opinion on, what happens with the "default" feature? Is it still automatically included? |
I think we should include it for now. Otherwise it would be a breaking change and that should be done separately
This worries me a bit. Users should still be allowed to define features of the same name as the environment. Also I think creating a feature is not the way to go. My feeling is that this should be handled separately also to avoid clashes with other tools that deal with features |
@ruben-arts Yeah, I figured. My goal was to at least get this discussion going with an initial attempt at this. Let me know if you find any issues. And, yes currently the default feature it is automatically included. I wanted to try to stick with the original behavior as much as possible
@Hofer-Julian , I see your point. I think I misunderstood your original intention. When I made these changes the intention was that you can use the dependencies defined in environment as another feature in itself. This synthetic feature approach can create potential confusion:
[feature.python.dependencies]
python = "3.14.*"
[environments.dev]
features = ["python"]
[environments.dev.dependencies]
pytest = "*"
[environments.test]
features = ["dev"] # User might expect python + pytest, but only gets pytest
QUESTION:
Or should we limit inline config to just dependencies/tasks for simplicity, keeping the more advanced configuration (like system-requirements, pypi-options) as feature-only? Proposed changesI guess the change should be instead of creating synthetic features, handle inline environment config as a separate, non-shareable concept. Inline dependencies are "private" to that environment and cannot be referenced by other environments. This is the rough outline for the changes I would make from the current implementation:
QUESTION: Does this align more closely with what you want? Let me know your thoughts! |
I was actually most excited and convinced of this feature because of the additional config. So I would like to keep this in. What would be the logic on merging the configurations? [feature.test.tasks]
test = "pytest"
[environments.dev]
tasks = { test = "python blabla" }
features = ["test"]Which contents of the test task will be selected? I think it should be the one in the I guess it could be a "special" type of feature that uses all the same logic but is always the highest prio one and is not exposed to the user in any way. I worry that we implement the exact same logic as the features and then sturgle to maintain the two code paths. |
Exactly what you said
I would agree with this. If you do make it public it might cause the issues I mentioned in the earlier comment: "Naming conflicts between a feature with the same name" and "Ambiguous sharing semantics. Is it sharing all the features apart of the environment or just the "private" feature. A user might get confused by this."
Exactly this! I felt bad about explicitly defining the feature fields onto the TomlEnvironemnt because any changes to what a TomlFeature is this would require an update to be compatible. I couldn't think of a better way to do it. pub struct TomlEnvironment {
pub features: Option<Spanned<Vec<Spanned<String>>>>,
pub solve_group: Option<String>,
pub no_default_feature: bool,
pub platforms: Option<Spanned<IndexSet<Platform>>>,
pub channels: Option<Vec<TomlPrioritizedChannel>>,
pub channel_priority: Option<ChannelPriority>,
pub solve_strategy: Option<SolveStrategy>,
pub system_requirements: SystemRequirements,
pub dependencies: Option<PixiSpanned<UniquePackageMap>>,
pub host_dependencies: Option<PixiSpanned<UniquePackageMap>>,
pub build_dependencies: Option<PixiSpanned<UniquePackageMap>>,
pub pypi_dependencies: Option<IndexMap<PypiPackageName, PixiPypiSpec>>,
pub dev: Option<IndexMap<rattler_conda_types::PackageName, pixi_spec::TomlLocationSpec>>,
pub activation: Option<Activation>,
pub tasks: HashMap<TaskName, Task>,
pub target: IndexMap<PixiSpanned<TargetSelector>, TomlTarget>,
pub pypi_options: Option<PypiOptions>,
pub warnings: Vec<Warning>,Maybe a "solution" would look something like this. (This is probably wrong with how you would define this formally) pub struct TomlEnvironment {
pub features: Option<Spanned<Vec<Spanned<String>>>>,
pub solve_group: Option<String>,
pub no_default_feature: bool,
pub feature Option<TomlFeature>However, I think the syntax would be "goofy" [feature.test.tasks]
test = "pytest"
[environments.dev]
features = ["python"]
feature.dev.dependencies = { ruff = "*" }
But maybe this is a non-issue. I couldn't think of a way to reuse the TomlFeature and flatten its field out. I was going back and forth with claude code about this design issue. I thought I would share its suggestion to assist in the discussion. You can't escape explicitly listing the fields somewhere if you want flat TOML syntax with toml_span's
manual deserialization.
The options are:
1. Current approach: Fields on TomlEnvironment, manual into_toml_feature() mapping
2. Helper struct: Fields on FeatureFields, then construct both TomlFeature and TomlEnvironment from it
3. Embed after parsing: Parse fields explicitly, then construct an embedded TomlFeature immediately during
deserialization
Option 3 might be a slight improvement:
pub struct TomlEnvironment {
pub features: Option<Spanned<Vec<Spanned<String>>>>,
pub solve_group: Option<String>,
pub no_default_feature: bool,
pub inline: Option<TomlFeature>, // constructed during deserialize
}
In deserialize(), after parsing all the feature fields, you'd construct the TomlFeature right there instead of storing
them separately. Then into_toml_feature() becomes self.inline.take().
But you're right - the explicit field parsing is unavoidable. The duplication is in the parsing code, not the struct
definition. Your current implementation is a valid approach given toml_span's constraints. The maintenance burden is real
but localized to one place (the deserializer).
If toml_span had #[flatten] like serde, this would be trivial. Without it, explicit parsing is the cost. |
Description
Allow users to define dependencies, tasks, and other feature configuration directly on environments using
[environments.dev.dependencies]syntax instead of requiring separate feature definitions.When inline config is detected, a synthetic feature is created with the same name as the environment and prepended to the environment's feature list.
Example usage:
This is equivalent to:
Fixes #5317
How Has This Been Tested?
Unit Tests (cargo test -p pixi_manifest environment):
Integration Tests (cargo test -p pixi --test integration_rust inline_environment):
Linting: pixi run lint passes (includes clippy)
pixi tests: pixi run test produced some failing tests. I was unsure if this is related to the changes here
(373 durations < 0.005s hidden. Use -vv to show these durations.)
================================================================================= short test summary info ==================================================================================
FAILED tests/integration_python/pixi_global/test_shortcuts.py::test_install_simple - AssertionError: Shortcut 'pixi-editor' should exist on linux-64
FAILED tests/integration_python/pixi_global/test_shortcuts.py::test_sync_empty_shortcut_list - AssertionError: Shortcut 'pixi-editor' should exist on linux-64
FAILED tests/integration_python/pixi_global/test_shortcuts.py::test_sync_creation_and_removal - AssertionError: Shortcut 'pixi-editor' should exist on linux-64
FAILED tests/integration_python/pixi_global/test_shortcuts.py::test_update_installs_new_shortcuts - AssertionError: Shortcut 'pixi-editor' should exist on linux-64
FAILED tests/integration_python/pixi_global/test_shortcuts.py::test_sync_removing_environment - AssertionError: Shortcut 'pixi-editor' should exist on linux-64
FAILED tests/integration_python/pixi_global/test_shortcuts.py::test_remove_shortcut - AssertionError: Shortcut 'pixi-editor' should exist on linux-64
FAILED tests/integration_python/pixi_global/test_shortcuts.py::test_add_shortcut - AssertionError: Shortcut 'pixi-editor' should exist on linux-64
======================================================================== 7 failed, 193 passed, 1 xfailed in 40.89s =========================================================================
AI Disclosure
Tools: Claude Code with Claude Opus 4.5
Process:
where in the codebase changes would need to be made if this feature were to be implemented.
2026-01-24-environments-direct-dependencies.md
)
2026-01-24-environments-direct-dependencies.md
)
Checklist:
""
schema/model.py.