Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
f2959bf
refactor `spec` into a separate submodule
keewis Nov 29, 2025
cf5c4c0
dispatch by kind
keewis Nov 29, 2025
9e1e9ec
default the kind to `conda`
keewis Nov 29, 2025
5a6b670
adapt the tests
keewis Nov 29, 2025
7eef3e5
configure project metadata
keewis Nov 29, 2025
a20e075
add `manifest_path` to the cli options
keewis Nov 29, 2025
8f37fef
formatting
keewis Nov 29, 2025
9f9990e
write the environment parsing for pixi envs
keewis Nov 29, 2025
1c0c0ee
raise a more descriptive error if no suitable releases were found
keewis Nov 30, 2025
fd07057
filter out excluded packages before fetching releases
keewis Nov 30, 2025
f1e6de1
consider unpinned specs as not matching
keewis Nov 30, 2025
5883e35
gracefully handle unpinned but not ignored dependencies
keewis Nov 30, 2025
0bb207d
install the module instead of modifying `sys.path`
keewis Nov 30, 2025
6795a27
allow passing `manifest-path` to the action
keewis Nov 30, 2025
851d0a6
misformatted input definition
keewis Nov 30, 2025
0144607
stop testing on the unsupported python 3.10
keewis Nov 30, 2025
741a436
install the package itself
keewis Nov 30, 2025
f56c537
use the warning style for unpinned versions
keewis Nov 30, 2025
afd85b4
warn about ignored PyPI dependencies
keewis Nov 30, 2025
51da18a
tests for most of the functions in `minimum_versions.release`
keewis Dec 2, 2025
c53cf33
tests for the environment functions
keewis Dec 8, 2025
36019e3
tests for parsing conda specs
keewis Dec 8, 2025
a1c08cf
also check parsing the entire conda env
keewis Dec 8, 2025
8f0fa55
rename
keewis Dec 8, 2025
b979674
check parsing pixi specs
keewis Dec 8, 2025
878aa35
fix a bug in the lower pin regex
keewis Dec 8, 2025
9cfadc7
additional spec parsing checks
keewis Dec 8, 2025
7f9863b
check invalid versions raise
keewis Dec 8, 2025
c7c1179
checks for parse_pixi_environment
keewis Dec 8, 2025
c2351bf
include the default feature in the features
keewis Dec 8, 2025
abf286f
rename the `environment-paths` input to `environments`
keewis Dec 8, 2025
8473575
describe how to analyze `pixi` environments
keewis Dec 8, 2025
2ed4375
rename `env-paths` to `envs`
keewis Dec 8, 2025
81c9530
e2e tests for pixi and mixed envs
keewis Dec 8, 2025
cc8ee84
typo
keewis Dec 8, 2025
3f55bc2
add policy files for the pixi tests
keewis Dec 8, 2025
a5ac008
another typo
keewis Dec 8, 2025
9d266d9
quotes and expected failure settings
keewis Dec 8, 2025
0d5add7
add a failing pixi env
keewis Dec 8, 2025
35ac1e9
also check the default pypi-dependencies
keewis Dec 8, 2025
b584476
back to `python=3.9`
keewis Dec 8, 2025
2d1976e
support the `no-default-feature` option
keewis Dec 9, 2025
3017270
support analyzing missing features
keewis Dec 9, 2025
285aee0
change the error text for unknown version specs
keewis Dec 9, 2025
fdfa6b6
add a note containing the package name
keewis Dec 9, 2025
f0fd56d
add more information
keewis Dec 9, 2025
c95acf7
support dict pins
keewis Dec 9, 2025
56aab76
configure coverage
keewis Dec 9, 2025
113d767
check the format detection
keewis Dec 9, 2025
4dcc7dc
raise on unknown features
keewis Dec 9, 2025
8171549
support no features
keewis Dec 9, 2025
2555235
check that `<=` is also detected
keewis Dec 9, 2025
99672dd
check pypi dependencies
keewis Dec 9, 2025
7bad70d
support local packages
keewis Dec 9, 2025
798e579
skip the local package, if any
keewis Dec 9, 2025
5e76d15
properly check that local packages are skipped
keewis Dec 9, 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
36 changes: 29 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13"]

steps:
- name: clone the repository
Expand All @@ -31,6 +31,7 @@ jobs:
- name: install dependencies
run: |
python -m pip install -r requirements.txt
python -m pip install .
python -m pip install pytest
- name: run tests
run: |
Expand All @@ -43,7 +44,7 @@ jobs:
strategy:
fail-fast: false
matrix:
env-paths:
envs:
- "envs/env1.yaml"
- "envs/env2.yaml"
- |
Expand All @@ -52,18 +53,38 @@ jobs:
expected-failure: ["false"]
policy-file: ["policy.yaml"]
include:
- env-paths: |
- envs: |
envs/failing-env1.yaml
policy-file: "policy.yaml"
expected-failure: "true"
- env-paths: |
- envs: |
envs/env1.yaml
envs/failing-env1.yaml
policy-file: "policy.yaml"
expected-failure: "true"
- env-paths: "envs/env1.yaml"
policy-file: policy_no_extra_options.yaml
- envs: "envs/env1.yaml"
policy-file: "policy_no_extra_options.yaml"
expected-failure: "false"
- envs: "pixi:env1"
manifest-path: "envs/pixi.toml"
policy-file: "policy.yaml"
expected-failure: "false"
- envs: |
pixi:env1
pixi:env2
manifest-path: "envs/pixi.toml"
policy-file: "policy.yaml"
expected-failure: "false"
- envs: |
pixi:env1
conda:envs/env2.yaml
manifest-path: "envs/pixi.toml"
policy-file: "policy.yaml"
expected-failure: "false"
- envs: "pixi:failing-env"
manifest-path: "envs/pixi.toml"
policy-file: "policy.yaml"
expected-failure: "true"

steps:
- name: clone the repository
Expand All @@ -74,8 +95,9 @@ jobs:
continue-on-error: true
with:
policy: ${{ matrix.policy-file }}
environment-paths: ${{ matrix.env-paths }}
environments: ${{ matrix.envs }}
today: 2024-12-20
manifest-path: ${{ matrix.manifest-path }}
- name: detect outcome
if: always()
shell: bash -l {0}
Expand Down
65 changes: 61 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ them to an empty mapping or sequence, respectively:
ignored_violations: []
```

Then add a new step to CI:
Then add a new step to CI.

### conda

To analyze conda environments, simply pass the path to the environment file (`env.yaml`) to the `environments` key.

```yaml
jobs:
Expand All @@ -53,7 +57,7 @@ jobs:
- uses: xarray-contrib/minimum-dependency-versions@version
with:
policy: policy.yaml
environment-paths: path/to/env.yaml
environments: path/to/env.yaml
```

To analyze multiple environments at the same time, pass a multi-line string:
Expand All @@ -67,8 +71,61 @@ jobs:

- uses: xarray-contrib/minimum-dependency-versions@version
with:
environment-paths: |
environments: |
path/to/env1.yaml
path/to/env2.yaml
path/to/env3.yaml
conda:path/to/env3.yaml # the conda: prefix is optional
```

### pixi

To analyze pixi environments, specify the environment name prefixed with `pixi:` and point to the manifest file using `manifest-path`:

```yaml
jobs:
my-job:
...
steps:
...

- uses: xarray-contrib/minimum-dependency-versions@version
with:
environments: pixi:env1
manifest-path: /path/to/pixi.toml # or pyproject.toml
```

Multiple environments can be analyzed at the same time:

```yaml
jobs:
my-job:
...
steps:
...

- uses: xarray-contrib/minimum-dependency-versions@version
with:
environments: |
pixi:env1
pixi:env2
manifest-path: /path/to/pixi.toml # or pyproject.toml
```

### Mixing environment types

It is even possible to mix environment types (once again, the `conda:` prefix is optional but recommended):

```yaml
jobs:
my-job:
...
steps:
...

- uses: xarray-contrib/minimum-dependency-versions@version
with:
environments: |
pixi:env1
conda:path/to/env.yaml
manifest-path: path/to/pixi.toml # or pyproject.toml
```
26 changes: 19 additions & 7 deletions action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,24 @@ inputs:
The path to the policy to follow
required: true
type: string
environment-paths:
today:
description: >-
Time machine for testing
required: false
type: string
environments:
description: >-
The paths to the environment files
The names or paths of the environments. Pixi environment names must be
prefixed with `pixi:`. Conda environment paths may be prefixed with
`conda:`. If there is no prefix, it is assumed to be a conda env path.
required: true
type: list
today:
manifest-path:
description: >-
Time machine for testing
Path to the manifest file of `pixi`. Required for `pixi` environments.
required: false
type: string
outputs: {}

runs:
using: "composite"

Expand All @@ -28,14 +34,20 @@ runs:
run: |
echo "::group::Install dependencies"
python -m pip install -r ${{ github.action_path }}/requirements.txt
python -m pip install ${{ github.action_path }}
echo "::endgroup::"
- name: analyze environments
shell: bash -l {0}
env:
COLUMNS: 120
FORCE_COLOR: 3
POLICY_PATH: ${{ inputs.policy }}
ENVIRONMENT_PATHS: ${{ inputs.environment-paths }}
ENVIRONMENTS: ${{ inputs.environments }}
TODAY: ${{ inputs.today }}
MANIFEST_PATH: ${{ inputs.manifest-path }}
run: |
PYTHONPATH=${{github.action_path}} python -m minimum_versions validate --today="$TODAY" --policy="$POLICY_PATH" $ENVIRONMENT_PATHS
python -m minimum_versions validate \
--today="$TODAY" \
--policy="$POLICY_PATH" \
--manifest-path="$MANIFEST_PATH" \
$ENVIRONMENTS
17 changes: 17 additions & 0 deletions envs/pixi.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[dependencies]
pandas = "2.1"
packaging = "23.1"

[feature.py39.dependencies]
python = "3.9"

[feature.py310.dependencies]
python = "3.10"

[feature.failing.dependencies]
numpy = "2.1"

[environments]
env1 = { features = ["py310"] }
env2 = ["py39"]
failing-env = { features = ["failing"] }
25 changes: 25 additions & 0 deletions minimum_versions/environments/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pathlib

from minimum_versions.environments.conda import parse_conda_environment
from minimum_versions.environments.pixi import parse_pixi_environment
from minimum_versions.environments.spec import Spec, compare_versions # noqa: F401

kinds = {
"conda": parse_conda_environment,
"pixi": parse_pixi_environment,
}


def parse_environment(specifier: str, manifest_path: pathlib.Path | None) -> list[Spec]:
split = specifier.split(":", maxsplit=1)
if len(split) == 1:
kind = "conda"
path = specifier
else:
kind, path = split

parser = kinds.get(kind)
if parser is None:
raise ValueError(f"Unknown kind {kind!r}, extracted from {specifier!r}.")

return parser(path, manifest_path)
46 changes: 46 additions & 0 deletions minimum_versions/environments/conda.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import pathlib

import yaml
from rattler import Version

from minimum_versions.environments.spec import Spec


def parse_spec(spec_text):
warnings = []
if ">" in spec_text or "<" in spec_text:
warnings.append(
f"package must be pinned with an exact version: {spec_text!r}."
" Using the version as an exact pin instead."
)

spec_text = spec_text.replace(">", "").replace("<", "")

if "=" in spec_text:
name, version_text = spec_text.split("=", maxsplit=1)
version = Version(version_text)
segments = version.segments()

if (len(segments) == 3 and segments[2] != [0]) or len(segments) > 3:
warnings.append(
f"package should be pinned to a minor version (got {version})"
)
else:
name = spec_text
version = None

return Spec(name, version), (name, warnings)


def parse_conda_environment(path: pathlib.Path, manifest_path: None):
env = yaml.safe_load(pathlib.Path(path).read_text())

specs = []
warnings = []
for dep in env["dependencies"]:
spec, warnings_ = parse_spec(dep)

specs.append(spec)
warnings.append(warnings_)

return specs, warnings
Loading
Loading