diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c6ab07e397..87e80ce7e5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,6 +12,19 @@ updates: - "*" cooldown: default-days: 7 + # Keep the pinned dev tooling in pyproject.toml's [dependency-groups] and the + # uv.lock current. Without this the exact pins (e.g. pytest) would never be + # bumped automatically and would silently rot. + - package-ecosystem: "uv" + directory: "/" + schedule: + interval: "weekly" + groups: + python-dependencies: + patterns: + - "*" + cooldown: + default-days: 7 - package-ecosystem: "github-actions" directory: "/" target-branch: "support/v2" diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml index 57ae10ff0d..b0f1000529 100644 --- a/.github/workflows/prepare_release.yml +++ b/.github/workflows/prepare_release.yml @@ -42,15 +42,12 @@ jobs: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: '3.12' - - - name: Install towncrier - run: pip install towncrier + - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - name: Build changelog - run: towncrier build --version "$VERSION" --yes + # Use the pinned towncrier from the `release` dependency group (single + # source of truth) rather than an unpinned standalone install. + run: uv run --only-group release towncrier build --version "$VERSION" --yes env: VERSION: ${{ inputs.version }} diff --git a/pyproject.toml b/pyproject.toml index 9f6005f981..08e49e8255 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,20 +83,29 @@ Discussions = "https://github.com/zarr-developers/zarr-python/discussions" documentation = "https://zarr.readthedocs.io/" homepage = "https://github.com/zarr-developers/zarr-python" +# Dev *tooling* is pinned to exact versions for reproducible CI: the hatch envs +# (see `tool.hatch.envs.*`) and bare `uv run` resolve these groups fresh from +# PyPI and do NOT consult uv.lock, so an unrelated tooling release can break CI +# without any change on our side (e.g. the pytest 9.1.0 `duplicate +# parametrization` regression). Runtime/integration deps (fsspec, obstore, s3fs, +# botocore, numcodecs, universal-pathlib) are intentionally left floating so the +# `optional` test matrix keeps exercising their latest releases; their floor and +# bleeding edge are covered by the `min_deps` and `upstream` hatch envs. Bump the +# pins deliberately, e.g. via dependabot or `uv lock --upgrade`. [dependency-groups] test = [ - "coverage>=7.10", - "pytest", - "pytest-asyncio", - "pytest-cov", - "pytest-accept", - "numpydoc", - "hypothesis", - "pytest-xdist", - "pytest-benchmark", - "pytest-codspeed", - "tomlkit", - "uv", + "coverage==7.14.0", + "pytest==9.0.3", + "pytest-asyncio==1.3.0", + "pytest-cov==7.1.0", + "pytest-accept==0.2.3", + "numpydoc==1.10.0", + "hypothesis==6.152.7", + "pytest-xdist==3.8.0", + "pytest-benchmark==5.2.3", + "pytest-codspeed==5.0.1", + "tomlkit==0.15.0", + "uv==0.11.15", ] remote-tests = [ {include-group = "test"}, @@ -104,35 +113,38 @@ remote-tests = [ "obstore>=0.5.1", "botocore", "s3fs>=2023.10.0", - "moto[s3,server]", - "requests", + "moto[s3,server]==5.2.1", + "requests==2.34.1", +] +release = [ + "towncrier==25.8.0", ] docs = [ # Doc building - "mkdocs-material[imaging]>=9.6.14", - "mkdocs>=1.6.1,<2", - "mkdocstrings>=0.29.1", - "mkdocstrings-python>=1.16.10", - "mike>=2.1.3", - "mkdocs-jupyter>=0.25.1", - "mkdocs-redirects>=1.2.0", - "markdown-exec[ansi]", - "griffe-inherited-docstrings", - "ruff", + "mkdocs-material[imaging]==9.7.6", + "mkdocs==1.6.1", + "mkdocstrings==1.0.4", + "mkdocstrings-python==2.0.3", + "mike==2.2.0", + "mkdocs-jupyter==0.26.3", + "mkdocs-redirects==1.2.3", + "markdown-exec[ansi]==1.12.1", + "griffe-inherited-docstrings==1.1.3", + "ruff==0.15.12", # Changelog generation - "towncrier", + {include-group = "release"}, # Optional dependencies to run examples "numcodecs[msgpack]", "s3fs>=2023.10.0", - "astroid<4", - "pytest", + "astroid==3.3.11", + "pytest==9.0.3", ] dev = [ {include-group = "test"}, {include-group = "remote-tests"}, {include-group = "docs"}, "universal-pathlib", - "mypy", + "mypy==2.1.0", ] [tool.coverage.report] diff --git a/uv.lock b/uv.lock index 39eebfb270..e4ab5681ac 100644 --- a/uv.lock +++ b/uv.lock @@ -4023,6 +4023,9 @@ docs = [ { name = "s3fs" }, { name = "towncrier" }, ] +release = [ + { name = "towncrier" }, +] remote-tests = [ { name = "botocore" }, { name = "coverage" }, @@ -4077,88 +4080,89 @@ provides-extras = ["cast-value-rs", "cli", "gpu", "optional", "remote"] [package.metadata.requires-dev] dev = [ - { name = "astroid", specifier = "<4" }, + { name = "astroid", specifier = "==3.3.11" }, { name = "botocore" }, - { name = "coverage", specifier = ">=7.10" }, + { name = "coverage", specifier = "==7.14.0" }, { name = "fsspec", specifier = ">=2023.10.0" }, - { name = "griffe-inherited-docstrings" }, - { name = "hypothesis" }, - { name = "markdown-exec", extras = ["ansi"] }, - { name = "mike", specifier = ">=2.1.3" }, - { name = "mkdocs", specifier = ">=1.6.1,<2" }, - { name = "mkdocs-jupyter", specifier = ">=0.25.1" }, - { name = "mkdocs-material", extras = ["imaging"], specifier = ">=9.6.14" }, - { name = "mkdocs-redirects", specifier = ">=1.2.0" }, - { name = "mkdocstrings", specifier = ">=0.29.1" }, - { name = "mkdocstrings-python", specifier = ">=1.16.10" }, - { name = "moto", extras = ["s3", "server"] }, - { name = "mypy" }, + { name = "griffe-inherited-docstrings", specifier = "==1.1.3" }, + { name = "hypothesis", specifier = "==6.152.7" }, + { name = "markdown-exec", extras = ["ansi"], specifier = "==1.12.1" }, + { name = "mike", specifier = "==2.2.0" }, + { name = "mkdocs", specifier = "==1.6.1" }, + { name = "mkdocs-jupyter", specifier = "==0.26.3" }, + { name = "mkdocs-material", extras = ["imaging"], specifier = "==9.7.6" }, + { name = "mkdocs-redirects", specifier = "==1.2.3" }, + { name = "mkdocstrings", specifier = "==1.0.4" }, + { name = "mkdocstrings-python", specifier = "==2.0.3" }, + { name = "moto", extras = ["s3", "server"], specifier = "==5.2.1" }, + { name = "mypy", specifier = "==2.1.0" }, { name = "numcodecs", extras = ["msgpack"] }, - { name = "numpydoc" }, + { name = "numpydoc", specifier = "==1.10.0" }, { name = "obstore", specifier = ">=0.5.1" }, - { name = "pytest" }, - { name = "pytest-accept" }, - { name = "pytest-asyncio" }, - { name = "pytest-benchmark" }, - { name = "pytest-codspeed" }, - { name = "pytest-cov" }, - { name = "pytest-xdist" }, - { name = "requests" }, - { name = "ruff" }, + { name = "pytest", specifier = "==9.0.3" }, + { name = "pytest-accept", specifier = "==0.2.3" }, + { name = "pytest-asyncio", specifier = "==1.3.0" }, + { name = "pytest-benchmark", specifier = "==5.2.3" }, + { name = "pytest-codspeed", specifier = "==5.0.1" }, + { name = "pytest-cov", specifier = "==7.1.0" }, + { name = "pytest-xdist", specifier = "==3.8.0" }, + { name = "requests", specifier = "==2.34.1" }, + { name = "ruff", specifier = "==0.15.12" }, { name = "s3fs", specifier = ">=2023.10.0" }, - { name = "tomlkit" }, - { name = "towncrier" }, + { name = "tomlkit", specifier = "==0.15.0" }, + { name = "towncrier", specifier = "==25.8.0" }, { name = "universal-pathlib" }, - { name = "uv" }, + { name = "uv", specifier = "==0.11.15" }, ] docs = [ - { name = "astroid", specifier = "<4" }, - { name = "griffe-inherited-docstrings" }, - { name = "markdown-exec", extras = ["ansi"] }, - { name = "mike", specifier = ">=2.1.3" }, - { name = "mkdocs", specifier = ">=1.6.1,<2" }, - { name = "mkdocs-jupyter", specifier = ">=0.25.1" }, - { name = "mkdocs-material", extras = ["imaging"], specifier = ">=9.6.14" }, - { name = "mkdocs-redirects", specifier = ">=1.2.0" }, - { name = "mkdocstrings", specifier = ">=0.29.1" }, - { name = "mkdocstrings-python", specifier = ">=1.16.10" }, + { name = "astroid", specifier = "==3.3.11" }, + { name = "griffe-inherited-docstrings", specifier = "==1.1.3" }, + { name = "markdown-exec", extras = ["ansi"], specifier = "==1.12.1" }, + { name = "mike", specifier = "==2.2.0" }, + { name = "mkdocs", specifier = "==1.6.1" }, + { name = "mkdocs-jupyter", specifier = "==0.26.3" }, + { name = "mkdocs-material", extras = ["imaging"], specifier = "==9.7.6" }, + { name = "mkdocs-redirects", specifier = "==1.2.3" }, + { name = "mkdocstrings", specifier = "==1.0.4" }, + { name = "mkdocstrings-python", specifier = "==2.0.3" }, { name = "numcodecs", extras = ["msgpack"] }, - { name = "pytest" }, - { name = "ruff" }, + { name = "pytest", specifier = "==9.0.3" }, + { name = "ruff", specifier = "==0.15.12" }, { name = "s3fs", specifier = ">=2023.10.0" }, - { name = "towncrier" }, + { name = "towncrier", specifier = "==25.8.0" }, ] +release = [{ name = "towncrier", specifier = "==25.8.0" }] remote-tests = [ { name = "botocore" }, - { name = "coverage", specifier = ">=7.10" }, + { name = "coverage", specifier = "==7.14.0" }, { name = "fsspec", specifier = ">=2023.10.0" }, - { name = "hypothesis" }, - { name = "moto", extras = ["s3", "server"] }, - { name = "numpydoc" }, + { name = "hypothesis", specifier = "==6.152.7" }, + { name = "moto", extras = ["s3", "server"], specifier = "==5.2.1" }, + { name = "numpydoc", specifier = "==1.10.0" }, { name = "obstore", specifier = ">=0.5.1" }, - { name = "pytest" }, - { name = "pytest-accept" }, - { name = "pytest-asyncio" }, - { name = "pytest-benchmark" }, - { name = "pytest-codspeed" }, - { name = "pytest-cov" }, - { name = "pytest-xdist" }, - { name = "requests" }, + { name = "pytest", specifier = "==9.0.3" }, + { name = "pytest-accept", specifier = "==0.2.3" }, + { name = "pytest-asyncio", specifier = "==1.3.0" }, + { name = "pytest-benchmark", specifier = "==5.2.3" }, + { name = "pytest-codspeed", specifier = "==5.0.1" }, + { name = "pytest-cov", specifier = "==7.1.0" }, + { name = "pytest-xdist", specifier = "==3.8.0" }, + { name = "requests", specifier = "==2.34.1" }, { name = "s3fs", specifier = ">=2023.10.0" }, - { name = "tomlkit" }, - { name = "uv" }, + { name = "tomlkit", specifier = "==0.15.0" }, + { name = "uv", specifier = "==0.11.15" }, ] test = [ - { name = "coverage", specifier = ">=7.10" }, - { name = "hypothesis" }, - { name = "numpydoc" }, - { name = "pytest" }, - { name = "pytest-accept" }, - { name = "pytest-asyncio" }, - { name = "pytest-benchmark" }, - { name = "pytest-codspeed" }, - { name = "pytest-cov" }, - { name = "pytest-xdist" }, - { name = "tomlkit" }, - { name = "uv" }, + { name = "coverage", specifier = "==7.14.0" }, + { name = "hypothesis", specifier = "==6.152.7" }, + { name = "numpydoc", specifier = "==1.10.0" }, + { name = "pytest", specifier = "==9.0.3" }, + { name = "pytest-accept", specifier = "==0.2.3" }, + { name = "pytest-asyncio", specifier = "==1.3.0" }, + { name = "pytest-benchmark", specifier = "==5.2.3" }, + { name = "pytest-codspeed", specifier = "==5.0.1" }, + { name = "pytest-cov", specifier = "==7.1.0" }, + { name = "pytest-xdist", specifier = "==3.8.0" }, + { name = "tomlkit", specifier = "==0.15.0" }, + { name = "uv", specifier = "==0.11.15" }, ]