Skip to content

Commit 8ca1185

Browse files
nicoddemusjaimeambruslvoliveira
authored
[EDEN-2843] First version of the pixi-devenv tool (#2)
EDEN-2843 --------- Co-authored-by: Jaime Ambrus <[email protected]> Co-authored-by: Lucas Oliveira <[email protected]>
1 parent f1d734d commit 8ca1185

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3277
-12
lines changed

.github/workflows/test.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: test
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
test:
15+
runs-on: ${{ matrix.os }}
16+
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
os: [ubuntu-latest, windows-latest]
21+
22+
env:
23+
UV_LOCKED: "true"
24+
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- name: Install uv
29+
uses: astral-sh/setup-uv@v6
30+
31+
- name: Lint
32+
run: |
33+
uv run pre-commit run --all --show-diff-on-failure
34+
35+
- name: Test
36+
run: |
37+
uv run python -m pytest --color=yes
38+
39+
- name: Check package
40+
shell: bash
41+
run: |
42+
uv build
43+
uvx --with `find dist/*.whl` pixi-devenv --help

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,4 @@ cython_debug/
205205
marimo/_static/
206206
marimo/_lsp/
207207
__marimo__/
208+
/.idea/

.pre-commit-config.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
repos:
2+
- repo: local
3+
hooks:
4+
- id: ruff-format
5+
name: ruff-format
6+
entry: uv run ruff format --force-exclude
7+
pass_filenames: true
8+
language: system
9+
types_or: [ python, pyi ]
10+
require_serial: true
11+
stages: [ pre-commit ]
12+
- id: ruff-check
13+
name: ruff-check
14+
entry: uv run ruff check --fix
15+
language: system
16+
types_or: [ python ]
17+
pass_filenames: true
18+
require_serial: false
19+
stages: [ pre-commit ]
20+
- id: mypy
21+
name: mypy
22+
entry: uv run mypy
23+
types_or: [ python, pyi ]
24+
pass_filenames: false
25+
language: system
26+
require_serial: true
27+
stages: [ pre-commit ]
28+

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# CHANGELOG
2+
3+
## 0.1.0
4+
5+
*2025-11-06*
6+
7+
* First release.

README.md

Lines changed: 273 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,295 @@
11
# pixi-devenv
22

3+
4+
## Why?
5+
36
pixi-devenv is tool to work with multiple pixi projects in development mode.
47

5-
**WORK IN PROGRESS**
8+
pixi currently does not have full support to work with multiple projects in development mode. Development mode allows one to have each project declaring its own dependencies, and work with all of them with live code so changes are reflected immediately, without the needing of creating/installing the projects as packages in the environment.
9+
10+
pixi-devenv makes it easy to aggregate multiple "projects" to create a single "product".
11+
12+
13+
## Usage
14+
15+
In a directory with a `pixi.devenv.toml` and the target `pixi.toml`, execute:
16+
17+
```console
18+
pixi exec -c conda-forge pixi-devenv update
19+
```
620

7-
## `pixi.devenv.toml`
21+
This will update the `pixi.toml` file from the `pixi.devenv.toml` configuration.
822

9-
Environment configuration is placed in `pixi.devenv.toml` files, next to their usual `pixi.toml` files.
23+
## Introduction
24+
25+
Here are a quick explanation of some `pixi` concepts that are important to understand to use `pixi-devenv`.
26+
27+
### `[dependencies]`
28+
29+
Lists the `conda` dependencies of a project. `[pypi-dependencies]` lists PyPI dependencies. `pixi` fully supports using PyPI packages, meaning PyPI packages are solved together with the conda packages.
1030

11-
A `pixi.devenv.toml` file *includes* declarations from other projects by using the `includes` property:
1231

1332
```toml
14-
includes = [
15-
"../core",
16-
"../calc",
33+
[dependencies]
34+
alive-progress = ">=3.2"
35+
artifacts_cache = ">=3.0"
36+
```
37+
38+
### `[activation]`
39+
40+
Defines which variables and scripts should be activated for the environment.
41+
42+
```toml
43+
[activation]
44+
scripts = [
45+
".pixi-activation/env-vars.sh",
46+
]
47+
CONDA_PY="310"
48+
PATH = "$PIXI_PROJECT_ROOT/bin:$PATH"
49+
```
50+
51+
### `[target.{NAME}]`
52+
53+
54+
A `[target.{NAME}]` section can be used to specify platform specific configuration, such as `[target.win]` or `[target.linux]`. Generic terms are valid (`win`, `unix`), down to more specific ones (`linux-64`, `windows-64`).
55+
56+
Each `[target.{NAME}]` section contains its own `[dependencies]` and `[activation]` sections.
57+
58+
```toml
59+
[target.win.dependencies]
60+
pywin32 = ">=305"
61+
62+
[target.unix.dependencies]
63+
sqlite = ">=3.40"
64+
65+
[target.linux-64.activation]
66+
env = { JOBS = "6" }
67+
```
68+
69+
70+
### `[feature.NAME]`
71+
72+
Think of a `feature` as a group of `dependencies` and `activation` sections. They can be used to have a different set of dependencies for different purposes, like testing or linting tools, as well as different dependency matrixes. They are *additive* to the default `[environment]` and `[activation]` sections:
73+
74+
```toml
75+
[feature.python310]
76+
dependencies = { python = "3.10.*" }
77+
activation = { env = { CONDA_PY = "310" } }
78+
79+
[feature.compile.target.win.dependencies]
80+
dependency-walker = "*"
81+
```
82+
83+
### `[environments]`
84+
85+
Environments are sets of one or more features. An environment will contain all the `dependencies` and `activation` of the features that compose the environment.
86+
87+
```toml
88+
[environment]
89+
py310 = ["python310", "compile"]
90+
py312 = ["python312", "compile"]
91+
```
92+
93+
## pixi-devenv
94+
95+
`pixi-devenv` configuration resides in a `pixi.devenv.toml` file. To update `pixi.toml` in case `pixi.devenv.toml` changes, execute:
96+
97+
```console
98+
pixi run pixi-devenv
99+
```
100+
101+
If your project includes `pixi-devenv` in its `dependencies`, but it can be run from a one-off environment:
102+
103+
```console
104+
pixi exec pixi-devenv
105+
```
106+
107+
Consider this project structure:
108+
109+
```
110+
workspace/
111+
core/
112+
src/
113+
pixi.devenv.toml
114+
calc/
115+
src/
116+
pixi.devenv.toml
117+
web/
118+
src/
119+
pixi.devenv.toml
120+
```
121+
122+
**Characteristics**
123+
124+
* `web` depends on `calc`, which depends on `core`.
125+
* We have two features defined in `core`:
126+
* `test`: adds test specific dependencies.
127+
* `py310`: Python 3.10.
128+
* `py312`: Python 3.12.
129+
130+
### `core/pixi.devenv.toml`
131+
132+
The `pixi-devenv` configuration resides in the `devenv` table. This avoids confusion when looking at both `pixi.devenv.toml` file and `pixi.toml`, making the distintion clear.
133+
134+
```toml
135+
[devenv]
136+
# Mandatory: name of this project
137+
# Question: should this actually be forbidden and forced to be the name of the directory?
138+
name = "core"
139+
channels = [
140+
"prefix.dev",
141+
"https://packages.company.com"
17142
]
143+
platforms = ["win-64", "linux-64"]
144+
```
145+
146+
Basic information about the project. `channels` and `platforms` are inherited by downstream projects by default, but can also be overwritten.
147+
148+
149+
```toml
150+
[devenv.dependencies]
151+
attrs = "*"
152+
boltons = "*"
153+
154+
[devenv.target.win.dependencies]
155+
pywin32 = "*"
156+
```
157+
158+
Default dependencies, identical to pixi's `[dependencies]` section. They are inherited by default by downstream projects.
159+
160+
161+
```toml
162+
[devenv.constraints]
163+
qt = ">=5.15"
164+
165+
[devenv.target.win.constraints]
166+
vc = ">=14"
167+
```
168+
169+
Default `constraints`. They are inherited by default by downstream projects.
170+
171+
`constraints` contain version specs similar to `[dependencies]`, but contrary to `dependencies` the specs are not part of the environment by default.
172+
173+
They will be added to the versions specifiers of the section *if* a downstream project explicitly declares that dependency.
174+
175+
176+
```toml
177+
[devenv.env-vars]
178+
# Lists are prepended to existing variable of same name, with the appropriate joiner for the platform (':' on Linux, ';' on Windows).
179+
# ${{ devenv_project_dir }} is replaced by the project directory.
180+
PYTHONPATH = ['${{ devenv_project_dir }}/src']
181+
182+
# Strings are set directly.
183+
JOBS = "6"
184+
185+
# Overwrite by platform uses the same syntax as usual.
186+
[devenv.target.unix.env-vars]
187+
CC = 'CC $CC'
188+
```
189+
190+
Variables might be used which will be replaced by the correct values when creating the `pixi.toml` file:
191+
192+
* `{devenv_project_dir}`: root of the directory containing the `pixi.devenv.toml` file. This will be replaced by a
193+
relative path to `pixi.toml` in the final file.
194+
195+
By default, they are inherited from upstream projects.
196+
197+
This takes the place of the `[activation]` section of the default pixi configuration.
198+
199+
200+
```toml
201+
[devenv.feature.python310]
202+
dependencies = { python = "3.10.*" }
203+
env-vars = { CONDA_PY = "310" }
204+
205+
[devenv.feature.python312]
206+
dependencies = { python = "3.12.*" }
207+
env-vars = { CONDA_PY = "312" }
208+
209+
[devenv.feature.test]
210+
dependencies = { pytest = "*" }
211+
212+
[devenv.feature.compile]
213+
dependencies = { cmake = "*" }
214+
```
215+
216+
Feature configuration, identical to pixi's `[feature]` section. Features **are not** inherited automatically. The reason for that is that features that are not used by environments generate a warning, which would cause false warnings in downstream projects only because they decide to not use a feature available on upstream projects.
217+
218+
219+
```toml
220+
[devenv.environment]
221+
py310 = ["python310"]
222+
py310-test = ["python310", "test", "compile"]
223+
py312 = ["python312"]
224+
py312-test = ["python312", "test", "compile"]
18225
```
19226

20-
Projects are included using paths to their directories, relative to the current file, as opposed to referencing a `.devenv` file (like `conda-devenv`).
227+
Note that features can be defined at any point in the hierarchy. Downstream projects control how/when they inherit features.
228+
229+
Environment configuration, identical to pixi's `[environment]` section. Same as features, environments **are not inherited** by default.
21230

22-
The reason is that `pixi-devenv` only supports a single `pixi.devenv.toml` file per project. Multiple environment and build variantes are contained all in the `pixi.devenv.toml` file, so there is no need for multiple `devenv` files.
23231

24-
To enable future extensions, this syntax is also valid:
232+
### `calc/pixi.devenv.toml`
233+
25234

26235
```toml
27-
includes = [
236+
[devenv]
237+
name = "calc"
238+
# platforms = ["linux-64"] # can overwrite platforms defined upstream.
239+
# channels = ["conda-forge"] # can overwrite platforms defined upstream.
240+
241+
242+
# Mandatory: List of upstream projects. This should be a list pointing to the directory, relative to this directory, of the upstream's project `pixi.devenv.toml` file.
243+
upstream = [
28244
"../core",
29-
{ path="../calc" },
30245
]
246+
247+
[devenv.dependencies]
248+
249+
250+
251+
[devenv.inherit] # Optional
252+
# Both settings can be a list instead of a bool, meaning to inherit dependencies only from the projects explicitly listed.
253+
# dependencies = ["core"]
254+
# Default to true, meaning default dependencies from all upstream projects are inherited. Using false means no dependencies are inherited.
255+
dependencies = true
256+
pypi-dependencies = true
257+
env-vars = true
258+
259+
# Controls which features will be inherited. By default this table is empty, meaning no features are inherited.
260+
[devenv.inherit.features] # Optional
261+
py310 = true # inherits all features defined upstream named 'py310'.
262+
# py310-test = ['core'] # instead of inheriting 'py310-test' from all upstream projects, inherit it only from 'core'.
31263
```
32264

265+
Note: `environments` **are never inherited**.
266+
33267

34268

269+
## Differences to `conda-devenv`
270+
271+
[conda-devenv](https://github.com/ESSS/conda-devenv) is a tool developed by ESSS with the same purpose as `pixi-devenv`: working with multiple projects in development mode.
272+
273+
There is one important difference on how the tools work:
274+
275+
`conda-devenv` is a frontend tool. Developers work with it directly on their day-to-day work, even if they are not changing dependencies or adding/removing projects -- developers call `conda devenv` to create their environments. One consequence of this is that developers must have `conda-devenv` installed in their root `conda` installation, which requires everyone to be using the exact same version `conda` version, because unfortunately bugs in conda happen (as in any software). The lack of native locking in `conda` requires using `conda-lock`, which by itself must also be of a compatible version with `conda` and `conda-devenv`, further complicating bootstrapping.
276+
277+
278+
`pixi-devenv` is a code generation tool. Developers don't need to use it on their day-to-day work, only using `pixi` and plain `pixi.toml` files directly. Developers only need `pixi-devenv` when they make changes to the `pixi.devenv.toml` file, changing package dependencies, adding/removing upstream projects -- in that case, developers must invoke `pixi-devenv` to update your `pixi.toml` file. The fact that `pixi-devenv` is a standalone tool resolves the bootstrapping problem that plagues `conda-devenv`.
279+
280+
281+
## Development
282+
283+
Because this is a pure Python package, we decided to use the more standard `uv` tool for development.
284+
285+
To run mypy:
286+
287+
```console
288+
uv run mypy
289+
```
290+
291+
To run tests:
292+
293+
```console
294+
uv run pytest
295+
```

0 commit comments

Comments
 (0)