diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml
index ec797f62c..34cab00ea 100644
--- a/.github/dependabot.yaml
+++ b/.github/dependabot.yaml
@@ -13,5 +13,26 @@ updates:
labels: [ci]
schedule:
interval: monthly
- time: "05:00"
- timezone: Etc/UTC
+ cooldown:
+ default-days: 7
+ groups:
+ actions:
+ patterns:
+ - "*"
+
+ # Update frozen requirements in repo2docker Dockerfile
+ - package-ecosystem: pip
+ directory: /
+ allow:
+ # need this to upgrade requirements.txt
+ # otherwise only direct dependencies in requirements.in will be updated
+ - dependency-type: all
+ labels: [dependencies]
+ groups:
+ dockerfile:
+ patterns:
+ - "*"
+ schedule:
+ interval: monthly
+ cooldown:
+ default-days: 7
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index d1fa1c5aa..8ffd7f1c7 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -32,13 +32,13 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
# https://github.com/docker/build-push-action
- name: Build Docker image
- uses: docker/build-push-action@v6
+ uses: docker/build-push-action@v7
with:
context: .
tags: jupyter/repo2docker:pr
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7dabbc8ca..5b918e7cf 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -31,10 +31,10 @@ jobs:
build-n-publish:
runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-python@v5
+ - uses: actions/checkout@v6
+ - uses: actions/setup-python@v6
with:
- python-version: "3.9"
+ python-version: "3.13"
- name: install build requirements
run: |
@@ -47,7 +47,7 @@ jobs:
ls -l dist
# ref: https://github.com/actions/upload-artifact#readme
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@v7
with:
name: repo2docker-${{ github.sha }}
path: "dist/*"
@@ -85,8 +85,8 @@ jobs:
echo "REGISTRY=$REGISTRY" >> $GITHUB_ENV
echo "Publishing to $REGISTRY"
- # versioneer requires the full git history for non-tags
- - uses: actions/checkout@v4
+ # git requires the full git history for non-tags
+ - uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -94,10 +94,10 @@ jobs:
# https://github.com/docker/build-push-action/tree/v2.4.0#usage
# https://github.com/docker/build-push-action/blob/v2.4.0/docs/advanced/multi-platform.md
- name: Set up QEMU (for docker buildx)
- uses: docker/setup-qemu-action@v3
+ uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx (for multi-arch builds)
- uses: docker/setup-buildx-action@v3
+ uses: docker/setup-buildx-action@v4
with:
# Allows pushing to registry on localhost:5000
driver-opts: network=host
@@ -111,7 +111,8 @@ jobs:
# also push jupyter/repo2docker:1.2.3-3.abcd1234 (replace + with -)
- name: Get list of repo2docker docker tags
run: |
- VERSION=$(python3 -c 'import versioneer; print(versioneer.get_version().replace("+", "-"))')
+ pip install setuptools-scm
+ VERSION=$(python3 -m setuptools_scm | sed s@+@-@g)
TAGS="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$VERSION"
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
TAGS="$TAGS,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main"
@@ -124,7 +125,7 @@ jobs:
echo "TAGS=$TAGS" >> $GITHUB_ENV
- name: Build and push repo2docker
- uses: docker/build-push-action@v6
+ uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c0d163cae..51d48a941 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -40,15 +40,21 @@ env:
GIT_AUTHOR_EMAIL: ci-user@github.local
GIT_AUTHOR_NAME: CI User
+
jobs:
test:
+ # Don't run scheduled tests on forks
+ if: ${{ github.repository_owner == 'jupyterhub' || github.event_name != 'schedule' }}
+
runs-on: ubuntu-${{ matrix.ubuntu_version }}
strategy:
fail-fast: false
matrix:
ubuntu_version: ["24.04"]
- python_version: ["3.10"]
+ python_version: ["3.13"]
+ container_cli:
+ - docker
repo_type:
- base
- conda
@@ -60,10 +66,43 @@ jobs:
- r
- unit
- venv
+ - contentproviders
+ - norun
+ # Playwright test
+ - ui
+ # If the item to be include would overwrite a value,
+ # it must have all the fields!
+ include:
+ # The earliest actions/setup-python versions depend on the runner.
+ - ubuntu_version: "24.04"
+ python_version: "3.10"
+ container_cli: docker
+ repo_type: venv
+ # Run all tests for both Docker and Podman is expensive.
+ # We only run the "smoke" test for Podman.
+ - ubuntu_version: "24.04"
+ python_version: "3.13"
+ container_cli: podman
+ repo_type: base
+ # We are observing "no space left on device" on GitHub Actions,
+ # https://github.com/jupyterhub/repo2docker/pull/1471#issuecomment-3744990787
+ # - ubuntu_version: "24.04"
+ # python_version: "3.13"
+ # container_cli: podman
+ # repo_type: conda
+ - ubuntu_version: "24.04"
+ python_version: "3.13"
+ container_cli: podman
+ repo_type: dockerfile
+ - ubuntu_version: "24.04"
+ python_version: "3.13"
+ container_cli: podman
+ repo_type: unit
steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-python@v5
+ - uses: actions/checkout@v6
+
+ - uses: actions/setup-python@v6
with:
python-version: "${{ matrix.python_version }}"
@@ -72,6 +111,12 @@ jobs:
pip install -r dev-requirements.txt
pip freeze
+ - name: Install UI test dependencies
+ if: matrix.repo_type == 'ui'
+ run: |
+ pip install -r playwright-requirements.txt
+ playwright install firefox
+
- name: Install repo2docker
run: |
python -m build --wheel .
@@ -84,6 +129,11 @@ jobs:
- name: Run pytest
run: |
+ if [ "${{ matrix.container_cli }}" = "podman" ]
+ then
+ systemctl --user start podman.socket
+ export DOCKER_HOST=unix://$(podman info --format '{{.Host.RemoteSocket.Path}}')
+ fi
pytest --verbose --color=yes --durations=10 --cov=repo2docker tests/${{ matrix.repo_type }}
- - uses: codecov/codecov-action@v4
+ - uses: codecov/codecov-action@v6
diff --git a/.gitignore b/.gitignore
index e73b983ab..0ff86a1e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -192,3 +192,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
+
+# generated by setuptools_scm
+repo2docker/_version.py
diff --git a/.gitpod.yml b/.gitpod.yml
deleted file mode 100644
index 69e7aa86d..000000000
--- a/.gitpod.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-tasks:
- - init: |
- pip3 install sphinx-autobuild
- pip3 install -r dev-requirements.txt
- pip3 install -r docs/requirements.txt
- pip3 install -e .
- command: |
- sphinx-autobuild docs/source/ docs/_build/html/
- name: Sphinx preview
-
-ports:
- - port: 8000
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3ac5a2955..7d79b7a5c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -11,40 +11,29 @@
repos:
# Autoformat: Python code, syntax patterns are modernized
- repo: https://github.com/asottile/pyupgrade
- rev: v3.19.0
+ rev: v3.21.2
hooks:
- id: pyupgrade
- args:
- - --py36-plus
# check-tmp is a Python based test script run in created environments
- # that can be at least Python 3.5 even though we require Python 3.6 for
- # repo2docker itself.
+ # that can be Python 3.5, older than the Python version required to run
+ # repo2docker.
exclude: check-tmp
# Autoformat: Python code
- - repo: https://github.com/psf/black
- rev: 24.10.0
+ - repo: https://github.com/psf/black-pre-commit-mirror
+ rev: 26.3.1
hooks:
- id: black
- args:
- - --target-version=py36
- - --target-version=py37
- - --target-version=py38
- - --target-version=py39
- - --target-version=py310
- - --target-version=py311
# Autoformat: Python code
- repo: https://github.com/pycqa/isort
- rev: 5.13.2
+ rev: 8.0.1
hooks:
- id: isort
- args:
- - --profile=black
# Autoformat: markdown
- - repo: https://github.com/pre-commit/mirrors-prettier
- rev: v4.0.0-alpha.8
+ - repo: https://github.com/rbubley/mirrors-prettier
+ rev: v3.8.1
hooks:
- id: prettier
files: ".md"
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index dde32e62f..c0e4658d6 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -6,11 +6,12 @@ version: 2
sphinx:
configuration: docs/source/conf.py
+ builder: dirhtml
build:
os: ubuntu-22.04
tools:
- python: "3.10"
+ python: "3.11"
python:
install:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1dfc19576..0056f4c3c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -5,23 +5,23 @@
(And thank you particularly for coming to read the guidelines! :heart_eyes:)
The repo2docker developer documentation is all rendered on our documentation website: [https://repo2docker.readthedocs.io](https://repo2docker.readthedocs.io).
-If you're here, you're probably looking for the [Contributing to repo2docker development](https://repo2docker.readthedocs.io/en/latest/contributing/contributing.html) page.
+If you're here, you're probably looking for the [Contributing to repo2docker development](https://repo2docker.readthedocs.io/en/latest/contribute/contributing.html) page.
Please make sure you've read the following sections before opening an issue/pull request:
-- [Process for making a contribution](https://repo2docker.readthedocs.io/en/latest/contributing/contributing.html#process-for-making-a-contribution).
+- [Process for making a contribution](https://repo2docker.readthedocs.io/en/latest/contribute/contributing.html#process-for-making-a-contribution).
- These steps talk you through choosing the right issue template (bug report or feature request) and making a change.
-- [Guidelines to getting a Pull Request merged](https://repo2docker.readthedocs.io/en/latest/contributing/contributing.html#guidelines-to-getting-a-pull-request-merged).
+- [Guidelines to getting a Pull Request merged](https://repo2docker.readthedocs.io/en/latest/contribute/contributing.html#guidelines-to-getting-a-pull-request-merged).
- These are tips and tricks to help make your contribution as smooth as possible for you and for the repo2docker maintenance team.
There are a few other pages to highlight:
-- [Our roadmap](https://repo2docker.readthedocs.io/en/latest/contributing/roadmap.html)
+- [Our roadmap](https://repo2docker.readthedocs.io/en/latest/contribute/roadmap.html)
- We use the roadmap to develop a shared understanding of the project's vision and direction amongst the community of users, contributors, and maintainers.
This is a great place to get a feel for what the maintainers are thinking about for the short, medium, and long term future of the project.
- [Design of repo2docker](https://repo2docker.readthedocs.io/en/latest/design.html)
- This page explains some of the design principles behind repo2docker.
Its a good place to understand _why_ the team have made the decisions that they have along the way!
- We absolutely encourage discussion around refactoring, updating or extending repo2docker, but please make sure that you've understood this page before opening an issue to discuss the change you'd like to propose.
-- [Common developer tasks and how-tos](https://repo2docker.readthedocs.io/en/latest/contributing/tasks.html)
+- [Common developer tasks and how-tos](https://repo2docker.readthedocs.io/en/latest/contribute/tasks.html)
- Some notes on running tests, buildpack dependencies, creating a release, and keeping the pip files up to date.
diff --git a/Dockerfile b/Dockerfile
index 74fbad894..2c9b67cb4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# syntax = docker/dockerfile:1.3
-ARG ALPINE_VERSION=3.19
+ARG ALPINE_VERSION=3.21
FROM alpine:${ALPINE_VERSION}
RUN apk add --no-cache git python3 python3-dev py3-pip py3-setuptools build-base
@@ -15,7 +15,7 @@ RUN cd /tmp/src && git clean -xfd && git status
RUN mkdir /tmp/wheelhouse \
&& cd /tmp/wheelhouse \
&& pip install wheel \
- && pip wheel --no-cache-dir /tmp/src \
+ && pip wheel --no-cache-dir -r /tmp/src/requirements.txt /tmp/src \
&& ls -l /tmp/wheelhouse
FROM alpine:${ALPINE_VERSION}
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 997a413f5..000000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,7 +0,0 @@
-include *.md
-include LICENSE
-include setup.cfg
-recursive-include repo2docker/buildpacks *
-include versioneer.py
-include repo2docker/_version.py
-include repo2docker/contentproviders/dataverse.json
diff --git a/README.md b/README.md
index 01d60eebc..17db70694 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[](https://github.com/jupyterhub/repo2docker/actions)
[](http://repo2docker.readthedocs.io/en/latest/?badge=latest)
-[](https://repo2docker.readthedocs.io/en/latest/contributing/contributing.html)
+[](https://repo2docker.readthedocs.io/en/latest/contribute/)
[](https://quay.io/repository/jupyterhub/repo2docker?tab=tags)
`repo2docker` fetches a git repository and builds a container image based on
@@ -36,13 +36,13 @@ For more information, please visit
1. Docker to build & run the repositories. The [community edition](https://store.docker.com/search?type=edition&offering=community)
is recommended.
-2. Python 3.6+.
+2. Python 3.9+.
-Supported on Linux and macOS. [See documentation note about Windows support.](http://repo2docker.readthedocs.io/en/latest/install.html#note-about-windows-support)
+Supported on Linux and macOS. [See documentation note about Windows support.](https://nickjanetakis.com/blog/setting-up-docker-for-windows-and-wsl-to-work-flawlessly)
### Installation
-This a quick guide to installing `repo2docker`, see our documentation for [a full guide](https://repo2docker.readthedocs.io/en/latest/install.html).
+This a quick guide to installing `repo2docker`, see our documentation for [a full guide](https://repo2docker.readthedocs.io/en/latest/start/#install-repo2docker).
To install from PyPI:
@@ -85,14 +85,14 @@ If you copy paste that URL into your browser you will see a Jupyter Notebook
with the contents of the repository you had just built!
For more information on how to use `repo2docker`, see the
-[usage guide](http://repo2docker.readthedocs.io/en/latest/usage.html).
+[user guide](https://repo2docker.readthedocs.io/en/latest/use/).
## Repository specifications
Repo2Docker looks for configuration files in the source repository to
determine how the Docker image should be built. For a list of the configuration
files that `repo2docker` can use, see the
-[complete list of configuration files](https://repo2docker.readthedocs.io/en/latest/config_files.html).
+[complete list of configuration files](https://repo2docker.readthedocs.io/en/latest/configuration/).
The philosophy of repo2docker is inspired by
[Heroku Build Packs](https://devcenter.heroku.com/articles/buildpacks).
diff --git a/RELEASE.md b/RELEASE.md
index abc38f61f..f9a3eedde 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -1 +1 @@
-See https://repo2docker.readthedocs.io/en/latest/contributing/tasks.html#creating-a-release
+See https://repo2docker.readthedocs.io/en/latest/contribute/tasks/#creating-a-release
diff --git a/dev-requirements.txt b/dev-requirements.txt
index ee9a7009e..f92ddae06 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -6,3 +6,4 @@ pytest-cov
pytest>=7
pyyaml
requests_mock
+bcrypt
\ No newline at end of file
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 5c38c561a..2e0c7627c 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,7 +1,8 @@
-myst-parser>=0.18
-pydata-sphinx-theme>=0.11
+myst-parser<4
+pydata-sphinx-theme
sphinx-autobuild
sphinx-copybutton
+sphinx-design
sphinxcontrib-autoprogram>=0.1.7
sphinxext-opengraph
sphinxext-rediraffe
diff --git a/docs/source/_static/images/logo-dark.png b/docs/source/_static/images/logo-dark.png
new file mode 100644
index 000000000..36c986804
Binary files /dev/null and b/docs/source/_static/images/logo-dark.png differ
diff --git a/docs/source/_static/images/logo.png b/docs/source/_static/images/logo.png
index 9e03007d0..c3a497984 100644
Binary files a/docs/source/_static/images/logo.png and b/docs/source/_static/images/logo.png differ
diff --git a/docs/source/_static/images/whentouse.svg b/docs/source/_static/images/whentouse.svg
new file mode 100644
index 000000000..089e25011
--- /dev/null
+++ b/docs/source/_static/images/whentouse.svg
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/docs/source/architecture.md b/docs/source/architecture.md
index c145010ca..40e91a4c3 100644
--- a/docs/source/architecture.md
+++ b/docs/source/architecture.md
@@ -20,11 +20,11 @@ with explicit **versions** is all that is needed.
In repo2docker, a Buildpack does the following things:
1. **Detect** if it can handle a given repository
-2. **Build** a base language environment in the docker image
-3. **Copy** the contents of the repository into the docker image
-4. **Assemble** a specific environment in the docker image based on repository contents
-5. **Push** the built docker image to a specific docker registry (optional)
-6. **Run** the build docker image as a docker container (optional)
+2. **Build** a base language environment in the Docker image
+3. **Copy** the contents of the repository into the Docker image
+4. **Assemble** a specific environment in the Docker image based on repository contents
+5. **Push** the built Docker image to a specific Docker registry (optional)
+6. **Run** the build Docker image as a Docker container (optional)
### Detect
@@ -56,15 +56,15 @@ repositories built with the same buildpack.
For example, in `CondaBuildPack`, the base environment consists of installing [miniconda](https://conda.io/miniconda.html)
and basic notebook packages (from `repo2docker/buildpacks/conda/environment.yml`). This is going
to be the same for most repositories built with `CondaBuildPack`, so we want to use
-[docker layer caching](https://thenewstack.io/understanding-the-docker-cache-for-faster-builds/) as
+[Docker layer caching](https://thenewstack.io/understanding-the-docker-cache-for-faster-builds/) as
much as possible for performance reasons. Next time a repository is built with `CondaBuildPack`,
-we can skip straight to the **copy** step (since the base environment docker image _layers_ have
+we can skip straight to the **copy** step (since the base environment Docker image _layers_ have
already been built and cached).
The `get_build_scripts` and `get_build_script_files` methods are primarily used for this.
`get_build_scripts` can return arbitrary bash script lines that can be run as different users,
and `get_build_script_files` is used to copy specific scripts (such as a conda installer) into
-the image to be run as pat of `get_build_scripts`. Code in either has following constraints:
+the image to be run as part of `get_build_scripts`. Code in either has following constraints:
1. You can _not_ use the contents of repository in them, since this happens before the repository
is copied into the image. For example, `pip install -r requirements.txt` will not work,
@@ -91,24 +91,20 @@ This usually means installing required libraries specified in a format native to
Most of this work is done in `get_assemble_scripts` method. It can return arbitrary bash script
lines that can be run as different users, and has access to the repository contents (unlike
-`get_build_scripts`). The docker image layers produced by this usually can not be cached,
+`get_build_scripts`). The Docker image layers produced by this usually can not be cached,
so less restrictions apply to this than to `get_build_scripts`.
-At the end of the assemble step, the docker image is ready to be used in various ways!
+At the end of the assemble step, the Docker image is ready to be used in various ways!
### Push
-Optionally, repo2docker can **push** a built image to a [docker registry](https://docs.docker.com/registry/).
-This is done as a convenience only (since you can do the same with a `docker push` after using repo2docker
-only to build), and implemented in `Repo2Docker.push` method. It is only activated if using the
-`--push` commandline flag.
+Optionally, repo2docker can **push** a built image to a [Docker registry](https://docs.docker.com/registry/),
+if you specify the `--push` flag.
### Run
-Optionally, repo2docker can **run** the built image and allow the user to access the Jupyter Notebook
-running inside by default. This is also done as a convenience only (since you can do the same with `docker run`
-after using repo2docker only to build), and implemented in `Repo2Docker.run`. It is activated by default
-unless the `--no-run` commandline flag is passed.
+Optionally, repo2docker can **run** the built image and allow the user to access the Jupyter Notebook running inside by default.
+This is also done as a convenience only (since you can do the same with `docker run` after using repo2docker only to build), and implemented in `Repo2Docker.run`. It is activated by default unless the `--no-run` commandline flag is passed.
## ContentProviders
diff --git a/docs/source/changelog.md b/docs/source/changelog.md
index 58cef1cff..f6746915e 100644
--- a/docs/source/changelog.md
+++ b/docs/source/changelog.md
@@ -1,5 +1,148 @@
# Changelog
+## 2025.12.0
+
+([full changelog](https://github.com/jupyterhub/repo2docker/compare/6cf91e45d5b03f79e365cd82eda09d1178d03327...b796c5a6189f70cabc3c7be1d206b752c515fa2b))
+
+## API and Breaking Changes
+
+- require Python 3.10 [#1481](https://github.com/jupyterhub/repo2docker/pull/1481) ([@minrk](https://github.com/minrk), [@manics](https://github.com/manics), [@rgaiacs](https://github.com/rgaiacs))
+- Support pyproject.toml for pip install in Python buildpack [#1444](https://github.com/jupyterhub/repo2docker/pull/1444) ([@rgaiacs](https://github.com/rgaiacs), [@minrk](https://github.com/minrk), [@manics](https://github.com/manics), [@yuvipanda](https://github.com/yuvipanda))
+- Add --vanilla to R call [#1432](https://github.com/jupyterhub/repo2docker/pull/1432) ([@rgaiacs](https://github.com/rgaiacs), [@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio))
+- Change default R version from 4.2 to 4.4 [#1401](https://github.com/jupyterhub/repo2docker/pull/1401) ([@rgaiacs](https://github.com/rgaiacs), [@yuvipanda](https://github.com/yuvipanda), [@manics](https://github.com/manics))
+- Move `runtime.txt` parsing into base class [#1428](https://github.com/jupyterhub/repo2docker/pull/1428) ([@manics](https://github.com/manics), [@minrk](https://github.com/minrk), [@rgaiacs](https://github.com/rgaiacs))
+
+## Enhancements made
+
+- fetch dataverse installations at runtime [#1479](https://github.com/jupyterhub/repo2docker/pull/1479) ([@minrk](https://github.com/minrk), [@manics](https://github.com/manics))
+- Add Python 3.14 [#1475](https://github.com/jupyterhub/repo2docker/pull/1475) ([@rgaiacs](https://github.com/rgaiacs), [@minrk](https://github.com/minrk))
+- Bump mamba from 2.1.0 to 2.3.3, conda from 24.11.0 to 25.9.1 [#1465](https://github.com/jupyterhub/repo2docker/pull/1465) ([@weiji14](https://github.com/weiji14), [@minrk](https://github.com/minrk), [@rgaiacs](https://github.com/rgaiacs), [@manics](https://github.com/manics), [@yuvipanda](https://github.com/yuvipanda))
+- add support for R 4.5.1 [#1460](https://github.com/jupyterhub/repo2docker/pull/1460) ([@shaneknapp](https://github.com/shaneknapp), [@yuvipanda](https://github.com/yuvipanda))
+- build: migrate from setup.py to pyproject.toml [#1458](https://github.com/jupyterhub/repo2docker/pull/1458) ([@lixiname](https://github.com/lixiname), [@minrk](https://github.com/minrk), [@rgaiacs](https://github.com/rgaiacs))
+- Add check for docker buildx [#1457](https://github.com/jupyterhub/repo2docker/pull/1457) ([@rgaiacs](https://github.com/rgaiacs), [@yuvipanda](https://github.com/yuvipanda))
+- Support pyproject.toml for pip install in Python buildpack [#1444](https://github.com/jupyterhub/repo2docker/pull/1444) ([@rgaiacs](https://github.com/rgaiacs), [@minrk](https://github.com/minrk), [@manics](https://github.com/manics), [@yuvipanda](https://github.com/yuvipanda))
+- COPY --chmod=0755 /usr/local/bin/{python3-login,repo2docker-entrypoint} for rootless podman [#1395](https://github.com/jupyterhub/repo2docker/pull/1395) ([@westurner](https://github.com/westurner), [@minrk](https://github.com/minrk))
+
+## Bugs fixed
+
+- Don't error out if there's no disk space left when writing the logs [#1489](https://github.com/jupyterhub/repo2docker/pull/1489) ([@GeorgianaElena](https://github.com/GeorgianaElena), [@minrk](https://github.com/minrk), [@rgaiacs](https://github.com/rgaiacs), [@yuvipanda](https://github.com/yuvipanda))
+- Fix the HydroShare download url [#1488](https://github.com/jupyterhub/repo2docker/pull/1488) ([@sblack-usu](https://github.com/sblack-usu), [@rgaiacs](https://github.com/rgaiacs))
+- Updating conda buildpacks for Python == 3.13 [#1476](https://github.com/jupyterhub/repo2docker/pull/1476) ([@rgaiacs](https://github.com/rgaiacs))
+- Always call `ContainerEngine.build()` when `push`ing [#1464](https://github.com/jupyterhub/repo2docker/pull/1464) ([@manics](https://github.com/manics), [@rgaiacs](https://github.com/rgaiacs))
+- Fail earlier when user try to install future version of R [#1456](https://github.com/jupyterhub/repo2docker/pull/1456) ([@rgaiacs](https://github.com/rgaiacs), [@minrk](https://github.com/minrk), [@manics](https://github.com/manics))
+- Replace --vanilla with --no-init-file [#1451](https://github.com/jupyterhub/repo2docker/pull/1451) ([@rgaiacs](https://github.com/rgaiacs), [@manics](https://github.com/manics))
+
+## Maintenance and upkeep improvements
+
+- [pre-commit.ci] pre-commit autoupdate [#1491](https://github.com/jupyterhub/repo2docker/pull/1491) ([@minrk](https://github.com/minrk))
+- [pre-commit.ci] pre-commit autoupdate [#1484](https://github.com/jupyterhub/repo2docker/pull/1484) ([@minrk](https://github.com/minrk))
+- [pre-commit.ci] pre-commit autoupdate [#1473](https://github.com/jupyterhub/repo2docker/pull/1473) ([@minrk](https://github.com/minrk))
+- Delete gitpod config [#1472](https://github.com/jupyterhub/repo2docker/pull/1472) ([@manics](https://github.com/manics), [@minrk](https://github.com/minrk))
+- Add base environment for Python 3.13 [#1447](https://github.com/jupyterhub/repo2docker/pull/1447) ([@rgaiacs](https://github.com/rgaiacs), [@minrk](https://github.com/minrk), [@manics](https://github.com/manics))
+- Add note on root users and make postbuild actions more discoverable [#1436](https://github.com/jupyterhub/repo2docker/pull/1436) ([@choldgraf](https://github.com/choldgraf), [@yuvipanda](https://github.com/yuvipanda), [@chuckwondo](https://github.com/chuckwondo), [@rgaiacs](https://github.com/rgaiacs))
+- Move `runtime.txt` parsing into base class [#1428](https://github.com/jupyterhub/repo2docker/pull/1428) ([@manics](https://github.com/manics), [@minrk](https://github.com/minrk), [@rgaiacs](https://github.com/rgaiacs))
+- Stop using datetime.utcnow() to fix warning [#1406](https://github.com/jupyterhub/repo2docker/pull/1406) ([@yuvipanda](https://github.com/yuvipanda), [@minrk](https://github.com/minrk))
+- [pre-commit.ci] pre-commit autoupdate [#1394](https://github.com/jupyterhub/repo2docker/pull/1394) ([@yuvipanda](https://github.com/yuvipanda))
+
+## Documentation improvements
+
+- Update link for creating a release [#1492](https://github.com/jupyterhub/repo2docker/pull/1492) ([@GeorgianaElena](https://github.com/GeorgianaElena), [@minrk](https://github.com/minrk), [@rgaiacs](https://github.com/rgaiacs))
+- Add item to FAQ regarding rootless mode [#1485](https://github.com/jupyterhub/repo2docker/pull/1485) ([@rgaiacs](https://github.com/rgaiacs), [@manics](https://github.com/manics))
+- Fix broken readme doc links [#1466](https://github.com/jupyterhub/repo2docker/pull/1466) ([@agitter](https://github.com/agitter), [@minrk](https://github.com/minrk))
+- Add missing 2025.08.0 release in changelog [#1462](https://github.com/jupyterhub/repo2docker/pull/1462) ([@rgaiacs](https://github.com/rgaiacs), [@manics](https://github.com/manics))
+- fix broken links in `CONTRIBUTING.md` [#1461](https://github.com/jupyterhub/repo2docker/pull/1461) ([@shaneknapp](https://github.com/shaneknapp), [@rgaiacs](https://github.com/rgaiacs))
+- Improve documentation index page [#1445](https://github.com/jupyterhub/repo2docker/pull/1445) ([@rgaiacs](https://github.com/rgaiacs), [@yuvipanda](https://github.com/yuvipanda))
+- Minor improvements to documentation [#1442](https://github.com/jupyterhub/repo2docker/pull/1442) ([@rgaiacs](https://github.com/rgaiacs), [@manics](https://github.com/manics), [@yuvipanda](https://github.com/yuvipanda))
+- Avoid jupyter-repo2docker in the commad line [#1438](https://github.com/jupyterhub/repo2docker/pull/1438) ([@rgaiacs](https://github.com/rgaiacs), [@yuvipanda](https://github.com/yuvipanda))
+- Simplify our release docs [#1434](https://github.com/jupyterhub/repo2docker/pull/1434) ([@choldgraf](https://github.com/choldgraf), [@yuvipanda](https://github.com/yuvipanda), [@minrk](https://github.com/minrk))
+- Revamp our documentation and refactor a bit [#1433](https://github.com/jupyterhub/repo2docker/pull/1433) ([@choldgraf](https://github.com/choldgraf), [@chuckwondo](https://github.com/chuckwondo), [@rgaiacs](https://github.com/rgaiacs), [@yuvipanda](https://github.com/yuvipanda))
+
+## Continuous integration improvements
+
+- Bump actions/checkout from 5 to 6 [#1490](https://github.com/jupyterhub/repo2docker/pull/1490) ([@manics](https://github.com/manics))
+- Bump actions/upload-artifact from 4 to 5 [#1482](https://github.com/jupyterhub/repo2docker/pull/1482) ([@manics](https://github.com/manics))
+- Bump actions/setup-python from 5 to 6 [#1470](https://github.com/jupyterhub/repo2docker/pull/1470) ([@consideRatio](https://github.com/consideRatio))
+- Bump actions/checkout from 4 to 5 [#1469](https://github.com/jupyterhub/repo2docker/pull/1469) ([@consideRatio](https://github.com/consideRatio))
+
+## Contributors to this release
+
+The following people contributed discussions, new ideas, code and documentation contributions, and review.
+See [our definition of contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports).
+
+([GitHub contributors page for this release](https://github.com/jupyterhub/repo2docker/graphs/contributors?from=2025-08-01&to=2025-12-09&type=c))
+
+@agitter ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Aagitter+updated%3A2025-08-01..2025-12-09&type=Issues)) | @choldgraf ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Acholdgraf+updated%3A2025-08-01..2025-12-09&type=Issues)) | @chuckwondo ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Achuckwondo+updated%3A2025-08-01..2025-12-09&type=Issues)) | @consideRatio ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3AconsideRatio+updated%3A2025-08-01..2025-12-09&type=Issues)) | @GeorgianaElena ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3AGeorgianaElena+updated%3A2025-08-01..2025-12-09&type=Issues)) | @lixiname ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Alixiname+updated%3A2025-08-01..2025-12-09&type=Issues)) | @manics ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Amanics+updated%3A2025-08-01..2025-12-09&type=Issues)) | @matthewfeickert ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Amatthewfeickert+updated%3A2025-08-01..2025-12-09&type=Issues)) | @minrk ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Aminrk+updated%3A2025-08-01..2025-12-09&type=Issues)) | @rgaiacs ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Argaiacs+updated%3A2025-08-01..2025-12-09&type=Issues)) | @sblack-usu ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Asblack-usu+updated%3A2025-08-01..2025-12-09&type=Issues)) | @shaneknapp ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Ashaneknapp+updated%3A2025-08-01..2025-12-09&type=Issues)) | @weiji14 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Aweiji14+updated%3A2025-08-01..2025-12-09&type=Issues)) | @westurner ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Awesturner+updated%3A2025-08-01..2025-12-09&type=Issues)) | @yuvipanda ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Ayuvipanda+updated%3A2025-08-01..2025-12-09&type=Issues))
+
+## 2025.08.0
+
+([full changelog](https://github.com/jupyterhub/repo2docker/compare/4da768765372c602c06606cb79d21a398fcc2987...6cf91e45d5b03f79e365cd82eda09d1178d03327))
+
+### API and Breaking Changes
+
+- Switch to using CLI for everything except running the container [#1421](https://github.com/jupyterhub/repo2docker/pull/1421) ([@yuvipanda](https://github.com/yuvipanda), [@manics](https://github.com/manics), [@minrk](https://github.com/minrk))
+- Require Python 3.9 to run repo2docker [#1411](https://github.com/jupyterhub/repo2docker/pull/1411) ([@manics](https://github.com/manics), [@minrk](https://github.com/minrk), [@yuvipanda](https://github.com/yuvipanda))
+- Shell out to `docker buildx build` to build images [#1402](https://github.com/jupyterhub/repo2docker/pull/1402) ([@yuvipanda](https://github.com/yuvipanda), [@manics](https://github.com/manics), [@minrk](https://github.com/minrk))
+- notebook 7 [#1363](https://github.com/jupyterhub/repo2docker/pull/1363) ([@minrk](https://github.com/minrk), [@manics](https://github.com/manics))
+- Major version bump node (18->20) and jupyterhub-singleuser (3->5) [#1359](https://github.com/jupyterhub/repo2docker/pull/1359) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda))
+
+### Enhancements made
+
+- add triplets for r versions 4.3, 4.4 [#1403](https://github.com/jupyterhub/repo2docker/pull/1403) ([@minrk](https://github.com/minrk), [@yuvipanda](https://github.com/yuvipanda), [@manics](https://github.com/manics))
+- Upgrade Shiny server to 2024.12 [#1400](https://github.com/jupyterhub/repo2docker/pull/1400) ([@rgaiacs](https://github.com/rgaiacs), [@manics](https://github.com/manics))
+- Upgrade RStudio to 2024.12 [#1399](https://github.com/jupyterhub/repo2docker/pull/1399) ([@rgaiacs](https://github.com/rgaiacs), [@manics](https://github.com/manics))
+- Use REST APIs to resolve DOIs + cleanup dataverse provider [#1390](https://github.com/jupyterhub/repo2docker/pull/1390) ([@yuvipanda](https://github.com/yuvipanda), [@minrk](https://github.com/minrk), [@pdurbin](https://github.com/pdurbin))
+- exclude defaults channel by default [#1365](https://github.com/jupyterhub/repo2docker/pull/1365) ([@minrk](https://github.com/minrk), [@rgaiacs](https://github.com/rgaiacs))
+
+### Bugs fixed
+
+- [MRG] Disable bash trace output in conda activation script [#1425](https://github.com/jupyterhub/repo2docker/pull/1425) ([@mfisher87](https://github.com/mfisher87), [@manics](https://github.com/manics))
+- Simulate json output from docker buildx build [#1413](https://github.com/jupyterhub/repo2docker/pull/1413) ([@yuvipanda](https://github.com/yuvipanda), [@manics](https://github.com/manics), [@minrk](https://github.com/minrk))
+- `set -e` should not be set after conda environment is sourced [#1409](https://github.com/jupyterhub/repo2docker/pull/1409) ([@manics](https://github.com/manics), [@yuvipanda](https://github.com/yuvipanda))
+- Use self.log rather than logging module directly [#1378](https://github.com/jupyterhub/repo2docker/pull/1378) ([@yuvipanda](https://github.com/yuvipanda), [@manics](https://github.com/manics))
+- julia_project.py: fix Pkg REPL api warning [#1376](https://github.com/jupyterhub/repo2docker/pull/1376) ([@fonsp](https://github.com/fonsp), [@GeorgianaElena](https://github.com/GeorgianaElena), [@agoose77](https://github.com/agoose77))
+- TarFile.add: don't add recursively [#1371](https://github.com/jupyterhub/repo2docker/pull/1371) ([@manics](https://github.com/manics), [@minrk](https://github.com/minrk))
+
+### Maintenance and upkeep improvements
+
+- Bump mamba from 2.0.5 to 2.1.0 [#1423](https://github.com/jupyterhub/repo2docker/pull/1423) ([@weiji14](https://github.com/weiji14), [@yuvipanda](https://github.com/yuvipanda))
+- maint: Adapt mamba activation [#1419](https://github.com/jupyterhub/repo2docker/pull/1419) ([@jjerphan](https://github.com/jjerphan), [@yuvipanda](https://github.com/yuvipanda), [@minrk](https://github.com/minrk))
+- Add a test for runtime.txt with full R version specified [#1416](https://github.com/jupyterhub/repo2docker/pull/1416) ([@yuvipanda](https://github.com/yuvipanda), [@minrk](https://github.com/minrk))
+- Bump alpine docker to 3.21 [#1412](https://github.com/jupyterhub/repo2docker/pull/1412) ([@manics](https://github.com/manics), [@minrk](https://github.com/minrk))
+- Refreeze base environment for package upgrades [#1407](https://github.com/jupyterhub/repo2docker/pull/1407) ([@yuvipanda](https://github.com/yuvipanda), [@manics](https://github.com/manics))
+- Remove unused \_urlopen method [#1392](https://github.com/jupyterhub/repo2docker/pull/1392) ([@yuvipanda](https://github.com/yuvipanda), [@minrk](https://github.com/minrk))
+- Upgraded to micromamba 2.0.5 [#1387](https://github.com/jupyterhub/repo2docker/pull/1387) ([@JohanMabille](https://github.com/JohanMabille), [@yuvipanda](https://github.com/yuvipanda), [@SylvainCorlay](https://github.com/SylvainCorlay))
+- Use self.log.warning instead of warnings.warn [#1384](https://github.com/jupyterhub/repo2docker/pull/1384) ([@yuvipanda](https://github.com/yuvipanda), [@manics](https://github.com/manics))
+- call close_handlers before garbage collection [#1380](https://github.com/jupyterhub/repo2docker/pull/1380) ([@minrk](https://github.com/minrk), [@manics](https://github.com/manics))
+- [pre-commit.ci] pre-commit autoupdate [#1377](https://github.com/jupyterhub/repo2docker/pull/1377) ([@minrk](https://github.com/minrk))
+- Update to mamba 1.5.9 [#1370](https://github.com/jupyterhub/repo2docker/pull/1370) ([@SylvainCorlay](https://github.com/SylvainCorlay), [@manics](https://github.com/manics))
+- Refreeze conda packages (JupyterLab 4.2.3 -> 4.2.5) [#1369](https://github.com/jupyterhub/repo2docker/pull/1369) ([@manics](https://github.com/manics), [@minrk](https://github.com/minrk))
+- Update for mamba 1.5.8 [#1367](https://github.com/jupyterhub/repo2docker/pull/1367) ([@jjerphan](https://github.com/jjerphan), [@manics](https://github.com/manics), [@SylvainCorlay](https://github.com/SylvainCorlay))
+- update import of shlex.quote [#1364](https://github.com/jupyterhub/repo2docker/pull/1364) ([@minrk](https://github.com/minrk), [@manics](https://github.com/manics))
+- [pre-commit.ci] pre-commit autoupdate [#1362](https://github.com/jupyterhub/repo2docker/pull/1362) ([@minrk](https://github.com/minrk))
+
+### Documentation improvements
+
+- Revamp our documentation and refactor a bit [#1433](https://github.com/jupyterhub/repo2docker/pull/1433) ([@choldgraf](https://github.com/choldgraf), [@chuckwondo](https://github.com/chuckwondo), [@rgaiacs](https://github.com/rgaiacs), [@yuvipanda](https://github.com/yuvipanda))
+- bug report.rst [#1429](https://github.com/jupyterhub/repo2docker/pull/1429) ([@nadiaguiffant](https://github.com/nadiaguiffant), [@manics](https://github.com/manics))
+- Add changelog for 2024.07.0 [#1356](https://github.com/jupyterhub/repo2docker/pull/1356) ([@yuvipanda](https://github.com/yuvipanda), [@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics))
+
+### Continuous integration improvements
+
+- Add basic UI Playwright tests [#1410](https://github.com/jupyterhub/repo2docker/pull/1410) ([@manics](https://github.com/manics), [@minrk](https://github.com/minrk))
+- Don't run scheduled workflows on forks [#1408](https://github.com/jupyterhub/repo2docker/pull/1408) ([@manics](https://github.com/manics), [@yuvipanda](https://github.com/yuvipanda))
+- build(deps): bump codecov/codecov-action from 4 to 5 [#1381](https://github.com/jupyterhub/repo2docker/pull/1381) ([@minrk](https://github.com/minrk))
+- [pre-commit.ci] pre-commit autoupdate [#1358](https://github.com/jupyterhub/repo2docker/pull/1358) ([@minrk](https://github.com/minrk))
+- build(deps): bump docker/build-push-action from 5 to 6 [#1357](https://github.com/jupyterhub/repo2docker/pull/1357) ([@manics](https://github.com/manics))
+
+### Contributors to this release
+
+The following people contributed discussions, new ideas, code and documentation contributions, and review.
+See [our definition of contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports).
+
+([GitHub contributors page for this release](https://github.com/jupyterhub/repo2docker/graphs/contributors?from=2024-07-01&to=2025-08-03&type=c))
+
+@agoose77 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Aagoose77+updated%3A2024-07-01..2025-08-03&type=Issues)) | @AliMirlou ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3AAliMirlou+updated%3A2024-07-01..2025-08-03&type=Issues)) | @betatim ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Abetatim+updated%3A2024-07-01..2025-08-03&type=Issues)) | @choldgraf ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Acholdgraf+updated%3A2024-07-01..2025-08-03&type=Issues)) | @chuckwondo ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Achuckwondo+updated%3A2024-07-01..2025-08-03&type=Issues)) | @consideRatio ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3AconsideRatio+updated%3A2024-07-01..2025-08-03&type=Issues)) | @d70-t ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Ad70-t+updated%3A2024-07-01..2025-08-03&type=Issues)) | @felder ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Afelder+updated%3A2024-07-01..2025-08-03&type=Issues)) | @fonsp ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Afonsp+updated%3A2024-07-01..2025-08-03&type=Issues)) | @GeorgianaElena ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3AGeorgianaElena+updated%3A2024-07-01..2025-08-03&type=Issues)) | @Hind-M ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3AHind-M+updated%3A2024-07-01..2025-08-03&type=Issues)) | @hiroyuki-sato ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Ahiroyuki-sato+updated%3A2024-07-01..2025-08-03&type=Issues)) | @jjerphan ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Ajjerphan+updated%3A2024-07-01..2025-08-03&type=Issues)) | @JohanMabille ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3AJohanMabille+updated%3A2024-07-01..2025-08-03&type=Issues)) | @manics ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Amanics+updated%3A2024-07-01..2025-08-03&type=Issues)) | @mfisher87 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Amfisher87+updated%3A2024-07-01..2025-08-03&type=Issues)) | @minrk ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Aminrk+updated%3A2024-07-01..2025-08-03&type=Issues)) | @nadiaguiffant ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Anadiaguiffant+updated%3A2024-07-01..2025-08-03&type=Issues)) | @pdurbin ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Apdurbin+updated%3A2024-07-01..2025-08-03&type=Issues)) | @rgaiacs ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Argaiacs+updated%3A2024-07-01..2025-08-03&type=Issues)) | @ryanlovett ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Aryanlovett+updated%3A2024-07-01..2025-08-03&type=Issues)) | @SylvainCorlay ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3ASylvainCorlay+updated%3A2024-07-01..2025-08-03&type=Issues)) | @weiji14 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Aweiji14+updated%3A2024-07-01..2025-08-03&type=Issues)) | @yuvipanda ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Frepo2docker+involves%3Ayuvipanda+updated%3A2024-07-01..2025-08-03&type=Issues))
+
## 2024.07.0
([full changelog](https://github.com/jupyterhub/repo2docker/compare/2024.03.0...2024.07.0))
@@ -134,7 +277,7 @@
- pre-commit update to fix ci [#1238](https://github.com/jupyterhub/repo2docker/pull/1238) ([@minrk](https://github.com/minrk))
- Upgrade to mamba 1.1 and enable rich SAT error messages [#1232](https://github.com/jupyterhub/repo2docker/pull/1232) ([@SylvainCorlay](https://github.com/SylvainCorlay))
- Upgrade to mamba 1.0 [#1213](https://github.com/jupyterhub/repo2docker/pull/1213) ([@SylvainCorlay](https://github.com/SylvainCorlay))
-- docker image: update alpine to 3.16 [#1212](https://github.com/jupyterhub/repo2docker/pull/1212) ([@consideRatio](https://github.com/consideRatio))
+- Docker image: update alpine to 3.16 [#1212](https://github.com/jupyterhub/repo2docker/pull/1212) ([@consideRatio](https://github.com/consideRatio))
- reconcile CLI/config priority [#1211](https://github.com/jupyterhub/repo2docker/pull/1211) ([@minrk](https://github.com/minrk))
- pipfile: pass --clear flag, and do it separetely to not be ignored [#1208](https://github.com/jupyterhub/repo2docker/pull/1208) ([@consideRatio](https://github.com/consideRatio))
- run submodule test over https [#1204](https://github.com/jupyterhub/repo2docker/pull/1204) ([@minrk](https://github.com/minrk))
@@ -299,12 +442,12 @@ The repo2docker container image has moved to [quay.io/jupyterhub/repo2docker](ht
- Workaround docker-py dependency's failure to import six {pr}`1066:` by {user}`consideratio`
- fix: add chardet, a not explicitly declared dependency {pr}`1064` by {user}`johnhoman`
-- Add build-base to build stage of docker image {pr}`1051` by {user}`yuvipanda`
+- Add build-base to build stage of Docker image {pr}`1051` by {user}`yuvipanda`
- Fix regression in hydroshare introduced after moving to requests {pr}`1034` by {user}`MridulS`
### Other merged PRs
-- Update README quay.io URL, Add docker latest tag {pr}`1075` by {user}`manics`
+- Update README quay.io URL, add Docker latest tag {pr}`1075` by {user}`manics`
- GitHub workflow build and push to Docker hub {pr}`1071` by {user}`manics`
- Rename master branch to main {pr}`1068` by {user}`manics`
- Remove Pipfile & Pipfile.lock {pr}`1054` by {user}`yuvipanda`
@@ -343,7 +486,7 @@ The repo2docker container image has moved to [quay.io/jupyterhub/repo2docker](ht
### Other merged PRs
- Cleanup install_requires including duplicates {pr}`1020` by {user}`manics`
-- bump docker action version {pr}`1018` by {user}`minrk`
+- bump Docker action version {pr}`1018` by {user}`minrk`
- bump python in circleci test {pr}`1013` by {user}`minrk`
- Investigating the missing logs {pr}`1008` by {user}`betatim`
- Experiment with different install mechanism to get code coverage stats again {pr}`982` by {user}`betatim`
@@ -376,7 +519,7 @@ The repo2docker container image has moved to [quay.io/jupyterhub/repo2docker](ht
- chmod start script from repo2docker-entrypoint {pr}`886` by {user}`danlester`
- pypi jupyter-offlinenotebook==0.1.0 {pr}`880` by {user}`manics`
- Add support for Julia 1.4.1 {pr}`878` by {user}`davidanthoff`
-- Change --env option to work like docker's {pr}`874` by {user}`hwine`
+- Change --env option to work like Docker's {pr}`874` by {user}`hwine`
- Add support for Julia 1.4.0 {pr}`870` by {user}`davidanthoff`
- Update server proxy and rsession proxy {pr}`869` by {user}`betatim`
- Use miniforge instead of miniconda to get conda {pr}`859` by {user}`yuvipanda`
@@ -549,7 +692,7 @@ Release date: 2019-02-21
### New features
-- Add additional metadata to docker images about how they were built {pr}`500` by
+- Add additional metadata to Docker images about how they were built {pr}`500` by
{user}`jrbourbeau`.
- Allow users to install global NPM packages: {pr}`573` by {user}`GladysNalvarte`.
- Add documentation on switching the user interface presented by a
@@ -579,7 +722,7 @@ Release date: 2019-02-21
- Fix quoting issue in `GIT_CREDENTIAL_ENV` environment variable by
{user}`minrk` in {pr}`572`.
- Change to using the first 8 characters of each Git commit, not the last 8,
- to tag each built docker image of repo2docker itself. {user}`minrk` in {pr}`562`.
+ to tag each built Docker image of repo2docker itself. {user}`minrk` in {pr}`562`.
- Allow users to select the Julia when using a `requirements.txt` by
{user}`yuvipanda` in {pr}`557`.
- Set `JULIA_DEPOT_PATH` to install packages outside the home directory by
diff --git a/docs/source/cli.md b/docs/source/cli.md
new file mode 100644
index 000000000..82a29c1bd
--- /dev/null
+++ b/docs/source/cli.md
@@ -0,0 +1,87 @@
+# Command-line usage and API
+
+`repo2docker` is called with this command:
+
+```
+repo2docker
+```
+
+where `` is a repository in one of [the supported repository providers](#repository-providers).
+
+For example, the following command will build an image of Peter Norvig's
+[Pytudes] repository:
+
+```
+repo2docker https://github.com/norvig/pytudes
+```
+
+Building the image may take a few minutes.
+
+[Pytudes] uses a [`requirements.txt` file](https://github.com/norvig/pytudes/blob/HEAD/requirements.txt) to specify its Python environment. Because of this, `repo2docker` will use `pip` to install dependencies listed in this `requirements.txt` file, and these will be present in the generated Docker image. To learn more about configuration files in `repo2docker` visit [](#config-files).
+
+When the image is built, a message will be output to your terminal:
+
+```
+Copy/paste this URL into your browser when you connect for the first time,
+to login with a token:
+ http://0.0.0.0:36511/?token=f94f8fabb92e22f5bfab116c382b4707fc2cade56ad1ace0
+```
+
+Pasting the URL into your browser will open Jupyter Notebook with the
+dependencies and contents of the source repository in the built image.
+
+## Debug repo2docker with `--debug` and `--no-build`
+
+To debug the container image being built, pass the `--debug` parameter:
+
+> ```bash
+> repo2docker --debug https://github.com/norvig/pytudes
+> ```
+
+This will print the generated `Dockerfile`, build it, and run it.
+
+To see the generated `Dockerfile` without actually building it, pass `--no-build` to the commandline.
+This `Dockerfile` output is for **debugging purposes** of `repo2docker` only - it can not be used by Docker directly.
+
+> ```bash
+> repo2docker --no-build --debug https://github.com/norvig/pytudes
+> ```
+
+## Build from a branch, commit or tag
+
+To build a particular branch and commit, use the argument `--ref` and
+specify the `branch-name` or `commit-hash`. For example:
+
+```
+repo2docker --ref 9ced85dd9a84859d0767369e58f33912a214a3cf https://github.com/norvig/pytudes
+```
+
+:::{tip}
+For reproducible builds, we recommend specifying a commit-hash to
+deterministically build a fixed version of a repository. Not specifying a
+commit-hash will result in the latest commit of the repository being built.
+:::
+
+## Set environment variables during builds
+
+When running repo2docker locally you can use the `-e` or `--env` command-line
+flag for each variable that you want to define.
+
+For example:
+
+```bash
+repo2docker -e VAR1=val1 -e VAR2=val2 ...
+```
+
+You can also configure environment variables for all users of a repository using the
+[](#config-start) configuration file.
+
+(command-line-api)=
+
+## Command-line API
+
+```{autoprogram} repo2docker.__main__:argparser
+:prog: repo2docker
+```
+
+[pytudes]: https://github.com/norvig/pytudes
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 2dcb08615..2105b87ac 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -22,6 +22,7 @@
"sphinxcontrib.autoprogram",
"sphinxext.opengraph",
"sphinxext.rediraffe",
+ "sphinx_design",
]
root_doc = "index"
source_suffix = [".md", ".rst"]
@@ -40,6 +41,7 @@
#
myst_enable_extensions = [
"colon_fence",
+ "substitution",
]
@@ -58,8 +60,11 @@
from repo2docker.buildpacks.conda import CondaBuildPack
-default_python = CondaBuildPack.major_pythons["3"]
-
+default_python = f"Python {CondaBuildPack.major_pythons['3']}"
+myst_substitutions = {
+ "default_python": default_python,
+ "default_python_version": default_python,
+}
rst_prolog = f"""
.. |default_python| replace:: **Python {default_python}**
.. |default_python_version| replace:: {default_python}
@@ -73,12 +78,20 @@
html_favicon = "_static/images/favicon.ico"
html_static_path = ["_static"]
html_css_files = ["custom.css"]
+html_sidebars = {
+ "changelog": [],
+ "start": [],
+}
# pydata_sphinx_theme reference: https://pydata-sphinx-theme.readthedocs.io/en/latest/
html_theme = "pydata_sphinx_theme"
html_theme_options = {
"use_edit_page_button": True,
"github_url": "https://github.com/jupyterhub/repo2docker",
+ "logo": {
+ "image_dark": "_static/images/logo-dark.png",
+ "image_light": "_static/images/logo.png",
+ },
}
html_context = {
"github_user": "jupyterhub",
@@ -125,4 +138,14 @@
rediraffe_branch = "main"
rediraffe_redirects = {
# "old-file": "new-folder/new-file-name",
+ "config_files.rst": "configuration/index.md",
+ "install.rst": "start.md",
+ "usage.rst": "use/index.md",
+ "contributing/buildpack.md": "contribute/buildpack.md",
+ "contributing/contentprovider.rst": "contribute/contentprovider.rst",
+ "contributing/contributing.md": "contribute/contributing.md",
+ "contributing/index.md": "contribute/index.md",
+ "contributing/roadmap.md": "contribute/roadmap.md",
+ "contributing/tasks.md": "contribute/tasks.md",
+ "getting-started/index.md": "use/index.md",
}
diff --git a/docs/source/config_files.rst b/docs/source/config_files.rst
deleted file mode 100644
index 0b057e825..000000000
--- a/docs/source/config_files.rst
+++ /dev/null
@@ -1,271 +0,0 @@
-.. _config-files:
-
-===================
-Configuration Files
-===================
-
-``repo2docker`` looks for configuration files in the repository being built
-to determine how to build it. In general, ``repo2docker`` uses the same
-configuration files as other software installation tools,
-rather than creating new custom configuration files.
-
-A number of ``repo2docker`` configuration files can be combined to compose more
-complex setups.
-
-The `binder examples `_ organization on
-GitHub contains a list of sample repositories for common configurations
-that ``repo2docker`` can build with various configuration files such as
-Python and R installation in a repository.
-
-A list of supported configuration files (roughly in the order of build priority)
-can be found on this page (and to the right).
-
-.. _environment.yml:
-
-``environment.yml`` - Install a conda environment
-=================================================
-
-``environment.yml`` is the standard configuration file used by `conda `_
-that lets you install any kind of package,
-including Python, R, and C/C++ packages.
-``repo2docker`` does not use your ``environment.yml`` to create and activate a new conda environment.
-Rather, it updates a base conda environment `defined here `_ with the packages listed in your ``environment.yml``.
-This means that the environment will always have the same default name, not the name
-specified in your ``environment.yml``.
-
-.. note::
-
- You can install files from pip in your ``environment.yml`` as well.
- For example, see the `binder-examples environment.yml
- `_
- file.
-
-You can also specify which Python version to install in your built environment
-with ``environment.yml``. By default, ``repo2docker`` installs
-|default_python| with your ``environment.yml`` unless you include the version of
-Python in this file. ``conda`` Should support all versions of Python,
-though ``repo2docker`` support is best with Python 3.7-3.11.
-
-.. warning::
- If you include a Python version in a ``runtime.txt`` file in addition to your
- ``environment.yml``, your ``runtime.txt`` will be ignored.
-
-.. _Pipfile:
-
-``Pipfile`` and/or ``Pipfile.lock`` - Install a Python environment
-==================================================================
-
-`pipenv `_ allows you to manage a virtual
-environment Python dependencies. When using ``pipenv``, you end up with
-``Pipfile`` and ``Pipfile.lock`` files. The lock file contains explicit details
-about the packages that has been installed that met the criteria within the
-``Pipfile``.
-
-If both ``Pipfile`` and ``Pipfile.lock`` are found by repo2docker, the former
-will be ignored in favor of the lock file. Also note that these files
-distinguish packages and development packages and that repo2docker will install
-both kinds.
-
-.. _requirements.txt:
-
-``requirements.txt`` - Install a Python environment
-===================================================
-
-This specifies a list of Python packages that should be installed in your
-environment. Our
-`requirements.txt example `_
-on GitHub shows a typical requirements file.
-
-
-.. _setup.py:
-
-``setup.py`` - Install Python packages
-======================================
-
-To install your repository like a Python package, you may include a
-``setup.py`` file. repo2docker installs ``setup.py`` files by running
-``pip install -e .``.
-
-.. _Project.toml:
-
-``Project.toml`` - Install a Julia environment
-==============================================
-
-A ``Project.toml`` (or ``JuliaProject.toml``) file can specify both the
-version of Julia to be used and a list of Julia packages to be installed.
-If a ``Manifest.toml`` is present, it will determine the exact versions
-of the Julia packages that are installed.
-
-
-.. _REQUIRE:
-
-``REQUIRE`` - Install a Julia environment (legacy)
-==================================================
-
-``REQUIRE`` files no longer work, and are no longer supported.
-The recommended way of installing a Julia environment is to use a ``Project.toml`` file.
-
-
-.. _install.R:
-
-``install.R`` - Install an R/RStudio environment
-================================================
-
-This is used to install R libraries pinned to a specific snapshot on
-`Posit Package Manager `_.
-To set the date of the snapshot add a runtime.txt_.
-For an example ``install.R`` file, visit our `example install.R file `_.
-
-
-.. _rstudio.yml:
-
-``rstudio.yml`` - Pin the RStudio Server version
-================================================
-
-Repositories with an R environment get RStudio Server installed. By
-default, the latest stable release published by Posit is installed at
-build time. To pin a specific version instead, add an ``rstudio.yml``:
-
-.. code-block:: yaml
-
- version: 2023.12.1+402
- sha256: 2ceeebe5d1d77068b36e85f7cf366cd1409f7642a80261b6bbeb3da945ef0888
-
-Available versions are listed on
-`Posit's previous versions page `_.
-The ``.deb`` package is downloaded from Posit over HTTPS. ``sha256`` is
-optional; when given, the download is verified against it.
-
-.. note::
-
- For R versions older than 3.6, which current RStudio releases no longer
- support, an older compatible RStudio (2023.12.1) is installed by default
- instead of the latest release.
-
-
-.. _apt.txt:
-
-``apt.txt`` - Install packages with apt-get
-===========================================
-
-A list of Debian packages that should be installed. The base image used is usually the latest released
-version of Ubuntu.
-
-We use ``apt.txt``, for example, to install LaTeX in our
-`example apt.txt for LaTeX `_.
-
-
-.. _DESCRIPTION:
-
-``DESCRIPTION`` - Install an R package
-======================================
-
-To install your repository like an R package, you may include a
-``DESCRIPTION`` file. repo2docker installs the package and dependencies
-from the ``DESCRIPTION`` by running ``devtools::install_local(getwd())``.
-
-You can also have have a ``runtime.txt`` file that is formatted as
-``r---
``, where YYYY-MM-DD is a snapshot of CRAN that will be used
-for your R installation. If ``runtime.txt`` isn't provided in this case, a
-recent date will be used.
-
-
-.. _postBuild:
-
-``postBuild`` - Run code after installing the environment
-=========================================================
-
-A script that can contain arbitrary commands to be run after the whole repository has been built. If you
-want this to be a shell script, make sure the first line is ``#!/bin/bash``.
-
-Note that by default the build will not be stopped if an error occurs inside a shell script.
-You should include ``set -e`` or the equivalent at the start of the script to avoid errors being silently ignored.
-
-An example use-case of ``postBuild`` file is JupyterLab's demo on mybinder.org.
-It uses a ``postBuild`` file in a folder called ``.binder`` to `prepare
-their demo for binder `_.
-
-
-.. _start:
-
-``start`` - Run code before the user sessions starts
-====================================================
-
-A script that can contain simple commands to be run at runtime (as an
-`ENTRYPOINT `_
-to the docker container). If you want this to be a shell script, make sure the
-first line is ``#!/bin/bash``. The last line must be ``exec "$@"``
-or equivalent.
-
-Use this to set environment variables that software installed in your container
-expects to be set. This script is executed each time your binder is started and
-should at most take a few seconds to run.
-
-If you only need to run things once during the build phase use :ref:`postBuild`.
-
-
-.. TODO: Discuss runtime limits, best practices, etc.
-
-.. _runtime.txt:
-
-``runtime.txt`` - Specifying runtimes
-=====================================
-
-Sometimes you want to specify the version of the runtime
-(e.g. the version of Python or R),
-but the environment specification format will not let you specify this information
-(e.g. requirements.txt or install.R).
-For these cases, we have a special file, ``runtime.txt``.
-
-.. note::
-
- ``runtime.txt`` is only supported when used with environment specifications
- that do not already support specifying the runtime
- (when using ``environment.yml`` for conda or ``Project.toml`` for Julia,
- ``runtime.txt`` will be ignored).
-
-Have ``python-x.y`` in ``runtime.txt`` to run the repository with Python version x.y.
-See our `Python2 example repository `_.
-
-Have ``r----
`` in ``runtime.txt`` to run the repository with R version RVERSION and libraries from a YYYY-MM-DD snapshot of `Posit Package Manager `__.
-RVERSION can be set to 3.4, 3.5, 3.6, or to patch releases for the 3.5 and 3.6 series.
-If you do not specify a version, the latest release will be used (currently R 3.6).
-See our `R example repository `_.
-
-.. _default.nix:
-
-``default.nix`` - the nix package manager
-=========================================
-
-Specify packages to be installed by the `nix package manager `_.
-When you use this config file all other configuration files (like ``requirements.txt``)
-that specify packages are ignored. When using ``nix`` you have to specify all
-packages and dependencies explicitly, including the Jupyter notebook package that
-repo2docker expects to be installed. If you do not install Jupyter explicitly
-repo2docker will no be able to start your container.
-
-`nix-shell `_ is used to evaluate
-a ``nix`` expression written in a ``default.nix`` file. Make sure to
-`pin your nixpkgs `_
-to produce a reproducible environment.
-
-To see an example repository visit
-`nix binder example `_.
-
-
-.. _dockerfile:
-
-``Dockerfile`` - Advanced environments
-======================================
-
-In the majority of cases, providing your own Dockerfile is not necessary as the base
-images provide core functionality, compact image sizes, and efficient builds. We recommend
-trying the other configuration files before deciding to use your own Dockerfile.
-
-With Dockerfiles, a regular Docker build will be performed.
-
-.. note::
- If a Dockerfile is present, all other configuration files will be ignored.
-
-See the `Advanced Binder Documentation `_ for
-best-practices with Dockerfiles.
diff --git a/docs/source/configuration/actions.md b/docs/source/configuration/actions.md
new file mode 100644
index 000000000..a441893da
--- /dev/null
+++ b/docs/source/configuration/actions.md
@@ -0,0 +1,39 @@
+# Configuration files for post-build actions
+
+These files control behavior that happens _after_ the image is initially built (AKA, after the packages and languages have been installed). It's useful if you need to ensure files are in a particular place, or certain commands are run when a new session launches.
+
+:::{note}
+After building the image, all actions are run as a user named `jovyan`.
+You do not have root permissions on the machine!
+
+See [the Jupyter Docker Stacks definition of `jovyan`](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/faq.html#who-is-jovyan) for more details.
+:::
+
+(postbuild)=
+
+## `postBuild` - Run code after installing the environment
+
+A script that can contain arbitrary commands to be run after the whole repository has been built. If you want this to be a shell script, make sure the first line is `#!/bin/bash`.
+
+Note that by default the build will not be stopped if an error occurs inside a shell script.
+You should include `set -e` or the equivalent at the start of the script to avoid errors being silently ignored.
+
+An example use-case of `postBuild` file is JupyterLab's demo on mybinder.org.
+It uses a `postBuild` file in a folder called `.binder` to [prepare
+their demo for binder](https://github.com/jupyterlab/jupyterlab-demo/blob/HEAD/.binder/postBuild).
+
+(config-start)=
+
+## `start` - Run code before the user sessions starts
+
+A script that can contain simple commands to be run at runtime (as an
+[ENTRYPOINT](https://docs.docker.com/engine/reference/builder/#entrypoint)
+to the Docker container). If you want this to be a shell script, make sure the first line is `#!/bin/bash`. The last line must be `exec "$@"` or equivalent.
+
+Use this to set environment variables that software installed in your container
+expects to be set. This script is executed each time your binder is started and
+should at most take a few seconds to run.
+
+If you only need to run things once during the build phase use [](#postBuild).
+
+% TODO: Discuss runtime limits, best practices, etc.
diff --git a/docs/source/configuration/development.md b/docs/source/configuration/development.md
new file mode 100644
index 000000000..632bcd136
--- /dev/null
+++ b/docs/source/configuration/development.md
@@ -0,0 +1,45 @@
+# Configuration files for software development workflows
+
+(pipfile)=
+
+## `Pipfile` and/or `Pipfile.lock` - Install a Python environment
+
+[pipenv](https://github.com/pypa/pipenv/) allows you to manage a virtual
+environment Python dependencies. When using `pipenv`, you end up with
+`Pipfile` and `Pipfile.lock` files. The lock file contains explicit details
+about the packages that has been installed that met the criteria within the
+`Pipfile`.
+
+If both `Pipfile` and `Pipfile.lock` are found by `repo2docker`, the former
+will be ignored in favor of the lock file. Also note that these files
+distinguish packages and development packages and that `repo2docker` will install
+both kinds.
+
+(requirements-txt)=
+
+## `requirements.txt` - Install a Python environment
+
+This specifies a list of Python packages that should be installed in your
+environment. Our
+[example `requirements.txt` file](https://github.com/binder-examples/requirements/blob/HEAD/requirements.txt)
+on GitHub shows a typical requirements file.
+
+(pyproject)=
+
+## `pyproject.toml` - Install Python packages
+
+To install your repository like a Python package, you may include a
+`pyproject.toml` file. `repo2docker`installs `pyproject.toml` files by running
+`pip install -e .`.
+
+(setup-py)=
+
+## `setup.py` - Install Python packages
+
+```{note}
+We recommend to use `pyproject.toml` as it is the recommended way since 2020 when PEPs [621](https://peps.python.org/pep-0621/) and [631](https://peps.python.org/pep-0631/) were accepted.
+```
+
+To install your repository like a Python package, you may include a
+`setup.py` file. `repo2docker` installs `setup.py` files by running
+`pip install -e .`.
diff --git a/docs/source/configuration/index.md b/docs/source/configuration/index.md
new file mode 100644
index 000000000..6734d9e27
--- /dev/null
+++ b/docs/source/configuration/index.md
@@ -0,0 +1,26 @@
+(config-files)=
+
+# Configuration files supported by `repo2docker`
+
+`repo2docker` [looks for configuration files](#usage-config-file-location) in
+the repository being built to determine how to build it. In general,
+`repo2docker` uses the same configuration files as other software installation
+tools, rather than creating new custom configuration files.
+
+:::{seealso}
+The [binder examples](https://github.com/binder-examples) organization on
+GitHub contains a list of sample repositories for common configurations
+that `repo2docker` can build with various configuration files such as
+Python and R installation in a repository.
+:::
+
+A list of supported configuration files (roughly in the order of build priority)
+can be found in the next sections.
+
+```{toctree}
+:maxdepth: 2
+./research
+./development
+./system
+./actions
+```
diff --git a/docs/source/configuration/index.rst b/docs/source/configuration/index.rst
deleted file mode 100644
index de8e579e3..000000000
--- a/docs/source/configuration/index.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-===========================
-Configuring your repository
-===========================
-
-Information about configuring your repository to work with repo2docker,
-and controlling elements of the built environment using configuration files.
-
-For information on where to put your configuration files see :ref:`usage-config-file-location`.
-
-.. toctree::
- :maxdepth: 2
- :caption: Complete list of configuration files
-
- ../config_files
- ../specification
diff --git a/docs/source/configuration/research.md b/docs/source/configuration/research.md
new file mode 100644
index 000000000..95bb1116a
--- /dev/null
+++ b/docs/source/configuration/research.md
@@ -0,0 +1,90 @@
+# Configuration for research and data science workflows
+
+(environment-yml)=
+
+## `environment.yml` - Install a conda environment
+
+`environment.yml` is the standard configuration file used by [conda](https://conda.io)
+that lets you install any kind of package,
+including Python, R, and C/C++ packages.
+`repo2docker` does not use your `environment.yml` to create and activate a new conda environment.
+Rather, it updates a [pre-defined base conda environment](https://github.com/jupyterhub/repo2docker/blob/HEAD/repo2docker/buildpacks/conda/environment.yml) with the packages listed in your `environment.yml`.
+This means that the environment will always have the same default name, not the name
+specified in your `environment.yml`.
+
+:::{note}
+You can install files from [PyPI](https://pypi.org/) in your `environment.yml` as well.
+For example, see the [example `environment.yml` file](https://github.com/binder-examples/python-conda_pip/blob/HEAD/environment.yml) file. See [the `conda` environment management instructions](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#creating-an-environment-file-manually) for more information.
+:::
+
+You can also specify which Python version to install in your built environment with `environment.yml`.
+By default, `repo2docker` installs {{ default_python }} with your `environment.yml` unless you include the version of Python in the `environment.yml` of your Git repository.
+`conda` should support all versions of Python, though `repo2docker` support is best with Python 3.7-3.11.
+
+:::{warning}
+If you include a Python version in a `runtime.txt` file in addition to your
+`environment.yml`, your `runtime.txt` will be ignored.
+:::
+
+(install-r)=
+
+## `install.R` - Install R packages
+
+This is used to install R libraries pinned to a specific snapshot on
+[Posit Package Manager](https://packagemanager.posit.co/).
+For an example `install.R` file, visit our [example `install.R` file](https://github.com/binder-examples/r/blob/HEAD/install.R).
+
+To set the date of the snapshot, or to specify a specific version of R, add a [runtime.txt](#runtime-txt).
+
+(rstudio-yml)=
+
+## `rstudio.yml` - Pin the RStudio Server version
+
+Repositories with an R environment get RStudio Server installed. By
+default, the latest stable release published by Posit is installed at
+build time. To pin a specific version instead, add an `rstudio.yml`:
+
+```yaml
+version: 2023.12.1+402
+sha256: 2ceeebe5d1d77068b36e85f7cf366cd1409f7642a80261b6bbeb3da945ef0888
+```
+
+Available versions are listed on
+[Posit's previous versions page](https://docs.posit.co/previous-versions/rstudio/).
+The `.deb` package is downloaded from Posit over HTTPS. `sha256` is
+optional; when given, the download is verified against it.
+
+:::{note}
+For R versions older than 3.6, which current RStudio releases no longer
+support, an older compatible RStudio (2023.12.1) is installed by default
+instead of the latest release.
+:::
+
+(description)=
+
+## `DESCRIPTION` - Install as an R package
+
+To install your repository like an R package, you may include a `DESCRIPTION` file.
+`repo2docker` installs the package and dependencies from the `DESCRIPTION` by running `devtools::install_local(getwd())`.
+
+To define the date of the package manager snapshot, add a line to [`runtime.txt`](#runtime-txt) like so:
+
+```
+r---
+```
+
+Where `YYYY-MM-DD` is a snapshot of [CRAN](https://cran.r-project.org/) that will be used for your R installation.
+If `runtime.txt` isn't provided in this case, the most recent date on CRAN will be used.
+
+(project-toml)=
+
+## `Project.toml` - Install a Julia environment
+
+A `Project.toml` (or `JuliaProject.toml`) file can specify both the
+version of Julia to be used and a list of Julia packages to be installed.
+If a `Manifest.toml` is present, it will determine the exact versions
+of the Julia packages that are installed.
+
+:::{admonition} `REQUIRE` files are no longer supported
+The recommended way of installing a Julia environment is to use a `Project.toml` file.
+:::
diff --git a/docs/source/configuration/system.md b/docs/source/configuration/system.md
new file mode 100644
index 000000000..df7ef4040
--- /dev/null
+++ b/docs/source/configuration/system.md
@@ -0,0 +1,90 @@
+# System-wide configuration
+
+(apt-txt)=
+
+## `apt.txt` - Install packages with `apt-get`
+
+A list of packages in the [`.deb`]() format that should be installed. The `.deb` files are downloaded from the server configured in the Docker base image.
+
+```{note}
+If a `Dockerfile` is not used to change the Docker base image, the base image is [Ubuntu](https://ubuntu.com/) [24.04 LTS (Noble Numbat)](https://releases.ubuntu.com/noble/). All `.deb` files availables are listed in [Ubuntu Packages](https://packages.ubuntu.com/noble/).
+```
+
+```{versionchanged}
+Base ubuntu image upgraded from 22.04 to 24.04 in January, 2026
+```
+
+We use `apt.txt`, for example, to install LaTeX in our
+[example `apt.txt` file for LaTeX](https://github.com/binder-examples/latex/blob/HEAD/apt.txt).
+
+(runtime-txt)=
+
+## `runtime.txt` - Specifying runtimes
+
+Sometimes you want to specify the version of the runtime (e.g. the version of Python or R), but the environment specification format will not let you specify this information (e.g. `requirements.txt` or `install.R`).
+For these cases, we have a special `runtime.txt` file.
+
+:::{warning}
+`runtime.txt` is only supported when used with environment specifications
+that do not already support specifying the runtime
+(when using [`environment.yml`](#environment-yml) for conda or [`Project.toml`](#project-toml) for Julia, `runtime.txt` will be ignored).
+:::
+
+### Set the Python version
+
+Add the line `python-x.y` in `runtime.txt` to run the repository with Python version `x.y`.
+See our [Python2 example repository](https://github.com/binder-examples/python2_runtime/blob/HEAD/runtime.txt).
+
+### Set the R version
+
+Add the line `r-x.y-YYYY-MM-DD` in `runtime.txt` to run the repository with R version `x.y` and libraries from a `YYYY-MM-DD` snapshot of the [Posit Package Manager](https://packagemanager.posit.co/client/#/repos/2/overview).
+
+The version of R, i.e. `x.y`, can be set to
+
+- `4.4`
+- `4.3`
+- `4.2`
+- `4.1`
+- `4.0`
+- `3.6`
+- `3.5`
+- `3.4`
+- `3.3`
+
+If you do not specify a version, the latest release will be used.
+
+See our [R example repository](https://github.com/binder-examples/r/blob/HEAD/runtime.txt).
+
+(default-nix)=
+
+## `default.nix` - the `nix` package manager
+
+Specify packages to be installed by the [`nix` package manager](https://github.com/NixOS/nixpkgs).
+When you use this config file all other configuration files (like `requirements.txt`)
+that specify packages are ignored. When using `nix` you have to specify all
+packages and dependencies explicitly, including the Jupyter notebook package that
+`repo2docker` expects to be installed. If you do not install Jupyter explicitly
+`repo2docker` will not be able to start your container.
+
+[nix-shell](https://nixos.org/nix/manual/#sec-nix-shell) is used to evaluate
+a `nix` expression written in a `default.nix` file. Make sure to
+[pin your nixpkgs](https://discourse.nixos.org/t/nixops-pinning-nixpkgs/734)
+to produce a reproducible environment.
+
+To see an example repository visit
+[nix example repository](https://github.com/binder-examples/nix).
+
+(dockerfile)=
+
+## `Dockerfile` - Advanced environments
+
+In the majority of cases, providing your own `Dockerfile` is not necessary as the base images provide core functionality, compact image sizes, and efficient builds. We recommend trying the other configuration files before deciding to use your own `Dockerfile`.
+
+With `Dockerfile`s, a regular Docker build will be performed.
+
+:::{warning}
+If a Dockerfile is present, all other configuration files will be ignored.
+:::
+
+See the [Advanced Binder Documentation](https://mybinder.readthedocs.io/en/latest/tutorials/dockerfile.html) for
+best-practices with Dockerfiles.
diff --git a/docs/source/contributing/buildpack.md b/docs/source/contribute/buildpack.md
similarity index 100%
rename from docs/source/contributing/buildpack.md
rename to docs/source/contribute/buildpack.md
diff --git a/docs/source/contributing/contentprovider.rst b/docs/source/contribute/contentprovider.rst
similarity index 100%
rename from docs/source/contributing/contentprovider.rst
rename to docs/source/contribute/contentprovider.rst
diff --git a/docs/source/contributing/contributing.md b/docs/source/contribute/contributing.md
similarity index 99%
rename from docs/source/contributing/contributing.md
rename to docs/source/contribute/contributing.md
index 8e26bcda2..d68ea5d4b 100644
--- a/docs/source/contributing/contributing.md
+++ b/docs/source/contribute/contributing.md
@@ -150,7 +150,7 @@ according to black's style guide. You can activate it with `pre-commit install`.
As part of our continuous integration tests we will check that code is
formatted properly and the tests will fail if this is not the case.
-### Verify that docker is installed and running
+### Verify that Docker is installed and running
If you do not already have [Docker](https://www.docker.com/), you should be able
to download and install it for your operating system using the links from the
diff --git a/docs/source/contributing/index.rst b/docs/source/contribute/index.rst
similarity index 71%
rename from docs/source/contributing/index.rst
rename to docs/source/contribute/index.rst
index 09939de58..75f2342da 100644
--- a/docs/source/contributing/index.rst
+++ b/docs/source/contribute/index.rst
@@ -1,6 +1,6 @@
-============
-Contributing
-============
+=================
+Contributor guide
+=================
The repo2docker community is welcoming of all kinds of help and
participation from others. Below are a few ways that you can get involved,
@@ -8,11 +8,16 @@ as well as resources for understanding the structure and design of the
repo2docker package.
.. toctree::
+ :caption: Contributing to repo2docker
contributing
roadmap
+ tasks
+
+.. toctree::
+ :caption: Developer guide
+
../architecture
../design
- tasks
buildpack
contentprovider
diff --git a/docs/source/contributing/roadmap.md b/docs/source/contribute/roadmap.md
similarity index 100%
rename from docs/source/contributing/roadmap.md
rename to docs/source/contribute/roadmap.md
diff --git a/docs/source/contributing/tasks.md b/docs/source/contribute/tasks.md
similarity index 77%
rename from docs/source/contributing/tasks.md
rename to docs/source/contribute/tasks.md
index 30be8c662..dc14ae255 100644
--- a/docs/source/contributing/tasks.md
+++ b/docs/source/contribute/tasks.md
@@ -39,10 +39,9 @@ Mercurial and hg-evolve), one can use the environment variable
Some of the tests have non-python requirements for your development machine. They are:
- `git-lfs` must be installed ([instructions](https://github.com/git-lfs/git-lfs)). It need not be activated -- there is no need to run the `git lfs install` command. It just needs to be available to the test suite.
-
- If your test failure messages include "`git-lfs filter-process: git-lfs: command not found`", this step should address the problem.
-- Minimum Docker Image size of 128GB is required. If you are not running docker on a linux OS, you may need to expand the runtime image size for your installation. See Docker's instructions for [macOS](https://docs.docker.com/docker-for-mac/space/) or [Windows 10](https://docs.docker.com/docker-for-windows/#resources) for more information.
+- Minimum Docker Image size of `128GB` is required. If you are not running Docker on a Linux OS, you may need to expand the runtime image size for your installation. See Docker's [instructions for macOS](https://docs.docker.com/docker-for-mac/space/) or [instructions for Windows 10](https://docs.docker.com/docker-for-windows/#resources) for more information.
- If your test failure messages include "`No space left on device: '/home/...`", this step should address the problem.
## Update and Freeze BuildPack Dependencies
@@ -69,7 +68,6 @@ See the subsections below for more detailed instructions.
### Conda dependencies
1. There are two files related to conda dependencies. Edit as needed.
-
- `repo2docker/buildpacks/conda/environment.yml`
Contains list of packages to install in Python3 conda environments,
@@ -114,47 +112,27 @@ See the subsections below for more detailed instructions.
## Creating a Release
-We make a release of whatever is on `main` every month. We use "calendar versioning".
-Monthly releases give users a predictable pattern for when releases are going to
-happen and prevents locking up improvements for fixes for long periods of time.
+We encourage a release of whatever is on `main` every month. We use "[calendar versioning](https://calver.org/)".
A new release will automatically be created when a new git tag is created
-and pushed to the repository.
+and pushed to the repository. See [previous releases](https://github.com/jupyterhub/repo2docker/releases) for examples.
To create a new release, follow these steps:
-### Create a new tag and push it
-
-First, tag a new release locally:
-
-```bash
-V=YYYY.MM.0; git tag -am "release $V" $V
-```
-
-> If you need to make a second (or third) release in a month increment the
-> trailing 0 of the version to 1 (or 2).
-
-Then push this change up to the main repository
-
-```
-git push origin --tags
-```
-
-GitHub Actions should create a
-new release on the [repo2docker PyPI](https://pypi.org/project/jupyter-repo2docker/).
-Once this has completed, make sure that the new version has been updated.
-
-### Create a new release on the GitHub repository
-
-Once the new release has been pushed to PyPI, we need to create a new
-release on the [GitHub repository releases page](https://github.com/jupyterhub/repo2docker/releases). Once on that page, follow these steps:
+- Add the output of `github-activity` between now and last release, to the changelog and open a PR with this content
+ - E.g. the activity since the date of the last release: `github-activity jupyterhub/repo2docker -s 2025-08-01`
+- Go to the [GitHub repository](https://github.com/jupyterhub/repo2docker)
+- Click `Draft a new release`
+- Click `Tag`
+ - Type in a new tag like `YYYY.MM.N` (where `N` is the release number this month, usually it is `0`)
+- Title: `` (E.g., `August 2025`)
+- Body: Paste the `github-activity` output from the changelog
+- Click `Publish release`
-- Click "Draft a new release"
-- Choose a tag version using the same tag you just created above
-- The release name is simply the tag version
-- Finally, click "Publish release"
+This will create a new release **and a new tag**.
+The creation of the tag will trigger the `.github/workflows/release.yml` action to publish the latest repo2docker package to PyPI.
-That's it!
+You can confirm the new package is released [in our PyPI page](https://pypi.org/project/jupyter-repo2docker/#history).
# Uncommon tasks
@@ -167,13 +145,13 @@ automates this test.
```bash
#! /bin/bash -e
-current_version=$(jupyter-repo2docker --version | sed s@+@-@)
+current_version=$(repo2docker --version | sed s@+@-@)
echo "Comparing $(pwd) (local $current_version vs. $R2D_COMPARE_TO)"
basename="dockerfilediff"
diff_r2d_dockerfiles_with_version () {
- docker run --rm -t -v "$(pwd)":"$(pwd)" --user 1000 jupyterhub/repo2docker:"$1" jupyter-repo2docker --no-build --debug "$(pwd)" &> "$basename"."$1"
- jupyter-repo2docker --no-build --debug "$(pwd)" &> "$basename"."$current_version"
+ docker run --rm -t -v "$(pwd)":"$(pwd)" --user 1000 jupyterhub/repo2docker:"$1" repo2docker --no-build --debug "$(pwd)" &> "$basename"."$1"
+ repo2docker --no-build --debug "$(pwd)" &> "$basename"."$current_version"
# remove first line logging the path
sed -i '/^\[Repo2Docker\]/d' "$basename"."$1"
diff --git a/docs/source/design.md b/docs/source/design.md
index 6077621d7..bb1d449b1 100644
--- a/docs/source/design.md
+++ b/docs/source/design.md
@@ -16,7 +16,7 @@ The use cases for `repo2docker` which drive most design decisions are:
1. Automated image building used by projects like
[BinderHub](http://github.com/jupyterhub/binderhub)
2. Manual image building and running the image from the command line client,
- `jupyter-repo2docker`, by users interactively on their workstations
+ `repo2docker`, by users interactively on their workstations
## Deterministic output
@@ -43,13 +43,14 @@ This provides a few advantages:
Many ingredients go into making an image from a repository:
-1. version of the base docker image
+1. version of the base Docker image
1. version of `repo2docker` itself
1. versions of the libraries installed by the repository
`repo2docker` controls the first two, the user controls the third one. The current
policy for the version of the base image is that we will use the current LTS
-version Jammy Jellyfish (22.04) for the foreseeable future.
+version (24.04) until the start to be build challenges associated with the age of the base system,
+at which point it will be increased to the 'current' LTS when that occurs - typically between 2-5 years from the age of release.
The version of `repo2docker` used to build an image can influence which packages
are installed by default and which features are supported during the build
@@ -62,7 +63,7 @@ explicitly in your dependencies.
`repo2docker` should do one thing, and do it well. This one thing is:
-> Given a repository, deterministically build a docker image from
+> Given a repository, deterministically build a Docker image from
> it.
There's also some convenience code (to run the built image) for users, but
diff --git a/docs/source/faq.md b/docs/source/faq.md
new file mode 100644
index 000000000..f9c083099
--- /dev/null
+++ b/docs/source/faq.md
@@ -0,0 +1,113 @@
+(faq)=
+
+# Frequently Asked Questions (FAQ)
+
+A collection of frequently asked questions with answers. If you have a question
+and have found an answer, send a PR to add it here!
+
+## Is rootless container runtime supported?
+
+To to mitigate potential vulnerabilities in the daemon and the container runtime, security experts recommend run Docker in [rootless mode](https://docs.docker.com/engine/security/rootless/). Rootless mode is supported by `repo2docker` but it has some limitations:
+
+- `repo2docker` does not allow the primary user in the image to be `root`.
+- the `--editable` feature will not work because of permission issues.
+
+## Why is my repository failing to build with `ResolvePackageNotFound` ?
+
+If you used `conda env export` to generate your `environment.yml` it will generate a list of packages and versions of packages that is pinned to platform specific versions.
+These very specific versions are not available in the Linux Docker image used by `repo2docker`. A typical error message will look like the following:
+
+```
+Step 39/44 : RUN conda env update -n root -f "environment.yml" && conda clean -tipsy && conda list -n root
+---> Running in ebe9a67762e4
+Solving environment: ...working... failed
+
+ResolvePackageNotFound:
+- jsonschema==2.6.0=py36hb385e00_0
+- libedit==3.1.20181209=hb402a30_0
+- tornado==5.1.1=py36h1de35cc_0
+...
+```
+
+We recommend to use `conda env export --no-builds -f environment.yml` to export
+your environment and then edit the file by hand to remove platform specific
+packages like `appnope`.
+
+See {ref}`export-environment` for a recipe on how to create strict exports of
+your environment that will work with `repo2docker`.
+
+## Can I add executable files to the user's PATH?
+
+Yes! With a [](#postBuild) file, you can place any files that should be called from the command line in the folder `~/.local/bin`.
+This folder will be available in a user's PATH, and can be run from the command line (or as a subsequent build step.)
+
+## Can I use repo2docker to bootstrap my own `Dockerfile` to edit by hand?
+
+No, you can't.
+
+If you pass the `--debug` flag to `repo2docker`, it outputs the
+intermediate `Dockerfile` that is used to build the Docker image. While
+it is tempting to copy this as a base for your own `Dockerfile`, that is
+not supported & in most cases will not work. The `--debug` output is
+just our intermediate generated `Dockerfile`, and is meant to be built
+in a very specific way. Hence the output of `--debug` can not be
+built with a normal `docker build -t .` or similar traditional
+Docker command.
+
+Check out the [binder-examples](http://github.com/binder-examples/) GitHub
+organization for example repositories you can copy & modify for your own use!
+
+## Can I use repo2docker to edit a local host repository within a Docker environment?
+
+Yes: use the `--editable` or `-E` flag (don't confuse this with
+the `-e` flag for environment variables), and run repo2docker on a
+local repository:
+
+```
+repo2docker -E my-repository/
+```
+
+This builds a Docker container from the files in that repository
+(using, for example, a `requirements.txt` or `install.R` file),
+then runs that container, while connecting the working directory
+inside the container to the local repository outside the
+container. For example, in case there is a notebook file (`.ipynb`),
+this will open in a local web browser, and one can edit it and save
+it. The resulting notebook is updated in both the Docker container and
+the local repository. Once the container is exited, the changed file
+will still be in the local repository.
+
+This allows for easy testing of the container while debugging some
+items, as well as using a fully customizable container to edit
+notebooks (among others).
+
+:::{note}
+Editable mode is a convenience option that will bind the
+repository to the container working directory (usually
+`$HOME`). If you need to mount to a different location in
+the container, use the `--volumes` option instead. Similarly,
+for a fully customized user Dockerfile, this option is not
+guaranteed to work.
+:::
+
+## Why is my R shiny app not launching?
+
+If you are trying to run an R shiny app using the `/shiny/folder_containing_shiny`
+url option, but the launch returns "The application exited during initialization.",
+there might be something wrong with the specification of the app. One way of debugging
+the app in the container is by running the `rstudio` url, open either the ui or
+server file for the app, and run the app in the container rstudio. This way you can
+see the rstudio logs as it tries to initialize the shiny app. If you are missing a
+package or other dependency for the container, this will be obvious at this stage.
+
+## Why does repo2docker need to exist? Why not use tool like source2image?
+
+The Jupyter community believes strongly in building on top of pre-existing tools whenever
+possible (this is why repo2docker buildpacks largely build off of patterns that already
+exist in the data analytics community). We try to perform due-diligence and search for
+other communities to leverage and help, but sometimes it makes the most sense to build
+our own new tool. In the case of repo2docker, we spent time integrating with a pre-existing
+tool called [source2image](https://github.com/openshift/source-to-image/).
+This is an excellent open tool for containerization, but we
+ultimately decided that it did not fit the use-case we wanted to address. For more information,
+read our [blog post about why we built repo2docker](https://github.com/yuvipanda/words/blob/fd096dd49d87e624acd8bdf6d13c0cecb930bb3f/content/post/why-not-s2i.md).
diff --git a/docs/source/faq.rst b/docs/source/faq.rst
deleted file mode 100644
index d643264b8..000000000
--- a/docs/source/faq.rst
+++ /dev/null
@@ -1,192 +0,0 @@
-.. _faq:
-
-Frequently Asked Questions (FAQ)
-================================
-
-A collection of frequently asked questions with answers. If you have a question
-and have found an answer, send a PR to add it here!
-
-How should I specify another version of Python?
------------------------------------------------
-
-One can specify a Python version in the ``environment.yml`` file of a repository
-or ``runtime.txt`` file if using ``requirements.txt`` instead of ``environment.yml``.
-
-What versions of Python (or R or Julia...) are supported?
----------------------------------------------------------
-
-Python
-~~~~~~
-
-Repo2docker officially supports the following versions of Python
-(specified in your :ref:`environment.yml ` or
-:ref:`runtime.txt ` file):
-
-- 3.11 (added in 2023)
-- 3.10 (added in 2022, default in 2023)
-- 3.9 (added in 2021)
-- 3.8 (added in 0.11)
-- 3.7 (added in 0.7, default in 0.8)
-- 3.6 (default in 0.7 and earlier)
-- 3.5
-- 2.7
-
-Additional versions may work, as long as the
-`base environment `_
-can be installed for your version of Python.
-The most likely source of incompatibility is if one of the packages
-in the base environment is not packaged for your Python,
-either because the version of the package is too new and your chosen Python is too old,
-or vice versa.
-
-If an old version of Python is specified (3.6 or earlier in 2023), a separate environment for the kernel will be installed with your requested Python version.
-The notebook server will run in the default |default_python| environment.
-That is, your _notebooks_ will run with Python 3.6, while your notebook _server_ will run with |default_python|.
-
-These two environments can be distinguished with ``$NB_PYTHON_PREFIX/bin/python`` for the server and ``$KERNEL_PYTHON_PREFIX/bin/python`` for the kernel.
-Both of these environment variables area always defined, even when they are the same.
-
-Starting in 2023, the default version of Python used when Python version is unspecified will be updated more often.
-Python itself releases a new version every year now, and repo2docker will follow, with the default Python version generally trailing the latest stable version of Python itself by 1-2 versions.
-
-If you choose not to specify a Python version, your repository is _guaranteed_ to stop working, eventually.
-We **strongly** recommend specifying a Python version (in environment.yml, runtime.txt, Pipfile, etc.)
-
-Julia
-~~~~~
-
-All Julia versions since Julia 1.3 are supported via a :ref:`Project.toml `
-file, and this is the recommended way to install Julia environments.
-
-Julia < 1.3 and the older Julia REQUIRE file is no longer supported because required infrastructure has been removed.
-
-R
-~
-
-The default version of R is currently R 4.2. You can select the version of
-R you want to use by specifying it in the :ref:`runtime.txt `
-file.
-
-We support R versions 3.4, 3.5, 3.6, 4.0, 4.1 and 4.2.
-
-
-Why is my repository failing to build with ``ResolvePackageNotFound`` ?
---------------------------------------------------------------------------
-
-If you used ``conda env export`` to generate your ``environment.yml`` it will
-generate a list of packages and versions of packages that is pinned to platform
-specific versions. These very specific versions are not available in the linux
-docker image used by ``repo2docker``. A typical error message will look like
-the following::
-
- Step 39/44 : RUN conda env update -n root -f "environment.yml" && conda clean -tipsy && conda list -n root
- ---> Running in ebe9a67762e4
- Solving environment: ...working... failed
-
- ResolvePackageNotFound:
- - jsonschema==2.6.0=py36hb385e00_0
- - libedit==3.1.20181209=hb402a30_0
- - tornado==5.1.1=py36h1de35cc_0
- ...
-
-We recommend to use ``conda env export --no-builds -f environment.yml`` to export
-your environment and then edit the file by hand to remove platform specific
-packages like ``appnope``.
-
-See :ref:`export-environment` for a recipe on how to create strict exports of
-your environment that will work with ``repo2docker``.
-
-
-Can I add executable files to the user's PATH?
-----------------------------------------------
-
-Yes! With a :ref:`postBuild` file, you can place any files that should be called
-from the command line in the folder ``~/.local/``. This folder will be
-available in a user's PATH, and can be run from the command line (or as
-a subsequent build step.)
-
-How do I set environment variables?
------------------------------------
-
-To configure environment variables for all users of a repository use the
-:ref:`start ` configuration file.
-
-When running repo2docker locally you can use the ``-e`` or ``--env`` command-line
-flag for each variable that you want to define.
-
-For example ``jupyter-repo2docker -e VAR1=val1 -e VAR2=val2 ...``
-
-Can I use repo2docker to bootstrap my own Dockerfile?
------------------------------------------------------
-
-No, you can't.
-
-If you pass the ``--debug`` flag to ``repo2docker``, it outputs the
-intermediate Dockerfile that is used to build the docker image. While
-it is tempting to copy this as a base for your own Dockerfile, that is
-not supported & in most cases will not work. The ``--debug`` output is
-just our intermediate generated Dockerfile, and is meant to be built
-in a very specific way. Hence the output of ``--debug`` can not be
-built with a normal ``docker build -t .`` or similar traditional
-docker command.
-
-Check out the `binder-examples `_ GitHub
-organization for example repositories you can copy & modify for your own use!
-
-Can I use repo2docker to edit a local host repository within a Docker environment?
-----------------------------------------------------------------------------------
-
-Yes: use the ``--editable`` or ``-E`` flag (don't confuse this with
-the ``-e`` flag for environment variables), and run repo2docker on a
-local repository::
-
- repo2docker -E my-repository/
-
-This builds a Docker container from the files in that repository
-(using, for example, a ``requirements.txt`` or ``install.R`` file),
-then runs that container, while connecting the working directory
-inside the container to the local repository outside the
-container. For example, in case there is a notebook file (``.ipynb``),
-this will open in a local web browser, and one can edit it and save
-it. The resulting notebook is updated in both the Docker container and
-the local repository. Once the container is exited, the changed file
-will still be in the local repository.
-
-This allows for easy testing of the container while debugging some
-items, as well as using a fully customizable container to edit
-notebooks (among others).
-
-.. note::
-
- Editable mode is a convenience option that will bind the
- repository to the container working directory (usually
- ``$HOME``). If you need to mount to a different location in
- the container, use the ``--volumes`` option instead. Similarly,
- for a fully customized user Dockerfile, this option is not
- guaranteed to work.
-
-
-Why is my R shiny app not launching?
-----------------------------------------------------------------------------------
-
-If you are trying to run an R shiny app using the ``/shiny/folder_containing_shiny``
-url option, but the launch returns "The application exited during initialization.",
-there might be something wrong with the specification of the app. One way of debugging
-the app in the container is by running the ``rstudio`` url, open either the ui or
-server file for the app, and run the app in the container rstudio. This way you can
-see the rstudio logs as it tries to initialise the shiny app. If you a missing a
-package or other dependency for the container, this will be obvious at this stage.
-
-
-Why does repo2docker need to exist? Why not use tool like source2image?
------------------------------------------------------------------------
-
-The Jupyter community believes strongly in building on top of pre-existing tools whenever
-possible (this is why repo2docker buildpacks largely build off of patterns that already
-exist in the data analytics community). We try to perform due-diligence and search for
-other communities to leverage and help, but sometimes it makes the most sense to build
-our own new tool. In the case of repo2docker, we spent time integrating with a pre-existing
-tool called `source2image `_.
-This is an excellent open tool for containerization, but we
-ultimately decided that it did not fit the use-case we wanted to address. For more information,
-`here `_ is a short blog post about the decision and the reasoning behind it.
diff --git a/docs/source/getting-started/index.rst b/docs/source/getting-started/index.rst
deleted file mode 100644
index 96e5f2958..000000000
--- a/docs/source/getting-started/index.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-===============
-Getting Started
-===============
-
-Instructions and information on how to get started with repo2docker
-on your own machine. Select from the pages listed below to begin.
-
-.. toctree::
- :maxdepth: 2
-
- ../install
- ../usage
- ../faq
\ No newline at end of file
diff --git a/docs/source/howto/breaking_changes.md b/docs/source/howto/breaking_changes.md
index 80ab1ed09..1c58043f0 100644
--- a/docs/source/howto/breaking_changes.md
+++ b/docs/source/howto/breaking_changes.md
@@ -2,17 +2,23 @@
Repo2docker occasionally has to make breaking changes in how repositories are built.
+## Upgrade of base image from Ubuntu 22.04 to 24.04
+
+The base image used by repo2docker was [upgraded from Ubuntu 22.04 to Ubuntu 24.04](https://github.com/jupyterhub/repo2docker/pull/1287) Janyary 2026 (2026.1).
+
+This is unlikely to affect you unless you are using {ref}`apt.txt `.
+
## Upgrade of base image from Ubuntu 18.04 to 22.04
The base image used by repo2docker was [upgraded from Ubuntu 18.04 to Ubuntu 22.04](https://github.com/jupyterhub/repo2docker/pull/1287) in version 2023.10.0 due to Ubuntu 18.04 going out of support.
-This is unlikely to affect you unless you are using {ref}`apt.txt `.
+This is unlikely to affect you unless you are using {ref}`apt.txt `.
-{ref}`apt.txt ` installs packages from the official Ubuntu package repositories, and is intrinsically tied to the Ubuntu version.
+{ref}`apt.txt ` installs packages from the official Ubuntu package repositories, and is intrinsically tied to the Ubuntu version.
Many packages will be available in both Ubuntu 18.04 and Ubuntu 22.04, however some may be renamed (for example if multiple incompatible versions are available).
Some packages may be removed, or may not be compatible with the previous version.
-In this case you should see if your packages can be installed using a {ref}`Conda environment.yml file ` using either the default [conda-forge channel](https://conda-forge.org/feedstock-outputs/) or in one of the many [third-party channels](https://docs.conda.io/projects/conda/en/latest/user-guide/concepts/channels.html).
+In this case you should see if your packages can be installed using a {ref}`Conda environment.yml file ` using either the default [conda-forge channel](https://conda-forge.org/feedstock-outputs/) or in one of the many [third-party channels](https://docs.conda.io/projects/conda/en/latest/user-guide/concepts/channels.html).
Alternatively you can try installing the packages from source, using a {ref}`postBuild ` script.
diff --git a/docs/source/howto/export_environment.rst b/docs/source/howto/export_environment.rst
index 4aa87f980..0aa72fd25 100644
--- a/docs/source/howto/export_environment.rst
+++ b/docs/source/howto/export_environment.rst
@@ -1,7 +1,7 @@
.. _export-environment:
=============================================================================
-How to automatically create a ``environment.yml`` that works with repo2docker
+Export your environment to an ``environment.yml`` that works with repo2docker
=============================================================================
This how-to explains how to create a ``environment.yml`` that specifies all
@@ -16,12 +16,9 @@ This is the most robust for reproducibility, but it does bake in potential
platform-specific packages, so you can only use an exported environment on the
same platform.
-``repo2docker`` uses a linux based image as the starting point for every docker
-image it creates. However a lot of people use OSX or Windows as their day to
-day operating system. This means that the ``environment.yml`` created by a strict
-export will not work with error messages saying that certain packages can not
-be resolved (``ResolvePackageNotFound``).
-
+``repo2docker`` uses a Linux-based image as the starting point for every Docker image it creates.
+However a lot of people use OSX or Windows as their day to day operating system.
+This means that the ``environment.yml`` created by a strict export will not work with error messages saying that certain packages can not be resolved (``ResolvePackageNotFound``).
The solution
============
@@ -48,7 +45,7 @@ Strict version export
Follow this procedure to create a strict export of your environment that will
work with ``repo2docker`` and sites like `mybinder.org `_.
-We will launch a terminal inside a basic docker image, install the packages
+We will launch a terminal inside a basic Docker image, install the packages
you need and then perform a strict export of the environment.
#. install repo2docker on your computer by following :ref:`install`
diff --git a/docs/source/howto/index.rst b/docs/source/howto/index.rst
deleted file mode 100644
index 12ce3595c..000000000
--- a/docs/source/howto/index.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-=============
-How-to Guides
-=============
-
-Short, actionable guides that cover specific topics with repo2docker.
-Select from the pages listed below to get started.
-
-.. toctree::
- :maxdepth: 2
- :caption: How-To guides
-
- user_interface
- languages
- export_environment
- lab_workspaces
- breaking_changes
- jupyterhub_images
- deploy
- base_image
diff --git a/docs/source/howto/languages.md b/docs/source/howto/languages.md
new file mode 100644
index 000000000..7a393bc68
--- /dev/null
+++ b/docs/source/howto/languages.md
@@ -0,0 +1,138 @@
+(languages)=
+
+# Choose languages for your environment
+
+You can define many different languages in your configuration files. This
+page describes how to use some of the more common ones.
+
+## Python
+
+Your environment will have Python (and specified dependencies) installed when
+you use one of the following configuration files:
+
+- `requirements.txt`
+- `environment.yml`
+
+By default, the environment will have {{ default_python }}.
+
+### Specify a version of Python
+
+To specify a specific version of Python, you have two options:
+
+- Use [environment.yml](#environment-yml). Conda environments let you define
+ the Python version in `environment.yml`.
+ To do so, add `python=X.X` to your dependencies section, like so:
+
+ ```
+ name: python 2.7
+ dependencies:
+ - python=2.7
+ - numpy
+ ```
+
+- Use [runtime.txt](#runtime-txt) with [requirements.txt](#requirements-txt).
+ If you are using `requirements.txt` instead of `environment.yml`,
+ you can specify the Python runtime version in a separate file called `runtime.txt`.
+ This file contains a single line of the following form:
+
+ ```
+ python-X.X
+ ```
+
+ For example:
+
+ ```
+ python-3.6
+ ```
+
+### Supported versions of Python
+
+Repo2docker officially supports the following versions of Python:
+
+- 3.11 (added in 2023)
+- 3.10 (added in 2022, default in 2023)
+- 3.9 (added in 2021)
+- 3.8 (added in 0.11)
+- 3.7 (added in 0.7, default in 0.8)
+- 3.6 (default in 0.7 and earlier)
+- 3.5
+- 2.7
+
+Additional versions may work, as long as the [base environment](https://github.com/jupyterhub/repo2docker/blob/HEAD/repo2docker/buildpacks/conda/environment.yml) can be installed for your version of Python.
+The most likely source of incompatibility is if one of the packages in the base environment is not packaged for your Python, either because the version of the package is too new and your chosen Python is too old, or vice versa.
+
+If an old version of Python is specified (3.6 or earlier in 2023), a separate environment for the kernel will be installed with your requested Python version.
+The notebook server will run in the default {{ default_python }} environment.
+That is, your _notebooks_ will run with Python 3.6, while your notebook _server_ will run with {{ default_python }}.
+
+These two environments can be distinguished with `$NB_PYTHON_PREFIX/bin/python` for the server and `$KERNEL_PYTHON_PREFIX/bin/python` for the kernel.
+Both of these environment variables are always defined, even when they are the same.
+
+Starting in 2023, the default version of Python used when Python version is unspecified will be updated more often.
+Python itself releases a new version every year now, and repo2docker will follow, with the default Python version generally trailing the latest stable version of Python itself by 1-2 versions.
+
+If you choose not to specify a Python version, your repository is _guaranteed_ to stop working, eventually.
+We **strongly** recommend specifying a Python version (in environment.yml, runtime.txt, Pipfile, etc.)
+
+## The R Language
+
+repo2docker supports R, the open source [RStudio IDE](https://www.rstudio.com/) as well
+as Jupyter support for R with the [IRKernel](https://irkernel.github.io/). To set it up,
+you need to create a `runtime.txt` file with the following format:
+
+> r-\-\-\-\
+
+This will provide you R of given version (such as 4.1, 3.6, etc), and a CRAN snapshot
+to install libraries from on the given date. You can install more R packages from CRAN
+by adding a [install.R](#install-R) file to your repo. RStudio and IRKernel are
+installed by default for all R versions. The latest stable RStudio Server is
+installed at build time (for R older than 3.6, an older compatible RStudio is
+installed instead); you can pin a specific version with a
+[rstudio.yml](#rstudio-yml) file.
+
+[packagemanager.posit.co](https://packagemanager.posit.co/client/#/)
+will be used to provide much faster installations via [binary packages](https://www.rstudio.com/blog/package-manager-v1-1-no-interruptions/).
+For _some_ packages, this might require you install underlying system libraries
+using [apt.txt](#apt-txt) - look at the page for the CRAN package you are interested in at
+[packagemanager.posit.co](https://packagemanager.posit.co/client/#/) to find
+a list.
+
+repo2docker stopped using the Microsoft mirror MRAN for older R versions after its shutdown in July, 2023.
+
+### Supported versions of R
+
+The default version of R is currently R 4.2. You can select the version of
+R you want to use by specifying it in the [runtime.txt](#runtime-txt)
+file.
+
+We support R versions 3.4, 3.5, 3.6, 4.0, 4.1 and 4.2.
+
+## Julia
+
+To build an environment with Julia, include a configuration file called
+`Project.toml`. The format of this file is documented at
+[the Julia Pkg.jl documentation](https://julialang.github.io/Pkg.jl/v1/).
+To specify a specific version of Julia to install, put a Julia version in the
+`[compat]` section of the `Project.toml` file, as described
+here: .
+
+### Supported versions of Julia
+
+All Julia versions since Julia 1.3 are supported via a [Project.toml](project-toml)
+file, and this is the recommended way to install Julia environments.
+
+Julia < 1.3 and the older Julia REQUIRE file is no longer supported because required infrastructure has been removed.
+
+## Languages not covered here
+
+If a language is not "officially" supported by a build pack, it can often be
+installed with a `postBuild` script. This will run arbitrary `bash` commands,
+and can be used to download / install a language.
+
+## Using multiple languages at once
+
+It may also be possible to combine multiple languages in a single environment.
+The details on how to accomplish this with all possible combinations are outside
+the scope of this guide. However we recommend that you take a look at the
+[Multi-Language Demo](https://github.com/binder-examples/multi-language-demo)
+repository for some inspiration.
diff --git a/docs/source/howto/languages.rst b/docs/source/howto/languages.rst
deleted file mode 100644
index 9f3c75d79..000000000
--- a/docs/source/howto/languages.rst
+++ /dev/null
@@ -1,105 +0,0 @@
-.. _languages:
-
-=====================================
-Choose languages for your environment
-=====================================
-
-You can define many different languages in your configuration files. This
-page describes how to use some of the more common ones.
-
-Python
-======
-
-Your environment will have Python (and specified dependencies) installed when
-you use one of the following configuration files:
-
-* ``requirements.txt``
-* ``environment.yml``
-
-.. note::
-
- By default, the environment will have |default_python|.
-
-.. versionchanged:: 0.8
-
- Upgraded default Python from 3.6 to 3.7.
-
-
-Specifying a version of Python
-------------------------------
-
-To specify a specific version of Python, you have two options:
-
-* Use :ref:`environment.yml `. Conda environments let you define
- the Python version in ``environment.yml``.
- To do so, add ``python=X.X`` to your dependencies section, like so::
-
- name: python 2.7
- dependencies:
- - python=2.7
- - numpy
-
-* Use :ref:`runtime.txt ` with :ref:`requirements.txt `.
- If you are using ``requirements.txt`` instead of ``environment.yml``,
- you can specify the Python runtime version in a separate file called ``runtime.txt``.
- This file contains a single line of the following form::
-
- python-X.X
-
- For example::
-
- python-3.6
-
-
-The R Language
-==============
-
-repo2docker supports R, the open source `RStudio IDE `_ as well
-as Jupyter support for R with the `IRKernel `_. To set it up,
-you need to create a ``runtime.txt`` file with the following format:
-
- r----
-
-This will provide you R of given version (such as 4.1, 3.6, etc), and a CRAN snapshot
-to install libraries from on the given date. You can install more R packages from CRAN
-by adding a :ref:`install.R` file to your repo. RStudio and IRKernel are
-installed by default for all R versions. The latest stable RStudio Server is
-installed at build time (for R older than 3.6, an older compatible RStudio is
-installed instead); you can pin a specific version with a
-:ref:`rstudio.yml` file.
-
-`packagemanager.posit.co `_
-will be used to provide much faster installations via `binary packages `_.
-For *some* packages, this might require you install underlying system libraries
-using :ref:`apt.txt` - look at the page for the CRAN package you are interested in at
-`packagemanager.posit.co `_ to find
-a list.
-
-repo2docker stopped using the Microsoft mirror MRAN for older R versions after its shutdown in July, 2023.
-
-
-Julia
-=====
-
-To build an environment with Julia, include a configuration file called
-``Project.toml``. The format of this file is documented at
-`the Julia Pkg.jl documentation `_.
-To specify a specific version of Julia to install, put a Julia version in the
-``[compat]`` section of the ``Project.toml`` file, as described
-here: https://julialang.github.io/Pkg.jl/v1/compatibility/.
-
-Languages not covered here
-==========================
-
-If a language is not "officially" supported by a build pack, it can often be
-installed with a ``postBuild`` script. This will run arbitrary ``bash`` commands,
-and can be used to download / install a language.
-
-Using multiple languages at once
-================================
-
-It may also be possible to combine multiple languages in a single environment.
-The details on how to accomplish this with all possible combinations are outside
-the scope of this guide. However we recommend that you take a look at the
-`Multi-Language Demo `_
-repository for some inspiration.
diff --git a/docs/source/index.md b/docs/source/index.md
new file mode 100644
index 000000000..fae2308a0
--- /dev/null
+++ b/docs/source/index.md
@@ -0,0 +1,95 @@
+# Welcome to `repo2docker`'s documentation
+
+```{important}
+Despite the name, `repo2docker` can be used by container technology other than [Docker](https://docs.docker.com/engine/), for example [Podman](https://podman.io/).
+```
+
+`repo2docker` lets you **reproducibly build and run user environment container images for interactive computing and data workflows from source code repositories**. Optionally, the container image can be pushed to a Docker registry.
+
+Also, `repo2docker` is the tool used to build container images for [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) and the tool used by [BinderHub](https://binderhub.readthedocs.io) to build images on demand.
+
+::::{grid}
+:::{grid-item-card} 🔧 Build reproducible data science environments from repositories
+Build a reproducible data science environment as a container image and execute code interactively. Use many [configuration files](#config-files) to control language, tools, and setup instructions.
+:::
+:::{grid-item-card} 🚀 Deploy environments in JupyterHub or Binder
+Push environment images to a container registry for re-use in data science environment services like [JupyterHub](https://jupyterhub.readthedocs.io) or [a Binder instance](https://mybinder.org), or for other communities to build upon your base environment.
+:::
+:::{grid-item-card} ☁️ Host repositories in many providers
+Host repositories in: a Git server like [GitHub](https://github.com/) or [GitLab](https://gitlab.com/), an open science repository like [Zenodo](https://zenodo.org) or [Figshare](https://figshare.com), a hosted data platform like a [Dataverse installation](https://dataverse.org/), an archive like the
+[Software Heritage Archive](https://archive.softwareheritage.org).
+:::
+::::
+
+## What is a user environment container image and why would I build one with `repo2docker`?
+
+A **user environment container image** contains the entire software environment that a user may access from an interactive data science session. For example, it might contain many **programming languages**, **software for data analysis**, or even **content files and datasets** available to anybody that accesses that environment. Container images are built in accordance with the spectifications published by the [Open Container Initiative](https://opencontainers.org/).
+
+Many data science platforms and services like [JupyterHub](https://jupyterhub.readthedocs.io) and [Binder](https://mybinder.org) launch interactive data science sessions **with a user environment container image attached**, meaning that the user gains access to whatever is in the container image. In short, this allows somebody to define and build the user image one time, in a way that users can reproducibly re-use many times.
+
+`Dockerfile`s are the standard way that Docker container images are defined.
+However, **building images with `Dockerfile`s takes expertise and time** that is outside the scope of a data scientist or researcher. `repo2docker` makes this workflow more accessible by using community-standard configuration files for computational reproducibility to build user environment container images.
+
+```{note}
+You can still use a `Dockerfile` with `repo2docker` if you really want to!
+```
+
+In short, `repo2docker` is a tool to reproducibly build and run these user environment container images for data science from source code repositories.
+
+## How `repo2docker` works
+
+When you call `repo2docker` like so:
+
+```
+repo2docker
+```
+
+It performs these steps:
+
+1. Inspects the repository for [configuration files](#config-files). These will be used to build the environment needed to run the repository.
+2. Builds a container image with an environment specified in these [configuration files](#config-files).
+3. Runs the image to let you explore the repository interactively via Jupyter notebooks, RStudio, or many other interfaces (this is optional).
+4. Pushes the images to a container registry so that it may be accessed remotely (this is optional).
+
+[swhid]: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html
+
+Please report [bugs](https://github.com/jupyterhub/repo2docker/issues),
+[ask questions](https://gitter.im/jupyterhub/binder) or
+[contribute to the project](https://github.com/jupyterhub/repo2docker/blob/HEAD/CONTRIBUTING.md).
+
+## Get started with `repo2docker`
+
+This tutorial walks you through setting up `repo2docker`, building your first environment image, and running it locally with a container engine.
+
+```{toctree}
+:maxdepth: 2
+
+start
+```
+
+## Learn how to use `repo2docker`
+
+Our user guide provides all of the information you need to configure and deploy the environment image you want.
+
+```{toctree}
+:maxdepth: 2
+use/index
+```
+
+## Contribute to `repo2docker`
+
+Our contributor guide describes how you can follow along with the project, learn how to collaborate with our open team, and learn developer workflows and information.
+
+```{toctree}
+:maxdepth: 2
+
+contribute/index
+```
+
+```{toctree}
+:caption: Changelog
+:maxdepth: 2
+:hidden: true
+
+changelog
+```
diff --git a/docs/source/index.rst b/docs/source/index.rst
deleted file mode 100644
index 09be2503f..000000000
--- a/docs/source/index.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-jupyter-repo2docker
-===================
-
-``jupyter-repo2docker`` is a tool to **build, run, and push Docker
-images from source code repositories**.
-
-``repo2docker`` fetches a repository
-(from GitHub, GitLab, Zenodo, Figshare, Dataverse installations, a Git repository or a local directory)
-and builds a container image in which the code can be executed.
-The image build process is based on the configuration files found in the repository.
-
-``repo2docker`` can be
-used to explore a repository locally by building and executing the
-constructed image of the repository, or as a means of building images that
-are pushed to a Docker registry.
-
-``repo2docker`` is the tool used by `BinderHub `_
-to build images on demand.
-
-Please report `Bugs `_,
-`ask questions `_ or
-`contribute to the project `_.
-
-.. toctree::
- :maxdepth: 2
- :caption: Getting started with repo2docker
-
- getting-started/index
- howto/index
- configuration/index
-
-.. toctree::
- :maxdepth: 2
- :caption: Contribute to repo2docker
-
- contributing/index
-
-.. toctree::
- :maxdepth: 2
- :caption: Changelog
-
- changelog
diff --git a/docs/source/install.rst b/docs/source/install.rst
deleted file mode 100644
index be9c91b23..000000000
--- a/docs/source/install.rst
+++ /dev/null
@@ -1,79 +0,0 @@
-.. _install:
-
-Installing ``repo2docker``
-==========================
-
-repo2docker requires Python 3.6 or above on Linux and macOS. See
-:ref:`below ` for more information about Windows support.
-
-Prerequisite: Docker
---------------------
-
-Install `Docker `_ as it is required
-to build Docker images. The
-`Community Edition `_,
-is available for free.
-
-Recent versions of Docker are recommended.
-The latest version of Docker, ``18.03``, successfully builds repositories from
-`binder-examples `_.
-The `BinderHub `_ helm chart uses version
-``17.11.0-ce-dind``. See the
-`helm chart `_
-for more details.
-
-Optional: Mercurial
--------------------
-
-For `Mercurial `_ repositories, Mercurial and
-`hg-evolve `_ need to be
-installed. For example, on Debian based distributions, one can do::
-
- sudo apt install mercurial
- $(hg debuginstall --template "{pythonexe}") -m pip install hg-evolve --user
-
-To install Mercurial on other systems, see `here
-`_.
-
-Note that for old Mercurial versions, you may need to specify a version for
-hg-evolve. For example, ``hg-evolve==9.2`` for hg 4.5 (which is installed with
-`apt` on Ubuntu 18.4).
-
-Installing with ``pip``
------------------------
-
-We recommend installing ``repo2docker`` with the ``pip`` tool::
-
- python3 -m pip install jupyter-repo2docker
-
-for the latest release. To install the most recent code from the upstream repository, run::
-
- python3 -m pip install https://github.com/jupyterhub/repo2docker/archive/main.zip
-
-For information on using ``repo2docker``, see :ref:`usage`.
-
-Installing from source code
----------------------------
-
-Alternatively, you can install repo2docker from a local source tree,
-e.g. in case you are contributing back to this project::
-
- git clone https://github.com/jupyterhub/repo2docker.git
- cd repo2docker
- python3 -m pip install -e .
-
-That's it! For information on using ``repo2docker``, see
-:ref:`usage`.
-
-.. _windows:
-
-Windows support
----------------
-
-Windows support for ``repo2docker`` is still in the experimental stage.
-
-An article about `using Windows and the WSL`_ (Windows Subsytem for Linux or
-Bash on Windows) provides additional information about Windows and docker.
-
-
-.. _using Windows and the WSL: https://nickjanetakis.com/blog/setting-up-docker-for-windows-and-wsl-to-work-flawlessly
diff --git a/docs/source/specification.md b/docs/source/specification.md
new file mode 100644
index 000000000..fb7cc40c9
--- /dev/null
+++ b/docs/source/specification.md
@@ -0,0 +1,30 @@
+(specification)=
+
+# The Reproducible Execution Environment Specification
+
+repo2docker scans a repository for particular {ref}`config-files`, such
+as `requirements.txt` or `Project.toml`. The collection of files, their contents,
+and the resulting actions that repo2docker takes is known
+as the **Reproducible Execution Environment Specification** (or REES).
+
+The goal of the REES is to automate and encourage existing community best practices
+for reproducible computational environments. This includes installing packages using
+community-standard specification files and their corresponding tools,
+such as `requirements.txt` (with `pip`), `Project.toml` (with Julia), or
+`apt.txt` (with `apt`). While repo2docker automates the
+creation of the environment, a human should be able to look at a REES-compliant
+repository and reproduce the environment using common, clear steps without
+repo2docker software.
+
+Currently, the definition of the REE Specification is the following:
+
+> Any directory containing zero or more files from the {ref}`config-files` list is a
+> valid reproducible execution environment as defined by the REES. The
+> configuration files have to all be placed either in the root of the
+> directory, in a `binder/` sub-directory or a `.binder/` sub-directory.
+
+For example, the REES recognises `requirements.txt` as a valid config file.
+The file format is as defined by the `requirements.txt` standard of the Python
+community. A REES-compliant tool will install a Python interpreter (of unspecified version)
+and perform the equivalent action of `pip install -r requirements.txt` so that the
+user can afterwards run python and use the packages installed.
diff --git a/docs/source/specification.rst b/docs/source/specification.rst
deleted file mode 100644
index 28226845e..000000000
--- a/docs/source/specification.rst
+++ /dev/null
@@ -1,32 +0,0 @@
-.. _specification:
-
-====================================================
-The Reproducible Execution Environment Specification
-====================================================
-
-repo2docker scans a repository for particular :ref:`config-files`, such
-as ``requirements.txt`` or ``Project.toml``. The collection of files, their contents,
-and the resulting actions that repo2docker takes is known
-as the **Reproducible Execution Environment Specification** (or REES).
-
-The goal of the REES is to automate and encourage existing community best practices
-for reproducible computational environments. This includes installing pacakges using
-community-standard specification files and their corresponding tools,
-such as ``requirements.txt`` (with ``pip``), ``Project.toml`` (with Julia), or
-``apt.txt`` (with ``apt``). While repo2docker automates the
-creation of the environment, a human should be able to look at a REES-compliant
-repository and reproduce the environment using common, clear steps without
-repo2docker software.
-
-Currently, the definition of the REE Specification is the following:
-
- Any directory containing zero or more files from the :ref:`config-files` list is a
- valid reproducible execution environment as defined by the REES. The
- configuration files have to all be placed either in the root of the
- directory, in a ``binder/`` sub-directory or a ``.binder/`` sub-directory.
-
-For example, the REES recognises ``requirements.txt`` as a valid config file.
-The file format is as defined by the ``requirements.txt`` standard of the Python
-community. A REES-compliant tool will install a Python interpreter (of unspecified version)
-and perform the equivalent action of ``pip install -r requirements.txt`` so that the
-user can afterwards run python and use the packages installed.
diff --git a/docs/source/start.md b/docs/source/start.md
new file mode 100644
index 000000000..d2742d540
--- /dev/null
+++ b/docs/source/start.md
@@ -0,0 +1,91 @@
+# Get started
+
+This tutorial guides you through installing `repo2docker` and building your first environment image.
+
+## Prerequisite
+
+### Python
+
+`repo2docker` requires Python 3.6 or above.
+
+### Container Engine
+
+`repo2docker` requires a container engine compatible with the specification published by the [Open Container Initiative](https://opencontainers.org/).
+
+#### Docker
+
+```{important}
+Only the [Docker Engine](https://docs.docker.com/engine/) is open source. [Docker Desktop](https://docs.docker.com/get-started/get-docker/) requires a license.
+```
+
+Follow [Docker's official installation steps](https://docs.docker.com/get-started/get-docker/).
+
+#### Podman
+
+Follow [Podman's official installation steps](https://podman.io/docs/installation).
+
+After completing the installation of Podman,
+
+1. create a [listening service for Podman](https://docs.podman.io/en/latest/markdown/podman-system-service.1.html) by running
+
+ ```bash
+ systemctl --user start podman.socket
+ ```
+
+1. configure the `DOCKER_HOST` environment variable following [Podman's official procedure](https://podman-desktop.io/docs/migrating-from-docker/using-the-docker_host-environment-variable#procedure). You might want to configure the `DOCKER_HOST` environment variable to persist in your `~/.bashrc`.
+
+(install)=
+
+## Install `repo2docker`
+
+### Install `repo2docker` with `pip`
+
+It is recommend to install `repo2docker` with the `pip` tool:
+
+```bash
+python3 -m pip install jupyter-repo2docker
+```
+
+(usage)=
+
+## Build a repository with `repo2docker`
+
+Now that you've installed a container engine and `repo2docker`, you can build a repository.
+To do so, continue following this guide.
+
+### Start the container engine
+
+Ensure that the container engine is running.
+
+#### Docker
+
+Follow the [offcial instructions for starting Docker](https://docs.docker.com/engine/daemon/start/).
+
+#### Podman
+
+Run
+
+```bash
+podman info
+```
+
+### Build an image from a URL
+
+Next we'll build a reproducible image from a URL. We'll use the [Binder `requirements.txt` example](https://github.com/binder-examples/requirements), which installs a simple Python environment. Run the following command:
+
+```bash
+jupyter-repo2docker https://github.com/binder-examples/requirements
+```
+
+You'll see `repo2docker` take the following actions:
+
+1. Inspect the repository for [configuration files](#config-files). It will detect the `requirements.txt` file in the repository.
+2. Build a container image using the configuration files. In this case, the `requirements.txt` file will correspond to a Python environment.
+3. Run the image to let you explore the repository interactively.
+
+Click the link provided and you'll be taken to an interactive Jupyter Notebook interface where you can run commands interactively inside the environment.
+
+## Learn more
+
+This is a simple example building an environment image for your repository.
+To learn more about the kinds of source repositories, environments, and use-cases that `repo2docker` supports, see [the `repo2docker` user guide](./use/index.md).
diff --git a/docs/source/usage.rst b/docs/source/usage.rst
deleted file mode 100644
index e89eb8ff6..000000000
--- a/docs/source/usage.rst
+++ /dev/null
@@ -1,140 +0,0 @@
-.. _usage:
-
-=====================
-Using ``repo2docker``
-=====================
-
-.. note::
-
- `Docker `_ **must be running** in
- order to run ``repo2docker``. For more information on installing
- ``repo2docker``, see :ref:`install`.
-
-``repo2docker`` can build a reproducible computational environment for any repository that
-follows :ref:`specification`. repo2docker is called with the URL of a Git repository,
-a `DOI `_ from Zenodo or Figshare,
-a `Handle `_ or DOI from a Dataverse installation,
-a `SWHID`_ of a directory of a revision archived in the
-`Software Heritage Archive `_,
-or a path to a local directory.
-
-It then performs these steps:
-
-1. Inspects the repository for :ref:`configuration files `. These will be used to build
- the environment needed to run the repository.
-2. Builds a Docker image with an environment specified in these :ref:`configuration files `.
-3. Launches the image to let you explore the
- repository interactively via Jupyter notebooks, RStudio, or many other interfaces (optional)
-4. Pushes the images to a Docker registry so that it may be accessed remotely
- (optional)
-
-Calling repo2docker
-===================
-
-repo2docker is called with this command::
-
- jupyter-repo2docker
-
-where ```` is:
-
- * a URL of a Git repository (``https://github.com/binder-examples/requirements``),
- * a Zenodo DOI (``10.5281/zenodo.1211089``),
- * a SWHID_ (``swh:1:rev:999dd06c7f679a2714dfe5199bdca09522a29649``),
- * a URL of a CKAN_ dataset (``https://demo.ckan.org/dataset/sample-dataset-1``), or
- * a path to a local directory (``a/local/directory``)
-
-of the source repository you want to build.
-
-For example, the following command will build an image of Peter Norvig's
-Pytudes_ repository::
-
- jupyter-repo2docker https://github.com/norvig/pytudes
-
-Building the image may take a few minutes.
-
-Pytudes_
-uses a `requirements.txt file `_
-to specify its Python environment. Because of this, ``repo2docker`` will use
-``pip`` to install dependencies listed in this ``requirement.txt`` file, and
-these will be present in the generated Docker image. To learn more about
-configuration files in ``repo2docker`` visit :ref:`config-files`.
-
-When the image is built, a message will be output to your terminal::
-
- Copy/paste this URL into your browser when you connect for the first time,
- to login with a token:
- http://0.0.0.0:36511/?token=f94f8fabb92e22f5bfab116c382b4707fc2cade56ad1ace0
-
-Pasting the URL into your browser will open Jupyter Notebook with the
-dependencies and contents of the source repository in the built image.
-
-
-Building a specific branch, commit or tag
-=========================================
-
-To build a particular branch and commit, use the argument ``--ref`` and
-specify the ``branch-name`` or ``commit-hash``. For example::
-
- jupyter-repo2docker --ref 9ced85dd9a84859d0767369e58f33912a214a3cf https://github.com/norvig/pytudes
-
-.. tip::
- For reproducible builds, we recommend specifying a commit-hash to
- deterministically build a fixed version of a repository. Not specifying a
- commit-hash will result in the latest commit of the repository being built.
-
-
-.. _usage-config-file-location:
-
-Where to put configuration files
-================================
-
-``repo2docker`` will look for configuration files in:
-
-* A folder named ``binder/`` in the root of the repository.
-* A folder named ``.binder/`` in the root of the repository.
-* The root directory of the repository.
-
-Having both ``binder/`` and ``.binder/`` folders is not allowed.
-If one of these folders exists, only configuration files in that folder are considered, configuration in the root directory will be ignored.
-
-Check the complete list of :ref:`configuration files ` supported
-by ``repo2docker`` to see how to configure the build process.
-
-.. note::
-
- ``repo2docker`` builds an environment with Python 3.7 by default. If you'd
- like a different version, you can specify this in your
- :ref:`configuration files `.
-
-
-Debugging repo2docker with ``--debug`` and ``--no-build``
-=========================================================
-
-To debug the docker image being built, pass the ``--debug`` parameter:
-
- .. code-block:: bash
-
- jupyter-repo2docker --debug https://github.com/norvig/pytudes
-
-This will print the generated ``Dockerfile``, build it, and run it.
-
-To see the generated ``Dockerfile`` without actually building it,
-pass ``--no-build`` to the commandline. This ``Dockerfile`` output
-is for **debugging purposes** of ``repo2docker`` only - it can not
-be used by docker directly.
-
- .. code-block:: bash
-
- jupyter-repo2docker --no-build --debug https://github.com/norvig/pytudes
-
-
-Command line API
-================
-
-.. autoprogram:: repo2docker.__main__:argparser
- :prog: jupyter-repo2docker
-
-
-.. _Pytudes: https://github.com/norvig/pytudes
-.. _SWHID: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html
-.. _CKAN: https://ckan.org
diff --git a/docs/source/use/action.md b/docs/source/use/action.md
new file mode 100644
index 000000000..ec2df5649
--- /dev/null
+++ b/docs/source/use/action.md
@@ -0,0 +1,7 @@
+(github-action)=
+
+# Use GitHub Actions to build environment images
+
+The [`repo2docker` GitHub Action](https://github.com/jupyterhub/repo2docker-action) allows you to use `repo2docker` in your GitHub CI/CD to build environment images and push them to a registry. It is a useful way to automate building and pushing images to reduce deployment toil.
+
+See [the `repo2docker` GitHub Action README](https://github.com/jupyterhub/repo2docker-action) for documentation about how to use this tool.
diff --git a/docs/source/use/actions-and-scripts.md b/docs/source/use/actions-and-scripts.md
new file mode 100644
index 000000000..71555b971
--- /dev/null
+++ b/docs/source/use/actions-and-scripts.md
@@ -0,0 +1,39 @@
+# Run scripts, commands, and actions as part of environment building
+
+In addition to installing packages using the [standard configuration files](#config-files), you can run your own custom actions and commands as part of the build or user session launch process.
+
+(commands-jovyan)=
+
+## Commands are run as the `jovyan` user and not root
+
+Whenever you run a custom command, it is run as a special user called `jovyan`.
+It **does not have `root` privileges**.
+So, you should choose actions that do not require these privileges.
+
+For example, if you're using a `postBuild` script to install and use a new environment manager like [the `pixi` CLI](https://pixi.sh/latest/), make sure to install and configure it to a location accessible (and writable) to a user. For example:
+
+- ✅ `$CONDA_BIN` would work, because `conda` puts its binary folder in a user-accessible space.
+- ✅ `~/.local/bin` would work, because it's in the home directory.
+- ❌ `/usr/bin` would _NOT_ work, because it requires root privileges to write to.
+
+## Run commands before finalizing your environment image
+
+You can run arbitrary commands before finalizing your environment image.
+This is useful in cases like:
+
+- You want to install and use custom environment management software that isn't supported by [repo2docker's configuration files](#config-files) (e.g., `pixi`).
+- You want to run custom commands to setup the environment properly (e.g., download a small dataset and put it in a specific location in the image).
+
+To do so, use a `postBuild` script, see [](#postbuild) for more information.
+
+## Run commands before launching a new user session
+
+You can run commands before launching a new **user session**.
+These are not baked into the environment image, they'll only be run once an image has been launched, but before the user's interactive session has been created.
+
+This is useful for things like:
+
+- Start a local server running that you want a user to have access to.
+- Updating a local dataset that always needs to have the latest information in it.
+
+To do so, use a `start` script. See [](#config-start) for more information.
diff --git a/docs/source/use/community-image.md b/docs/source/use/community-image.md
new file mode 100644
index 000000000..d499768c5
--- /dev/null
+++ b/docs/source/use/community-image.md
@@ -0,0 +1,54 @@
+# Use a community-maintained image in a JupyterHub
+
+The simplest way to deploy environment images to a JupyterHub or BinderHub is to _find and re-use a pre-existing image maintained by a community_.
+In this case, you won't have to maintain anything -- you'll simply use a community's image and contribute upstream if you wish.
+
+Here are some steps to follow.
+
+## First, find a community-maintained image for your workflow
+
+Many communities define their own user envionment images for re-use. Here are a few to look into:
+
+1. [jupyter Docker-stacks](https://jupyter-docker-stacks.readthedocs.io/) has a number of user images for general data science workflows.
+2. [pangeo Docker-stacks](https://github.com/pangeo-data/pangeo-docker-images) has images for geospatial workflows in the cloud
+3. [rocker](https://rocker-project.org/) has images meant for reproducibility with the R computing language.
+
+For example, the [Jupyter Docker Stacks images](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#core-stacks) define images for several core datascience workflows. Each one points to a **hosted version of the image** on an image repository (such as [quay.io](https://quay.io)).
+
+## Pick a tag for the image
+
+Tags are a way to checkpoint an image so that you know exactly what is inside. They are similar to released versions of software. Tags will be listed in the image provider used by the community. For example:
+
+[Here's the list of tags for the `Docker-stacks-foundation` image](https://quay.io/repository/jupyter/docker-stacks-foundation?tab=tags).
+
+:::{admonition} Don't use the `latest` tag
+:class: warning
+`latest` is a special tag for "the latest tag to be released". This is generally a bad idea when using environment images for your interactive data science sessions. Here are a few reasons why:
+
+- The environment may regularly change without warning as new versions of the image are released.
+- The `JUPYTER_IMAGE` environment variable will not be useful because it's a generic `latest` variable.
+- If you use tools that _themselves_ use Docker, you might have different images on your interactive environment vs. the images the tools use, because they haven't all updated at once.
+
+Instead, we recommend periodically manually updating the tag you use over time.
+:::
+
+## Configure JupyterHub to use the tag
+
+Once you have an image and a tag, you can configure your JupyterHub to use it.
+
+Here's a sample configuration if you're using the [Zero to JupyterHub for Kubernetes distribution](https://z2jh.jupyter.org):
+
+```yaml
+singleuser:
+ image:
+ name: pangeo/pangeo-notebook
+ tag: 2023.04.15
+```
+
+Here's a sample configuration if you're using the [Dockerspawner for JupyterHub spawner](https://jupyterhub-dockerspawner.readthedocs.io/en/latest/):
+
+```python
+c.DockerSpawner.image = 'pangeo/pangeo-notebook:2023.04.15'
+```
+
+When you re-deploy your JupyterHub application, it should now spawn user environments from the image you've configured.
diff --git a/docs/source/use/extend-community-image.md b/docs/source/use/extend-community-image.md
new file mode 100644
index 000000000..f4bf5159d
--- /dev/null
+++ b/docs/source/use/extend-community-image.md
@@ -0,0 +1,91 @@
+# Extend a community image with your own packages
+
+If [using a community image alone](./community-image.md) doesn't provide the environment you need, you might get away with just installing one or two extra things on top of it.
+
+To do this, you should define a `Dockerfile` that "inherits" the community image, and "extend" it to add a few extra things.
+
+[Here's a repository that demonstrates how to do this using the `repo2docker` github action](https://github.com/2i2c-org/example-inherit-from-community-image). Below is a summary of the most important parts.
+
+A [local `environment.yml` file](https://github.com/2i2c-org/example-inherit-from-community-image/blob/main/environment.yml) defines **only the extra things to install** in addition to the upstream image.
+
+```{code-block}
+:caption: environment.yml
+channels:
+ - conda-forge
+
+dependencies:
+ - jupyterhub-singleuser>=3.0,<4.0
+
+ # Everyone wants to use nbgitpuller for everything, so let's do that
+ - nbgitpuller=1.1.*
+ # Install packages from pip
+ - pip
+ - pip:
+ - otter-grader
+```
+
+A local `Dockerfile` does the following things:
+
+- Inherits from the upstream image.
+- Copies your local `environment.yml` file into a directory in the image.
+- Updates the `conda` environment with it to install the extra packages.
+
+For example, the following inherits the `scipy-notebook` image with the `2023-05-01` tag and takes the above steps. (you could follow a similar workflow with other package managers).
+
+```{code-block} Dockerfile
+:caption: Dockerfile
+FROM jupyter/scipy-notebook:2023-05-01
+COPY environment.yml /tmp/environment.yml
+RUN mamba env update --prefix ${CONDA_DIR} --file /tmp/environment.yml
+COPY image-tests image-tests
+RUN ls
+```
+
+A [github workflow](https://github.com/2i2c-org/example-inherit-from-community-image/blob/main/.github/workflows/build.yaml) uses the [`repo2docker` GitHub action](#github-action) to build and push a Docker image using repo2docker. Here's an example of what the GitHub workflow looks like:
+
+````{dropdown} Example GitHub workflow code
+```{code-block} yaml
+:caption: .github/workflows/build.yaml
+name: Build and push container image
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+ steps:
+
+ # For biggish images, github actions runs out of disk space.
+ # So we cleanup some unwanted things in the disk image, and reclaim that space for our Docker use
+ # https://github.com/actions/virtual-environments/issues/2606#issuecomment-772683150
+ # and https://github.com/easimon/maximize-build-space/blob/b4d02c14493a9653fe7af06cc89ca5298071c66e/action.yml#L104
+ # This gives us a total of about 52G of free space, which should be enough for now
+ - name: cleanup disk space
+ run: |
+ sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc
+ df -h
+
+ - name: Checkout files in repo
+ uses: actions/checkout@main
+
+ - name: Build and push the image to quay.io
+ uses: jupyterhub/repo2docker-action@master
+ with:
+ # Make sure username & password/token pair matches your registry credentials
+ DOCKER_USERNAME: ${{ secrets.QUAY_USERNAME }}
+ DOCKER_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
+ DOCKER_REGISTRY: "quay.io"
+ # Disable pushing a 'latest' tag, as this often just causes confusion
+ LATEST_TAG_OFF: true
+ #
+ # Uncomment and modify the following line with your image name, otherwise no push will happen
+ IMAGE_NAME: "yuvipanda/example-inherit-from-community-image"
+
+ # Lets us monitor disks getting full as images get bigger over time
+ - name: Show how much disk space is left
+ run: df -h
+```
+````
diff --git a/docs/source/use/index.md b/docs/source/use/index.md
new file mode 100644
index 000000000..e83bf75c4
--- /dev/null
+++ b/docs/source/use/index.md
@@ -0,0 +1,48 @@
+# User guide
+
+Short, actionable guides that cover specific topics with `repo2docker`.
+Select from the pages listed below to get started.
+
+Information about configuring your repository to work with `repo2docker`,
+and controlling elements of the built environment using configuration files.
+
+```{toctree}
+:caption: Image building basics
+:maxdepth: 1
+pathways
+../configuration/index
+repository
+../cli
+```
+
+```{toctree}
+:maxdepth: 1
+:caption: Customize your environment
+
+../howto/user_interface
+../howto/languages
+../howto/lab_workspaces
+./actions-and-scripts
+../howto/export_environment
+../howto/base_image
+```
+
+```{toctree}
+:maxdepth: 1
+:caption: Deploy images into production
+
+../howto/deploy
+./community-image
+./extend-community-image
+../howto/jupyterhub_images
+./action
+../howto/breaking_changes
+```
+
+```{toctree}
+:maxdepth: 2
+:caption: Learn about repo2docker
+
+../faq
+../specification
+```
diff --git a/docs/source/use/pathways.md b/docs/source/use/pathways.md
new file mode 100644
index 000000000..f4906d34c
--- /dev/null
+++ b/docs/source/use/pathways.md
@@ -0,0 +1,104 @@
+# Four ways to use `repo2docker` images in a JupyterHub
+
+Many users of `repo2docker` primarily wish to define the environment for a Binder or a JupyterHub. Here are four ways to accomplish this, from least-to-most work.
+
+% TO EDIT THIS FIGURE: Load the linked SVG into excalidraw.com, make edits, and re-export.
+
+```{figure} ../_static/images/whentouse.svg
+A decision tree for the recommended way to get your reproducible user environment using `repo2docker` or a community image built with `repo2docker`.
+```
+
+## 1. Use a community maintained image
+
+The simplest thing to do is check whether another community already maintains and offers an image that you can simply re-use. This reduces the burden on you to keep the image up-to-date, and gives you an opportunity to collaborate and make contributions rather than building something yourself from scratch.
+
+See [](./community-image.md) for a brief how-to on using a community maintained image.
+
+::::{grid} 2
+:::{grid-item-card} Benefits
+
+- A community of experts maintains the image with you!
+- Don’t struggle alone with your problems, drag others along!
+- Good choices (base image, how python is installed, etc) made on your behalf
+- Updates happen without you needing to do much
+ :::
+ :::{grid-item-card} Drawbacks
+- Might have lots of stuff you don’t need
+- Large image sizes, slower pulling
+- Might get you 98% of the way there, but that isn't 100%
+- Architectural choices made might not fit your use case
+- Updates are not on your schedule
+ :::
+ ::::
+
+## 2. Inherit from a community maintained image and add a few extras
+
+Sometimes a community-maintained environment image has **almost** what you need, but there are a few extra packages you'd like to install yourself. In this case, it's easiest to **inherit and extend the community image**.
+
+This is not the same thing as forking the repository for the community image, modifying it, and re-building it from scratch. It's similar to depending on an upstream piece of software and then building upon it in your own tool.
+
+::::{grid} 2
+:::{grid-item-card} Benefits
+
+- Only need to maintain the changes you make
+- Update the `FROM` tag to keep up with upstream changes
+- You must manage a Git repository
+- Works well with [mybinder.org]
+ :::
+ :::{grid-item-card} Drawbacks
+- Need to understand how upstream image is built so you can customize
+- Documentation for this method currently sucks (but can be fixed!)
+- Removing existing packages might break things
+- You must manage a Git repository
+ :::
+ ::::
+
+See [](./extend-community-image.md) for a how-to guide.
+
+## 3. Use `repo2docker` to build your environment image
+
+If your needs differ far enough from the community-maintained images that you find, you can use `repo2docker` to build your environment image using [supported configuration files](#config-files).
+To learn how to do this, follow the [getting started with repo2docker guide](../start.md) and look at the [list of supported configuration files](#config-files).
+Check out the other sections under "Image building basics" for more useful how-tos.
+
+::::{grid} 2
+:::{grid-item-card} Benefits
+
+- Works well with [mybinder.org]
+- No need to learn Dockerfile syntax
+- Use language specific, well understood file formats
+- Only get whatever packages you want
+- Good defaults chosen by the community
+ :::
+ :::{grid-item-card} Drawbacks
+- Build time is slower
+- Images may be bigger
+- Might not support 100% of what you want, and at some point you might have to take the ramp-off to a Dockerfile
+ :::
+ ::::
+
+## 4. Use a full fledged custom Dockerfile
+
+If you need full control over the entire computational environment, you can always create your own `Dockerfile` from scratch, and `repo2docker` will build an image out of it.
+
+This is for advanced users only that know what they're doing - full guidance on how to use `Dockerfiles` is out of scope for this tutorial.
+
+[Here's an example repository that uses a `Dockerfile` to build with repo2docker](https://github.com/yuvipanda/example-use-dockerfile).
+
+::::{grid} 2
+:::{grid-item-card} Benefits
+
+- Get exactly what you want
+- Can be optimized for small image size & fast build times
+- Lots of existing documentation in the site reliability engineer world on how to use these
+
+:::
+:::{grid-item-card} Drawbacks
+
+- Requires a lot of knowledge for ongoing maintenance
+- Might have to solve problems yourself that were solved in upstream images / repo2docker
+- Need to adapt existing documentation from the site reliability engineer use case to interactive computing use case
+ :::
+ ::::
+
+[mybinder.org]: https://mybinder.org
diff --git a/docs/source/use/repository.md b/docs/source/use/repository.md
new file mode 100644
index 000000000..008d04658
--- /dev/null
+++ b/docs/source/use/repository.md
@@ -0,0 +1,65 @@
+# Using source repositories
+
+(usage-config-file-location)=
+
+## Where to put configuration files in a repository
+
+`repo2docker` will look for configuration files in the following order:
+
+- A folder named `binder/` or `.binder/` in the root of the repository.
+- The root directory of the repository.
+
+If one of the folders `binder/` or `.binder/` exists, only configuration files
+in that folder are considered, and configuration files in the root directory
+will be ignored. Having both `binder/` and `.binder/` folders is not allowed,
+and will result in an error.
+
+Check the complete list of [configuration files](#config-files) supported
+by `repo2docker` to see how to configure the build process.
+
+(repository-providers)=
+
+## Supported repository providers
+
+`repo2docker` can fetch repositories from a number of repositories. Here are the various types of _repository references_ we support:
+
+- A URL of a Git repository (`https://github.com/binder-examples/requirements`),
+- A Zenodo DOI (`10.5281/zenodo.1211089`),
+- A [SWHID] (`swh:1:rev:999dd06c7f679a2714dfe5199bdca09522a29649`),
+- A URL of a [CKAN] dataset (`https://demo.ckan.org/dataset/sample-dataset-1`)
+- A path to a local directory (`a/local/directory`)
+
+[swhid]: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html
+[ckan]: https://ckan.org
+
+In each case you can build from these repository sources like so:
+
+```bash
+repo2docker
+```
+
+## Supported version control systems
+
+These Version Control Systems are supported by `repo2docker`.
+
+### Git
+
+Any `git` repository is supported with `repo2docker`.
+These are generally stored in [a git repository provider](#repository-providers) like [GitHub](https://github.com).
+
+### Mercurial
+
+For [Mercurial](https://www.mercurial-scm.org) repositories, Mercurial and
+[hg-evolve](https://www.mercurial-scm.org/doc/evolution/) need to be
+installed. For example, on Debian based distributions, one can do:
+
+```bash
+sudo apt install mercurial
+$(hg debuginstall --template "{pythonexe}") -m pip install hg-evolve --user
+```
+
+Alternatively, see [how to install Mercurial on other systems](https://www.mercurial-scm.org/download).
+
+Note that for old Mercurial versions, you may need to specify a version for
+hg-evolve. For example, `hg-evolve==9.2` for hg 4.5 (which is installed with
+`apt` on Ubuntu 18.4).
diff --git a/playwright-requirements.txt b/playwright-requirements.txt
new file mode 100644
index 000000000..d01da0110
--- /dev/null
+++ b/playwright-requirements.txt
@@ -0,0 +1,2 @@
+-r dev-requirements.txt
+pytest-playwright
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..389eb5be9
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,76 @@
+[build-system]
+requires = ["setuptools>=61.0", "wheel", "setuptools-scm"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "jupyter-repo2docker"
+dynamic = ["version"]
+description = "repo2docker: Turn code repositories into Jupyter enabled Docker Images"
+readme = "README.md"
+license = "BSD-3-Clause"
+authors = [
+ {name = "Project Jupyter Contributors", email = "jupyter@googlegroups.com"}
+]
+maintainers = [
+ {name = "Project Jupyter Contributors", email = "jupyter@googlegroups.com"}
+]
+keywords = ["reproducible", "science", "environments", "docker"]
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Environment :: Console",
+ "Intended Audience :: Developers",
+ "Intended Audience :: System Administrators",
+ "Intended Audience :: Science/Research",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Topic :: Scientific/Engineering",
+ "Topic :: Software Development",
+ "Topic :: System :: Systems Administration",
+]
+requires-python = ">=3.10"
+# after changing dependencies, run ./scripts/refreeze
+# so that the Dockerfile reflects the changes
+dependencies = [
+ "charset-normalizer",
+ "docker!=5.0.0",
+ "entrypoints",
+ "escapism",
+ "iso8601",
+ "jinja2",
+ "python-json-logger",
+ "requests",
+ "ruamel.yaml>=0.15",
+ "semver",
+ "osfclient @ git+https://github.com/RCOSDP/rdmclient.git@master",
+ "toml",
+ "traitlets",
+ "packaging",
+ "beautifulsoup4>=4.10.0",
+]
+
+[project.urls]
+Homepage = "https://repo2docker.readthedocs.io"
+Documentation = "https://repo2docker.readthedocs.io"
+Funding = "https://jupyter.org/about#donate"
+Source = "https://github.com/jupyterhub/repo2docker"
+Tracker = "https://github.com/jupyterhub/repo2docker/issues"
+
+[project.scripts]
+jupyter-repo2docker = "repo2docker.__main__:main"
+repo2docker = "repo2docker.__main__:main"
+
+[project.entry-points."repo2docker.engines"]
+docker = "repo2docker.docker:DockerEngine"
+
+[tool.setuptools]
+include-package-data = true
+
+[tool.setuptools.packages.find]
+where = [""]
+include = ["repo2docker"]
+
+[tool.setuptools_scm]
+write_to = "repo2docker/_version.py"
+
+[tool.isort]
+profile = "black"
diff --git a/repo2docker/__init__.py b/repo2docker/__init__.py
index 23bc5700e..002d83e0d 100644
--- a/repo2docker/__init__.py
+++ b/repo2docker/__init__.py
@@ -1,9 +1,9 @@
-from ._version import get_versions
+try:
+ from ._version import __version__
+except ImportError:
+ __version__ = "0.0.0+unknown"
-__version__ = get_versions()["version"]
-del get_versions
-
-from . import _version
from .app import Repo2Docker
-__version__ = _version.get_versions()["version"]
+# You can add this if you want an __all__ variable to control imports
+__all__ = ["__version__", "Repo2Docker"]
diff --git a/repo2docker/__main__.py b/repo2docker/__main__.py
index a61ef918a..62d67f026 100644
--- a/repo2docker/__main__.py
+++ b/repo2docker/__main__.py
@@ -140,7 +140,8 @@ def get_argparser():
argparser.add_argument(
"--build-memory-limit",
- help="Total Memory that can be used by the docker build process",
+ # Removed argument, but we still want to support printing an error message if this is passed
+ help=argparse.SUPPRESS,
)
argparser.add_argument(
@@ -434,12 +435,13 @@ def make_r2d(argv=None):
sys.exit(1)
if args.build_memory_limit:
- # if the string only contains numerals we assume it should be an int
- # and specifies a size in bytes
- if args.build_memory_limit.isnumeric():
- r2d.build_memory_limit = int(args.build_memory_limit)
- else:
- r2d.build_memory_limit = args.build_memory_limit
+ # We no longer support build_memory_limit, it must be set in the builder instance
+ print("--build-memory-limit is no longer supported", file=sys.stderr)
+ print(
+ "Check your build engine documentation to set memory limits. Use `docker buildx create` if using the default builder to create a custom builder with appropriate memory limits",
+ file=sys.stderr,
+ )
+ sys.exit(-1)
if args.environment and not r2d.run:
print("To specify environment variables, you also need to run " "the container")
@@ -482,6 +484,15 @@ def main():
if r2d.log_level == logging.DEBUG:
r2d.log.exception(e)
sys.exit(1)
+ finally:
+ # workaround bug in traitlets Application.__del__:
+ # https://github.com/ipython/traitlets/pull/912
+ # make sure close_handlers is called before process teardown
+ try:
+ r2d.close_handlers()
+ except AttributeError:
+ # traitlets < 5.10
+ pass
if __name__ == "__main__":
diff --git a/repo2docker/_version.py b/repo2docker/_version.py
deleted file mode 100644
index 065fdc81f..000000000
--- a/repo2docker/_version.py
+++ /dev/null
@@ -1,716 +0,0 @@
-# This file helps to compute a version number in source trees obtained from
-# git-archive tarball (such as those provided by githubs download-from-tag
-# feature). Distribution tarballs (built by setup.py sdist) and build
-# directories (produced by setup.py build) will contain a much shorter file
-# that just contains the computed version number.
-
-# This file is released into the public domain.
-# Generated by versioneer-0.29
-# https://github.com/python-versioneer/python-versioneer
-
-"""Git implementation of _version.py."""
-
-import errno
-import functools
-import os
-import re
-import subprocess
-import sys
-from typing import Any, Callable, Dict, List, Optional, Tuple
-
-
-def get_keywords() -> Dict[str, str]:
- """Get the keywords needed to look up the version information."""
- # these strings will be replaced by git during git-archive.
- # setup.py/versioneer.py will grep for the variable names, so they must
- # each be defined on a line of their own. _version.py will just call
- # get_keywords().
- git_refnames = "$Format:%d$"
- git_full = "$Format:%H$"
- git_date = "$Format:%ci$"
- keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
- return keywords
-
-
-class VersioneerConfig:
- """Container for Versioneer configuration parameters."""
-
- VCS: str
- style: str
- tag_prefix: str
- parentdir_prefix: str
- versionfile_source: str
- verbose: bool
-
-
-def get_config() -> VersioneerConfig:
- """Create, populate and return the VersioneerConfig() object."""
- # these strings are filled in when 'setup.py versioneer' creates
- # _version.py
- cfg = VersioneerConfig()
- cfg.VCS = "git"
- cfg.style = "pep440"
- cfg.tag_prefix = ""
- cfg.parentdir_prefix = "repo2docker-"
- cfg.versionfile_source = "repo2docker/_version.py"
- cfg.verbose = False
- return cfg
-
-
-class NotThisMethod(Exception):
- """Exception raised if a method is not valid for the current scenario."""
-
-
-LONG_VERSION_PY: Dict[str, str] = {}
-HANDLERS: Dict[str, Dict[str, Callable]] = {}
-
-
-def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator
- """Create decorator to mark a method as the handler of a VCS."""
-
- def decorate(f: Callable) -> Callable:
- """Store f in HANDLERS[vcs][method]."""
- if vcs not in HANDLERS:
- HANDLERS[vcs] = {}
- HANDLERS[vcs][method] = f
- return f
-
- return decorate
-
-
-def run_command(
- commands: List[str],
- args: List[str],
- cwd: Optional[str] = None,
- verbose: bool = False,
- hide_stderr: bool = False,
- env: Optional[Dict[str, str]] = None,
-) -> Tuple[Optional[str], Optional[int]]:
- """Call the given command(s)."""
- assert isinstance(commands, list)
- process = None
-
- popen_kwargs: Dict[str, Any] = {}
- if sys.platform == "win32":
- # This hides the console window if pythonw.exe is used
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- popen_kwargs["startupinfo"] = startupinfo
-
- for command in commands:
- try:
- dispcmd = str([command] + args)
- # remember shell=False, so use git.cmd on windows, not just git
- process = subprocess.Popen(
- [command] + args,
- cwd=cwd,
- env=env,
- stdout=subprocess.PIPE,
- stderr=(subprocess.PIPE if hide_stderr else None),
- **popen_kwargs,
- )
- break
- except OSError as e:
- if e.errno == errno.ENOENT:
- continue
- if verbose:
- print("unable to run %s" % dispcmd)
- print(e)
- return None, None
- else:
- if verbose:
- print(f"unable to find command, tried {commands}")
- return None, None
- stdout = process.communicate()[0].strip().decode()
- if process.returncode != 0:
- if verbose:
- print("unable to run %s (error)" % dispcmd)
- print("stdout was %s" % stdout)
- return None, process.returncode
- return stdout, process.returncode
-
-
-def versions_from_parentdir(
- parentdir_prefix: str,
- root: str,
- verbose: bool,
-) -> Dict[str, Any]:
- """Try to determine the version from the parent directory name.
-
- Source tarballs conventionally unpack into a directory that includes both
- the project name and a version string. We will also support searching up
- two directory levels for an appropriately named parent directory
- """
- rootdirs = []
-
- for _ in range(3):
- dirname = os.path.basename(root)
- if dirname.startswith(parentdir_prefix):
- return {
- "version": dirname[len(parentdir_prefix) :],
- "full-revisionid": None,
- "dirty": False,
- "error": None,
- "date": None,
- }
- rootdirs.append(root)
- root = os.path.dirname(root) # up a level
-
- if verbose:
- print(
- "Tried directories %s but none started with prefix %s"
- % (str(rootdirs), parentdir_prefix)
- )
- raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
-
-
-@register_vcs_handler("git", "get_keywords")
-def git_get_keywords(versionfile_abs: str) -> Dict[str, str]:
- """Extract version information from the given file."""
- # the code embedded in _version.py can just fetch the value of these
- # keywords. When used from setup.py, we don't want to import _version.py,
- # so we do it with a regexp instead. This function is not used from
- # _version.py.
- keywords: Dict[str, str] = {}
- try:
- with open(versionfile_abs) as fobj:
- for line in fobj:
- if line.strip().startswith("git_refnames ="):
- mo = re.search(r'=\s*"(.*)"', line)
- if mo:
- keywords["refnames"] = mo.group(1)
- if line.strip().startswith("git_full ="):
- mo = re.search(r'=\s*"(.*)"', line)
- if mo:
- keywords["full"] = mo.group(1)
- if line.strip().startswith("git_date ="):
- mo = re.search(r'=\s*"(.*)"', line)
- if mo:
- keywords["date"] = mo.group(1)
- except OSError:
- pass
- return keywords
-
-
-@register_vcs_handler("git", "keywords")
-def git_versions_from_keywords(
- keywords: Dict[str, str],
- tag_prefix: str,
- verbose: bool,
-) -> Dict[str, Any]:
- """Get version information from git keywords."""
- if "refnames" not in keywords:
- raise NotThisMethod("Short version file found")
- date = keywords.get("date")
- if date is not None:
- # Use only the last line. Previous lines may contain GPG signature
- # information.
- date = date.splitlines()[-1]
-
- # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
- # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
- # -like" string, which we must then edit to make compliant), because
- # it's been around since git-1.5.3, and it's too difficult to
- # discover which version we're using, or to work around using an
- # older one.
- date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
- refnames = keywords["refnames"].strip()
- if refnames.startswith("$Format"):
- if verbose:
- print("keywords are unexpanded, not using")
- raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
- refs = {r.strip() for r in refnames.strip("()").split(",")}
- # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
- # just "foo-1.0". If we see a "tag: " prefix, prefer those.
- TAG = "tag: "
- tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)}
- if not tags:
- # Either we're using git < 1.8.3, or there really are no tags. We use
- # a heuristic: assume all version tags have a digit. The old git %d
- # expansion behaves like git log --decorate=short and strips out the
- # refs/heads/ and refs/tags/ prefixes that would let us distinguish
- # between branches and tags. By ignoring refnames without digits, we
- # filter out many common branch names like "release" and
- # "stabilization", as well as "HEAD" and "master".
- tags = {r for r in refs if re.search(r"\d", r)}
- if verbose:
- print("discarding '%s', no digits" % ",".join(refs - tags))
- if verbose:
- print("likely tags: %s" % ",".join(sorted(tags)))
- for ref in sorted(tags):
- # sorting will prefer e.g. "2.0" over "2.0rc1"
- if ref.startswith(tag_prefix):
- r = ref[len(tag_prefix) :]
- # Filter out refs that exactly match prefix or that don't start
- # with a number once the prefix is stripped (mostly a concern
- # when prefix is '')
- if not re.match(r"\d", r):
- continue
- if verbose:
- print("picking %s" % r)
- return {
- "version": r,
- "full-revisionid": keywords["full"].strip(),
- "dirty": False,
- "error": None,
- "date": date,
- }
- # no suitable tags, so version is "0+unknown", but full hex is still there
- if verbose:
- print("no suitable tags, using unknown + full revision id")
- return {
- "version": "0+unknown",
- "full-revisionid": keywords["full"].strip(),
- "dirty": False,
- "error": "no suitable tags",
- "date": None,
- }
-
-
-@register_vcs_handler("git", "pieces_from_vcs")
-def git_pieces_from_vcs(
- tag_prefix: str, root: str, verbose: bool, runner: Callable = run_command
-) -> Dict[str, Any]:
- """Get version from 'git describe' in the root of the source tree.
-
- This only gets called if the git-archive 'subst' keywords were *not*
- expanded, and _version.py hasn't already been rewritten with a short
- version string, meaning we're inside a checked out source tree.
- """
- GITS = ["git"]
- if sys.platform == "win32":
- GITS = ["git.cmd", "git.exe"]
-
- # GIT_DIR can interfere with correct operation of Versioneer.
- # It may be intended to be passed to the Versioneer-versioned project,
- # but that should not change where we get our version from.
- env = os.environ.copy()
- env.pop("GIT_DIR", None)
- runner = functools.partial(runner, env=env)
-
- _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=not verbose)
- if rc != 0:
- if verbose:
- print("Directory %s not under git control" % root)
- raise NotThisMethod("'git rev-parse --git-dir' returned error")
-
- # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
- # if there isn't one, this yields HEX[-dirty] (no NUM)
- describe_out, rc = runner(
- GITS,
- [
- "describe",
- "--tags",
- "--dirty",
- "--always",
- "--long",
- "--match",
- f"{tag_prefix}[[:digit:]]*",
- ],
- cwd=root,
- )
- # --long was added in git-1.5.5
- if describe_out is None:
- raise NotThisMethod("'git describe' failed")
- describe_out = describe_out.strip()
- full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
- if full_out is None:
- raise NotThisMethod("'git rev-parse' failed")
- full_out = full_out.strip()
-
- pieces: Dict[str, Any] = {}
- pieces["long"] = full_out
- pieces["short"] = full_out[:7] # maybe improved later
- pieces["error"] = None
-
- branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root)
- # --abbrev-ref was added in git-1.6.3
- if rc != 0 or branch_name is None:
- raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
- branch_name = branch_name.strip()
-
- if branch_name == "HEAD":
- # If we aren't exactly on a branch, pick a branch which represents
- # the current commit. If all else fails, we are on a branchless
- # commit.
- branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
- # --contains was added in git-1.5.4
- if rc != 0 or branches is None:
- raise NotThisMethod("'git branch --contains' returned error")
- branches = branches.split("\n")
-
- # Remove the first line if we're running detached
- if "(" in branches[0]:
- branches.pop(0)
-
- # Strip off the leading "* " from the list of branches.
- branches = [branch[2:] for branch in branches]
- if "master" in branches:
- branch_name = "master"
- elif not branches:
- branch_name = None
- else:
- # Pick the first branch that is returned. Good or bad.
- branch_name = branches[0]
-
- pieces["branch"] = branch_name
-
- # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
- # TAG might have hyphens.
- git_describe = describe_out
-
- # look for -dirty suffix
- dirty = git_describe.endswith("-dirty")
- pieces["dirty"] = dirty
- if dirty:
- git_describe = git_describe[: git_describe.rindex("-dirty")]
-
- # now we have TAG-NUM-gHEX or HEX
-
- if "-" in git_describe:
- # TAG-NUM-gHEX
- mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe)
- if not mo:
- # unparsable. Maybe git-describe is misbehaving?
- pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out
- return pieces
-
- # tag
- full_tag = mo.group(1)
- if not full_tag.startswith(tag_prefix):
- if verbose:
- fmt = "tag '%s' doesn't start with prefix '%s'"
- print(fmt % (full_tag, tag_prefix))
- pieces["error"] = "tag '{}' doesn't start with prefix '{}'".format(
- full_tag,
- tag_prefix,
- )
- return pieces
- pieces["closest-tag"] = full_tag[len(tag_prefix) :]
-
- # distance: number of commits since tag
- pieces["distance"] = int(mo.group(2))
-
- # commit: short hex revision ID
- pieces["short"] = mo.group(3)
-
- else:
- # HEX: no tags
- pieces["closest-tag"] = None
- out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root)
- pieces["distance"] = len(out.split()) # total number of commits
-
- # commit date: see ISO-8601 comment in git_versions_from_keywords()
- date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip()
- # Use only the last line. Previous lines may contain GPG signature
- # information.
- date = date.splitlines()[-1]
- pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
-
- return pieces
-
-
-def plus_or_dot(pieces: Dict[str, Any]) -> str:
- """Return a + if we don't already have one, else return a ."""
- if "+" in pieces.get("closest-tag", ""):
- return "."
- return "+"
-
-
-def render_pep440(pieces: Dict[str, Any]) -> str:
- """Build up version string, with post-release "local version identifier".
-
- Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
- get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
-
- Exceptions:
- 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += plus_or_dot(pieces)
- rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- else:
- # exception #1
- rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- return rendered
-
-
-def render_pep440_branch(pieces: Dict[str, Any]) -> str:
- """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
-
- The ".dev0" means not master branch. Note that .dev0 sorts backwards
- (a feature branch will appear "older" than the master branch).
-
- Exceptions:
- 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += plus_or_dot(pieces)
- rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- else:
- # exception #1
- rendered = "0"
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- return rendered
-
-
-def pep440_split_post(ver: str) -> Tuple[str, Optional[int]]:
- """Split pep440 version string at the post-release segment.
-
- Returns the release segments before the post-release and the
- post-release version number (or -1 if no post-release segment is present).
- """
- vc = str.split(ver, ".post")
- return vc[0], int(vc[1] or 0) if len(vc) == 2 else None
-
-
-def render_pep440_pre(pieces: Dict[str, Any]) -> str:
- """TAG[.postN.devDISTANCE] -- No -dirty.
-
- Exceptions:
- 1: no tags. 0.post0.devDISTANCE
- """
- if pieces["closest-tag"]:
- if pieces["distance"]:
- # update the post release segment
- tag_version, post_version = pep440_split_post(pieces["closest-tag"])
- rendered = tag_version
- if post_version is not None:
- rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"])
- else:
- rendered += ".post0.dev%d" % (pieces["distance"])
- else:
- # no commits, use the tag as the version
- rendered = pieces["closest-tag"]
- else:
- # exception #1
- rendered = "0.post0.dev%d" % pieces["distance"]
- return rendered
-
-
-def render_pep440_post(pieces: Dict[str, Any]) -> str:
- """TAG[.postDISTANCE[.dev0]+gHEX] .
-
- The ".dev0" means dirty. Note that .dev0 sorts backwards
- (a dirty tree will appear "older" than the corresponding clean one),
- but you shouldn't be releasing software with -dirty anyways.
-
- Exceptions:
- 1: no tags. 0.postDISTANCE[.dev0]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += ".post%d" % pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- rendered += plus_or_dot(pieces)
- rendered += "g%s" % pieces["short"]
- else:
- # exception #1
- rendered = "0.post%d" % pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- rendered += "+g%s" % pieces["short"]
- return rendered
-
-
-def render_pep440_post_branch(pieces: Dict[str, Any]) -> str:
- """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
-
- The ".dev0" means not master branch.
-
- Exceptions:
- 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += ".post%d" % pieces["distance"]
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += plus_or_dot(pieces)
- rendered += "g%s" % pieces["short"]
- if pieces["dirty"]:
- rendered += ".dirty"
- else:
- # exception #1
- rendered = "0.post%d" % pieces["distance"]
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += "+g%s" % pieces["short"]
- if pieces["dirty"]:
- rendered += ".dirty"
- return rendered
-
-
-def render_pep440_old(pieces: Dict[str, Any]) -> str:
- """TAG[.postDISTANCE[.dev0]] .
-
- The ".dev0" means dirty.
-
- Exceptions:
- 1: no tags. 0.postDISTANCE[.dev0]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += ".post%d" % pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- else:
- # exception #1
- rendered = "0.post%d" % pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- return rendered
-
-
-def render_git_describe(pieces: Dict[str, Any]) -> str:
- """TAG[-DISTANCE-gHEX][-dirty].
-
- Like 'git describe --tags --dirty --always'.
-
- Exceptions:
- 1: no tags. HEX[-dirty] (note: no 'g' prefix)
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"]:
- rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
- else:
- # exception #1
- rendered = pieces["short"]
- if pieces["dirty"]:
- rendered += "-dirty"
- return rendered
-
-
-def render_git_describe_long(pieces: Dict[str, Any]) -> str:
- """TAG-DISTANCE-gHEX[-dirty].
-
- Like 'git describe --tags --dirty --always -long'.
- The distance/hash is unconditional.
-
- Exceptions:
- 1: no tags. HEX[-dirty] (note: no 'g' prefix)
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
- else:
- # exception #1
- rendered = pieces["short"]
- if pieces["dirty"]:
- rendered += "-dirty"
- return rendered
-
-
-def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]:
- """Render the given version pieces into the requested style."""
- if pieces["error"]:
- return {
- "version": "unknown",
- "full-revisionid": pieces.get("long"),
- "dirty": None,
- "error": pieces["error"],
- "date": None,
- }
-
- if not style or style == "default":
- style = "pep440" # the default
-
- if style == "pep440":
- rendered = render_pep440(pieces)
- elif style == "pep440-branch":
- rendered = render_pep440_branch(pieces)
- elif style == "pep440-pre":
- rendered = render_pep440_pre(pieces)
- elif style == "pep440-post":
- rendered = render_pep440_post(pieces)
- elif style == "pep440-post-branch":
- rendered = render_pep440_post_branch(pieces)
- elif style == "pep440-old":
- rendered = render_pep440_old(pieces)
- elif style == "git-describe":
- rendered = render_git_describe(pieces)
- elif style == "git-describe-long":
- rendered = render_git_describe_long(pieces)
- else:
- raise ValueError("unknown style '%s'" % style)
-
- return {
- "version": rendered,
- "full-revisionid": pieces["long"],
- "dirty": pieces["dirty"],
- "error": None,
- "date": pieces.get("date"),
- }
-
-
-def get_versions() -> Dict[str, Any]:
- """Get version information or return default if unable to do so."""
- # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
- # __file__, we can work backwards from there to the root. Some
- # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
- # case we can only use expanded keywords.
-
- cfg = get_config()
- verbose = cfg.verbose
-
- try:
- return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose)
- except NotThisMethod:
- pass
-
- try:
- root = os.path.realpath(__file__)
- # versionfile_source is the relative path from the top of the source
- # tree (where the .git directory might live) to this file. Invert
- # this to find the root from __file__.
- for _ in cfg.versionfile_source.split("/"):
- root = os.path.dirname(root)
- except NameError:
- return {
- "version": "0+unknown",
- "full-revisionid": None,
- "dirty": None,
- "error": "unable to find root of source tree",
- "date": None,
- }
-
- try:
- pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
- return render(pieces, cfg.style)
- except NotThisMethod:
- pass
-
- try:
- if cfg.parentdir_prefix:
- return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
- except NotThisMethod:
- pass
-
- return {
- "version": "0+unknown",
- "full-revisionid": None,
- "dirty": None,
- "error": "unable to compute version",
- "date": None,
- }
diff --git a/repo2docker/app.py b/repo2docker/app.py
index 6865b69b6..6dca79aee 100755
--- a/repo2docker/app.py
+++ b/repo2docker/app.py
@@ -16,7 +16,6 @@
import sys
import tempfile
import time
-import warnings
from urllib.parse import urlparse
import entrypoints
@@ -38,7 +37,7 @@
RBuildPack,
)
from .engine import BuildError, ContainerEngineException, ImageLoadError
-from .utils import ByteSpecification, R2dState, chdir, get_platform
+from .utils import ByteSpecification, R2dState, chdir, get_free_port, get_platform
class Repo2Docker(Application):
@@ -168,13 +167,23 @@ def _default_log_level(self):
build_memory_limit = ByteSpecification(
0,
help="""
- Total memory that can be used by the docker image building process.
+ Unsupported.
- Set to 0 for no limits.
+ When using docker, please use `docker buildx create` to create a new buildkit
+ builder with appropriate limits instead.
""",
config=True,
)
+ @observe("build_memory_limit")
+ def build_memory_limit_changed(self, change):
+ print("Setting build_memory_limit is not supported", file=sys.stderr)
+ print(
+ "Check your build engine documentation to set memory limits. Use `docker buildx create` if using the default builder to create a custom builder with appropriate memory limits",
+ file=sys.stderr,
+ )
+ sys.exit(-1)
+
volumes = Dict(
{},
help="""
@@ -269,7 +278,7 @@ def _platform_default(self):
"""
p = get_platform()
if p == "linux/arm64":
- warnings.warn(
+ self.log.warning(
"Building for linux/arm64 is experimental. "
"To use the recommended platform set --Repo2Docker.platform=linux/amd64. "
"To silence this warning set --Repo2Docker.platform=linux/arm64."
@@ -452,7 +461,7 @@ def _dry_run_changed(self, change):
)
base_image = Unicode(
- "docker.io/library/buildpack-deps:jammy",
+ "docker.io/library/buildpack-deps:24.04",
config=True,
help="""
Base image to use when building docker images.
@@ -565,56 +574,6 @@ def initialize(self, *args, **kwargs):
if self.volumes and not self.run:
raise ValueError("Cannot mount volumes if container is not run")
- def push_image(self):
- """Push docker image to registry"""
- client = self.get_engine()
- # Build a progress setup for each layer, and only emit per-layer
- # info every 1.5s
- progress_layers = {}
- layers = {}
- last_emit_time = time.time()
- for chunk in client.push(self.output_image_spec):
- if client.string_output:
- self.log.info(chunk, extra=dict(phase=R2dState.PUSHING))
- continue
- # else this is Docker output
-
- # each chunk can be one or more lines of json events
- # split lines here in case multiple are delivered at once
- for line in chunk.splitlines():
- line = line.decode("utf-8", errors="replace")
- try:
- progress = json.loads(line)
- except Exception as e:
- self.log.warning("Not a JSON progress line: %r", line)
- continue
- if "error" in progress:
- self.log.error(progress["error"], extra=dict(phase=R2dState.FAILED))
- raise ImageLoadError(progress["error"])
- if "id" not in progress:
- continue
- # deprecated truncated-progress data
- if "progressDetail" in progress and progress["progressDetail"]:
- progress_layers[progress["id"]] = progress["progressDetail"]
- else:
- progress_layers[progress["id"]] = progress["status"]
- # include full progress data for each layer in 'layers' data
- layers[progress["id"]] = progress
- if time.time() - last_emit_time > 1.5:
- self.log.info(
- "Pushing image\n",
- extra=dict(
- progress=progress_layers,
- layers=layers,
- phase=R2dState.PUSHING,
- ),
- )
- last_emit_time = time.time()
- self.log.info(
- f"Successfully pushed {self.output_image_spec}",
- extra=dict(phase=R2dState.PUSHING),
- )
-
def run_image(self):
"""Run docker container from built image
@@ -632,7 +591,11 @@ def start_container(self):
docker_host = os.environ.get("DOCKER_HOST")
if docker_host:
- host_name = urlparse(docker_host).hostname
+ docker_host_parsed = urlparse(docker_host)
+ if docker_host_parsed.scheme == "unix":
+ host_name = "127.0.0.1"
+ else:
+ host_name = docker_host_parsed.hostname
else:
host_name = "127.0.0.1"
self.hostname = host_name
@@ -653,7 +616,7 @@ def start_container(self):
container_port = int(container_port_proto.split("/", 1)[0])
else:
# no port specified, pick a random one
- container_port = host_port = str(self._get_free_port())
+ container_port = host_port = str(get_free_port())
self.ports = {f"{container_port}/tcp": host_port}
self.port = host_port
# To use the option --NotebookApp.custom_display_url
@@ -664,7 +627,7 @@ def start_container(self):
"notebook",
"--ip=0.0.0.0",
f"--port={container_port}",
- f"--ServerApp.custom_display_url=http://{host_name}:{host_port}",
+ f"--ServerApp.custom_display_url=http://{self.hostname}:{self.port}",
"--ServerApp.default_url=/lab",
]
else:
@@ -737,30 +700,14 @@ def wait_for_container(self, container):
if exit_code:
sys.exit(exit_code)
- def _get_free_port(self):
- """
- Hacky method to get a free random port on local host
- """
- import socket
-
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.bind(("", 0))
- port = s.getsockname()[1]
- s.close()
- return port
-
def find_image(self):
# if this is a dry run it is Ok for dockerd to be unreachable so we
# always return False for dry runs.
if self.dry_run:
return False
# check if we already have an image for this content
- client = self.get_engine()
- for image in client.images():
- for tag in image.tags:
- if tag == self.output_image_spec + ":latest":
- return True
- return False
+ engine = self.get_engine()
+ return engine.inspect_image(self.output_image_spec) is not None
def build(self):
"""
@@ -792,7 +739,10 @@ def build(self):
try:
self.fetch(self.repo, self.ref, checkout_path)
- if self.find_image():
+ if self.find_image() and not self.push:
+ # If push is requested we don't have a general way to query the registry.
+ # ContainerEngine.build() also handles pushing, so always "build" and
+ # rely on the implementation to decide whether to rebuild.
self.log.info(
f"Reusing existing image ({self.output_image_spec}), not building."
)
@@ -817,9 +767,15 @@ def build(self):
picked_buildpack = bp
break
else:
- picked_buildpack = self.default_buildpack(
- base_image=self.base_image
- )
+ if self.default_buildpack:
+ picked_buildpack = self.default_buildpack(
+ base_image=self.base_image
+ )
+ else:
+ self.log.error(
+ "No environment specification found. See https://repo2docker.readthedocs.io/en/latest/configuration/ for supported files.\n"
+ )
+ self.exit(1)
picked_buildpack.platform = self.platform
picked_buildpack.appendix = self.appendix
@@ -856,13 +812,20 @@ def build(self):
extra=dict(phase=R2dState.BUILDING),
)
+ extra_build_kwargs = self.extra_build_kwargs.copy()
+ # Set "push" and "load" parameters in a backwards compat way, without
+ # having to change the signature of every buildpack
+ extra_build_kwargs["push"] = self.push
+ extra_build_kwargs["load"] = self.run
+
for l in picked_buildpack.build(
docker_client,
self.output_image_spec,
+ # This is deprecated, but passing it anyway to not break backwards compatibility
self.build_memory_limit,
build_args,
self.cache_from,
- self.extra_build_kwargs,
+ extra_build_kwargs,
platform=self.platform,
):
if docker_client.string_output:
@@ -894,8 +857,5 @@ def build(self):
def start(self):
self.build()
- if self.push:
- self.push_image()
-
if self.run:
self.run_image()
diff --git a/repo2docker/buildpacks/_r_base.py b/repo2docker/buildpacks/_r_base.py
index b2fbad8a1..b6f7a80be 100644
--- a/repo2docker/buildpacks/_r_base.py
+++ b/repo2docker/buildpacks/_r_base.py
@@ -14,9 +14,9 @@ def rstudio_base_scripts(r_version, rstudio_config):
"""Base steps to install RStudio and shiny-server."""
# Shiny server (not the package!) seems to be the same version for all R versions
- shiny_server_url = "https://download3.rstudio.org/ubuntu-14.04/x86_64/shiny-server-1.5.17.973-amd64.deb"
- shiny_proxy_version = "1.1"
- shiny_sha256sum = "80f1e48f6c824be7ef9c843bb7911d4981ac7e8a963e0eff823936a8b28476ee"
+ shiny_server_url = "https://download3.rstudio.org/ubuntu-18.04/x86_64/shiny-server-1.5.22.1017-amd64.deb"
+ shiny_proxy_version = "1.4"
+ shiny_sha256sum = "0fa40054f038de464a26f3f8c40180a072228454762b7a12ed50568b3256c236"
rstudio_url, rstudio_sha256sum = rstudio_server_installer(r_version, rstudio_config)
# the URL comes from external metadata (downloads.json) or is derived
@@ -27,7 +27,7 @@ def rstudio_base_scripts(r_version, rstudio_config):
else:
# user-pinned version without a hash; trust HTTPS
rstudio_verify = "true"
- rsession_proxy_version = "2.2.0"
+ rsession_proxy_version = "2.3.0"
return [
(
diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py
index e3138a4ff..1bdf0fee5 100644
--- a/repo2docker/buildpacks/base.py
+++ b/repo2docker/buildpacks/base.py
@@ -1,3 +1,4 @@
+import datetime
import hashlib
import io
import logging
@@ -43,17 +44,30 @@
ENV USER=${NB_USER} \
HOME=/home/${NB_USER}
-RUN groupadd \
- --gid ${NB_UID} \
- ${NB_USER} && \
- useradd \
+# Ubuntu 24.04 has a predefined uid/gid 1000, older Ubuntu versions don't
+RUN if getent group ${NB_UID}; then \
+ GROUP_1000="$(getent group ${NB_UID} | cut -d: -f1)"; \
+ if [ "$GROUP_1000" != "$NB_USER" ]; then \
+ groupmod --new-name ${NB_USER} "$GROUP_1000"; \
+ fi; \
+ else \
+ groupadd --gid ${NB_UID} ${NB_USER}; \
+ fi
+RUN if id ${NB_UID}; then \
+ USER_1000="$(id ${NB_UID} -un)"; \
+ if [ "$USER_1000" != "$NB_USER" ]; then \
+ usermod --home "/home/$NB_USER" --login "$NB_USER" --move-home "$USER_1000"; \
+ fi; \
+ else \
+ useradd \
--comment "Default user" \
--create-home \
--gid ${NB_UID} \
--no-log-init \
--shell /bin/bash \
--uid ${NB_UID} \
- ${NB_USER}
+ ${NB_USER}; \
+ fi
# Base package installs are not super interesting to users, so hide their outputs
# If install fails for some reason, errors will still be printed
@@ -196,8 +210,8 @@
# Add entrypoint
ENV PYTHONUNBUFFERED=1
-COPY /python3-login /usr/local/bin/python3-login
-COPY /repo2docker-entrypoint /usr/local/bin/repo2docker-entrypoint
+COPY --chmod=0755 /python3-login /usr/local/bin/python3-login
+COPY --chmod=0755 /repo2docker-entrypoint /usr/local/bin/repo2docker-entrypoint
ENTRYPOINT ["/usr/local/bin/repo2docker-entrypoint"]
# Specify the default command to run
@@ -246,7 +260,7 @@ def __init__(self, base_image):
)
self.platform = ""
- @lru_cache()
+ @lru_cache
def get_packages(self):
"""
List of packages that are installed in this BuildPack.
@@ -256,7 +270,7 @@ def get_packages(self):
"""
return set()
- @lru_cache()
+ @lru_cache
def get_base_packages(self):
"""
Base set of apt packages that are installed for all images.
@@ -275,7 +289,7 @@ def get_base_packages(self):
"gettext-base",
}
- @lru_cache()
+ @lru_cache
def get_build_env(self):
"""
Ordered list of environment variables to be set for this image.
@@ -291,7 +305,7 @@ def get_build_env(self):
"""
return []
- @lru_cache()
+ @lru_cache
def get_env(self):
"""
Ordered list of environment variables to be set for this image.
@@ -306,7 +320,7 @@ def get_env(self):
"""
return []
- @lru_cache()
+ @lru_cache
def get_path(self):
"""
Ordered list of file system paths to look for executables in.
@@ -316,14 +330,14 @@ def get_path(self):
"""
return []
- @lru_cache()
+ @lru_cache
def get_labels(self):
"""
Docker labels to set on the built image.
"""
return self.labels
- @lru_cache()
+ @lru_cache
def get_build_script_files(self):
"""
Dict of files to be copied to the container image for use in building.
@@ -348,7 +362,7 @@ def _check_stencila(self):
f"Found a stencila manifest.xml at {root}. Stencila is no longer supported."
)
- @lru_cache()
+ @lru_cache
def get_build_scripts(self):
"""
Ordered list of shell script snippets to build the base image.
@@ -370,7 +384,7 @@ def get_build_scripts(self):
return []
- @lru_cache()
+ @lru_cache
def get_preassemble_script_files(self):
"""
Dict of files to be copied to the container image for use in preassembly.
@@ -384,7 +398,7 @@ def get_preassemble_script_files(self):
"""
return {}
- @lru_cache()
+ @lru_cache
def get_preassemble_scripts(self):
"""
Ordered list of shell snippets to build an image for this repository.
@@ -401,7 +415,7 @@ def get_preassemble_scripts(self):
"""
return []
- @lru_cache()
+ @lru_cache
def get_assemble_scripts(self):
"""
Ordered list of shell script snippets to build the repo into the image.
@@ -428,7 +442,7 @@ def get_assemble_scripts(self):
"""
return []
- @lru_cache()
+ @lru_cache
def get_post_build_scripts(self):
"""
An ordered list of executable scripts to execute after build.
@@ -441,7 +455,7 @@ def get_post_build_scripts(self):
"""
return []
- @lru_cache()
+ @lru_cache
def get_start_script(self):
"""
The path to a script to be executed at container start up.
@@ -692,14 +706,14 @@ def _filter_tar(tarinfo):
class BaseImage(BuildPack):
- @lru_cache()
+ @lru_cache
def get_build_env(self):
"""Return env directives required for build"""
return [
("APP_BASE", "/srv"),
]
- @lru_cache()
+ @lru_cache
def get_env(self):
"""Return env directives to be set after build"""
return []
@@ -707,7 +721,7 @@ def get_env(self):
def detect(self):
return True
- @lru_cache()
+ @lru_cache
def get_preassemble_scripts(self):
scripts = []
try:
@@ -736,9 +750,7 @@ def get_preassemble_scripts(self):
apt-get -qq purge && \
apt-get -qq clean && \
rm -rf /var/lib/apt/lists/*
- """.format(
- " ".join(sorted(extra_apt_packages))
- ),
+ """.format(" ".join(sorted(extra_apt_packages))),
)
)
@@ -747,7 +759,7 @@ def get_preassemble_scripts(self):
return scripts
- @lru_cache()
+ @lru_cache
def get_assemble_scripts(self):
"""Return directives to run after the entire repository has been added to the image"""
scripts = []
@@ -756,14 +768,14 @@ def get_assemble_scripts(self):
scripts.append(("root", f"bash {prepare_mnt}"))
return scripts
- @lru_cache()
+ @lru_cache
def get_post_build_scripts(self):
post_build = self.binder_path("postBuild")
if os.path.exists(post_build):
return [post_build]
return []
- @lru_cache()
+ @lru_cache
def get_start_script(self):
start = self.binder_path("start")
if os.path.exists(start):
@@ -774,3 +786,50 @@ def get_start_script(self):
# the only path evaluated at container start time rather than build time
return os.path.join("${REPO_DIR}", start)
return None
+
+ @property
+ def runtime(self):
+ """
+ Return parsed contents of runtime.txt
+
+ Returns (runtime, version, date), tuple components may be None.
+ Returns (None, None, None) if runtime.txt not found.
+
+ Supported formats:
+ name-version
+ name-version-yyyy-mm-dd
+ name-yyyy-mm-dd
+ """
+ if hasattr(self, "_runtime"):
+ return self._runtime
+
+ self._runtime = (None, None, None)
+
+ runtime_path = self.binder_path("runtime.txt")
+ try:
+ with open(runtime_path) as f:
+ runtime_txt = f.read().strip()
+ except FileNotFoundError:
+ return self._runtime
+
+ name = None
+ version = None
+ date = None
+
+ parts = runtime_txt.split("-")
+ if len(parts) not in (2, 4, 5) or any(not (p) for p in parts):
+ raise ValueError(f"Invalid runtime.txt: {runtime_txt}")
+
+ name = parts[0]
+
+ if len(parts) in (2, 5):
+ version = parts[1]
+
+ if len(parts) in (4, 5):
+ date = "-".join(parts[-3:])
+ if not re.match(r"\d\d\d\d-\d\d-\d\d", date):
+ raise ValueError(f"Invalid runtime.txt date: {date}")
+ date = datetime.datetime.fromisoformat(date).date()
+
+ self._runtime = (name, version, date)
+ return self._runtime
diff --git a/repo2docker/buildpacks/conda/__init__.py b/repo2docker/buildpacks/conda/__init__.py
index 7130931df..92b0634dd 100644
--- a/repo2docker/buildpacks/conda/__init__.py
+++ b/repo2docker/buildpacks/conda/__init__.py
@@ -2,7 +2,6 @@
import os
import re
-import warnings
from collections.abc import Mapping
from functools import lru_cache
@@ -55,7 +54,7 @@ def _conda_platform(self):
return "linux-aarch64"
raise ValueError(f"Unknown platform {self.platform}")
- @lru_cache()
+ @lru_cache
def get_build_env(self):
"""Return environment variables to be set.
@@ -99,14 +98,14 @@ def get_build_env(self):
env.append(("KERNEL_PYTHON_PREFIX", "${NB_PYTHON_PREFIX}"))
return env
- @lru_cache()
+ @lru_cache
def get_env(self):
"""Make kernel env the default for `conda install`"""
env = super().get_env() + [("CONDA_DEFAULT_ENV", "${KERNEL_PYTHON_PREFIX}")]
env += self._get_env_for_matlab()
return env
- @lru_cache()
+ @lru_cache
def get_path(self):
"""Return paths (including conda environment path) to be added to
the PATH environment variable.
@@ -121,7 +120,7 @@ def get_path(self):
path.append("${NPM_DIR}/bin")
return path
- @lru_cache()
+ @lru_cache
def get_build_scripts(self):
"""
Return series of build-steps common to all Python 3 repositories.
@@ -159,7 +158,7 @@ def get_build_scripts(self):
major_pythons = {"2": "2.7", "3": "3.10"}
- @lru_cache()
+ @lru_cache
def get_build_script_files(self):
"""
Dict of files to be copied to the container image for use in building.
@@ -239,6 +238,23 @@ def get_build_script_files(self):
files.update(super().get_build_script_files())
return files
+ _environment_yaml_path = None
+
+ @property
+ def environment_yaml_path(self):
+ if self._environment_yaml_path is not None:
+ return self._environment_yaml_path
+
+ for filename in ["environment.yml", "environment.yaml"]:
+ environment_yaml_path = self.binder_path(filename)
+ if os.path.exists(environment_yaml_path):
+ self._environment_yaml_path = environment_yaml_path
+ break
+
+ # Because of backward compatibility, we return None
+ # instead of raise RuntimeError().
+ return self._environment_yaml_path
+
_environment_yaml = None
@property
@@ -246,12 +262,11 @@ def environment_yaml(self):
if self._environment_yaml is not None:
return self._environment_yaml
- environment_yml = self.binder_path("environment.yml")
- if not os.path.exists(environment_yml):
+ if self.environment_yaml_path is None:
self._environment_yaml = {}
return self._environment_yaml
- with open(environment_yml) as f:
+ with open(self.environment_yaml_path) as f:
env = YAML().load(f)
# check if the env file is empty, if so instantiate an empty dictionary.
if env is None:
@@ -259,7 +274,9 @@ def environment_yaml(self):
# check if the env file provided a dict-like thing not a list or other data structure.
if not isinstance(env, Mapping):
raise TypeError(
- "environment.yml should contain a dictionary. Got %r" % type(env)
+ "{} should contain a dictionary. Got {!r}".format(
+ self.environment_yaml_path, type(env)
+ )
)
self._environment_yaml = env
@@ -364,7 +381,7 @@ def uses_r(self):
@property
def py2(self):
"""Am I building a Python 2 kernel environment?"""
- warnings.warn(
+ self.log.warning(
"CondaBuildPack.py2 is deprecated in 2023.2. Use CondaBuildPack.separate_kernel_env.",
DeprecationWarning,
stacklevel=2,
@@ -384,7 +401,7 @@ def separate_kernel_env(self):
self.kernel_env_cutoff_version
)
- @lru_cache()
+ @lru_cache
def get_preassemble_script_files(self):
"""preassembly only requires environment.yml
@@ -392,23 +409,21 @@ def get_preassemble_script_files(self):
repo contents change
"""
assemble_files = super().get_preassemble_script_files()
- if self._should_preassemble_env:
- environment_yml = self.binder_path("environment.yml")
- if os.path.exists(environment_yml):
- assemble_files[environment_yml] = environment_yml
+ if self._should_preassemble_env and self.environment_yaml_path:
+ assemble_files[self.environment_yaml_path] = self.environment_yaml_path
return assemble_files
- @lru_cache()
+ @lru_cache
def get_env_scripts(self):
"""Return series of build-steps specific to this source repository."""
scripts = []
- environment_yml = self.binder_path("environment.yml")
env_prefix = (
"${KERNEL_PYTHON_PREFIX}"
if self.separate_kernel_env
else "${NB_PYTHON_PREFIX}"
)
- if os.path.exists(environment_yml):
+
+ if self.environment_yaml_path:
# TODO: when using micromamba, we call $MAMBA_EXE install -p ...
# whereas mamba/conda need `env update -p ...` when it's an env.yaml file
scripts.append(
@@ -416,7 +431,7 @@ def get_env_scripts(self):
"${NB_USER}",
rf"""
TIMEFORMAT='time: %3R' \
- bash -c 'time ${{MAMBA_EXE}} env update -p {env_prefix} --file "{environment_yml}" && \
+ bash -c 'time ${{MAMBA_EXE}} env update -p {env_prefix} --file "{self.environment_yaml_path}" && \
time ${{MAMBA_EXE}} clean --all -f -y && \
${{MAMBA_EXE}} list -p {env_prefix} \
'
@@ -467,14 +482,14 @@ def get_env_scripts(self):
]
return scripts
- @lru_cache()
+ @lru_cache
def get_preassemble_scripts(self):
scripts = super().get_preassemble_scripts()
if self._should_preassemble_env:
scripts.extend(self.get_env_scripts())
return scripts
- @lru_cache()
+ @lru_cache
def get_assemble_scripts(self):
scripts = super().get_assemble_scripts()
if not self._should_preassemble_env:
@@ -640,4 +655,4 @@ def get_custom_extension_script(self, post):
def detect(self):
"""Check if current repo should be built with the Conda BuildPack."""
- return os.path.exists(self.binder_path("environment.yml")) and super().detect()
+ return bool(self.environment_yaml_path)
diff --git a/repo2docker/buildpacks/conda/activate-conda.sh b/repo2docker/buildpacks/conda/activate-conda.sh
index 48372318e..9676234e1 100755
--- a/repo2docker/buildpacks/conda/activate-conda.sh
+++ b/repo2docker/buildpacks/conda/activate-conda.sh
@@ -1,9 +1,24 @@
-# enable conda and activate the notebook environment
-eval $(micromamba shell hook -s posix -p ${CONDA_DIR})
-for name in conda mamba; do
- CONDA_PROFILE="${CONDA_DIR}/etc/profile.d/${name}.sh"
- test -f $CONDA_PROFILE && . $CONDA_PROFILE
-done
+set -e
+
+# Setup conda
+CONDA_PROFILE="${CONDA_DIR}/etc/profile.d/conda.sh"
+echo "Activating profile: ${CONDA_PROFILE}"
+test -f $CONDA_PROFILE && . $CONDA_PROFILE
+
+# Setup micromamba
+eval $(micromamba shell hook -s posix -r ${CONDA_DIR})
+
+# Setup mamba
+export MAMBA_ROOT_PREFIX="${CONDA_DIR}"
+__mamba_setup="$("${CONDA_DIR}/bin/mamba" shell hook --shell posix 2> /dev/null)"
+if [ $? -eq 0 ]; then
+ eval "$__mamba_setup"
+else
+ alias mamba="${CONDA_DIR}/bin/mamba" # Fallback on help from mamba activate
+fi
+unset __mamba_setup
+
+# Activate the environment
if [[ "${KERNEL_PYTHON_PREFIX}" != "${NB_PYTHON_PREFIX}" ]]; then
# if the kernel is a separate env, stack them
# so both are on PATH, notebook first
@@ -19,3 +34,5 @@ if [[ "${KERNEL_PYTHON_PREFIX}" != "${NB_PYTHON_PREFIX}" ]]; then
else
mamba activate ${NB_PYTHON_PREFIX}
fi
+
+set +e
diff --git a/repo2docker/buildpacks/conda/environment.py-3.10-linux-64.lock b/repo2docker/buildpacks/conda/environment.py-3.10-linux-64.lock
index 998b18e68..e4b658141 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.10-linux-64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.10-linux-64.lock
@@ -1,177 +1,177 @@
# AUTO GENERATED FROM environment.py-3.10.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:02:53 UTC
+# Frozen on 2025-10-16 07:17:13 UTC
# Generated by conda-lock.
# platform: linux-64
# input_hash: 9914f3a970025d1c558275701d8a6a00f78464325df880885601a031faf3452a
@EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
-https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.7.4-hbcca054_0.conda#23ab7665c5f63cfb9f1f6195256daac6
-https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda#b80f2f396ca2c28b8c14c437a4ed1e74
-https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.10-5_cp310.conda#2921c34715e74b3587b4cff4d36844f9
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda#23c255b008c4f2ae008f81edcabaca89
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda#05e00f3b21e88bb3d658ac700b2ce58c
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066
-https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553
-https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
-https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7
-https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.18-h36c2ea0_1.tar.bz2#c3788462a6fbddafdb413a9f9053e58d
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e
-https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b
-https://conda.anaconda.org/conda-forge/linux-64/libuv-1.48.0-hd590300_0.conda#7e8b914b1062dd4386e3de4d82a3ead6
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-h767d61c_7.conda#c0374badb3a5d4b1372db28d19462c53
+https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc
+https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3
+https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6
+https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda#35f29eec58405aaf55e01cb470d8c26a
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363
+https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc
+https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda#5b767048b1b3ee9a954b06f4084f93dc
+https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1
+https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda#0f03292cc56bf91a077a134ea8747118
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8
+https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0
+https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a
+https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b
+https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda#a587892d3c13b6621a6091be690dbca2
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda#0b367fad34931cb79e0d6b7e5c06bb1c
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-h4852527_7.conda#f627678cf829bd70bccf141a19c3ad3e
https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc
-https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda#57d7dc60e9325e3de37ff8dffd18e814
-https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe
-https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda#6c566a46baae794daf34775d41eb180a
-https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
-https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
+https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8
+https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9
https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3
-https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.0-hde9e2c9_0.conda#18aa975d2094c34aef978060ae7da7d8
-https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4
-https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc
-https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-h4ab18f5_1.conda#9653f1bf3766164d0e65fa723cabbc54
-https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368
-https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.12.2-hc19f0b3_1.conda#0b86ff6ab7bdce5076269f2bbbf6857d
-https://conda.anaconda.org/conda-forge/linux-64/python-3.10.14-hd12c33a_0_cpython.conda#2b4ba962994e8bd4be9ff5b64b75aff2
-https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
-https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
-https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
-https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hc6cd4ac_1.conda#1f95722c94f00b69af69a066c7433714
+https://conda.anaconda.org/conda-forge/linux-64/python-3.10.19-hd994cfb_1_cpython.conda#3dc262bc5af2a90635384b5ab76545ba
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hea6c23e_4.conda#6ef43db290647218e1e04c2601675bff
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
-https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.5-py310hea249c9_0.conda#8750c69e97e0d019deb9971adc44e2f2
-https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.17-py310h25320af_0.conda#df12e1e922f79a4a407bc9566e9fba3f
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
-https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
-https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
-https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.0.3-py310hc6cd4ac_0.conda#fb478fd83c001cbf8ab01c29b857262e
-https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
-https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
-https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
-https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py310hff52083_0.conda#e546c847260e094f41d0d2f8d17fcbdd
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py310h2372a71_0.conda#f6703fa0214a00bf49d1bef6dc7672d0
-https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
-https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
-https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.2.4-py310hea6c23e_1.conda#a6a889b87b0358306e257dc7767d58be
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py310hff52083_2.conda#71d5cc5161f9ddac9d9f50c26cf0d85f
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py310h3406613_0.conda#8854df4fb4e37cc3ea0a024e48c9c180
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.19.5-hf7ee748_0.conda#2cbd9a01ac939d85f137ee44c64fb705
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
-https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
-https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
-https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.8-py310h2372a71_0.conda#bd19b3096442ea342c4a5208379660b1
-https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
-https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
-https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088
-https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1
-https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba575830ec18d5c51c59f403310e2c7
-https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
-https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.0-py310h7c4b9e2_0.conda#b1683bdb8b834126823a034d5f29efb2
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py310h5b4e0ec_0.conda#ad8de7b4ac482217959cc1e8fe0cc56d
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py310h3406613_0.conda#bc058b3b89fcb525bb4977832aa52014
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
-https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.20.0-py310h42e942d_0.conda#825361e63187e65a9da05a8b4274a0e1
-https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
-https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
-https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py310hc51659f_0.conda#c5a6aac4a1e0989986d9f06b3c2be2a0
-https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
-https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
-https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
-https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#68f0738df502a14213624b288c60c9ad
-https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
-https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
-https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
-https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h75354e8_4.conda#03cc8d9838ad9dd0060ab532e81ccb21
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
-https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
-https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
-https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
+https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.27.1-py310hd8f68c5_1.conda#7afa2dfd1c7d29316b36697e25ccb5d9
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py310h7c4b9e2_1.conda#c5f63ba41df24b9025c9196353541ed5
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda#8035e5b54c08429354d5d64027041cad
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py310h2fdcea3_0.conda#6ac912f6208a15d35955ab73a4e5efda
-https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
-https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
-https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
-https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
-https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-64/jupyter_core-5.7.2-py310hff52083_0.conda#cb92c27600d5716fd526a206aa43342c
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
-https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
-https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
-https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
-https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py310h71f11fc_0.conda#a2d7306f56bbef9d203518707ba90e89
-https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
-https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py310h8deb56e_0.conda#1fc24a3196ad5ede2a68148be61894f4
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py310h4f33d48_0.conda#d175993378311ef7c74f17971a380655
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
-https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
-https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-21.2.0-py310h2372a71_4.conda#68ee85860502d53c8cbfa0e4cef0f6cb
-https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
-https://conda.anaconda.org/conda-forge/linux-64/cryptography-43.0.0-py310h4909e49_0.conda#107b81f60a7391acd1f397dc5713d397
-https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
-https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.20.1-py310h42e942d_0.conda#51dcc27558e8cbe3aa6d3641cd78aa6d
-https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.32-py310h5b4e0ec_0.conda#db50567b518bc3dcc5168d51dae66ef4
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
-https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py310h64cae3c_0.conda#b527de1849629f2635dafc77745b015a
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
-https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
-https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
-https://conda.anaconda.org/conda-forge/noarch/ipython-8.26.0-pyh707e725_0.conda#f64d3520d5d00321c10f4dabb5b903f3
-https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
-https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
-https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
-https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
-https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
-https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
-https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
-https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
-https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
-https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
-https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8ed1ab_0.conda#c64f9b9f15a79239b864c455cc0a67d4
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
-https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
-https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py310h7c4b9e2_1.conda#aa3adecd8dd686ae1c28008b6d976b3f
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.2-py310hed992bd_0.conda#0c0f1a59e82a6477ef00a602920a89c3
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-8.37.0-pyh8f84b5b_0.conda#177cfa19fe3d74c87a8889286dc64090
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.41.4-py310hd8f68c5_0.conda#e6a2c9696ff99ae8a47c898f643cc2b0
+https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.44-py310h7c4b9e2_0.conda#60c06302d59e6b96f899c53fba8ab776
+https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py310h139afa4_0.conda#6b243b9f9477ad0b0a90552ebddb27e7
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.10-linux-aarch64.lock b/repo2docker/buildpacks/conda/environment.py-3.10-linux-aarch64.lock
index 64472fdbe..74bd30f78 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.10-linux-aarch64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.10-linux-aarch64.lock
@@ -1,176 +1,176 @@
# AUTO GENERATED FROM environment.py-3.10.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:03:03 UTC
+# Frozen on 2025-10-16 07:17:25 UTC
# Generated by conda-lock.
# platform: linux-aarch64
# input_hash: f532024d27b7260ff8e8e6db3f3e99af408d28961320c7a5c8046c9b6df251ee
@EXPLICIT
-https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.7.4-hcefe29a_0.conda#c4c784a1336d72fff54f6b207f3dd75f
-https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.40-h9fc2d93_7.conda#1b0feef706f4d03eff0b76626ead64fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.1.0-he277a41_1.conda#59d463d51eda114031e52667843f9665
-https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.10-5_cp310.conda#c6694ec383fb171da3ab68cae8d0e8f1
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.44-h9df1782_2.conda#c82b1aeb48ef8d5432cbc592716464ba
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-he277a41_7.conda#34cef4753287c36441f907d5fdd78d42
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda#05e00f3b21e88bb3d658ac700b2ce58c
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2#6168d71addc746e8f2b8d57dfd2edcea
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.1.0-he277a41_1.conda#2cb475709e327bb76f74645784582e6a
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.1.0-he9431aa_1.conda#842a1a0cf6f995091734a723e5d291ef
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.1.0-h3f4de04_1.conda#6c2afef2109372440a90c566bcb6391c
-https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda#56398c28220513b9ea13d7b450acfb20
-https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2#1f24853e59c68892452ef94ddd8afd4b
-https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2#dddd85f4d52121fab0a8b099c5e06501
-https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda#c14f32510f694e3185704d89967ec422
-https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.18-hb9de7d4_1.tar.bz2#d09ab3c60eebb6f14eb4d07e172775cc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.1.0-hf1166c9_1.conda#51f54efdd1d2ed5d7e9c67381b75fdb1
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda#000e30b09db0b7c775b21695dff30969
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.48.0-h31becfc_0.conda#97f754b22f63a943345bd807e1d51e01
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-he277a41_7.conda#afa05d91f8d57dd30985827a09c21464
+https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_8.conda#2921ac0b541bf37c69e66bd6d9a43bca
+https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h86ecc28_0.conda#e7df0aab10b9cbb73ab2a467ebfaf8c7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.1-hfae3067_0.conda#f75d19f3755461db2eb69401f5514f4c
+https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.5.2-hd65408f_0.conda#0c5ad486dcfb188885e3cf8ba209b97b
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_7.conda#a5ce1f0a32f02c75c11580c5b2f9258a
+https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_2.conda#7d362346a479256857ab338588190da0
+https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h86ecc28_1.conda#d5d58b2dc3e57073fe22303f5fed4db7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-h3f4de04_7.conda#6a2f0ee17851251a85fbebafbe707d2d
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.2-h3e4203c_0.conda#3a68e44fdf2a2811672520fdd62996bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.51.0-he30d5cf_1.conda#8e62bf5af966325ee416f19c6f14ffa3
+https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda#08aad7cbe9f5a6b460d0976076b6ae64
+https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda#182afabe009dc78d8b73100255ee6868
+https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda#9303e8887afe539f78517951ce25cd13
+https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-h80f16a2_3.conda#032d8030e4a24fe1f72c74423a46fb88
+https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20250104-pl5321h976ea20_0.conda#fb640d776fc92b682a14e001980825b1
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda#2e4a8f23bebdcb85ca8e5a0fbe75666a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.50.4-h022381a_0.conda#0ad1b73a3df7e3376c14efe6dabe6987
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-15.2.0-hf1166c9_7.conda#9e5deec886ad32f3c6791b3b75c78681
https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda#b4df5d7d4b63579d081fd3a4cf99740e
-https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h68df207_1.conda#b13fb82f88902e34dd0638cd7d378c21
-https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda#91d49c85cacd92caa40cf375ef72a25d
-https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.3.1-h86ecc28_3.conda#7f591390401ad65781372240424ab7fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/xz-5.2.6-h9cdd2b7_0.tar.bz2#83baad393a31d59c20b63ba4da6592df
-https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2#b853307650cb226731f653aa623936a4
+https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda#c0f08fc2737967edde1a272d4bf41ed9
+https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h5688188_102.conda#2562c9bfd1de3f9c590f0fe53858d85c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h86ecc28_2.conda#bc230abb5d21b63ff4799b0e75204783
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-hbcf94c1_2.conda#5be90c5a3e4b43c53e38f50a85e11527
https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda#268203e8b983fddb6412b36f2024e75c
-https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#29371161d77933a54fccf1bb66b96529
-https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.46.0-hf51ef55_0.conda#a8ae63fd6fb7d007f74ef3df95e5edf3
-https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda#105eb1e16bf83bfb2eb380a48032b655
-https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda#f75105e0585851f818e0009dd1dde4dc
-https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h68df207_1.conda#6031f9e32654fbdb9fdba406ab980517
-https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda#be8d5f8cf21aed237b8b182ea86b3dd6
https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda#29c10432a2ca1472b53f299ffb2ffa37
-https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.12.2-h9de0dd0_1.conda#ea2fbe77db884c344c72d10c895266d3
-https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.10.14-hbbe8eec_0_cpython.conda#8a8ee3a8c62032c554debc785a3b5aba
-https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
-https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
-https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
-https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py310hbb3657e_1.conda#5ed52d1d3c480022fe67ae00d1cab792
+https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.10.19-hcb9654d_1_cpython.conda#c9f6883e1812ecb434221f6b67ee84e2
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py310h57cea71_4.conda#c6d7c1743e0cac81664337a27069bdbc
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
-https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.5-py310hcaf1f41_0.conda#1099ea012f1c41375f9fe38dcf84cfb3
-https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.17-py310heccc163_0.conda#de593b3f2962851aa4a34bdf6a50cf1c
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
-https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
-https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
-https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.0.3-py310hbb3657e_0.conda#8666638e8cdaf3a8503c8c86e3a1e667
-https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
-https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
-https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
-https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py310h4c7bcd0_0.conda#1b1172686c6b9fb6ecd607f41326040b
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
-https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-2.1.5-py310h7c1f4a2_0.conda#cd83a00c52970ab794e9e991f0b32db4
-https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
-https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
-https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.2.4-py310h57cea71_1.conda#a8a63571ca1c1ca1c602ef60f268d9f0
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py310h4c7bcd0_2.conda#c4239e3839f15bd173875bb364a11bc2
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.3-py310h3b5aacf_0.conda#9a2b8d81828ff25f9eadc71e225bd223
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.19.5-h6feb2b4_0.conda#5b886141d3ae96e3fcfddbe1a1edd81b
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
-https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
-https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
-https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
-https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-5.9.8-py310hb299538_0.conda#3f89fc474876c9fd15bb3d9fd51976e6
-https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
-https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
-https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088
-https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1
-https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba575830ec18d5c51c59f403310e2c7
-https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
-https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-7.1.0-py310h5b55623_0.conda#4727a9b22e24bc3053eb943f0ae5f0ec
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
-https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py310hb52b2da_0.conda#54c1d90c8f9a926630989465fc01fddb
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.3-py310h2d8da20_0.conda#872dcca76a05bdbc9ddcdbb77896d464
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
-https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.20.0-py310h59d1b7a_0.conda#19ad8b38641c5f52a1ecc5b1c9f51acb
-https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
-https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
-https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
-https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.1-py310h03727f4_0.conda#92e196159f87f3029f44fca35158d6e8
-https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
-https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
-https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
-https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#68f0738df502a14213624b288c60c9ad
-https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
-https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
-https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
-https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h28faeed_4.conda#ae8c0108b4d6a5981bce484eeaaf1b55
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
-https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
-https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
-https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
+https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.27.1-py310haddc216_1.conda#05e007a25a5aed9dc6760250ef6a2a61
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.5.2-py310ha7967c6_1.conda#0b562e3fe4edc05b105e95a595079dd2
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-hefbcea8_9.conda#5676806bba055c901a62f969cb3fbe02
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
-https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.0-py310h7c0babf_0.conda#87c0d5fba91d5a527b4d7f20f76751ad
-https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
-https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
-https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
-https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
-https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-aarch64/jupyter_core-5.7.2-py310h4c7bcd0_0.conda#c73a831af0204c79654ef7e3cda8b212
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
-https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
-https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
-https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
-https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py310h55e1596_0.conda#dd93429f1ed04be736bb268b6ac16644
-https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
-https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py310h1451162_0.conda#c845d656240655e00d62f28efccac070
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-27.1.0-py310h5df05d2_0.conda#e43ca0f0b397a3b6085f1658dccc5722
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
-https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
-https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-21.2.0-py310hb299538_4.conda#f24c78f33054d3c0ce4b73f6155e16a5
-https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
-https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-43.0.0-py310hf601767_0.conda#147a251e0a1251d6628ff43f81536cda
-https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
-https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.20.1-py310h4719f56_0.conda#54a9b6da1da9302dc9a7f34bc0d9d287
-https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.32-py310h03727f4_0.conda#0518a1bcd530529900569f20141b6a38
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
-https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py310h0cd3344_0.conda#5f47d3b6e39007dc5c97fec8bd2dd860
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
-https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
-https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
-https://conda.anaconda.org/conda-forge/noarch/ipython-8.26.0-pyh707e725_0.conda#f64d3520d5d00321c10f4dabb5b903f3
-https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
-https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
-https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
-https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
-https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
-https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
-https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
-https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
-https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
-https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
-https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8ed1ab_0.conda#c64f9b9f15a79239b864c455cc0a67d4
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
-https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
-https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-25.1.0-py310h5b55623_1.conda#e6292cc16634cb3f23a495e20149cfd2
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-46.0.2-py310hd154c19_0.conda#eb25cea45cf70d29392f4112a35e4134
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-8.37.0-pyh8f84b5b_0.conda#177cfa19fe3d74c87a8889286dc64090
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.41.4-py310h598bb90_0.conda#efa04a8b32c9a042c4f01f8084517675
+https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.44-py310ha7967c6_0.conda#f5e6bcceafc979c2f0e3f8004ba0331c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.25.0-py310hef25091_0.conda#338ea1a8618f7f0c126d1945f71a553b
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.10.yml b/repo2docker/buildpacks/conda/environment.py-3.10.yml
index b2714cab4..64fd9fb38 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.10.yml
+++ b/repo2docker/buildpacks/conda/environment.py-3.10.yml
@@ -1,5 +1,5 @@
# AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY
-# Generated on 2024-08-29 07:02:53 UTC
+# Generated on 2025-10-16 07:17:13 UTC
channels:
- conda-forge
dependencies:
diff --git a/repo2docker/buildpacks/conda/environment.py-3.11-linux-64.lock b/repo2docker/buildpacks/conda/environment.py-3.11-linux-64.lock
index a16494bac..0778a8cbb 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.11-linux-64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.11-linux-64.lock
@@ -1,178 +1,178 @@
# AUTO GENERATED FROM environment.py-3.11.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:03:21 UTC
+# Frozen on 2025-10-16 07:17:45 UTC
# Generated by conda-lock.
# platform: linux-64
# input_hash: 5214f3fab8b6c89efc84e2138d15b78e5cad1444f8a79cea06169a23bd87ff15
@EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
-https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.7.4-hbcca054_0.conda#23ab7665c5f63cfb9f1f6195256daac6
-https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda#b80f2f396ca2c28b8c14c437a4ed1e74
-https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-5_cp311.conda#139a8d40c8a2f430df31048949e450de
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda#23c255b008c4f2ae008f81edcabaca89
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda#8fcb6b0e2161850556231336dae58358
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066
-https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553
-https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
-https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.2-h59595ed_0.conda#e7ba12deb7020dd080c6c70e7b6f6a3d
-https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7
-https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.18-h36c2ea0_1.tar.bz2#c3788462a6fbddafdb413a9f9053e58d
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e
-https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b
-https://conda.anaconda.org/conda-forge/linux-64/libuv-1.48.0-hd590300_0.conda#7e8b914b1062dd4386e3de4d82a3ead6
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-h767d61c_7.conda#c0374badb3a5d4b1372db28d19462c53
+https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc
+https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3
+https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6
+https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363
+https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc
+https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda#5b767048b1b3ee9a954b06f4084f93dc
+https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1
+https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda#0f03292cc56bf91a077a134ea8747118
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8
+https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0
+https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a
+https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b
+https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda#a587892d3c13b6621a6091be690dbca2
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda#0b367fad34931cb79e0d6b7e5c06bb1c
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-h4852527_7.conda#f627678cf829bd70bccf141a19c3ad3e
https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc
-https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda#57d7dc60e9325e3de37ff8dffd18e814
-https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe
-https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda#6c566a46baae794daf34775d41eb180a
-https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
-https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
+https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8
+https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9
https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3
-https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.0-hde9e2c9_0.conda#18aa975d2094c34aef978060ae7da7d8
-https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4
-https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc
-https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-h4ab18f5_1.conda#9653f1bf3766164d0e65fa723cabbc54
-https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368
-https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.12.2-hc19f0b3_1.conda#0b86ff6ab7bdce5076269f2bbbf6857d
-https://conda.anaconda.org/conda-forge/linux-64/python-3.11.9-hb806964_0_cpython.conda#ac68acfa8b558ed406c75e98d3428d7b
-https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
-https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
-https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
-https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311hb755f60_1.conda#cce9e7c3f1c307f2a5fb08a2922d6164
+https://conda.anaconda.org/conda-forge/linux-64/python-3.11.14-hfe2f287_1_cpython.conda#e87c753e04bffcda4cbfde7d052c1f7a
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311h1ddb823_4.conda#7138a06a7b0d11a23cfae323e6010a08
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
-https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.5-py311hf86e51f_0.conda#748a22f229ec0e62963b8045b8e6786c
-https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.17-py311hc665b79_0.conda#af20efc4f52675e7ce9a3e3ed8447fbb
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
-https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
-https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
-https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.0.3-py311hb755f60_0.conda#6f4b03b4d1e0da0962ea02113382677c
-https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
-https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
-https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
-https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py311h38be061_0.conda#01a505ab9b4e3af12baa98b82f5fcafa
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py311h459d7ec_0.conda#a322b4185121935c871d201ae00ac143
-https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
-https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
-https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.2.4-py311h1ddb823_1.conda#210ecbcac24f0148147fb8def5652d7a
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py311h38be061_2.conda#5dd29601defbcc14ac6953d9504a80a7
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_0.conda#0954f1a6a26df4a510b54f73b2a0345c
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.19.5-hf7ee748_0.conda#2cbd9a01ac939d85f137ee44c64fb705
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
-https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
-https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
-https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.8-py311h459d7ec_0.conda#9bc62d25dcf64eec484974a3123c9d57
-https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
-https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
-https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088
-https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1
-https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba575830ec18d5c51c59f403310e2c7
-https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
-https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.0-py311h49ec1c0_0.conda#eaf20d52642262d2987c3cdc7423649e
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py311h61187de_0.conda#76439451605390254b85d8da6f8d962a
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_0.conda#707c3d23f2476d3bfde8345b4e7d7853
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
-https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.20.0-py311hb3a8bbb_0.conda#db475e65fb621c2ec1dcdcc4e170b6f1
-https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
-https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
-https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py311h331c9d8_0.conda#e29e451c96bf8e81a5760b7565c6ed2c
-https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
-https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
-https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
-https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#68f0738df502a14213624b288c60c9ad
-https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
-https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
-https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
-https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h75354e8_4.conda#03cc8d9838ad9dd0060ab532e81ccb21
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
-https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
-https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
-https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
+https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.27.1-py311h902ca64_1.conda#622c389c080689ba1575a0750eb0209d
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py311h49ec1c0_1.conda#18a98f4444036100d78b230c94453ff4
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda#8035e5b54c08429354d5d64027041cad
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py311ha8e6434_0.conda#32259cd17741b52be10cd23a26cca23a
-https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
-https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
-https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
-https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
-https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-64/jupyter_core-5.7.2-py311h38be061_0.conda#f85e78497dfed6f6a4b865191f42de2e
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
-https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
-https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
-https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
-https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py311h7deb3e3_0.conda#536c998639c24ff56b79e86f9e422272
-https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
-https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h5b438cf_0.conda#6cb6c4d57d12dfa0ecdd19dbe758ffc9
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda#bd80ba060603cc228d9d81c257093119
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py311h2315fbb_0.conda#6c87a0f4566469af3585b11d89163fd7
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
-https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
-https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-21.2.0-py311h459d7ec_4.conda#de5b16869a430949b02161b04b844a30
-https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
-https://conda.anaconda.org/conda-forge/linux-64/cryptography-43.0.0-py311hc6616f6_0.conda#f392b3f7a26db16f37cf82996dcfc84d
-https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
-https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.20.1-py311hb3a8bbb_0.conda#6cb8806e9e920bd9c32205128d848a00
-https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.32-py311h61187de_0.conda#2248e75f001930ba8389fe91c51be50b
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
-https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py311h5cd10c7_0.conda#8efe4fe2396281627b3450af8357b190
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
-https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
-https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
-https://conda.anaconda.org/conda-forge/noarch/ipython-8.26.0-pyh707e725_0.conda#f64d3520d5d00321c10f4dabb5b903f3
-https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
-https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
-https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
-https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
-https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
-https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
-https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
-https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
-https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
-https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
-https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8ed1ab_0.conda#c64f9b9f15a79239b864c455cc0a67d4
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
-https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
-https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py311h49ec1c0_1.conda#f3d6bb9cae7a99bb6cd6fdaa09fe394d
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.2-py311h8488d03_0.conda#fee8a1f28d20aa480aff55318fc18dfd
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-9.6.0-pyhfa0c392_0.conda#ad6641ef96dd7872acbb802fa3fcb8d1
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.41.4-py311h902ca64_0.conda#a440367c3ba0b93cfb7c6b1b93989720
+https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.44-py311h49ec1c0_0.conda#1a4718360f83e252305752c5dee5a31d
+https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py311haee01d2_0.conda#0fd242142b0691eb9311dc32c1d4ab76
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.11-linux-aarch64.lock b/repo2docker/buildpacks/conda/environment.py-3.11-linux-aarch64.lock
index 435925107..d5d569c46 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.11-linux-aarch64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.11-linux-aarch64.lock
@@ -1,177 +1,177 @@
# AUTO GENERATED FROM environment.py-3.11.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:03:31 UTC
+# Frozen on 2025-10-16 07:17:57 UTC
# Generated by conda-lock.
# platform: linux-aarch64
# input_hash: 01b1a051ebf91f5c3936a15c8560ba613c4a46024cfd45434b27e1aa53b4d00f
@EXPLICIT
-https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.7.4-hcefe29a_0.conda#c4c784a1336d72fff54f6b207f3dd75f
-https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.40-h9fc2d93_7.conda#1b0feef706f4d03eff0b76626ead64fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.1.0-he277a41_1.conda#59d463d51eda114031e52667843f9665
-https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.11-5_cp311.conda#c2078141f21872cc34d9305123ba08f2
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.44-h9df1782_2.conda#c82b1aeb48ef8d5432cbc592716464ba
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-he277a41_7.conda#34cef4753287c36441f907d5fdd78d42
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda#8fcb6b0e2161850556231336dae58358
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2#6168d71addc746e8f2b8d57dfd2edcea
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.1.0-he277a41_1.conda#2cb475709e327bb76f74645784582e6a
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.1.0-he9431aa_1.conda#842a1a0cf6f995091734a723e5d291ef
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.1.0-h3f4de04_1.conda#6c2afef2109372440a90c566bcb6391c
-https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda#56398c28220513b9ea13d7b450acfb20
-https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2#1f24853e59c68892452ef94ddd8afd4b
-https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.2-h2f0025b_0.conda#1b9f46b804a2c3c5d7fd6a80b77c35f9
-https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2#dddd85f4d52121fab0a8b099c5e06501
-https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda#c14f32510f694e3185704d89967ec422
-https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.18-hb9de7d4_1.tar.bz2#d09ab3c60eebb6f14eb4d07e172775cc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.1.0-hf1166c9_1.conda#51f54efdd1d2ed5d7e9c67381b75fdb1
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda#000e30b09db0b7c775b21695dff30969
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.48.0-h31becfc_0.conda#97f754b22f63a943345bd807e1d51e01
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-he277a41_7.conda#afa05d91f8d57dd30985827a09c21464
+https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_8.conda#2921ac0b541bf37c69e66bd6d9a43bca
+https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h86ecc28_0.conda#e7df0aab10b9cbb73ab2a467ebfaf8c7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.1-hfae3067_0.conda#f75d19f3755461db2eb69401f5514f4c
+https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.6-he21f813_1.conda#15a131f30cae36e9a655ca81fee9a285
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_7.conda#a5ce1f0a32f02c75c11580c5b2f9258a
+https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_2.conda#7d362346a479256857ab338588190da0
+https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h86ecc28_1.conda#d5d58b2dc3e57073fe22303f5fed4db7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-h3f4de04_7.conda#6a2f0ee17851251a85fbebafbe707d2d
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.2-h3e4203c_0.conda#3a68e44fdf2a2811672520fdd62996bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.51.0-he30d5cf_1.conda#8e62bf5af966325ee416f19c6f14ffa3
+https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda#08aad7cbe9f5a6b460d0976076b6ae64
+https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda#182afabe009dc78d8b73100255ee6868
+https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda#9303e8887afe539f78517951ce25cd13
+https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-h80f16a2_3.conda#032d8030e4a24fe1f72c74423a46fb88
+https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20250104-pl5321h976ea20_0.conda#fb640d776fc92b682a14e001980825b1
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda#2e4a8f23bebdcb85ca8e5a0fbe75666a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.50.4-h022381a_0.conda#0ad1b73a3df7e3376c14efe6dabe6987
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-15.2.0-hf1166c9_7.conda#9e5deec886ad32f3c6791b3b75c78681
https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda#b4df5d7d4b63579d081fd3a4cf99740e
-https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h68df207_1.conda#b13fb82f88902e34dd0638cd7d378c21
-https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda#91d49c85cacd92caa40cf375ef72a25d
-https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.3.1-h86ecc28_3.conda#7f591390401ad65781372240424ab7fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/xz-5.2.6-h9cdd2b7_0.tar.bz2#83baad393a31d59c20b63ba4da6592df
-https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2#b853307650cb226731f653aa623936a4
+https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda#c0f08fc2737967edde1a272d4bf41ed9
+https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h5688188_102.conda#2562c9bfd1de3f9c590f0fe53858d85c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h86ecc28_2.conda#bc230abb5d21b63ff4799b0e75204783
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-hbcf94c1_2.conda#5be90c5a3e4b43c53e38f50a85e11527
https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda#268203e8b983fddb6412b36f2024e75c
-https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#29371161d77933a54fccf1bb66b96529
-https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.46.0-hf51ef55_0.conda#a8ae63fd6fb7d007f74ef3df95e5edf3
-https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda#105eb1e16bf83bfb2eb380a48032b655
-https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda#f75105e0585851f818e0009dd1dde4dc
-https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h68df207_1.conda#6031f9e32654fbdb9fdba406ab980517
-https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda#be8d5f8cf21aed237b8b182ea86b3dd6
https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda#29c10432a2ca1472b53f299ffb2ffa37
-https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.12.2-h9de0dd0_1.conda#ea2fbe77db884c344c72d10c895266d3
-https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.11.9-hddfb980_0_cpython.conda#4846e936e27dd709ad78f91fa33a48e8
-https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
-https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
-https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
-https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py311h8715677_1.conda#22c060f41b1407ed9f40ce6468bdc338
+https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.11.14-hcfbf8c2_1_cpython.conda#6190c58a8b68e9f18fb751f7702870c1
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py311h2cb90db_4.conda#885e3ee3d1d7019a8b6c2e1cf8492bea
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
-https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.5-py311h113a62e_0.conda#ae0e00026f3646e7d0378735d14b217a
-https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.17-py311h8e4e6a5_0.conda#b4ca891da67dc4f17e387fa9f05fa2da
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
-https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
-https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
-https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.0.3-py311h8715677_0.conda#4558bf1dc476a6ecc8438d29b5bb2548
-https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
-https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
-https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
-https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py311hec3470c_0.conda#3a41e40f73e44585e267ea9ea08d22b5
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
-https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-2.1.5-py311hc8f2f60_0.conda#e0b4d562accc096561afce52343d72d8
-https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
-https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
-https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.2.4-py311h2cb90db_1.conda#924d4bbdcc31c59a535873b5466cda02
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py311hec3470c_2.conda#49be99374ac54a8cb6c98948233170a4
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.3-py311h2dad8b0_0.conda#ff6e48e0ba263a5f446237001fd59c4a
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.19.5-h6feb2b4_0.conda#5b886141d3ae96e3fcfddbe1a1edd81b
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
-https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
-https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
-https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
-https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-5.9.8-py311hcd402e7_0.conda#858767f880caa447ae640a991ec5ba9e
-https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
-https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
-https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088
-https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1
-https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba575830ec18d5c51c59f403310e2c7
-https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
-https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-7.1.0-py311h19352d5_0.conda#39cbc056a3c90098a1b8b13e31c1e8c6
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
-https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py311hf4892ed_0.conda#619e11e109a330e7b2035b8d57fbfe96
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.3-py311h164a683_0.conda#3b798096c0411fe74c24a95c3965a62a
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
-https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.20.0-py311h949f54a_0.conda#382d01f4f74e8cf3fa7c42269be26ab9
-https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
-https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
-https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
-https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.1-py311h323e239_0.conda#8a9c05086dbc79ae0a8d63e6d79b71d6
-https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
-https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
-https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
-https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#68f0738df502a14213624b288c60c9ad
-https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
-https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
-https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
-https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h28faeed_4.conda#ae8c0108b4d6a5981bce484eeaaf1b55
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
-https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
-https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
-https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
+https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.27.1-py311hc91c717_1.conda#6315a262e3d9feb00eb5e768689d5a0f
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.5.2-py311hb9158a3_1.conda#9355a7de2012e18e6ae1d2d0395260d8
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-hefbcea8_9.conda#5676806bba055c901a62f969cb3fbe02
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
-https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.0-py311h458092e_0.conda#2271597156ba20eeae18b4e634ce28e3
-https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
-https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
-https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
-https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
-https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-aarch64/jupyter_core-5.7.2-py311hec3470c_0.conda#eb1788f50737ec7c56c5c730e9dc55d7
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
-https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
-https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
-https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
-https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py311h826da9f_0.conda#ae101dc799cda02d3586c0a397437f54
-https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
-https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-2.0.0-py311h3324b35_0.conda#606a6f422c769fd5180ceca519200ee9
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda#bd80ba060603cc228d9d81c257093119
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-27.1.0-py311h5e4e491_0.conda#e000dfab1843362bc7030a3a957b3146
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
-https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
-https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-21.2.0-py311hcd402e7_4.conda#836640e8a482dc22decea414117bb2ad
-https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
-https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-43.0.0-py311h0290c5f_0.conda#c8308816d1ad3fa2d43054d257f07b2d
-https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
-https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.20.1-py311h4713408_0.conda#b9af63f80e78adc8eef8df6b42b6af63
-https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.32-py311h323e239_0.conda#b2e51878f80bd2036ddd6fb3d4c8859a
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
-https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py311h8938cd4_0.conda#c3c69abb865a71cefe44bcc4bfc3997d
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
-https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
-https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
-https://conda.anaconda.org/conda-forge/noarch/ipython-8.26.0-pyh707e725_0.conda#f64d3520d5d00321c10f4dabb5b903f3
-https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
-https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
-https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
-https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
-https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
-https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
-https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
-https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
-https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
-https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
-https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8ed1ab_0.conda#c64f9b9f15a79239b864c455cc0a67d4
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
-https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
-https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-25.1.0-py311h19352d5_1.conda#59a01216703b8f29b4da69cb34858d1c
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-46.0.2-py311h2822d24_0.conda#c0a7cc61793cf7ec5f1fd02ddb862122
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-9.6.0-pyhfa0c392_0.conda#ad6641ef96dd7872acbb802fa3fcb8d1
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.41.4-py311hddf1d3d_0.conda#ea720d76d38146a7beb7a9d00279c689
+https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.44-py311hb9158a3_0.conda#74701eefe002e59ab62dfe4f4e205619
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.25.0-py311h51cfe5d_0.conda#295a2d74143c0b2621123d0c628c599f
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.11.yml b/repo2docker/buildpacks/conda/environment.py-3.11.yml
index e7ccedcb1..acf07daf2 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.11.yml
+++ b/repo2docker/buildpacks/conda/environment.py-3.11.yml
@@ -1,5 +1,5 @@
# AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY
-# Generated on 2024-08-29 07:03:21 UTC
+# Generated on 2025-10-16 07:17:45 UTC
channels:
- conda-forge
dependencies:
diff --git a/repo2docker/buildpacks/conda/environment.py-3.12-linux-64.lock b/repo2docker/buildpacks/conda/environment.py-3.12-linux-64.lock
index 92dc966cb..4854584f5 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.12-linux-64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.12-linux-64.lock
@@ -1,178 +1,181 @@
# AUTO GENERATED FROM environment.py-3.12.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:03:48 UTC
+# Frozen on 2025-10-16 07:18:17 UTC
# Generated by conda-lock.
# platform: linux-64
# input_hash: 879482ba73e3629ab14f1a6fdd329717bfdaaeb40e99a61c7084b15f78e4cc60
@EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
-https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.7.4-hbcca054_0.conda#23ab7665c5f63cfb9f1f6195256daac6
-https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda#b80f2f396ca2c28b8c14c437a4ed1e74
-https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda#0424ae29b104430108f5218a66db7260
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda#23c255b008c4f2ae008f81edcabaca89
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066
-https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553
-https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
-https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.2-h59595ed_0.conda#e7ba12deb7020dd080c6c70e7b6f6a3d
-https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7
-https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.18-h36c2ea0_1.tar.bz2#c3788462a6fbddafdb413a9f9053e58d
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e
-https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b
-https://conda.anaconda.org/conda-forge/linux-64/libuv-1.48.0-hd590300_0.conda#7e8b914b1062dd4386e3de4d82a3ead6
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-h767d61c_7.conda#c0374badb3a5d4b1372db28d19462c53
+https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc
+https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3
+https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6
+https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363
+https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc
+https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda#5b767048b1b3ee9a954b06f4084f93dc
+https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1
+https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda#0f03292cc56bf91a077a134ea8747118
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8
+https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0
+https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a
+https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b
+https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda#a587892d3c13b6621a6091be690dbca2
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda#0b367fad34931cb79e0d6b7e5c06bb1c
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-h4852527_7.conda#f627678cf829bd70bccf141a19c3ad3e
https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc
-https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda#57d7dc60e9325e3de37ff8dffd18e814
-https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe
-https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda#6c566a46baae794daf34775d41eb180a
-https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
-https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
+https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8
+https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9
https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3
-https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.0-hde9e2c9_0.conda#18aa975d2094c34aef978060ae7da7d8
-https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4
-https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc
-https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-h4ab18f5_1.conda#9653f1bf3766164d0e65fa723cabbc54
-https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368
-https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.12.2-hc19f0b3_1.conda#0b86ff6ab7bdce5076269f2bbbf6857d
-https://conda.anaconda.org/conda-forge/linux-64/python-3.12.5-h2ad013b_0_cpython.conda#9c56c4df45f6571b13111d8df2448692
-https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
-https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
-https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
-https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h30efb56_1.conda#45801a89533d3336a365284d93298e36
+https://conda.anaconda.org/conda-forge/linux-64/python-3.12.12-hfe2f287_0_cpython.conda#ceada987beec823b3c702710ee073fba
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h1289d80_4.conda#fd0e7746ed0676f008daacb706ce69e4
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
-https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.5-py312hca68cad_0.conda#6c56579c537feaafdf62d6c3b5424c53
-https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.12-py312hd8ed1ab_0.conda#f929705f660039f25f18e96a11ff17a0
+https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.17-py312h8285ef7_0.conda#14938d17d7a91e2bf132330c7f2f61a2
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
-https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
-https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
-https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.0.3-py312h30efb56_0.conda#a8a85268e01ddee952f4105f3a8122a4
-https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
-https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
-https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
-https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py312h7900ff3_0.conda#320338762418ae59539ae368d4386085
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py312h98912ed_0.conda#6ff0b9582da2d4a74a1f9ae1f9ce2af6
-https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
-https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
-https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.2.4-py312h1289d80_1.conda#e5e4c495ffa157da0c9a0457736f18cd
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py312h7900ff3_2.conda#eeaf37c3dc2d1660668bd102c841f783
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_0.conda#f775a43412f7f3d7ed218113ad233869
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.19.5-hf7ee748_0.conda#2cbd9a01ac939d85f137ee44c64fb705
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
-https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
-https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
-https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.8-py312h98912ed_0.conda#3facaca6cc0f7988df3250efccd32da3
-https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
-https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
-https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088
-https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1
-https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba575830ec18d5c51c59f403310e2c7
-https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
-https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.0-py312h4c3975b_0.conda#d99ab14339ac25676f1751b76b26c9b2
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h41a817b_0.conda#1779c9cbd9006415ab7bb9e12747e9d1
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_0.conda#fba10c2007c8b06f77c5a23ce3a635ad
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
-https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.20.0-py312hf008fa9_0.conda#0735929f1a2a89c62b91d07ef5a76645
-https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
-https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
-https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py312h9a8786e_0.conda#fd9c83fde763b494f07acee1404c280e
-https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
-https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
-https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
-https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#68f0738df502a14213624b288c60c9ad
-https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
-https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
-https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
-https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h75354e8_4.conda#03cc8d9838ad9dd0060ab532e81ccb21
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
-https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
-https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
-https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
+https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.27.1-py312h868fb18_1.conda#0e32f9c8ca00c1b926a1b77be6937112
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py312h4c3975b_1.conda#66b988f7f1dc9fcc9541483cb0ab985b
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda#8035e5b54c08429354d5d64027041cad
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py312h1671c18_0.conda#33dee889f41b0ba6dbe5ddbe70ebf263
-https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
-https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
-https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
-https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
-https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-64/jupyter_core-5.7.2-py312h7900ff3_0.conda#eee5a2e3465220ed87196bbb5665f420
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
-https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
-https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
-https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
-https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_0.conda#14861767c6836065ee65b45a40be2ed1
-https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
-https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h35888ee_0.conda#60b9cd087d22272885a6b8366b1d3d43
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda#bd80ba060603cc228d9d81c257093119
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.12-hd8ed1ab_0.conda#b317ae484dd8214de8ee7f0839be3500
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
-https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
-https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-21.2.0-py312h98912ed_4.conda#00536e0a1734dcde9815fe227f32fc5a
-https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
-https://conda.anaconda.org/conda-forge/linux-64/cryptography-43.0.0-py312h8aaac84_0.conda#08214176ce216a0d6a345593ffa1c16c
-https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
-https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.20.1-py312hf008fa9_0.conda#8cc8f335b7e355558854236d86b2bea4
-https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.32-py312h41a817b_0.conda#3ac8df9507553117fd7b9daf79ff1269
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
-https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312h3483029_0.conda#eab52e88c858d87cf5a069f79d10bb50
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
-https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
-https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
-https://conda.anaconda.org/conda-forge/noarch/ipython-8.26.0-pyh707e725_0.conda#f64d3520d5d00321c10f4dabb5b903f3
-https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
-https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
-https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
-https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
-https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
-https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
-https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
-https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
-https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
-https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
-https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8ed1ab_0.conda#c64f9b9f15a79239b864c455cc0a67d4
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
-https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
-https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py312h4c3975b_1.conda#161fbbd6a2f41743c687ef83ba220ce9
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.2-py312hee9fe19_0.conda#3c4a18c7e247b6db8cf5605b1846d511
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-9.6.0-pyhfa0c392_0.conda#ad6641ef96dd7872acbb802fa3fcb8d1
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.41.4-py312h868fb18_0.conda#e0767518fa45df8b484421a405f764c6
+https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.44-py312h4c3975b_0.conda#21edaf3d8f04da6258c30be095012ea7
+https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_0.conda#05d73100768745631ab3de9dc1e08da2
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hfb55c3c_0.conda#3399d43f564c905250c1aea268ebb935
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.12-linux-aarch64.lock b/repo2docker/buildpacks/conda/environment.py-3.12-linux-aarch64.lock
index 37665d94d..c8507741a 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.12-linux-aarch64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.12-linux-aarch64.lock
@@ -1,177 +1,180 @@
# AUTO GENERATED FROM environment.py-3.12.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:03:58 UTC
+# Frozen on 2025-10-16 07:18:29 UTC
# Generated by conda-lock.
# platform: linux-aarch64
# input_hash: e263b45a59d809c19e20cb9963be2bc27bee0b7135e9e5d7b43ae4e6ed990812
@EXPLICIT
-https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.7.4-hcefe29a_0.conda#c4c784a1336d72fff54f6b207f3dd75f
-https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.40-h9fc2d93_7.conda#1b0feef706f4d03eff0b76626ead64fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.1.0-he277a41_1.conda#59d463d51eda114031e52667843f9665
-https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda#62b20f305498284a07dc6c45fd0e5c87
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.44-h9df1782_2.conda#c82b1aeb48ef8d5432cbc592716464ba
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-he277a41_7.conda#34cef4753287c36441f907d5fdd78d42
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2#6168d71addc746e8f2b8d57dfd2edcea
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.1.0-he277a41_1.conda#2cb475709e327bb76f74645784582e6a
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.1.0-he9431aa_1.conda#842a1a0cf6f995091734a723e5d291ef
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.1.0-h3f4de04_1.conda#6c2afef2109372440a90c566bcb6391c
-https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda#56398c28220513b9ea13d7b450acfb20
-https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2#1f24853e59c68892452ef94ddd8afd4b
-https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.2-h2f0025b_0.conda#1b9f46b804a2c3c5d7fd6a80b77c35f9
-https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2#dddd85f4d52121fab0a8b099c5e06501
-https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda#c14f32510f694e3185704d89967ec422
-https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.18-hb9de7d4_1.tar.bz2#d09ab3c60eebb6f14eb4d07e172775cc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.1.0-hf1166c9_1.conda#51f54efdd1d2ed5d7e9c67381b75fdb1
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda#000e30b09db0b7c775b21695dff30969
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.48.0-h31becfc_0.conda#97f754b22f63a943345bd807e1d51e01
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-he277a41_7.conda#afa05d91f8d57dd30985827a09c21464
+https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_8.conda#2921ac0b541bf37c69e66bd6d9a43bca
+https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h86ecc28_0.conda#e7df0aab10b9cbb73ab2a467ebfaf8c7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.1-hfae3067_0.conda#f75d19f3755461db2eb69401f5514f4c
+https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.6-he21f813_1.conda#15a131f30cae36e9a655ca81fee9a285
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_7.conda#a5ce1f0a32f02c75c11580c5b2f9258a
+https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_2.conda#7d362346a479256857ab338588190da0
+https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h86ecc28_1.conda#d5d58b2dc3e57073fe22303f5fed4db7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-h3f4de04_7.conda#6a2f0ee17851251a85fbebafbe707d2d
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.2-h3e4203c_0.conda#3a68e44fdf2a2811672520fdd62996bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.51.0-he30d5cf_1.conda#8e62bf5af966325ee416f19c6f14ffa3
+https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda#08aad7cbe9f5a6b460d0976076b6ae64
+https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda#182afabe009dc78d8b73100255ee6868
+https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda#9303e8887afe539f78517951ce25cd13
+https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-h80f16a2_3.conda#032d8030e4a24fe1f72c74423a46fb88
+https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20250104-pl5321h976ea20_0.conda#fb640d776fc92b682a14e001980825b1
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda#2e4a8f23bebdcb85ca8e5a0fbe75666a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.50.4-h022381a_0.conda#0ad1b73a3df7e3376c14efe6dabe6987
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-15.2.0-hf1166c9_7.conda#9e5deec886ad32f3c6791b3b75c78681
https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda#b4df5d7d4b63579d081fd3a4cf99740e
-https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h68df207_1.conda#b13fb82f88902e34dd0638cd7d378c21
-https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda#91d49c85cacd92caa40cf375ef72a25d
-https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.3.1-h86ecc28_3.conda#7f591390401ad65781372240424ab7fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/xz-5.2.6-h9cdd2b7_0.tar.bz2#83baad393a31d59c20b63ba4da6592df
-https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2#b853307650cb226731f653aa623936a4
+https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda#c0f08fc2737967edde1a272d4bf41ed9
+https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h5688188_102.conda#2562c9bfd1de3f9c590f0fe53858d85c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h86ecc28_2.conda#bc230abb5d21b63ff4799b0e75204783
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-hbcf94c1_2.conda#5be90c5a3e4b43c53e38f50a85e11527
https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda#268203e8b983fddb6412b36f2024e75c
-https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#29371161d77933a54fccf1bb66b96529
-https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.46.0-hf51ef55_0.conda#a8ae63fd6fb7d007f74ef3df95e5edf3
-https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda#105eb1e16bf83bfb2eb380a48032b655
-https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda#f75105e0585851f818e0009dd1dde4dc
-https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h68df207_1.conda#6031f9e32654fbdb9fdba406ab980517
-https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda#be8d5f8cf21aed237b8b182ea86b3dd6
https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda#29c10432a2ca1472b53f299ffb2ffa37
-https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.12.2-h9de0dd0_1.conda#ea2fbe77db884c344c72d10c895266d3
-https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.5-hb188aa9_0_cpython.conda#29d0b64c0b5e812fe96a42dcb471a7da
-https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
-https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
-https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
-https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h2aa54b4_1.conda#7253fd6feb797007a3d290bbcfd23a84
+https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.12-hcfbf8c2_0_cpython.conda#08c82423fa3c2153c34f54505a2897c9
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h1ab2c47_4.conda#53b2f879d6c80546213803f756ddedab
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
-https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.5-py312h7f10901_0.conda#064f2725df3ddc1c1295840a9f512836
-https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.12-py312hd8ed1ab_0.conda#f929705f660039f25f18e96a11ff17a0
+https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.17-py312hf55c4e8_0.conda#e66a45ad7233832259fb5f985b6d6963
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
-https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
-https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
-https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.0.3-py312h2aa54b4_0.conda#68e5a7d27bbe600bc098175e25509351
-https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
-https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
-https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
-https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py312h996f985_0.conda#c3d6917a5a167cf0121e79a89545ff70
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
-https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-2.1.5-py312h9ef2f89_0.conda#a00135adde3dfe19b9962c3c767c2129
-https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
-https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
-https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.2.4-py312h1ab2c47_1.conda#9e4aa6b9e34017304d6e44d45e519521
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py312h996f985_2.conda#46075b80a4b1295bffdeb9dd1f43b1c5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.3-py312hd077ced_0.conda#d4e5ac7000bdc398b3cfba57f01e7e63
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.19.5-h6feb2b4_0.conda#5b886141d3ae96e3fcfddbe1a1edd81b
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
-https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
-https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
-https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
-https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-5.9.8-py312hdd3e373_0.conda#1a87f588762a48fb42311b0c40fa1920
-https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
-https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
-https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088
-https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1
-https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba575830ec18d5c51c59f403310e2c7
-https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
-https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-7.1.0-py312hcd1a082_0.conda#56488e632cba932320e5b0833a61126c
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
-https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312h396f95a_0.conda#0d8fb5e4b02256354c754455152d0eb6
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.3-py312ha4530ae_0.conda#428aed4a70236d95492c11da941fe1dc
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
-https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.20.0-py312heb99873_0.conda#4543f8659d413d0a2a7f15fa5ff62bb4
-https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
-https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
-https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
-https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.1-py312h5adff4d_0.conda#8d17acc3a7d9542b53880e50c273268d
-https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
-https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
-https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
-https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#68f0738df502a14213624b288c60c9ad
-https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
-https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
-https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
-https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h28faeed_4.conda#ae8c0108b4d6a5981bce484eeaaf1b55
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
-https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
-https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
-https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
+https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.27.1-py312h75d7d99_1.conda#b7c5243a280c24a2f97d9b5f2407cf8e
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.5.2-py312hefbd42c_1.conda#d979131a81d6ac66c26e8cc7b41cdf16
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-hefbcea8_9.conda#5676806bba055c901a62f969cb3fbe02
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
-https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.0-py312h75e8286_0.conda#c88525d1b103277eb9b47a97842c7077
-https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
-https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
-https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
-https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
-https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-aarch64/jupyter_core-5.7.2-py312h996f985_0.conda#a7ef8076152a1490e2e8a519e81660d7
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
-https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
-https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
-https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
-https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_0.conda#1263d3ca90dcf80d39a9636b8db3ade8
-https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
-https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-2.0.0-py312h2fc7fbd_0.conda#b1e3867af2555753272e3d53798147bb
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda#bd80ba060603cc228d9d81c257093119
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.12-hd8ed1ab_0.conda#b317ae484dd8214de8ee7f0839be3500
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
-https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
-https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-21.2.0-py312hdd3e373_4.conda#7aebbb1531055d298dc01002e1286f3a
-https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
-https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-43.0.0-py312h608731a_0.conda#2c244a4e011232667765a0ce476dcd66
-https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
-https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.20.1-py312h3dd116e_0.conda#db340c73d8a147ae99fb521634e8b638
-https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.32-py312h5adff4d_0.conda#5722a5f2275bd0b6a92ec486afa1afe6
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
-https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312h9fc3309_0.conda#1903935398241d9772c260a4d0ee5bc3
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
-https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
-https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
-https://conda.anaconda.org/conda-forge/noarch/ipython-8.26.0-pyh707e725_0.conda#f64d3520d5d00321c10f4dabb5b903f3
-https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
-https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
-https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
-https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
-https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
-https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
-https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
-https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
-https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
-https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
-https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8ed1ab_0.conda#c64f9b9f15a79239b864c455cc0a67d4
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
-https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
-https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-25.1.0-py312hcd1a082_1.conda#320cb23e259bf7b6cd375d570f0348ac
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-46.0.2-py312h4cd2d69_0.conda#4c5929a449a1cf0c7751e8ce72a5bb0b
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-9.6.0-pyhfa0c392_0.conda#ad6641ef96dd7872acbb802fa3fcb8d1
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.41.4-py312h5eb8f6c_0.conda#56be5302d17deaf7776c8f112d755359
+https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.44-py312hefbd42c_0.conda#94976c89cb4a8812e27e404d11db9461
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.25.0-py312hd41f8a7_0.conda#74aa2fbcdcbb153dc324cd09e437b63e
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-27.1.0-py312h4552c38_0.conda#c9aadf2edd39b56ad34dc5f775626d5b
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.12.yml b/repo2docker/buildpacks/conda/environment.py-3.12.yml
index 7d6eecaba..815f7d85b 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.12.yml
+++ b/repo2docker/buildpacks/conda/environment.py-3.12.yml
@@ -1,5 +1,5 @@
# AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY
-# Generated on 2024-08-29 07:03:48 UTC
+# Generated on 2025-10-16 07:18:17 UTC
channels:
- conda-forge
dependencies:
diff --git a/repo2docker/buildpacks/conda/environment.py-3.13-linux-64.lock b/repo2docker/buildpacks/conda/environment.py-3.13-linux-64.lock
new file mode 100644
index 000000000..930ed014d
--- /dev/null
+++ b/repo2docker/buildpacks/conda/environment.py-3.13-linux-64.lock
@@ -0,0 +1,179 @@
+# AUTO GENERATED FROM environment.py-3.13.yml, DO NOT MANUALLY MODIFY
+# Frozen on 2025-10-16 07:18:48 UTC
+# Generated by conda-lock.
+# platform: linux-64
+# input_hash: ee50f459b6ce40535eaf2ee74bdcb5ab6d90d2523f9b369c7e2a3b938901c080
+@EXPLICIT
+https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda#94305520c52a4aa3f6c2b1ff6008d9f8
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe
+https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-h767d61c_7.conda#c0374badb3a5d4b1372db28d19462c53
+https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc
+https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3
+https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6
+https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363
+https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc
+https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda#c7e925f37e3b40d893459e625f6a53f1
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda#5b767048b1b3ee9a954b06f4084f93dc
+https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1
+https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda#0f03292cc56bf91a077a134ea8747118
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8
+https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0
+https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a
+https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b
+https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda#a587892d3c13b6621a6091be690dbca2
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda#0b367fad34931cb79e0d6b7e5c06bb1c
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-h4852527_7.conda#f627678cf829bd70bccf141a19c3ad3e
+https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8
+https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9
+https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3
+https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368
+https://conda.anaconda.org/conda-forge/linux-64/python-3.13.8-h2b335a9_101_cp313.conda#ae8cf86b9140c7b6a6593a582a8eab8a
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py313h7033f15_4.conda#bc8624c405856b1d047dd0a81829b08c
+https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.8-py313hd8ed1ab_101.conda#d60198f8c2b5cf84e195791f540935ba
+https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.17-py313h5d5ffb9_0.conda#be318961d544421f4c8d8a91bff4f118
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
+https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.2.4-py313h7033f15_1.conda#54e4dec31235bbc794d091af9afcd845
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py313h78bf25f_2.conda#c1375f38e5f3ee38a9ee0e405a601c35
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py313h3dea7bd_0.conda#c14389156310b8ed3520d84f854be1ee
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.19.5-hf7ee748_0.conda#2cbd9a01ac939d85f137ee44c64fb705
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
+https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh145f28c_0.conda#e7ab34d5a93e0819b62563c78635d937
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.0-py313h07c4f96_0.conda#f25cdf145885936fe458452d73d991dc
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
+https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py313h3dea7bd_0.conda#4794ea0adaebd9f844414e594b142cb2
+https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
+https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.27.1-py313h843e2db_1.conda#06c117e49934b564ef9ff6e61f279301
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py313h07c4f96_1.conda#45821154b9cb2fb63c2b354c76086954
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda#8035e5b54c08429354d5d64027041cad
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
+https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
+https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py313hf01b4d8_0.conda#062317cc1cd26fbf6454e86ddd3622c4
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda#bd80ba060603cc228d9d81c257093119
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.8-h4df99d1_101.conda#2af36fadd7f3804bc6d414655c282fa5
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
+https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py313h07c4f96_1.conda#0d56c6a245ef9528644a33947a4dca4f
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.2-py313hafb0bba_0.conda#5473e4a4a4fd596593e65357ab6cd188
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-9.6.0-pyhfa0c392_0.conda#ad6641ef96dd7872acbb802fa3fcb8d1
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.41.4-py313h843e2db_0.conda#d42ccdecefbe670d2a50ee3ce784166b
+https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.44-py313h07c4f96_0.conda#57247c3902e6466acad8dad9963bb146
+https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py313h54dd161_0.conda#1fe43bd1fc86e22ad3eb0edec637f8a2
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hfb55c3c_0.conda#3399d43f564c905250c1aea268ebb935
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.13-linux-aarch64.lock b/repo2docker/buildpacks/conda/environment.py-3.13-linux-aarch64.lock
new file mode 100644
index 000000000..a304b5984
--- /dev/null
+++ b/repo2docker/buildpacks/conda/environment.py-3.13-linux-aarch64.lock
@@ -0,0 +1,178 @@
+# AUTO GENERATED FROM environment.py-3.13.yml, DO NOT MANUALLY MODIFY
+# Frozen on 2025-10-16 07:19:00 UTC
+# Generated by conda-lock.
+# platform: linux-aarch64
+# input_hash: e406ec9c02cfaad96f1da2e1a38a4d5e71dc18ba2e5e0d7a126fa0592e7f7f55
+@EXPLICIT
+https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.44-h9df1782_2.conda#c82b1aeb48ef8d5432cbc592716464ba
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-he277a41_7.conda#34cef4753287c36441f907d5fdd78d42
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda#94305520c52a4aa3f6c2b1ff6008d9f8
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
+https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2#6168d71addc746e8f2b8d57dfd2edcea
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-he277a41_7.conda#afa05d91f8d57dd30985827a09c21464
+https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_8.conda#2921ac0b541bf37c69e66bd6d9a43bca
+https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h86ecc28_0.conda#e7df0aab10b9cbb73ab2a467ebfaf8c7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.1-hfae3067_0.conda#f75d19f3755461db2eb69401f5514f4c
+https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.6-he21f813_1.conda#15a131f30cae36e9a655ca81fee9a285
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_7.conda#a5ce1f0a32f02c75c11580c5b2f9258a
+https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_2.conda#7d362346a479256857ab338588190da0
+https://conda.anaconda.org/conda-forge/linux-aarch64/libmpdec-4.0.0-h86ecc28_0.conda#78cfed3f76d6f3f279736789d319af76
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-h3f4de04_7.conda#6a2f0ee17851251a85fbebafbe707d2d
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.2-h3e4203c_0.conda#3a68e44fdf2a2811672520fdd62996bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.51.0-he30d5cf_1.conda#8e62bf5af966325ee416f19c6f14ffa3
+https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda#08aad7cbe9f5a6b460d0976076b6ae64
+https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda#182afabe009dc78d8b73100255ee6868
+https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda#9303e8887afe539f78517951ce25cd13
+https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-h80f16a2_3.conda#032d8030e4a24fe1f72c74423a46fb88
+https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20250104-pl5321h976ea20_0.conda#fb640d776fc92b682a14e001980825b1
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda#2e4a8f23bebdcb85ca8e5a0fbe75666a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.50.4-h022381a_0.conda#0ad1b73a3df7e3376c14efe6dabe6987
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-15.2.0-hf1166c9_7.conda#9e5deec886ad32f3c6791b3b75c78681
+https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda#c0f08fc2737967edde1a272d4bf41ed9
+https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h5688188_102.conda#2562c9bfd1de3f9c590f0fe53858d85c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h86ecc28_2.conda#bc230abb5d21b63ff4799b0e75204783
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-hbcf94c1_2.conda#5be90c5a3e4b43c53e38f50a85e11527
+https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda#268203e8b983fddb6412b36f2024e75c
+https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda#29c10432a2ca1472b53f299ffb2ffa37
+https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.13.8-h23354eb_101_cp313.conda#6e3659f24f886b4148dfee0fe670b307
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py313he352c24_4.conda#d5993a664b52718233b0d7d8c72f71aa
+https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.8-py313hd8ed1ab_101.conda#d60198f8c2b5cf84e195791f540935ba
+https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.17-py313h59403f9_0.conda#57d79b7e7af2bf4b268f7e983b2c3d6c
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
+https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.2.4-py313he352c24_1.conda#170153d3987689b4f25ff40699c0cfcf
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py313hd81a959_2.conda#d4ef48fa823e725c9cd29e85214f09d0
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.3-py313hfa222a2_0.conda#59fc93a010d6e8a08a4fa32424d86a82
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.19.5-h6feb2b4_0.conda#5b886141d3ae96e3fcfddbe1a1edd81b
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
+https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh145f28c_0.conda#e7ab34d5a93e0819b62563c78635d937
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-7.1.0-py313h6194ac5_0.conda#2561a70b42eb4837cb2175260dac6224
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
+https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.3-py313hd3a54cf_0.conda#cd0891668088a005cb45b344d84a3955
+https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
+https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.27.1-py313h8f1d341_1.conda#fe4c2012bb7dcd65bb93de1d6874a3d9
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.5.2-py313he149459_1.conda#f896700632061a8dafa2f80bb178909c
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-hefbcea8_9.conda#5676806bba055c901a62f969cb3fbe02
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
+https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
+https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-2.0.0-py313h0f41b0d_0.conda#fdf143708b2f5ffadcd8eefb2a45f021
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda#bd80ba060603cc228d9d81c257093119
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.8-h4df99d1_101.conda#2af36fadd7f3804bc6d414655c282fa5
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
+https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-25.1.0-py313h6194ac5_1.conda#62d8cb2d243865ea058634dfcda33db6
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-46.0.2-py313hbc6eba2_0.conda#d778fd3fa7204c76043ef94db686c239
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-9.6.0-pyhfa0c392_0.conda#ad6641ef96dd7872acbb802fa3fcb8d1
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.41.4-py313h5e7b836_0.conda#b57f95d059ea5468dd99a5c08a20f63c
+https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.44-py313he149459_0.conda#36dc21f9851a86b88563752507fbac11
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.25.0-py313h62ef0ea_0.conda#fcde9124e91d6525cc8357273adc06f4
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-27.1.0-py312h4552c38_0.conda#c9aadf2edd39b56ad34dc5f775626d5b
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.13.yml b/repo2docker/buildpacks/conda/environment.py-3.13.yml
new file mode 100644
index 000000000..5e0930f8b
--- /dev/null
+++ b/repo2docker/buildpacks/conda/environment.py-3.13.yml
@@ -0,0 +1,15 @@
+# AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY
+# Generated on 2025-10-16 07:18:48 UTC
+channels:
+- conda-forge
+dependencies:
+- python=3.13.*
+- nodejs=20
+- pip
+- ipywidgets==8.* # https://github.com/jupyter-widgets/ipywidgets
+- jupyter-offlinenotebook==0.3.* # https://github.com/manics/jupyter-offlinenotebook
+- jupyter-resource-usage==1.* # https://github.com/jupyter-server/jupyter-resource-usage
+- jupyter_server==2.* # https://github.com/jupyter-server/jupyter_server
+- jupyterhub-singleuser==5.* # https://github.com/jupyterhub/jupyterhub
+- jupyterlab==4.* # https://github.com/jupyterlab/jupyterlab
+- notebook==7.* # https://github.com/jupyter/notebook
diff --git a/repo2docker/buildpacks/conda/environment.py-3.14-linux-64.lock b/repo2docker/buildpacks/conda/environment.py-3.14-linux-64.lock
new file mode 100644
index 000000000..2f5c4d135
--- /dev/null
+++ b/repo2docker/buildpacks/conda/environment.py-3.14-linux-64.lock
@@ -0,0 +1,179 @@
+# AUTO GENERATED FROM environment.py-3.14.yml, DO NOT MANUALLY MODIFY
+# Frozen on 2025-10-16 07:19:22 UTC
+# Generated by conda-lock.
+# platform: linux-64
+# input_hash: 1c090db128135ee50a4880ad7ceebffccc9d1f840ba314aa2893d9f7bcd5fe6f
+@EXPLICIT
+https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda#0539938c55b6b1a59b560e843ad864a4
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe
+https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-h767d61c_7.conda#c0374badb3a5d4b1372db28d19462c53
+https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc
+https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3
+https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6
+https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363
+https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc
+https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda#c7e925f37e3b40d893459e625f6a53f1
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda#5b767048b1b3ee9a954b06f4084f93dc
+https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1
+https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda#0f03292cc56bf91a077a134ea8747118
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8
+https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0
+https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a
+https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b
+https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda#a587892d3c13b6621a6091be690dbca2
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda#0b367fad34931cb79e0d6b7e5c06bb1c
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-h4852527_7.conda#f627678cf829bd70bccf141a19c3ad3e
+https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8
+https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9
+https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3
+https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368
+https://conda.anaconda.org/conda-forge/linux-64/python-3.14.0-h5989046_101_cp314.conda#b2ad21488149ec2c4d83640619de2430
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py314ha160325_4.conda#a5529cb388bf7c6c9ae3d9d1ae54be56
+https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.0-py314hd8ed1ab_101.conda#d1caec8d6086bcd2c30dfdd0af222d2d
+https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.17-py314h8c728da_0.conda#c9367a2d02bab08fa4eb887349f0ca4a
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
+https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.2.4-py314ha160325_1.conda#99623248a2c58194abe517fd26cc4aa4
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py314hdafbbf9_2.conda#719919b86b506f0307c5c93cbe23917e
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/noarch/markupsafe-3.0.3-pyh7db6752_0.conda#fab1be106a50e20f10fe5228fd1d1651
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.19.5-hf7ee748_0.conda#2cbd9a01ac939d85f137ee44c64fb705
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
+https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh145f28c_0.conda#e7ab34d5a93e0819b62563c78635d937
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.0-py314h5bd0f2a_0.conda#3bba9ff64ab2030c4c28c0d225864196
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
+https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/noarch/pyyaml-6.0.3-pyh7db6752_0.conda#b12f41c0d7fb5ab81709fcc86579688f
+https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
+https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.27.1-py314h68536ef_1.conda#b1397d3afad6dbd7c33a1993699a735b
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py314h5bd0f2a_1.conda#aa5552df9b306a67e7d2cd3333fb2ea5
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda#8035e5b54c08429354d5d64027041cad
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
+https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
+https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py314ha6a4811_0.conda#f572c769fb8bfa15708594c6f7f354e0
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda#bd80ba060603cc228d9d81c257093119
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.0-h4df99d1_101.conda#ff70037e37dbf735066dec79c1e93c76
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
+https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py314h5bd0f2a_1.conda#632897d781dc39527007493c6cfe9d21
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.2-py314hb613cbf_0.conda#19ed3f93a65fe3f057594cb3195e956f
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-9.6.0-pyhfa0c392_0.conda#ad6641ef96dd7872acbb802fa3fcb8d1
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.41.4-py314h2e6c369_0.conda#ae0e353f3f4f4c556526d4687545d9d8
+https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.44-py314h5bd0f2a_0.conda#82327fd3a951d713637da7a4d98a2866
+https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314h31f8a6b_0.conda#68bd13651618354987763f746ee1fadc
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hfb55c3c_0.conda#3399d43f564c905250c1aea268ebb935
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.14-linux-aarch64.lock b/repo2docker/buildpacks/conda/environment.py-3.14-linux-aarch64.lock
new file mode 100644
index 000000000..b71a8925b
--- /dev/null
+++ b/repo2docker/buildpacks/conda/environment.py-3.14-linux-aarch64.lock
@@ -0,0 +1,178 @@
+# AUTO GENERATED FROM environment.py-3.14.yml, DO NOT MANUALLY MODIFY
+# Frozen on 2025-10-16 07:19:33 UTC
+# Generated by conda-lock.
+# platform: linux-aarch64
+# input_hash: d0b876e5fc2ac8930813bd91d3f1882993042b8ded7b205a5210eb88213ee95a
+@EXPLICIT
+https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.44-h9df1782_2.conda#c82b1aeb48ef8d5432cbc592716464ba
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-he277a41_7.conda#34cef4753287c36441f907d5fdd78d42
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda#0539938c55b6b1a59b560e843ad864a4
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
+https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2#6168d71addc746e8f2b8d57dfd2edcea
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-he277a41_7.conda#afa05d91f8d57dd30985827a09c21464
+https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_8.conda#2921ac0b541bf37c69e66bd6d9a43bca
+https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h86ecc28_0.conda#e7df0aab10b9cbb73ab2a467ebfaf8c7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.1-hfae3067_0.conda#f75d19f3755461db2eb69401f5514f4c
+https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.6-he21f813_1.conda#15a131f30cae36e9a655ca81fee9a285
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_7.conda#a5ce1f0a32f02c75c11580c5b2f9258a
+https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_2.conda#7d362346a479256857ab338588190da0
+https://conda.anaconda.org/conda-forge/linux-aarch64/libmpdec-4.0.0-h86ecc28_0.conda#78cfed3f76d6f3f279736789d319af76
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-h3f4de04_7.conda#6a2f0ee17851251a85fbebafbe707d2d
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.2-h3e4203c_0.conda#3a68e44fdf2a2811672520fdd62996bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.51.0-he30d5cf_1.conda#8e62bf5af966325ee416f19c6f14ffa3
+https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda#08aad7cbe9f5a6b460d0976076b6ae64
+https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda#182afabe009dc78d8b73100255ee6868
+https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda#9303e8887afe539f78517951ce25cd13
+https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-h80f16a2_3.conda#032d8030e4a24fe1f72c74423a46fb88
+https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20250104-pl5321h976ea20_0.conda#fb640d776fc92b682a14e001980825b1
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda#2e4a8f23bebdcb85ca8e5a0fbe75666a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.50.4-h022381a_0.conda#0ad1b73a3df7e3376c14efe6dabe6987
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-15.2.0-hf1166c9_7.conda#9e5deec886ad32f3c6791b3b75c78681
+https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda#c0f08fc2737967edde1a272d4bf41ed9
+https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h5688188_102.conda#2562c9bfd1de3f9c590f0fe53858d85c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h86ecc28_2.conda#bc230abb5d21b63ff4799b0e75204783
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-hbcf94c1_2.conda#5be90c5a3e4b43c53e38f50a85e11527
+https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda#268203e8b983fddb6412b36f2024e75c
+https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda#29c10432a2ca1472b53f299ffb2ffa37
+https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.14.0-ha9132a3_101_cp314.conda#9c53b4419016565f7a004dfa04a709f9
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py314h02b5315_4.conda#f9701a01df90cefee62d0ac602107bfd
+https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.0-py314hd8ed1ab_101.conda#d1caec8d6086bcd2c30dfdd0af222d2d
+https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.17-py314h3642cf7_0.conda#c515777bac2a8d4f6d9c55dc52aef24d
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
+https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda#ff9efb7f7469aed3c4a8106ffa29593c
+https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.2.4-py314h02b5315_1.conda#6df10bfe6137db723c18f1d9ac6208b1
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py314h28a4750_2.conda#149accb2f37040efbb393a0ed7b1b32b
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.3.0-pyhd8ed1ab_0.conda#c9ee16acbcea5cc91d9f3eb1d8f903bd
+https://conda.anaconda.org/conda-forge/noarch/markupsafe-3.0.3-pyh7db6752_0.conda#fab1be106a50e20f10fe5228fd1d1651
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.19.5-h6feb2b4_0.conda#5b886141d3ae96e3fcfddbe1a1edd81b
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
+https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.5-pyhcf101f3_0.conda#a110716cdb11cf51482ff4000dc253d7
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh145f28c_0.conda#e7ab34d5a93e0819b62563c78635d937
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda#a1e91db2d17fd258c64921cb38e6745a
+https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-7.1.0-py314h51f160d_0.conda#dcaddc080ba0140f801b22be85df88ee
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
+https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/noarch/pyyaml-6.0.3-pyh7db6752_0.conda#b12f41c0d7fb5ab81709fcc86579688f
+https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
+https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.27.1-py314h7a73458_1.conda#aa31149bfbd82a68c2a3271dc347d782
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#18c019ccf43769d211f2cf78e9ad46c2
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53
+https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.5.2-py314hafb4487_1.conda#daa0018e557e1e2e7587e3ec4a048b8c
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20251008-pyhd8ed1ab_0.conda#6835489fc689d7ca90cb7bffb01eaac1
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda#7e1e5ff31239f9cd5855714df8a3783d
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda#2f1ed718fcd829c184a6d4f0f2e07409
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-hefbcea8_9.conda#5676806bba055c901a62f969cb3fbe02
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
+https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
+https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-2.0.0-py314h7fea217_0.conda#f28e78fdcb6b2e9ef869372d78fdf6dd
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda#bd80ba060603cc228d9d81c257093119
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.4-pyhcf101f3_0.conda#f5a4d548d1d3bdd517260409fc21e205
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda#edb16f14d920fb3faf17f5ce582942d6
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.0-h4df99d1_101.conda#ff70037e37dbf735066dec79c1e93c76
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
+https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda#399701494e731ce73fdd86c185a3d1b4
+https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda#814472b61da9792fae28156cb9ee54f5
+https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-25.1.0-py314h51f160d_1.conda#19257b153ca4a3309d17788fbed6fc68
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-46.0.2-py314h606d956_0.conda#aa72d21b96dddc1c70ee1bc0d8f69d7e
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
+https://conda.anaconda.org/conda-forge/noarch/ipython-9.6.0-pyhfa0c392_0.conda#ad6641ef96dd7872acbb802fa3fcb8d1
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.41.4-py314h451b6cc_0.conda#628ba641f070f008f56a14bdccd1139b
+https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.44-py314hafb4487_0.conda#d5e9ed49f0cfc37f9f628125eef6cdae
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.25.0-py314hc032435_0.conda#0a3cf4cedfa2f5ce88b1f4025a2df4d6
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.17.0-pyhd8ed1ab_0.conda#2ef5bb4a16331cfac0f1c92d4bca115e
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.2-pyh3cfb1c2_0.conda#fc3a3515b4e71b22b635c4ae34e2f3ea
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-27.1.0-py312h4552c38_0.conda#c9aadf2edd39b56ad34dc5f775626d5b
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.0.1-pyha191276_0.conda#637e206802904ecc10a558262631f132
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.4.0-pyhc90fa1f_0.conda#593072c956378468fbadead64d403b17
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda#62b7c96c6cd77f8173cc5cada6a9acaa
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.2-pyh9692d8f_0.conda#f1ba04028264a73d571eecbbdb26d589
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.9-pyhd8ed1ab_0.conda#6fcc0ffe96c13a864ec6a1defc830526
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.4.0-hd843c07_0.conda#4fe3d3bb1cf147634aea983a483c773a
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.7-pyhd8ed1ab_0.conda#8265e246510553a4bcec1af4378d3c96
diff --git a/repo2docker/buildpacks/conda/environment.py-3.14.yml b/repo2docker/buildpacks/conda/environment.py-3.14.yml
new file mode 100644
index 000000000..f449ac61a
--- /dev/null
+++ b/repo2docker/buildpacks/conda/environment.py-3.14.yml
@@ -0,0 +1,15 @@
+# AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY
+# Generated on 2025-10-16 07:19:22 UTC
+channels:
+- conda-forge
+dependencies:
+- python=3.14.*
+- nodejs=20
+- pip
+- ipywidgets==8.* # https://github.com/jupyter-widgets/ipywidgets
+- jupyter-offlinenotebook==0.3.* # https://github.com/manics/jupyter-offlinenotebook
+- jupyter-resource-usage==1.* # https://github.com/jupyter-server/jupyter-resource-usage
+- jupyter_server==2.* # https://github.com/jupyter-server/jupyter_server
+- jupyterhub-singleuser==5.* # https://github.com/jupyterhub/jupyterhub
+- jupyterlab==4.* # https://github.com/jupyterlab/jupyterlab
+- notebook==7.* # https://github.com/jupyter/notebook
diff --git a/repo2docker/buildpacks/conda/environment.py-3.8-linux-64.lock b/repo2docker/buildpacks/conda/environment.py-3.8-linux-64.lock
index b0ba51e0d..711d55663 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.8-linux-64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.8-linux-64.lock
@@ -1,74 +1,80 @@
# AUTO GENERATED FROM environment.py-3.8.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:01:59 UTC
+# Frozen on 2025-10-16 07:16:06 UTC
# Generated by conda-lock.
# platform: linux-64
# input_hash: f8b3a4482f740b178955326cfa6805eb6b64fb0f84fab45fc27a59d87113d326
@EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
-https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.7.4-hbcca054_0.conda#23ab7665c5f63cfb9f1f6195256daac6
-https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda#b80f2f396ca2c28b8c14c437a4ed1e74
-https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.8-5_cp38.conda#7caf714d16205dca32687a95ae71a472
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda#23c255b008c4f2ae008f81edcabaca89
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.8-8_cp38.conda#4f7b6e3de4f15cc44e0f93b39f07205d
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066
-https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553
-https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
-https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-h767d61c_7.conda#c0374badb3a5d4b1372db28d19462c53
+https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc
+https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3
+https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda#35f29eec58405aaf55e01cb470d8c26a
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363
+https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc
+https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda#5b767048b1b3ee9a954b06f4084f93dc
+https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1
+https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda#0f03292cc56bf91a077a134ea8747118
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8
+https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0
+https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a
+https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b
+https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.1-hb9d3cd8_2.conda#f61edadbb301530bd65a32646bd81552
https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.18-h36c2ea0_1.tar.bz2#c3788462a6fbddafdb413a9f9053e58d
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e
-https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b
-https://conda.anaconda.org/conda-forge/linux-64/libuv-1.48.0-hd590300_0.conda#7e8b914b1062dd4386e3de4d82a3ead6
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda#0b367fad34931cb79e0d6b7e5c06bb1c
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-h4852527_7.conda#f627678cf829bd70bccf141a19c3ad3e
https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc
-https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda#57d7dc60e9325e3de37ff8dffd18e814
-https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe
-https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda#6c566a46baae794daf34775d41eb180a
-https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
-https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
+https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8
+https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.1-hbcc6ac9_2.conda#bf627c16aa26231720af037a2709ab09
+https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.1-hb9d3cd8_2.conda#1bad2995c8f1c8075c6c331bf96e46fb
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8
https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3
-https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.0-hde9e2c9_0.conda#18aa975d2094c34aef978060ae7da7d8
-https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4
-https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc
-https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-h4ab18f5_1.conda#9653f1bf3766164d0e65fa723cabbc54
-https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368
-https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.12.2-hc19f0b3_1.conda#0b86ff6ab7bdce5076269f2bbbf6857d
-https://conda.anaconda.org/conda-forge/linux-64/python-3.8.19-hd12c33a_0_cpython.conda#53aabe8cf596487ec6f1ce319c93a741
+https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.1-hbcc6ac9_2.conda#68eae977d7d1196d32b636a026dc015d
+https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45
+https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.19.5-hf7ee748_0.conda#2cbd9a01ac939d85f137ee44c64fb705
+https://conda.anaconda.org/conda-forge/linux-64/python-3.8.20-h4a871b0_2_cpython.conda#05ffff2f44ad60b94ecb53d029c6bdf7
+https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h75354e8_4.conda#03cc8d9838ad9dd0060ab532e81ccb21
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_0.conda#454950e9fa3a0c59880de0ac7a6bc8c4
https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
https://conda.anaconda.org/conda-forge/noarch/backcall-0.2.0-pyh9f0ad1d_0.tar.bz2#6006a6d08a3fa99268a2681c7fb55213
https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py38h17151c0_1.conda#7a5a699c8992fc51ef25e980f4502c2a
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
+https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda#12f7d00853807b0531775e9be891cb11
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_0.conda#a374efa97290b8799046df7c5ca17164
https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.5-py38h6d02427_0.conda#41aaa2fe5a415fbf79ce09b0e2746320
https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
+https://conda.anaconda.org/conda-forge/noarch/executing-2.1.0-pyhd8ed1ab_0.conda#d0441db20c827c11721889a241df1220
https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.0.3-py38h17151c0_0.conda#96428abcd2b3b8e945f8b26e6d9f82f8
https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
+https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_0.conda#7ba2ede0e7c795ff95088daf0dc59753
https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py38h578d9bd_0.conda#35f7346da8d31ccef2c97db678b2c390
https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py38h01eb140_0.conda#aeeb09febb02542e020c3ba7084ead01
https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_0.conda#fd8f2b18b65bbf62e8f653100690c8d2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.0-pyhd8ed1ab_0.conda#07e9550ddff45150bfc7da146268e165
https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.8-py38h01eb140_0.conda#571da172be3f0f94f49c069e5b775f05
https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
@@ -78,19 +84,20 @@ https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba
https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
+https://conda.anaconda.org/conda-forge/noarch/pytz-2024.2-pyhd8ed1ab_0.conda#260009d03c9d5c0f111904d851f053dc
https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py38h2019614_0.conda#47a3b59acbb1626316500e178e25b944
+https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py38h6c80b9a_0.conda#d16a4460421bc385dfb20dba5314bd29
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.20.0-py38h4005ec7_0.conda#e18d1fb2fddcc693a91d7dc326916b2b
https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
+https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda#2ce9825396daf72baabaade36cee16da
https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.6-pyhd8ed1ab_0.conda#cbee5eb469aa483fee70f6a1b4e955dd
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda#e977934e00b355ff55ed154904044727
https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py38hfb59056_0.conda#ca6a0889b55d598c8fe154005cee15ef
https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20241003-pyhff2d567_0.conda#3d326f8a2aa2d14d51d8c513426b5def
https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
@@ -98,14 +105,12 @@ https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#
https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_0.conda#bdb2f437ce62fd2f1fef9119a37a12d9
https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h75354e8_4.conda#03cc8d9838ad9dd0060ab532e81ccb21
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_0.conda#fee389bf8a4843bd7a2248ce11b7f188
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.5.0-pyhd8ed1ab_0.conda#c720e5c85c349c5cbc99d9dfc4b200f8
https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
+https://conda.anaconda.org/conda-forge/noarch/babel-2.16.0-pyhd8ed1ab_0.conda#6d4e9ecca8d88977147e109fc7053184
https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
@@ -113,60 +118,58 @@ https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py38heb5c249_0.conda
https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda#54198435fce4d64d8a89af22573012a8
+https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_0.conda#c808991d29b9838fb4d96ce8267ec9ec
https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-64/jupyter_core-5.7.2-py38h578d9bd_0.conda#196ff32a507055c7c6457f83262d2803
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
+https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_0.conda#4c05134c48b6a74f33bbb9938e4a115e
https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py38h6c80b9a_0.conda#d16a4460421bc385dfb20dba5314bd29
https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-21.2.0-py38h01eb140_4.conda#234c813c9d745d4bfc6a2a40514d88a6
https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
https://conda.anaconda.org/conda-forge/linux-64/cryptography-43.0.0-py38hf793753_0.conda#0ec2678d5f680052842221b17da90f0f
https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda#2ca8e6dbc86525c8b95e3c0ffa26442e
+https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.5.0-hd8ed1ab_1.conda#c70dd0718dbccdcc6d5828de3e71399d
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2024.10.1-pyhd8ed1ab_0.conda#720745920222587ef942acfbc578b584
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_0.conda#a14218cfb29662b4a19ceb04e93e298e
https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.47-hd8ed1ab_0.conda#3e0c82ddcfe27eb4ae77f887cfd9f45b
+https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.48-hd8ed1ab_1.conda#bf730bb1f201e3f5a961c1fb2ffc4f05
https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.20.1-py38h4005ec7_0.conda#7fcbd5ffc4f54bbc3e5e0f87726f8d87
https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.32-py38h2019614_0.conda#d7d8b5b111716b5079b1f72524ef6a86
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py38h62bed22_0.conda#7cdf6cb1dcfbaf868ff42b15189f7e59
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.14.0-pyhd8ed1ab_0.conda#4882d44aa5f5d9de49ba84266cfe56b1
https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.1-pyhd8ed1ab_0.conda#7566bb010833e59fb579ed520b9a7bc5
https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
https://conda.anaconda.org/conda-forge/noarch/ipython-8.12.2-pyh41d4057_0.conda#acebfd89278ecac2a67b60b657e00d5c
https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_0.conda#6b55867f385dd762ed99ea687af32a69
https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_1.conda#a5b1a8065857cc4bd8b7a38d063bb728
https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.2.1-pyh31011fe_0.conda#2d61d811311bedad7bb86b29bd537cd5
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhff2d567_2.conda#0457fdf55c88e52e0e7b63691eafcc48
https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
@@ -174,5 +177,5 @@ https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8
https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.2.1-pyh31011fe_0.conda#26b02c4e1060f33741de65e2983cc51f
https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
diff --git a/repo2docker/buildpacks/conda/environment.py-3.8-linux-aarch64.lock b/repo2docker/buildpacks/conda/environment.py-3.8-linux-aarch64.lock
index 1383994a1..e69a93990 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.8-linux-aarch64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.8-linux-aarch64.lock
@@ -1,73 +1,79 @@
# AUTO GENERATED FROM environment.py-3.8.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:02:09 UTC
+# Frozen on 2025-10-16 07:16:22 UTC
# Generated by conda-lock.
# platform: linux-aarch64
# input_hash: e0b2884c1574bbc561e310e8ca9cd0e290d8a28c115e948c1aae949cd36da52a
@EXPLICIT
-https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.7.4-hcefe29a_0.conda#c4c784a1336d72fff54f6b207f3dd75f
-https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.40-h9fc2d93_7.conda#1b0feef706f4d03eff0b76626ead64fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.1.0-he277a41_1.conda#59d463d51eda114031e52667843f9665
-https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.8-5_cp38.conda#2c2cf003fc370241ed16a73f4ee6d798
+https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.44-h9df1782_2.conda#c82b1aeb48ef8d5432cbc592716464ba
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-he277a41_7.conda#34cef4753287c36441f907d5fdd78d42
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.8-8_cp38.conda#4f7b6e3de4f15cc44e0f93b39f07205d
https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2#6168d71addc746e8f2b8d57dfd2edcea
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.1.0-he277a41_1.conda#2cb475709e327bb76f74645784582e6a
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.1.0-he9431aa_1.conda#842a1a0cf6f995091734a723e5d291ef
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.1.0-h3f4de04_1.conda#6c2afef2109372440a90c566bcb6391c
-https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda#56398c28220513b9ea13d7b450acfb20
-https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2#1f24853e59c68892452ef94ddd8afd4b
-https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2#dddd85f4d52121fab0a8b099c5e06501
-https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda#c14f32510f694e3185704d89967ec422
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-he277a41_7.conda#afa05d91f8d57dd30985827a09c21464
+https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_8.conda#2921ac0b541bf37c69e66bd6d9a43bca
+https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h86ecc28_0.conda#e7df0aab10b9cbb73ab2a467ebfaf8c7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.5.2-hd65408f_0.conda#0c5ad486dcfb188885e3cf8ba209b97b
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_7.conda#a5ce1f0a32f02c75c11580c5b2f9258a
+https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_2.conda#7d362346a479256857ab338588190da0
+https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h86ecc28_1.conda#d5d58b2dc3e57073fe22303f5fed4db7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-h3f4de04_7.conda#6a2f0ee17851251a85fbebafbe707d2d
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.2-h3e4203c_0.conda#3a68e44fdf2a2811672520fdd62996bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.51.0-he30d5cf_1.conda#8e62bf5af966325ee416f19c6f14ffa3
+https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda#08aad7cbe9f5a6b460d0976076b6ae64
+https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda#182afabe009dc78d8b73100255ee6868
+https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda#9303e8887afe539f78517951ce25cd13
+https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-h80f16a2_3.conda#032d8030e4a24fe1f72c74423a46fb88
+https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20250104-pl5321h976ea20_0.conda#fb640d776fc92b682a14e001980825b1
+https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-devel-5.8.1-h86ecc28_2.conda#236d1ebc954a963b3430ce403fbb0896
https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.18-hb9de7d4_1.tar.bz2#d09ab3c60eebb6f14eb4d07e172775cc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.1.0-hf1166c9_1.conda#51f54efdd1d2ed5d7e9c67381b75fdb1
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda#000e30b09db0b7c775b21695dff30969
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.48.0-h31becfc_0.conda#97f754b22f63a943345bd807e1d51e01
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.50.4-h022381a_0.conda#0ad1b73a3df7e3376c14efe6dabe6987
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-15.2.0-hf1166c9_7.conda#9e5deec886ad32f3c6791b3b75c78681
https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda#b4df5d7d4b63579d081fd3a4cf99740e
-https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h68df207_1.conda#b13fb82f88902e34dd0638cd7d378c21
-https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda#91d49c85cacd92caa40cf375ef72a25d
-https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.3.1-h86ecc28_3.conda#7f591390401ad65781372240424ab7fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/xz-5.2.6-h9cdd2b7_0.tar.bz2#83baad393a31d59c20b63ba4da6592df
-https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2#b853307650cb226731f653aa623936a4
+https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda#c0f08fc2737967edde1a272d4bf41ed9
+https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h5688188_102.conda#2562c9bfd1de3f9c590f0fe53858d85c
+https://conda.anaconda.org/conda-forge/linux-aarch64/xz-gpl-tools-5.8.1-h2dbfc1b_2.conda#0d5f95b450e75655b5e76ae626197383
+https://conda.anaconda.org/conda-forge/linux-aarch64/xz-tools-5.8.1-h86ecc28_2.conda#04a5f1734c23daf8c7fe59045f755efb
+https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h86ecc28_2.conda#bc230abb5d21b63ff4799b0e75204783
https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda#268203e8b983fddb6412b36f2024e75c
-https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#29371161d77933a54fccf1bb66b96529
-https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.46.0-hf51ef55_0.conda#a8ae63fd6fb7d007f74ef3df95e5edf3
-https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda#105eb1e16bf83bfb2eb380a48032b655
-https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda#f75105e0585851f818e0009dd1dde4dc
-https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h68df207_1.conda#6031f9e32654fbdb9fdba406ab980517
-https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda#be8d5f8cf21aed237b8b182ea86b3dd6
https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda#29c10432a2ca1472b53f299ffb2ffa37
-https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.12.2-h9de0dd0_1.conda#ea2fbe77db884c344c72d10c895266d3
-https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.8.19-hbbe8eec_0_cpython.conda#5e5f0e9986cf1e00d2e1c8f32c6c2a0f
+https://conda.anaconda.org/conda-forge/linux-aarch64/xz-5.8.1-h2dbfc1b_2.conda#176b552740e8836d92c4935244d6756b
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda#be8d5f8cf21aed237b8b182ea86b3dd6
+https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.19.5-h6feb2b4_0.conda#5b886141d3ae96e3fcfddbe1a1edd81b
+https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.8.20-hbf90c55_2_cpython.conda#077f3c1075e039bbd186570ea88f42f7
+https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h28faeed_4.conda#ae8c0108b4d6a5981bce484eeaaf1b55
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_0.conda#454950e9fa3a0c59880de0ac7a6bc8c4
https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
https://conda.anaconda.org/conda-forge/noarch/backcall-0.2.0-pyh9f0ad1d_0.tar.bz2#6006a6d08a3fa99268a2681c7fb55213
https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py38hb83fbf6_1.conda#481316b31f7fba6fa88cb613f37fecf7
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
+https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda#12f7d00853807b0531775e9be891cb11
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_0.conda#a374efa97290b8799046df7c5ca17164
https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.5-py38h8529334_0.conda#d080d51fb55cbadfba94202a5210ad9f
https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
+https://conda.anaconda.org/conda-forge/noarch/executing-2.1.0-pyhd8ed1ab_0.conda#d0441db20c827c11721889a241df1220
https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.0.3-py38hb83fbf6_0.conda#1478a14b93a1245fd3c1447e091fa234
https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
+https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_0.conda#7ba2ede0e7c795ff95088daf0dc59753
https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py38he3eb160_0.conda#86fc6edc1871ad3808152ad92a7bfa16
https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-2.1.5-py38hea3b116_0.conda#33014d4448003d2d713a81f0c9f6b95a
https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_0.conda#fd8f2b18b65bbf62e8f653100690c8d2
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.0-pyhd8ed1ab_0.conda#07e9550ddff45150bfc7da146268e165
https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-5.9.8-py38h9579f32_0.conda#48ba0498b0437fadaae891a0851d1223
https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
@@ -77,19 +83,20 @@ https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba
https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
+https://conda.anaconda.org/conda-forge/noarch/pytz-2024.2-pyhd8ed1ab_0.conda#260009d03c9d5c0f111904d851f053dc
https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py38ha5227f9_0.conda#c44c86df86a1b306bac8e1c0227d14c8
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py38h857d6e0_0.conda#74fcae38ab70e4a052759a33db8433bc
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.20.0-py38h3fc512f_0.conda#67d3b9b8e31799316b558ab8c5e6e85f
https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
+https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda#2ce9825396daf72baabaade36cee16da
https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.6-pyhd8ed1ab_0.conda#cbee5eb469aa483fee70f6a1b4e955dd
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda#e977934e00b355ff55ed154904044727
https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.1-py38h34c6dc0_0.conda#d116dc4e4b2ac2f644c1945907303a99
https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20241003-pyhff2d567_0.conda#3d326f8a2aa2d14d51d8c513426b5def
https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
@@ -97,14 +104,12 @@ https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#
https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_0.conda#bdb2f437ce62fd2f1fef9119a37a12d9
https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h28faeed_4.conda#ae8c0108b4d6a5981bce484eeaaf1b55
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_0.conda#fee389bf8a4843bd7a2248ce11b7f188
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.5.0-pyhd8ed1ab_0.conda#c720e5c85c349c5cbc99d9dfc4b200f8
https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
+https://conda.anaconda.org/conda-forge/noarch/babel-2.16.0-pyhd8ed1ab_0.conda#6d4e9ecca8d88977147e109fc7053184
https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
@@ -112,60 +117,58 @@ https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.0-py38hf2f4906_0.
https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda#54198435fce4d64d8a89af22573012a8
+https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_0.conda#c808991d29b9838fb4d96ce8267ec9ec
https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-aarch64/jupyter_core-5.7.2-py38he3eb160_0.conda#f9a21df73f706043a100ef0aee02109f
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
+https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_0.conda#4c05134c48b6a74f33bbb9938e4a115e
https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py38h857d6e0_0.conda#74fcae38ab70e4a052759a33db8433bc
https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-21.2.0-py38h9579f32_4.conda#3460f222597376592b6e1ac5eac0bdce
https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-43.0.0-py38ha9ee602_0.conda#853ec42a7a7e9917162f81e50a3c23ef
https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda#2ca8e6dbc86525c8b95e3c0ffa26442e
+https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.5.0-hd8ed1ab_1.conda#c70dd0718dbccdcc6d5828de3e71399d
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2024.10.1-pyhd8ed1ab_0.conda#720745920222587ef942acfbc578b584
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_0.conda#a14218cfb29662b4a19ceb04e93e298e
https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.47-hd8ed1ab_0.conda#3e0c82ddcfe27eb4ae77f887cfd9f45b
+https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.48-hd8ed1ab_1.conda#bf730bb1f201e3f5a961c1fb2ffc4f05
https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.20.1-py38hf0fcd82_0.conda#6937a428ec8d2065e00b8e236f471357
https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.32-py38h34c6dc0_0.conda#40a952bd0720dfede0768ab8db47737f
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py38h134131f_0.conda#c63985d0da2f961ca7f09d683e00ea6b
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.14.0-pyhd8ed1ab_0.conda#4882d44aa5f5d9de49ba84266cfe56b1
https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.1-pyhd8ed1ab_0.conda#7566bb010833e59fb579ed520b9a7bc5
https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
https://conda.anaconda.org/conda-forge/noarch/ipython-8.12.2-pyh41d4057_0.conda#acebfd89278ecac2a67b60b657e00d5c
https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_0.conda#6b55867f385dd762ed99ea687af32a69
https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_1.conda#a5b1a8065857cc4bd8b7a38d063bb728
https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.2.1-pyh31011fe_0.conda#2d61d811311bedad7bb86b29bd537cd5
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhff2d567_2.conda#0457fdf55c88e52e0e7b63691eafcc48
https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
@@ -173,5 +176,5 @@ https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8
https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.2.1-pyh31011fe_0.conda#26b02c4e1060f33741de65e2983cc51f
https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.yml b/repo2docker/buildpacks/conda/environment.py-3.8.yml
index 44dd7948e..d5742a96d 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.8.yml
+++ b/repo2docker/buildpacks/conda/environment.py-3.8.yml
@@ -1,5 +1,5 @@
# AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY
-# Generated on 2024-08-29 07:01:59 UTC
+# Generated on 2025-10-16 07:16:06 UTC
channels:
- conda-forge
dependencies:
diff --git a/repo2docker/buildpacks/conda/environment.py-3.9-linux-64.lock b/repo2docker/buildpacks/conda/environment.py-3.9-linux-64.lock
index 651404ffc..332e72525 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.9-linux-64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.9-linux-64.lock
@@ -1,177 +1,176 @@
# AUTO GENERATED FROM environment.py-3.9.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:02:26 UTC
+# Frozen on 2025-10-16 07:16:41 UTC
# Generated by conda-lock.
# platform: linux-64
# input_hash: bd4d374cae58bd84b0ba870367f38a90b671ab1c952a6cf4a62296b73b6accc9
@EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
-https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.7.4-hbcca054_0.conda#23ab7665c5f63cfb9f1f6195256daac6
-https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda#b80f2f396ca2c28b8c14c437a4ed1e74
-https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.9-5_cp39.conda#40363a30db350596b5f225d0d5a33328
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda#23c255b008c4f2ae008f81edcabaca89
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.9-8_cp39.conda#c2f0c4bf417925c27b62ab50264baa98
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066
-https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553
-https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
-https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7
-https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.18-h36c2ea0_1.tar.bz2#c3788462a6fbddafdb413a9f9053e58d
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e
-https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b
-https://conda.anaconda.org/conda-forge/linux-64/libuv-1.48.0-hd590300_0.conda#7e8b914b1062dd4386e3de4d82a3ead6
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-h767d61c_7.conda#c0374badb3a5d4b1372db28d19462c53
+https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc
+https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3
+https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6
+https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda#35f29eec58405aaf55e01cb470d8c26a
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363
+https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc
+https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda#5b767048b1b3ee9a954b06f4084f93dc
+https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1
+https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda#0f03292cc56bf91a077a134ea8747118
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8
+https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0
+https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a
+https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b
+https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda#a587892d3c13b6621a6091be690dbca2
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda#0b367fad34931cb79e0d6b7e5c06bb1c
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-h4852527_7.conda#f627678cf829bd70bccf141a19c3ad3e
https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc
-https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda#57d7dc60e9325e3de37ff8dffd18e814
-https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe
-https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda#6c566a46baae794daf34775d41eb180a
-https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
-https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
+https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8
https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3
-https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.0-hde9e2c9_0.conda#18aa975d2094c34aef978060ae7da7d8
-https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4
-https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc
-https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-h4ab18f5_1.conda#9653f1bf3766164d0e65fa723cabbc54
-https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368
-https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.12.2-hc19f0b3_1.conda#0b86ff6ab7bdce5076269f2bbbf6857d
-https://conda.anaconda.org/conda-forge/linux-64/python-3.9.19-h0755675_0_cpython.conda#d9ee3647fbd9e8595b8df759b2bbefb8
-https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
-https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
-https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
-https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py39h3d6467e_1.conda#c48418c8b35f1d59ae9ae1174812b40a
+https://conda.anaconda.org/conda-forge/linux-64/python-3.9.23-hc30ae73_0_cpython.conda#624ab0484356d86a54297919352d52b6
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.3.0-pyh71513ae_0.conda#a10d11958cadc13fdb43df75f8b1903f
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py39hf88036b_3.conda#1ccd2aba673acca7aa2f289266efe2db
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
-https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.5-py39h98e3656_0.conda#b9255625c117959fc39dd5e28e2800e0
-https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.8.3-pyhd8ed1ab_0.conda#11f59985f49df4620890f3e746ed7102
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda#7e7d5ef1b9ed630e4a1c358d6bc62284
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.16-py39haef64b4_0.conda#ad12118b06c2596073c843e6d47978c2
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
-https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
-https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
-https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.0.3-py39h3d6467e_0.conda#a240d46f8f326c4af01de1a1d8e615d0
-https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
-https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
-https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
-https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py39hf3d152e_0.conda#00ff509ce4edc43e9f9181c7f6996ac2
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py39hd1e30aa_0.conda#9a9a22eb1f83c44953319ee3b027769f
-https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
-https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
-https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.0-pyhd8ed1ab_0.conda#81d30c08f9a3e556e8ca9e124b044d14
+https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.2.4-py39h818bca1_0.conda#38d510d03b8fc261a73366f10f649188
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda#39a4f67be3286c86d696df570b1201b7
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py39hf3d152e_1.conda#ab01fa677a681147a10f39680e6886fa
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.2.2-pyhd8ed1ab_1.conda#3a8063b25e603999188ed4bbf3485404
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py39h9399b63_1.conda#7821f0938aa629b9f17efd98c300a487
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.19.5-hf7ee748_0.conda#2cbd9a01ac939d85f137ee44c64fb705
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
-https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
-https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
-https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.8-py39hd1e30aa_0.conda#ec86403fde8793ac1c36f8afa3d15902
-https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
-https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
-https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088
-https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1
-https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba575830ec18d5c51c59f403310e2c7
-https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
-https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_1.conda#5c092057b6badd30f75b06244ecd01c9
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.8-pyhe01879c_0.conda#424844562f5d337077b445ec6b1398a7
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.22.1-pyhd8ed1ab_0.conda#c64b77ccab10b822722904d889fa83b5
+https://conda.anaconda.org/conda-forge/linux-64/psutil-7.0.0-py39h8cd3c5a_0.conda#851ab4da2babaf8d6968a64dd348ca88
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py39hcd6043d_0.conda#40f1dd93ac87fff4b776d6fb8033ddb9
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py39h9399b63_2.conda#13fd88296a9f19f5e3ac0c69d4b64cc6
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
-https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.20.0-py39h5cde264_0.conda#9255b3f2a439d3b113ef8e36159a3fde
-https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
-https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
-https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py39hd3abc70_0.conda#c183e99f9320e5e2d0f9c43efcb3fb22
-https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
-https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
-https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
-https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#68f0738df502a14213624b288c60c9ad
-https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
-https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
-https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
-https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h75354e8_4.conda#03cc8d9838ad9dd0060ab532e81ccb21
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
-https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
-https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
-https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
+https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.27.0-py39h17f49b6_0.conda#de935a1880d17308167b723231e87f9d
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.7-pyhd8ed1ab_0.conda#fb32097c717486aa34b38a9db57eb49e
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhe01879c_2.conda#30a0a26c8abccf4b7991d590fe17c699
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py39hd399759_0.conda#9546db52eccafee2fa2675653fec04f9
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20250809-pyhd8ed1ab_0.conda#63a644e158c4f8eeca0d1290ac25e0cc
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda#b68980f2495d096e71c7fd9d7ccf63e6
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_1.conda#84f8f77f0a9c6ef401ee96611745da8f
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda#8035e5b54c08429354d5d64027041cad
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py39h49a4b6b_0.conda#278cc676a7e939cf2561ce4a5cfaa484
-https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
-https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
-https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
-https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
-https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-64/jupyter_core-5.7.2-py39hf3d152e_0.conda#612f7a003a8a407955572c0d53952ceb
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
-https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
-https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
-https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
-https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py39h4e4fb57_0.conda#68670c3c584e5aae692bac41108d8385
-https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
-https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py39h15c3d72_0.conda#7e61b8777f42e00b08ff059f9e8ebc44
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda#b4754fb1bdcb70c8fd54f918301582c6
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.3-pyh29332c3_0.conda#7ec6576e328bc128f4982cd646eeba85
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda#d17ae9db4dc594267181bd199bf9a551
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.0.2-py39haa5fa38_0.conda#a12afe53883b54e2df984dd45bc36063
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.36.2-pyh29332c3_0.conda#9140f1c09dd5489549c6a33931b943c7
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
-https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
-https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-21.2.0-py39hd1e30aa_4.conda#6a04738e75f877b68552fc19cb045233
-https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
-https://conda.anaconda.org/conda-forge/linux-64/cryptography-43.0.0-py39h5c34e2d_0.conda#e060dbcfd38c74d22cc9813d9a57e288
-https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
-https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.20.1-py39h5cde264_0.conda#237c3e6f11936e9875c2ba37e8f8be8e
-https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.32-py39hcd6043d_0.conda#89b3c057e28607bce2d8cb43404b5ccd
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
-https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py39h623c9ba_0.conda#a19d023682384c637cb356d270c276c0
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
-https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
-https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.1-h4440ef1_0.conda#75be1a943e0a7f99fcf118309092c635
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.1-pyhd8ed1ab_0.conda#e0c3cd765dc15751ee2f0b03cd015712
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.10.0-pyhe01879c_0.conda#cc2613bfa71dec0eb2113ee21ac9ccbf
+https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py39hd399759_0.conda#dfa88a4227076fa51b9a8afd0ed06cee
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.13.4-pyha770c72_0.conda#9f07c4fc992adb2d6c30da7fab3959a7
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-64/cryptography-45.0.6-py39hb2f7f84_0.conda#09c640d5a5798c2f520baf821a0fc0fa
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
https://conda.anaconda.org/conda-forge/noarch/ipython-8.18.1-pyh707e725_3.conda#15c6f45a45f7ac27f6d60b0b084f6761
-https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
-https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
-https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
-https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
-https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
-https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
-https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
-https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
-https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
-https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
-https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8ed1ab_0.conda#c64f9b9f15a79239b864c455cc0a67d4
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
-https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
-https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.4.1-pyh29332c3_0.conda#41ff526b1083fde51fbdc93f29282e0e
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py39h20260ba_0.conda#38b79310c98c3a2270e1d19589205a6a
+https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.43-py39hd399759_0.conda#227867c51ee09b270f31ca28221e57d1
+https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py39hd399759_3.conda#2f6845f6cdf545845a60c4dcbd017c78
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.16.4-pyhd8ed1ab_0.conda#b17cf31f353008d23a6fd8bd90efcfc2
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.30.1-pyh82676e8_0.conda#b0cc25825ce9212b8bee37829abad4d6
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.7-pyh3cfb1c2_0.conda#1b337e3d378cde62889bb735c024b7a2
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.3.0-pyhda7c55b_0.conda#2a47623cd374e67413707b9925908313
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.16.0-pyhe01879c_0.conda#f062e04d7cd585c937acbf194dceec36
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.6-pyhe01879c_0.conda#7129ed52335cc7164baf4d6508a3f233
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyh885dcc9_1.conda#aff9028145d26b0987d0959a989e40cf
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.6-pyhd8ed1ab_0.conda#70cb2903114eafc6ed5d70ca91ba6545
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.3.0-h4422900_0.conda#c4e0da69399fb5bd8e314ec2da0f0119
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.5-pyhd8ed1ab_0.conda#28062c17cdb444388c00903eaec1ba0e
diff --git a/repo2docker/buildpacks/conda/environment.py-3.9-linux-aarch64.lock b/repo2docker/buildpacks/conda/environment.py-3.9-linux-aarch64.lock
index 0d3a0878c..5ef6b572c 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.9-linux-aarch64.lock
+++ b/repo2docker/buildpacks/conda/environment.py-3.9-linux-aarch64.lock
@@ -1,176 +1,175 @@
# AUTO GENERATED FROM environment.py-3.9.yml, DO NOT MANUALLY MODIFY
-# Frozen on 2024-08-29 07:02:36 UTC
+# Frozen on 2025-10-16 07:16:53 UTC
# Generated by conda-lock.
# platform: linux-aarch64
# input_hash: 6f3b845e2c93dcbf3f05c9ce5f84b712a90b3d0fe3853e8acd6d5a969499a235
@EXPLICIT
-https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.7.4-hcefe29a_0.conda#c4c784a1336d72fff54f6b207f3dd75f
-https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.40-h9fc2d93_7.conda#1b0feef706f4d03eff0b76626ead64fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.1.0-he277a41_1.conda#59d463d51eda114031e52667843f9665
-https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.9-5_cp39.conda#2d2843f11ec622f556137d72d9c72d89
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.44-h9df1782_2.conda#c82b1aeb48ef8d5432cbc592716464ba
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-he277a41_7.conda#34cef4753287c36441f907d5fdd78d42
+https://conda.anaconda.org/conda-forge/noarch/python_abi-3.9-8_cp39.conda#c2f0c4bf417925c27b62ab50264baa98
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a
https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2#6168d71addc746e8f2b8d57dfd2edcea
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.1.0-he277a41_1.conda#2cb475709e327bb76f74645784582e6a
-https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.1.0-he9431aa_1.conda#842a1a0cf6f995091734a723e5d291ef
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.1.0-h3f4de04_1.conda#6c2afef2109372440a90c566bcb6391c
-https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda#56398c28220513b9ea13d7b450acfb20
-https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2#1f24853e59c68892452ef94ddd8afd4b
-https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2#dddd85f4d52121fab0a8b099c5e06501
-https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda#c14f32510f694e3185704d89967ec422
-https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.18-hb9de7d4_1.tar.bz2#d09ab3c60eebb6f14eb4d07e172775cc
-https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.1.0-hf1166c9_1.conda#51f54efdd1d2ed5d7e9c67381b75fdb1
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda#000e30b09db0b7c775b21695dff30969
-https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.48.0-h31becfc_0.conda#97f754b22f63a943345bd807e1d51e01
+https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-he277a41_7.conda#afa05d91f8d57dd30985827a09c21464
+https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_8.conda#2921ac0b541bf37c69e66bd6d9a43bca
+https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h86ecc28_0.conda#e7df0aab10b9cbb73ab2a467ebfaf8c7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.1-hfae3067_0.conda#f75d19f3755461db2eb69401f5514f4c
+https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.5.2-hd65408f_0.conda#0c5ad486dcfb188885e3cf8ba209b97b
+https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_7.conda#a5ce1f0a32f02c75c11580c5b2f9258a
+https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_2.conda#7d362346a479256857ab338588190da0
+https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h86ecc28_1.conda#d5d58b2dc3e57073fe22303f5fed4db7
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-h3f4de04_7.conda#6a2f0ee17851251a85fbebafbe707d2d
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.2-h3e4203c_0.conda#3a68e44fdf2a2811672520fdd62996bd
+https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.51.0-he30d5cf_1.conda#8e62bf5af966325ee416f19c6f14ffa3
+https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda#08aad7cbe9f5a6b460d0976076b6ae64
+https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda#182afabe009dc78d8b73100255ee6868
+https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda#9303e8887afe539f78517951ce25cd13
+https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-h80f16a2_3.conda#032d8030e4a24fe1f72c74423a46fb88
+https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20250104-pl5321h976ea20_0.conda#fb640d776fc92b682a14e001980825b1
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda#2e4a8f23bebdcb85ca8e5a0fbe75666a
+https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.50.4-h022381a_0.conda#0ad1b73a3df7e3376c14efe6dabe6987
+https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-15.2.0-hf1166c9_7.conda#9e5deec886ad32f3c6791b3b75c78681
https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda#b4df5d7d4b63579d081fd3a4cf99740e
-https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h68df207_1.conda#b13fb82f88902e34dd0638cd7d378c21
-https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda#91d49c85cacd92caa40cf375ef72a25d
-https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.3.1-h86ecc28_3.conda#7f591390401ad65781372240424ab7fc
-https://conda.anaconda.org/conda-forge/linux-aarch64/xz-5.2.6-h9cdd2b7_0.tar.bz2#83baad393a31d59c20b63ba4da6592df
-https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2#b853307650cb226731f653aa623936a4
+https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda#c0f08fc2737967edde1a272d4bf41ed9
+https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h5688188_102.conda#2562c9bfd1de3f9c590f0fe53858d85c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h86ecc28_2.conda#bc230abb5d21b63ff4799b0e75204783
https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda#268203e8b983fddb6412b36f2024e75c
-https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#29371161d77933a54fccf1bb66b96529
-https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.46.0-hf51ef55_0.conda#a8ae63fd6fb7d007f74ef3df95e5edf3
-https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda#105eb1e16bf83bfb2eb380a48032b655
-https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda#f75105e0585851f818e0009dd1dde4dc
-https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-1.3.1-h68df207_1.conda#6031f9e32654fbdb9fdba406ab980517
-https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda#be8d5f8cf21aed237b8b182ea86b3dd6
https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda#29c10432a2ca1472b53f299ffb2ffa37
-https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.12.2-h9de0dd0_1.conda#ea2fbe77db884c344c72d10c895266d3
-https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.9.19-h4ac3b42_0_cpython.conda#1501507cd9451472ec8900d587ce872f
-https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_1.conda#bcee238f99c34ca11add65c1684c5a2d
-https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda#6732fa52eb8e66e5afeb32db8701a791
-https://conda.anaconda.org/conda-forge/noarch/blinker-1.8.2-pyhd8ed1ab_0.conda#cf85c002319c15e9721934104aaa1137
-https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py39h387a81e_1.conda#983fb0d6a8d0ad30f7213dccadea185d
+https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.9.23-h0819846_0_cpython.conda#21a981b12ae698947a1119a089761db6
+https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda#8f587de4bcf981e26228f268df374a9b
+https://conda.anaconda.org/conda-forge/noarch/async_generator-1.10-pyhd8ed1ab_2.conda#0c07617cd436b9cd5570dc34f3af642b
+https://conda.anaconda.org/conda-forge/noarch/attrs-25.3.0-pyh71513ae_0.conda#a10d11958cadc13fdb43df75f8b1903f
+https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085
+https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py39h7dbf29c_3.conda#93037d0a51c2eaa7d9c48e73fd539460
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a
-https://conda.anaconda.org/conda-forge/noarch/certifi-2024.7.4-pyhd8ed1ab_0.conda#24e7fd6ca65997938fff9e5ab6f653e4
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
-https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.5-py39h6e76b30_0.conda#adc268cb032b4f4d2f270f0ad03ef1bf
-https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364
+https://conda.anaconda.org/conda-forge/noarch/certifi-2025.8.3-pyhd8ed1ab_0.conda#11f59985f49df4620890f3e746ed7102
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda#7e7d5ef1b9ed630e4a1c358d6bc62284
+https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda#2da13f2b299d8e1995bafbbe9689a2f7
+https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.16-py39h32b2888_0.conda#b3d18c17e230a3e0245a68d8d7d85931
+https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda#9ce473d1d1be1cc3810856a48b3fab32
https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2
-https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d
-https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0
-https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda#e16be50e378d8a4533b989035b196ab8
-https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.0.3-py39h387a81e_0.conda#e61522dece30c7cd969b11e1f2bcb27d
-https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71
-https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4
-https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975
-https://conda.anaconda.org/conda-forge/noarch/json5-0.9.25-pyhd8ed1ab_0.conda#5d8c241a9261e720a34a07a3e1ac4109
-https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py39h4420490_0.conda#e70e714d000a4d164a03c18eb9575d39
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.13-pyhd8ed1ab_0.conda#ccea946e6dce9f330fbf7fca97fe8de7
-https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-2.1.5-py39h7cc1d5f_0.conda#d6fda92ac61840449b8053eaee6a682e
-https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
-https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda#6598c056f64dc8800d40add25e4e2c34
-https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db
-https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhff2d567_0.conda#adb3a4c7f269923b7f2ccff0ef071102
+https://conda.anaconda.org/conda-forge/noarch/executing-2.2.0-pyhd8ed1ab_0.conda#81d30c08f9a3e556e8ca9e124b044d14
+https://conda.anaconda.org/conda-forge/linux-aarch64/greenlet-3.2.4-py39h6794839_0.conda#7b6e993444bb86d657b361fe14afdb89
+https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e
+https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac
+https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda#39a4f67be3286c86d696df570b1201b7
+https://conda.anaconda.org/conda-forge/noarch/json5-0.12.1-pyhd8ed1ab_0.conda#0fc93f473c31a2f85c0bde213e7c63ca
+https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py39h4420490_1.conda#30963643485d0a06b6d4405a6900ac15
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-3.0.15-pyhd8ed1ab_0.conda#ad100d215fad890ab0ee10418f36876f
+https://conda.anaconda.org/conda-forge/noarch/lark-1.2.2-pyhd8ed1ab_1.conda#3a8063b25e603999188ed4bbf3485404
+https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py39h36a3f59_1.conda#1f1db1d0f7440f52e35cba3c061b97f1
+https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda#598fd7d4d0de2455fb74f56063969a97
+https://conda.anaconda.org/conda-forge/linux-aarch64/nodejs-20.19.5-h6feb2b4_0.conda#5b886141d3ae96e3fcfddbe1a1edd81b
+https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9
+https://conda.anaconda.org/conda-forge/noarch/pamela-1.2.0-pyhd8ed1ab_1.conda#a3a069b6dbf63e1a635f3feeffdaeb4e
https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9
-https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_0.conda#81534b420deb77da8833f2289b8d47ac
-https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761
-https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_1.conda#405678b942f2481cecdb3e010f4925d9
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.2.2-pyhd8ed1ab_0.conda#6f6cf28bf8e021933869bae3f84b8fc9
-https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.20.0-pyhd8ed1ab_0.conda#9a19b94034dd3abb2b348c8b93388035
-https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-5.9.8-py39h898b7ef_0.conda#a26ed32a36891fcd36f5ea88f0b8a2f7
-https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263
-https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_0.conda#0f051f09d992e0d08941706ad519ee0e
-https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088
-https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1
-https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.9.0-pyhd8ed1ab_1.conda#5ba575830ec18d5c51c59f403310e2c7
-https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
-https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.20.0-pyhd8ed1ab_0.conda#b98d2018c01ce9980c03ee2850690fab
+https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_1.conda#5c092057b6badd30f75b06244ecd01c9
+https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda#11a9d1d09a3615fc07c3faf79bc0b943
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.8-pyhe01879c_0.conda#424844562f5d337077b445ec6b1398a7
+https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.22.1-pyhd8ed1ab_0.conda#c64b77ccab10b822722904d889fa83b5
+https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-7.0.0-py39h060674a_0.conda#5d0d600ba618fe729153d87ff3303401
+https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54
+https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda#3bfdfb8dbcdc4af1ae3f9a8eb3948f04
+https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef
+https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066
+https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac
+https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f
https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960
-https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
-https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py39he257ee7_0.conda#5f51f36e01c69370f7e4e12829b786d8
+https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py39hbebea31_2.conda#59d83d45572c9e21d865dd6e4d75c20f
https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f
-https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.20.0-py39hb8f4057_0.conda#4cfe41ec971c5c2c3d1e1980e309c8aa
-https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_0.conda#778594b20097b5a948c59e50ae42482a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda#1462aa8b243aad09ef5d0841c745eb89
-https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
-https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_0.conda#490730480d76cf9c8f8f2849719c6e2b
-https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561
-https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
-https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.1-py39ha3e8b56_0.conda#60ad0fcecca6af49fe5888a408618d8a
-https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda#3df84416a021220d8b5700c613af2dc5
-https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20240821-pyhd8ed1ab_0.conda#a0637bb6a2428c10448807b9d87f082c
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7
-https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_0.tar.bz2#eb67e3cace64c66233e2d35949e20f92
-https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_0.conda#0944dc65cb4a9b5b68522c3bb585d41c
-https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_0.conda#68f0738df502a14213624b288c60c9ad
-https://conda.anaconda.org/conda-forge/noarch/webcolors-24.8.0-pyhd8ed1ab_0.conda#eb48b812eb4fbb9ff238a6651fdbbcae
-https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_2.conda#daf5160ff9cde3a468556965329085b9
-https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_0.conda#f372c576b8774922da83cda2b12f9d29
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584
-https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.13-pyhd8ed1ab_0.conda#6372cd99502721bd7499f8d16b56268d
-https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h28faeed_4.conda#ae8c0108b4d6a5981bce484eeaaf1b55
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.1-pyhd8ed1ab_0.conda#74a4befb4b38897e19a107693e49da20
-https://conda.anaconda.org/conda-forge/noarch/anyio-4.4.0-pyhd8ed1ab_0.conda#1fa97c6e8db1f82c64ff17a5efc4ae8e
-https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
-https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_0.conda#3d081de3a6ea9f894bbb585e8e3a4dcb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
-https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
-https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
+https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.27.0-py39h8aac8b2_0.conda#9cd7386f22dccc206be8fe68d9f6eba0
+https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9
+https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e
+https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
+https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9
+https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.7-pyhd8ed1ab_0.conda#fb32097c717486aa34b38a9db57eb49e
+https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhe01879c_2.conda#30a0a26c8abccf4b7991d590fe17c699
+https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.5.2-py39hd7e5afa_0.conda#e47bef54bd34bc1184ef75509f3de787
+https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed
+https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20250809-pyhd8ed1ab_0.conda#63a644e158c4f8eeca0d1290ac25e0cc
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f
+https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c
+https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb
+https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda#b68980f2495d096e71c7fd9d7ccf63e6
+https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337
+https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d
+https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_1.conda#84f8f77f0a9c6ef401ee96611745da8f
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986
+https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.14-pyhd8ed1ab_0.conda#2f1f99b13b9d2a03570705030a0b3e7c
+https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-hefbcea8_9.conda#5676806bba055c901a62f969cb3fbe02
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f
+https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.5-pyh29332c3_0.conda#d9d0f99095a9bb7e3641bca8c6ad2ac7
+https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4
+https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551
-https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.0-py39h26f80ff_0.conda#7313a9f2d6f58cda446f4576293b4ebb
-https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_0.conda#948d84721b578d426294e17a02e24cbb
-https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_0.tar.bz2#b21ed0883505ba1910994f1df031a428
-https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097
-https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda#6e3dbc422d3749ad72659243d6ac8b2b
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.4-pyhd8ed1ab_0.conda#99aa3edd3f452d61c305a30e78140513
-https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
-https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2
-https://conda.anaconda.org/conda-forge/linux-aarch64/jupyter_core-5.7.2-py39h4420490_0.conda#7d9cb6936e2b7c33480a8890d4f2d41c
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
-https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda#779345c95648be40d22aaa89de7d4254
-https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda#24fba5a9d161ad8103d4e84c0e1a3ed4
-https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
-https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyhd8ed1ab_0.conda#6721aef6bfe5937abe70181545dd2c51
-https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.47-pyha770c72_0.conda#1247c861065d227781231950e14fe817
-https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c
-https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py39he601760_0.conda#13be16152d950751929876d918d223d8
-https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_0.conda#0fc8b52192a8898627c3efae1003e9f6
-https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_0.tar.bz2#fed45fc5ea0813240707998abe49f520
+https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py39hecfc5ed_0.conda#5c431ce74f9fcffca9e9a29990b318e5
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a
+https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda#4b69232755285701bc86a5afe4d9933a
+https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda#b4754fb1bdcb70c8fd54f918301582c6
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8
+https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda#a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9
+https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646
+https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d
+https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda#af6ab708897df59bd6e7283ceab1b56b
+https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.3-pyh29332c3_0.conda#7ec6576e328bc128f4982cd646eeba85
+https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587
+https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda#d0d408b1f18883a944376da5cf8101ea
+https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5
+https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda#d17ae9db4dc594267181bd199bf9a551
+https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
+https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-27.0.2-py39h8dcef23_0.conda#55c47cd39123fa01060b990722951b87
+https://conda.anaconda.org/conda-forge/noarch/referencing-0.36.2-pyh29332c3_0.conda#9140f1c09dd5489549c6a33931b943c7
+https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706
+https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2
+https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda#b1b505328da7a6b246787df4b5a49fbc
https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93
-https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.3.0-pyhd8ed1ab_0.conda#8662629d9a05f9cff364e31ca106c1ac
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda#52d648bd608f5737b123f510bb5514b5
-https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_0.conda#7e9f4612544c8edbfd6afad17f1bd045
-https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-21.2.0-py39h898b7ef_4.conda#4baffaf857eafd23b03ca7974695df21
-https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_0.conda#b77d8c2313158e6e461ca0efb1c2c508
-https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-43.0.0-py39h33ea94c_0.conda#67d79f30e30e103a7ff11ccffc695cc8
-https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_0.tar.bz2#642d35437078749ef23a5dca2c9bb1f3
-https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.5-pyhd8ed1ab_0.conda#a6b9a0158301e697e4d0a36a3d60e133
-https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.4.0-hd8ed1ab_0.conda#01b7411c765c3d863dcc920207f258bd
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.1-pyhd8ed1ab_0.conda#a0e4efb5f35786a05af4809a2fb1f855
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_0.conda#219b3833aa8ed91d47d1be6ca03f30be
-https://conda.anaconda.org/conda-forge/noarch/mako-1.3.5-pyhd8ed1ab_0.conda#29fddbfa0e2361636a98de4f46ead2ac
-https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.20.1-py39hb170bb1_0.conda#0cde31242b4542eae849bae5b3d4fa43
-https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.32-py39ha3e8b56_0.conda#c6ae4590cf07decee5164daa469bde5e
-https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af
-https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py39h6b56de1_0.conda#4e9daef09df3b119e1ba338fee682995
-https://conda.anaconda.org/conda-forge/noarch/alembic-1.13.2-pyhd8ed1ab_0.conda#0045ed5e28a7f2c252a30442408b36c4
-https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_0.conda#3afef1f55a1366b4d3b6a0d92e2235e4
-https://conda.anaconda.org/conda-forge/noarch/httpx-0.27.2-pyhd8ed1ab_0.conda#7e9ac3faeebdbd7b53b462c41891e7f7
+https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.1-h4440ef1_0.conda#75be1a943e0a7f99fcf118309092c635
+https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.1-pyhd8ed1ab_0.conda#e0c3cd765dc15751ee2f0b03cd015712
+https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c
+https://conda.anaconda.org/conda-forge/noarch/anyio-4.10.0-pyhe01879c_0.conda#cc2613bfa71dec0eb2113ee21ac9ccbf
+https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-25.1.0-py39h0f7a62b_0.conda#9bd48007d6957f1e5750e6123cf0d83c
+https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9
+https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.13.4-pyha770c72_0.conda#9f07c4fc992adb2d6c30da7fab3959a7
+https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d
+https://conda.anaconda.org/conda-forge/linux-aarch64/cryptography-45.0.6-py39hd05fe37_0.conda#c99a5e93aca7691707bafb653a14fd04
+https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110
https://conda.anaconda.org/conda-forge/noarch/ipython-8.18.1-pyh707e725_3.conda#15c6f45a45f7ac27f6d60b0b084f6761
-https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_0.tar.bz2#4cb68948e0b8429534380243d063a27a
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_0.conda#da304c192ad59975202859b367d0f6a2
-https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda#3cdbb2fa84490e5fd44c9f9806c0d292
-https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.2.2-pyhd8ed1ab_0.tar.bz2#8f882b197fd9c4941a787926baea4868
-https://conda.anaconda.org/conda-forge/noarch/pydantic-2.8.2-pyhd8ed1ab_0.conda#539a038a24a959662df1fcaa2cfc5c3e
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-24.2.1-pyhd8ed1ab_2.conda#85fa2fdd26d5a38792eb57bc72463f07
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8
-https://conda.anaconda.org/conda-forge/noarch/certipy-0.1.3-pyhd8ed1ab_1.conda#ac4247381b9397ace149d73cd2141b0b
-https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda#b40131ab6a36ac2c09b7c57d4d3fbf99
-https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.5-pyhd8ed1ab_0.conda#a022d34163147d16b27de86dc53e93fc
-https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_0.conda#16b37612b3a2fd77f409329e213b530c
-https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda#0b57b5368ab7fc7cdc9e3511fa867214
-https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52
-https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_0.conda#ed45423c41b3da15ea1df39b1f80c2ca
-https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda#15b51397e0fe8ea7d7da60d83eb76ebc
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.1.0-pyh31011fe_0.conda#8958ebf09fe082db40ec9ab4d6f1a20c
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda#e2d2abb421c13456a9a9f80272fdf543
-https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_0.conda#ca23c71f70a7c7935b3d03f0f1a5801d
-https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_0.conda#885867f6adab3d7ecdf8ab6ca0785f51
-https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyhd8ed1ab_0.conda#e614f229e0c636b6c55a6f1222684d38
-https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.1.0-pyhd8ed1ab_0.conda#c64f9b9f15a79239b864c455cc0a67d4
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_0.conda#af8239bf1ba7e8c69b689f780f653488
-https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_0.conda#3d85618e2c97ab896b5b5e298d32b5b3
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.2.5-pyhd8ed1ab_0.conda#594762eddc55b82feac6097165a88e3c
-https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.1.0-pyh31011fe_0.conda#bb9841dbb34c753706c85cd4264ff710
-https://conda.anaconda.org/conda-forge/noarch/notebook-7.2.2-pyhd8ed1ab_0.conda#c4d5a58f43ce9ffa430e6ecad6c30a42
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.4.1-pyh29332c3_0.conda#41ff526b1083fde51fbdc93f29282e0e
+https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd
+https://conda.anaconda.org/conda-forge/noarch/mako-1.3.10-pyhd8ed1ab_0.conda#c5bb3eea5f1a00fcf3d7ea186209ce33
+https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.33.2-py39h4e6e209_0.conda#2559b148415324e44af043e168ea85fe
+https://conda.anaconda.org/conda-forge/linux-aarch64/sqlalchemy-2.0.43-py39hd7e5afa_0.conda#b5b22a9ed1abb261031a2d040bd75ab2
+https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py39h0f7a62b_3.conda#dcca87609d31463211de720255de713c
+https://conda.anaconda.org/conda-forge/noarch/alembic-1.16.4-pyhd8ed1ab_0.conda#b17cf31f353008d23a6fd8bd90efcfc2
+https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b
+https://conda.anaconda.org/conda-forge/noarch/certipy-0.2.2-pyhd8ed1ab_0.conda#7bacdbd6bb3b4fe905a3235d2799ca78
+https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb
+https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.30.1-pyh82676e8_0.conda#b0cc25825ce9212b8bee37829abad4d6
+https://conda.anaconda.org/conda-forge/noarch/ipywidgets-8.1.7-pyhd8ed1ab_0.conda#7c9449eac5975ef2d7753da262a72707
+https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda#341fd940c242cf33e832c0402face56f
+https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf
+https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.7-pyh3cfb1c2_0.conda#1b337e3d378cde62889bb735c024b7a2
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a
+https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285
+https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.1-he01879c_0.conda#13e31c573c884962318a738405ca3487
+https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea
+https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4
+https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0
+https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-base-5.3.0-pyhda7c55b_0.conda#2a47623cd374e67413707b9925908313
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b
+https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.16.0-pyhe01879c_0.conda#f062e04d7cd585c937acbf194dceec36
+https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.6-pyhe01879c_0.conda#7129ed52335cc7164baf4d6508a3f233
+https://conda.anaconda.org/conda-forge/noarch/jupyter-offlinenotebook-0.3.1-pyh885dcc9_1.conda#aff9028145d26b0987d0959a989e40cf
+https://conda.anaconda.org/conda-forge/noarch/jupyter-resource-usage-1.2.0-pyhd8ed1ab_0.conda#9ef4adccd3a32f3f01fc998f2ad4bdf5
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357
+https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.6-pyhd8ed1ab_0.conda#70cb2903114eafc6ed5d70ca91ba6545
+https://conda.anaconda.org/conda-forge/noarch/jupyterhub-singleuser-5.3.0-h4422900_0.conda#c4e0da69399fb5bd8e314ec2da0f0119
+https://conda.anaconda.org/conda-forge/noarch/notebook-7.4.5-pyhd8ed1ab_0.conda#28062c17cdb444388c00903eaec1ba0e
diff --git a/repo2docker/buildpacks/conda/environment.py-3.9.yml b/repo2docker/buildpacks/conda/environment.py-3.9.yml
index f6c60c1fa..ea3ededfa 100644
--- a/repo2docker/buildpacks/conda/environment.py-3.9.yml
+++ b/repo2docker/buildpacks/conda/environment.py-3.9.yml
@@ -1,5 +1,5 @@
# AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY
-# Generated on 2024-08-29 07:02:26 UTC
+# Generated on 2025-10-16 07:16:41 UTC
channels:
- conda-forge
dependencies:
diff --git a/repo2docker/buildpacks/conda/freeze.py b/repo2docker/buildpacks/conda/freeze.py
index d4697e69e..c4fb7bb19 100755
--- a/repo2docker/buildpacks/conda/freeze.py
+++ b/repo2docker/buildpacks/conda/freeze.py
@@ -12,11 +12,14 @@
import os
import pathlib
from argparse import ArgumentParser
-from datetime import datetime
+from datetime import datetime, timezone
from subprocess import check_call
from ruamel.yaml import YAML
+from .supported_python_version import SUPPORTED_PYTHON_VERSION
+
+UTC = timezone.utc
HERE = pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
ENV_FILE = HERE / "environment.yml"
@@ -68,7 +71,7 @@ def freeze(env_file, frozen_file, platform="linux-64"):
f.write(
f"# AUTO GENERATED FROM {env_file.relative_to(HERE)}, DO NOT MANUALLY MODIFY\n"
)
- f.write(f"# Frozen on {datetime.utcnow():%Y-%m-%d %H:%M:%S UTC}\n")
+ f.write(f"# Frozen on {datetime.now(UTC):%Y-%m-%d %H:%M:%S UTC}\n")
with frozen_tempfile.open() as temp:
f.write(temp.read())
@@ -98,7 +101,7 @@ def set_python(py_env_file, py):
f.write(
f"# AUTO GENERATED FROM {ENV_FILE.relative_to(HERE)}, DO NOT MANUALLY MODIFY\n"
)
- f.write(f"# Generated on {datetime.utcnow():%Y-%m-%d %H:%M:%S UTC}\n")
+ f.write(f"# Generated on {datetime.now(UTC):%Y-%m-%d %H:%M:%S UTC}\n")
yaml.dump(env, f)
@@ -113,7 +116,7 @@ def set_python(py_env_file, py):
"py",
nargs="*",
help="Python version(s) to update and freeze",
- default=("3.8", "3.9", "3.10", "3.11", "3.12"),
+ default=SUPPORTED_PYTHON_VERSION,
)
parser.add_argument(
"platform",
diff --git a/repo2docker/buildpacks/conda/install-base-env.bash b/repo2docker/buildpacks/conda/install-base-env.bash
index 4eb93f00c..c3545200d 100755
--- a/repo2docker/buildpacks/conda/install-base-env.bash
+++ b/repo2docker/buildpacks/conda/install-base-env.bash
@@ -5,8 +5,8 @@ set -ex
cd $(dirname $0)
-export MAMBA_VERSION=1.5.9
-export CONDA_VERSION=24.5.0
+export MAMBA_VERSION="2.3.3"
+export CONDA_VERSION="25.9.1"
URL="https://anaconda.org/conda-forge/micromamba/${MAMBA_VERSION}/download/${CONDA_PLATFORM}/micromamba-${MAMBA_VERSION}-0.tar.bz2"
@@ -21,7 +21,7 @@ time wget -qO- ${URL} | tar -xvj bin/micromamba
mv bin/micromamba "$MICROMAMBA_EXE"
chmod 0755 "$MICROMAMBA_EXE"
-eval "$(${MICROMAMBA_EXE} shell hook -p ${CONDA_DIR} -s posix)"
+eval "$(${MICROMAMBA_EXE} shell hook --root-prefix ${CONDA_DIR} -s posix)"
micromamba activate
diff --git a/repo2docker/buildpacks/conda/matlab.py b/repo2docker/buildpacks/conda/matlab.py
index c1af63f04..716e059d2 100644
--- a/repo2docker/buildpacks/conda/matlab.py
+++ b/repo2docker/buildpacks/conda/matlab.py
@@ -3,6 +3,7 @@
UBUNTU_NAMES_FOR_BASE_IMAGES = {
"buildpack-deps:jammy": "ubuntu22.04",
+ "buildpack-deps:24.04": "ubuntu24.04",
}
def matlab_requirements_scripts(release, base_image):
@@ -55,12 +56,7 @@ def matlab_python_engine_installation_scripts(release, dest_dir):
Based on https://github.com/mathworks-ref-arch/matlab-integration-for-jupyter/blob/a4ac9b9ce5c2880596c77670eddab93f7ef9d4fc/matlab/Dockerfile#L100-L108
"""
- script = f"""export DEBIAN_FRONTEND=noninteractive && apt-get update \
- && apt-get install --no-install-recommends -y python3-distutils \
- && apt-get clean \
- && apt-get -y autoremove \
- && rm -rf /var/lib/apt/lists/* \
- && cd {dest_dir}/extern/engines/python \
+ script = f"""cd {dest_dir}/extern/engines/python \
&& python setup.py install || true"""
return [("root", script)]
diff --git a/repo2docker/buildpacks/conda/supported_python_version.py b/repo2docker/buildpacks/conda/supported_python_version.py
new file mode 100644
index 000000000..0a07edf72
--- /dev/null
+++ b/repo2docker/buildpacks/conda/supported_python_version.py
@@ -0,0 +1 @@
+SUPPORTED_PYTHON_VERSION = ("3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14")
diff --git a/repo2docker/buildpacks/docker.py b/repo2docker/buildpacks/docker.py
index 9185307ad..b162cb1f1 100644
--- a/repo2docker/buildpacks/docker.py
+++ b/repo2docker/buildpacks/docker.py
@@ -1,10 +1,7 @@
-"""Generates a variety of Dockerfiles based on an input matrix
-"""
+"""Generates a variety of Dockerfiles based on an input matrix"""
import os
-import docker
-
from .base import BuildPack
diff --git a/repo2docker/buildpacks/julia/julia_project.py b/repo2docker/buildpacks/julia/julia_project.py
index 0985a33e6..76cc1cd39 100644
--- a/repo2docker/buildpacks/julia/julia_project.py
+++ b/repo2docker/buildpacks/julia/julia_project.py
@@ -69,7 +69,7 @@ def julia_version(self):
raise RuntimeError("Failed to find a matching Julia version: {compat}")
return match
- @lru_cache()
+ @lru_cache
def get_build_env(self):
"""Get additional environment settings for Julia and Jupyter
@@ -112,11 +112,11 @@ def project_dir(self):
else:
return "${REPO_DIR}"
- @lru_cache()
+ @lru_cache
def get_env(self):
return super().get_env() + [("JULIA_PROJECT", self.project_dir)]
- @lru_cache()
+ @lru_cache
def get_path(self):
"""Adds path to Julia binaries to user's PATH.
@@ -127,7 +127,7 @@ def get_path(self):
"""
return super().get_path() + ["${JULIA_PATH}/bin"]
- @lru_cache()
+ @lru_cache
def get_build_scripts(self):
"""
Return series of build-steps common to "ALL" Julia repositories
@@ -156,7 +156,7 @@ def get_build_scripts(self):
),
]
- @lru_cache()
+ @lru_cache
def get_assemble_scripts(self):
"""
Return series of build-steps specific to "this" Julia repository
@@ -177,10 +177,8 @@ def get_assemble_scripts(self):
"${NB_USER}",
r"""
JULIA_PROJECT="" julia -e "using Pkg; Pkg.add(\"IJulia\"); using IJulia; installkernel(\"Julia\", \"--project={project}\");" && \
- julia --project={project} -e 'using Pkg; Pkg.instantiate(); Pkg.resolve(); pkg"precompile"'
- """.format(
- project=self.project_dir
- ),
+ julia --project={project} -e 'using Pkg; Pkg.instantiate(); Pkg.resolve(); Pkg.instantiate()'
+ """.format(project=self.project_dir),
)
)
return scripts
diff --git a/repo2docker/buildpacks/nix/__init__.py b/repo2docker/buildpacks/nix/__init__.py
index 7da3be603..06d2a7aa8 100644
--- a/repo2docker/buildpacks/nix/__init__.py
+++ b/repo2docker/buildpacks/nix/__init__.py
@@ -9,12 +9,12 @@
class NixBuildPack(BaseImage):
"""A nix Package Manager BuildPack"""
- @lru_cache()
+ @lru_cache
def get_path(self):
"""Return paths to be added to PATH environemnt variable"""
return super().get_path() + ["/home/${NB_USER}/.nix-profile/bin"]
- @lru_cache()
+ @lru_cache
def get_env(self):
"""Ordered list of environment variables to be set for this image"""
@@ -24,7 +24,7 @@ def get_env(self):
("GIT_SSL_CAINFO", "/etc/ssl/certs/ca-certificates.crt"),
]
- @lru_cache()
+ @lru_cache
def get_build_scripts(self):
"""
Return series of build-steps common to all nix repositories.
@@ -60,7 +60,7 @@ def get_build_scripts(self):
),
]
- @lru_cache()
+ @lru_cache
def get_build_script_files(self):
"""Dict of files to be copied to the container image for use in building"""
return {
@@ -68,7 +68,7 @@ def get_build_script_files(self):
"nix/nix-shell-wrapper": "/usr/local/bin/nix-shell-wrapper",
}
- @lru_cache()
+ @lru_cache
def get_assemble_scripts(self):
"""Return series of build-steps specific to this source repository."""
return super().get_assemble_scripts() + [
@@ -82,7 +82,7 @@ def get_assemble_scripts(self):
)
]
- @lru_cache()
+ @lru_cache
def get_start_script(self):
"""The path to a script to be executed as ENTRYPOINT"""
# the shell wrapper script duplicates the behaviour of other buildpacks
diff --git a/repo2docker/buildpacks/pipfile/__init__.py b/repo2docker/buildpacks/pipfile/__init__.py
index cb6d1ef2b..da3f1fd3b 100644
--- a/repo2docker/buildpacks/pipfile/__init__.py
+++ b/repo2docker/buildpacks/pipfile/__init__.py
@@ -80,7 +80,7 @@ def python_version(self):
)
return self._python_version
- @lru_cache()
+ @lru_cache
def get_preassemble_script_files(self):
"""Return files needed for preassembly"""
files = super().get_preassemble_script_files()
@@ -90,7 +90,7 @@ def get_preassemble_script_files(self):
files[path] = path
return files
- @lru_cache()
+ @lru_cache
def get_preassemble_scripts(self):
"""scripts to run prior to staging the repo contents"""
scripts = super().get_preassemble_scripts()
@@ -108,7 +108,7 @@ def get_preassemble_scripts(self):
)
return scripts
- @lru_cache()
+ @lru_cache
def get_assemble_scripts(self):
"""Return series of build-steps specific to this repository."""
# If we have either Pipfile.lock, Pipfile, or runtime.txt declare the
@@ -187,12 +187,9 @@ def get_assemble_scripts(self):
def detect(self):
"""Check if current repo should be built with the Pipfile buildpack."""
# first make sure python is not explicitly unwanted
- runtime_txt = self.binder_path("runtime.txt")
- if os.path.exists(runtime_txt):
- with open(runtime_txt) as f:
- runtime = f.read().strip()
- if not runtime.startswith("python-"):
- return False
+ name = self.runtime[0]
+ if name and name != "python":
+ return False
pipfile = self.binder_path("Pipfile")
pipfile_lock = self.binder_path("Pipfile.lock")
diff --git a/repo2docker/buildpacks/python/__init__.py b/repo2docker/buildpacks/python/__init__.py
index 36740795a..e5db6f9b4 100644
--- a/repo2docker/buildpacks/python/__init__.py
+++ b/repo2docker/buildpacks/python/__init__.py
@@ -3,8 +3,17 @@
import os
from functools import lru_cache
+try:
+ import tomllib
+except ImportError:
+ import tomli as tomllib
+
+from packaging.specifiers import SpecifierSet
+from packaging.version import Version
+
from ...utils import is_local_pip_requirement, open_guess_encoding
from ..conda import CondaBuildPack
+from ..conda.supported_python_version import SUPPORTED_PYTHON_VERSION
class PythonBuildPack(CondaBuildPack):
@@ -15,29 +24,69 @@ def python_version(self):
if hasattr(self, "_python_version"):
return self._python_version
- try:
- with open(self.binder_path("runtime.txt")) as f:
- runtime = f.read().strip()
- except FileNotFoundError:
- runtime = ""
+ name, version, _ = self.runtime
- if not runtime.startswith("python-"):
- # not a Python runtime (e.g. R, which subclasses this)
+ if name is not None and name != "python":
+ # Runtime specified, but not Python (e.g. R, which subclasses this)
# use the default Python
self._python_version = self.major_pythons["3"]
+ self._python_version_source = "default"
self.log.warning(
f"Python version unspecified, using current default Python version {self._python_version}. This will change in the future."
)
return self._python_version
- py_version_info = runtime.split("-", 1)[1].split(".")
- py_version = ""
- if len(py_version_info) == 1:
- py_version = self.major_pythons[py_version_info[0]]
+ if name is None or version is None:
+ self._python_version = self.major_pythons["3"]
+ self._python_version_source = "default"
else:
- # get major.minor
- py_version = ".".join(py_version_info[:2])
- self._python_version = py_version
+ if len(Version(version).release) <= 1:
+ self._python_version = self.major_pythons[version]
+ self._python_version_source = "default"
+ else:
+ self._python_version = version
+ self._python_version_source = "runtime"
+
+ runtime_version = Version(self._python_version)
+
+ # pyproject.toml can be used by some tools for minimum version of Python
+ # even if the Git repository is not a Python package.
+ pyproject_toml = "pyproject.toml"
+ if not self.binder_dir and os.path.exists(pyproject_toml):
+ with open(pyproject_toml, "rb") as _pyproject_file:
+ pyproject = tomllib.load(_pyproject_file)
+
+ if "project" in pyproject and "requires-python" in pyproject["project"]:
+ # This is the minumum version!
+ # https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#python-requires
+ pyproject_python_specifier = SpecifierSet(
+ pyproject["project"]["requires-python"]
+ )
+
+ if runtime_version not in pyproject_python_specifier:
+ # repo2docker MUST honor the user decision in runtime.txt
+ if self._python_version_source == "runtime":
+ raise RuntimeError(
+ "runtime.txt version not supported by pyproject.toml."
+ )
+ # repo2docker tries to find a new suitable version
+ else:
+ possible_python_versions = list(
+ pyproject_python_specifier.filter(SUPPORTED_PYTHON_VERSION)
+ )
+ if possible_python_versions:
+ self._python_version = possible_python_versions[-1]
+ self._python_version_source = "fallback"
+ else:
+ raise RuntimeError(
+ "repo2docker does not support a Python version compatible with pyproject.toml."
+ )
+
+ if self._python_version_source in ["default", "fallback"]:
+ self.log.warning(
+ f"Python version unspecified, using Python version {self._python_version}. This will change in the future."
+ )
+
return self._python_version
def _get_pip_scripts(self):
@@ -82,9 +131,10 @@ def _should_preassemble_pip(self):
If there are any local references, e.g. `-e .`,
stage the whole repo prior to installation.
"""
- if not os.path.exists("binder") and os.path.exists("setup.py"):
- # can't install from subset if we're using setup.py
+ # can't install from subset
+ if self._is_python_package():
return False
+
for name in ("requirements.txt", "requirements3.txt"):
requirements_txt = self.binder_path(name)
if not os.path.exists(requirements_txt):
@@ -98,7 +148,21 @@ def _should_preassemble_pip(self):
# allow assembly from subset
return True
- @lru_cache()
+ @lru_cache
+ def _is_python_package(self):
+ if self.binder_dir:
+ return False
+ if os.path.exists("setup.py"):
+ return True
+ if os.path.exists("pyproject.toml"):
+ with open("pyproject.toml", "rb") as _pyproject_file:
+ pyproject = tomllib.load(_pyproject_file)
+
+ if ("project" in pyproject) and ("build-system" in pyproject):
+ return True
+ return False
+
+ @lru_cache
def get_preassemble_script_files(self):
assemble_files = super().get_preassemble_script_files()
for name in ("requirements.txt", "requirements3.txt"):
@@ -107,7 +171,7 @@ def get_preassemble_script_files(self):
assemble_files[requirements_txt] = requirements_txt
return assemble_files
- @lru_cache()
+ @lru_cache
def get_preassemble_scripts(self):
"""Return scripts to run before adding the full repository"""
scripts = super().get_preassemble_scripts()
@@ -115,7 +179,7 @@ def get_preassemble_scripts(self):
scripts.extend(self._get_pip_scripts())
return scripts
- @lru_cache()
+ @lru_cache
def get_assemble_scripts(self):
"""Return series of build steps that require the full repository"""
# If we have a runtime.txt & that's set to python-2.7,
@@ -123,31 +187,26 @@ def get_assemble_scripts(self):
# and requirements3.txt (if it exists)
# will be installed in the python 3 notebook server env.
assemble_scripts = super().get_assemble_scripts()
- setup_py = "setup.py"
# KERNEL_PYTHON_PREFIX is the env with the kernel,
# whether it's distinct from the notebook or the same.
pip = "${KERNEL_PYTHON_PREFIX}/bin/pip"
if not self._should_preassemble_pip:
assemble_scripts.extend(self._get_pip_scripts())
- # setup.py exists *and* binder dir is not used
- if not self.binder_dir and os.path.exists(setup_py):
+ if self._is_python_package():
assemble_scripts.append(("${NB_USER}", f"{pip} install --no-cache-dir ."))
+
return assemble_scripts
def detect(self):
"""Check if current repo should be built with the Python buildpack."""
requirements_txt = self.binder_path("requirements.txt")
- runtime_txt = self.binder_path("runtime.txt")
- setup_py = "setup.py"
- if os.path.exists(runtime_txt):
- with open(runtime_txt) as f:
- runtime = f.read().strip()
- if runtime.startswith("python-"):
- return True
- else:
- return False
- if not self.binder_dir and os.path.exists(setup_py):
+ name = self.runtime[0]
+ if name:
+ return name == "python"
+
+ if self._is_python_package():
return True
+
return os.path.exists(requirements_txt)
diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py
index 75c78c2cf..3ce007952 100644
--- a/repo2docker/buildpacks/r.py
+++ b/repo2docker/buildpacks/r.py
@@ -1,6 +1,7 @@
import datetime
import os
import re
+import warnings
from functools import lru_cache
import requests
@@ -46,21 +47,6 @@ class RBuildPack(PythonBuildPack):
R is installed from https://docs.rstudio.com/resources/install-r/
"""
- @property
- def runtime(self):
- """
- Return contents of runtime.txt if it exists, '' otherwise
- """
- if not hasattr(self, "_runtime"):
- runtime_path = self.binder_path("runtime.txt")
- try:
- with open(runtime_path) as f:
- self._runtime = f.read().strip()
- except FileNotFoundError:
- self._runtime = ""
-
- return self._runtime
-
@property
def r_version(self):
"""Detect the R version for a given `runtime.txt`
@@ -70,7 +56,10 @@ def r_version(self):
"""
# Available versions at https://cran.r-project.org/src/base/
version_map = {
- "4.2": "4.2.1",
+ "4.5": "4.5.1",
+ "4.4": "4.4.2",
+ "4.3": "4.3.3",
+ "4.2": "4.2.3",
"4.1": "4.1.3",
"4.0": "4.0.5",
"3.6": "3.6.3",
@@ -86,18 +75,35 @@ def r_version(self):
# - tests/unit/test_r.py -> test_version_specification
# - tests/r/r-rspm-apt/verify
#
- r_version = version_map["4.2"]
+ r_version = version_map["4.4"]
if not hasattr(self, "_r_version"):
- parts = self.runtime.split("-")
- # If runtime.txt is not set, or if it isn't of the form r----
,
+ _, version, date = self.runtime
+ # If runtime.txt is not set, or if it isn't of the form r--*,
# we don't use any of it in determining r version and just use the default
- if len(parts) == 5:
- r_version = parts[1]
+ if version:
+ r_version = version
# For versions of form x.y, we want to explicitly provide x.y.z - latest patchlevel
# available. Users can however explicitly specify the full version to get something specific
if r_version in version_map:
r_version = version_map[r_version]
+ else:
+ r_version_parts = r_version.split(".")
+ if len(r_version_parts) == 3:
+ self.log.info(
+ f"Using R full version, {r_version}, provided by user."
+ )
+ else:
+ # repo2docker fails earlier with a meaningful message to the user.
+ # If repo2docker doesn't fail here, repo2docker might fail later
+ # without a meaningful message to the user.
+ raise RuntimeError(
+ f"R version {r_version} is not supported. Please open an issue at https://github.com/jupyterhub/repo2docker/issues/new/choose.",
+ )
+ else:
+ self.log.warning(
+ f"R version unspecified, using default R version {r_version}. You can specify the R version in runtime.txt."
+ )
# translate to the full version string
self._r_version = r_version
@@ -112,15 +118,11 @@ def checkpoint_date(self):
Returns '' if no date is specified
"""
if not hasattr(self, "_checkpoint_date"):
- match = re.match(r"r-(\d.\d(.\d)?-)?(\d\d\d\d)-(\d\d)-(\d\d)", self.runtime)
- if not match:
- self._checkpoint_date = False
+ name, version, date = self.runtime
+ if name == "r" and date:
+ self._checkpoint_date = date
else:
- # turn the last three groups of the match into a date
- self._checkpoint_date = datetime.date(
- *[int(s) for s in match.groups()[-3:]]
- )
-
+ self._checkpoint_date = False
return self._checkpoint_date
def detect(self):
@@ -138,16 +140,12 @@ def detect(self):
description_R = "DESCRIPTION"
if not self.binder_dir and os.path.exists(description_R):
- if not self.checkpoint_date:
- # no R snapshot date set through runtime.txt
- # Set it to two days ago from today
- self._checkpoint_date = datetime.date.today() - datetime.timedelta(
- days=2
- )
- self._runtime = f"r-{str(self._checkpoint_date)}"
+ # no R snapshot date set through runtime.txt
+ # Set it to two days ago from today
+ self._checkpoint_date = datetime.date.today() - datetime.timedelta(days=2)
return True
- @lru_cache()
+ @lru_cache
def get_env(self):
"""
Set custom env vars needed for RStudio to load
@@ -162,7 +160,7 @@ def get_env(self):
("LD_LIBRARY_PATH", "${R_HOME}/lib:${LD_LIBRARY_PATH}"),
]
- @lru_cache()
+ @lru_cache
def get_path(self):
"""
Return paths to be added to the PATH environment variable.
@@ -172,7 +170,7 @@ def get_path(self):
"""
return super().get_path() + ["/usr/lib/rstudio-server/bin/"]
- @lru_cache()
+ @lru_cache
def get_build_env(self):
"""
Return environment variables to be set.
@@ -186,7 +184,7 @@ def get_build_env(self):
("R_LIBS_USER", "${APP_BASE}/rlibs")
]
- @lru_cache()
+ @lru_cache
def get_packages(self):
"""
Return list of packages to be installed.
@@ -205,7 +203,7 @@ def get_packages(self):
return super().get_packages().union(packages)
- @lru_cache()
+ @lru_cache
def get_rspm_snapshot_url(self, snapshot_date, max_days_prior=7):
for i in range(max_days_prior):
snapshots = requests.post(
@@ -233,7 +231,7 @@ def get_rspm_snapshot_url(self, snapshot_date, max_days_prior=7):
)
)
- @lru_cache()
+ @lru_cache
def get_devtools_snapshot_url(self):
"""
Return url of snapshot to use for getting devtools install
@@ -250,7 +248,7 @@ def get_devtools_snapshot_url(self):
# appropriate binary packages without having to hard code version names here.
return "https://packagemanager.posit.co/all/__linux__/${VERSION_CODENAME}/2022-06-03+Y3JhbiwyOjQ1MjYyMTU7RkM5ODcwN0M"
- @lru_cache()
+ @lru_cache
def get_build_scripts(self):
"""
Return series of build-steps common to all R repositories
@@ -364,7 +362,7 @@ def get_build_scripts(self):
return super().get_build_scripts() + scripts
- @lru_cache()
+ @lru_cache
def get_preassemble_script_files(self):
files = super().get_preassemble_script_files()
installR_path = self.binder_path("install.R")
@@ -373,7 +371,7 @@ def get_preassemble_script_files(self):
return files
- @lru_cache()
+ @lru_cache
def get_preassemble_scripts(self):
"""Install contents of install.R
@@ -399,7 +397,7 @@ def get_preassemble_scripts(self):
return super().get_preassemble_scripts() + scripts
- @lru_cache()
+ @lru_cache
def get_assemble_scripts(self):
"""Install the dependencies of or the repository itself"""
assemble_scripts = super().get_assemble_scripts()
diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint
index 56e427f71..d33e634bb 100755
--- a/repo2docker/buildpacks/repo2docker-entrypoint
+++ b/repo2docker/buildpacks/repo2docker-entrypoint
@@ -6,6 +6,7 @@
# - preserve signal handling of subprocess (kill -TERM and friends)
# - tee output to a log file
+import errno
import fcntl
import os
import select
@@ -44,7 +45,7 @@ def main():
break
# raise Exception if log_file could not be set
if log_file is None:
- raise Exception("Could not open '.jupyter-server-log.txt' log file " )
+ raise Exception("Could not open '.jupyter-server-log.txt' log file")
# build the command
# like `exec "$@"`
@@ -73,15 +74,27 @@ def main():
for signum in SIGNALS:
signal.signal(signum, relay_signal)
+ # keep a record of the output streams that ran out space
+ writable = {
+ sys.stdout.buffer: True,
+ log_file: True
+ }
+
# tee output from child to both our stdout and the log file
def tee(chunk):
"""Tee output from child to both our stdout and the log file"""
for f in [sys.stdout.buffer, log_file]:
- try:
- f.write(chunk)
- f.flush()
- except OSError:
- pass
+ if writable[f]:
+ try:
+ f.write(chunk)
+ f.flush()
+ except OSError as e:
+ if e.errno == errno.ENOSPC:
+ # mark the stream as not writable anymore
+ writable[f] = False
+ continue
+ else:
+ raise
# make stdout pipe non-blocking
# this means child.stdout.read(nbytes)
@@ -109,6 +122,8 @@ def main():
tee(chunk)
chunk = child.stdout.read()
+ log_file.close()
+
# make our returncode match the child's returncode
sys.exit(child.returncode)
diff --git a/repo2docker/contentproviders/dataverse.json b/repo2docker/contentproviders/dataverse.json
deleted file mode 100644
index 3444069f6..000000000
--- a/repo2docker/contentproviders/dataverse.json
+++ /dev/null
@@ -1,680 +0,0 @@
-{
- "installations": [
- {
- "description": "Open for researchers associated with British Columbia universities to deposit data.",
- "full_name": "Abacus (British Columbia Research Libraries' Data Services) Dataverse",
- "id": 1740,
- "is_active": true,
- "lat": 49.259982,
- "lng": -123.250212,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/abacus-46x46.jpg",
- "name": "Abacus",
- "slug": "abacus",
- "url": "https://dvn.library.ubc.ca/dvn/",
- "version": "3.6"
- },
- {
- "description": "Free, open access repository of research data and software produced and developed by CIMMYT scientists.",
- "full_name": "International Maize and Wheat Improvement Center",
- "id": 1741,
- "is_active": true,
- "lat": 19.531535,
- "lng": -98.846064,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/cimmyt-46x46.jpg",
- "name": "CIMMYT Research Data",
- "slug": "cimmyt-research-data",
- "url": "http://data.cimmyt.org/",
- "version": "3.0"
- },
- {
- "description": "Open for researchers and organizations associated with Dutch universities to deposit data.",
- "full_name": "DataverseNL",
- "id": 1742,
- "is_active": true,
- "lat": 52.54726,
- "lng": 5.242346,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/logosdataverseNL-46x46.png",
- "name": "DataverseNL",
- "slug": "dataversenl",
- "url": "https://dataverse.nl/",
- "version": "4.6.1"
- },
- {
- "description": "",
- "full_name": "DataSpace@HKUST",
- "id": 1743,
- "is_active": true,
- "lat": 22.336281,
- "lng": 114.266721,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/dataspace-46x46.jpg",
- "name": "DataSpace@HKUST",
- "slug": "dataspacehkust",
- "url": "https://dataspace.ust.hk/",
- "version": "4.2"
- },
- {
- "description": "Open for Fudan University affiliated researchers to deposit data.",
- "full_name": "Fudan University Dataverse",
- "id": 1744,
- "is_active": true,
- "lat": 31.298531,
- "lng": 121.501446,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/fudan-46x46.png",
- "name": "Fudan University",
- "slug": "fudan-university",
- "url": "https://dvn.fudan.edu.cn/home/",
- "version": "4.x"
- },
- {
- "description": "Share, archive, and get credit for your data. Find and cite data across all research fields.",
- "full_name": "Harvard University",
- "id": 1745,
- "is_active": true,
- "lat": 42.380098,
- "lng": -71.116629,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/harvard-46x46.png",
- "name": "Harvard Dataverse",
- "slug": "harvard-dataverse",
- "url": "https://dataverse.harvard.edu",
- "version": "4.12"
- },
- {
- "description": "Open for Heidelberg University affiliated researchers to deposit data.",
- "full_name": "Heidelberg University",
- "id": 1746,
- "is_active": true,
- "lat": 49.39875,
- "lng": 8.672434,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/heidelberg-46x46.jpg",
- "name": "HeiDATA",
- "slug": "heidata",
- "url": "https://heidata.uni-heidelberg.de/",
- "version": "4.8.2"
- },
- {
- "description": "The network Cariniana, cariniana.ibict.br, is funded entirely by the Brazilian government and in particular by MCTI (Minist\u00e9rio da Ci\u00eancia, Tecnologia e Inova\u00e7\u00e3o). It is a project for long-term preservation of scientific publications in Brazil.",
- "full_name": "IBICT (Brazil)",
- "id": 1747,
- "is_active": true,
- "lat": -15.805842,
- "lng": -47.881369,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/ibict-46x46.jpg",
- "name": "IBICT",
- "slug": "ibict",
- "url": "https://repositoriopesquisas.ibict.br/",
- "version": "4.5.1"
- },
- {
- "description": "The IISH Dataverse contains micro-, meso-, and macro-level datasets on social and economic history.",
- "full_name": "International Institute of Social History",
- "id": 1748,
- "is_active": true,
- "lat": 52.369021,
- "lng": 4.939226,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/internationalInstituteOfSocialHistory-46x46.jpg",
- "name": "IISH Dataverse",
- "slug": "iish-dataverse",
- "url": "https://datasets.socialhistory.org/",
- "version": "4.3"
- },
- {
- "description": "Johns Hopkins University Data Archive",
- "full_name": "Johns Hopkins",
- "id": 1749,
- "is_active": true,
- "lat": 39.329055,
- "lng": -76.620335,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/johns-46x46.jpg",
- "name": "Johns Hopkins University",
- "slug": "johns-hopkins-university",
- "url": "https://archive.data.jhu.edu/",
- "version": "4.6"
- },
- {
- "description": "Libra Data is a place for UVA researchers to share data publicly, and is part of the Libra Scholarly Repository suite of services which includes works of UVA scholarship such as articles, books, theses, and data.",
- "full_name": "Libra Data (University of Virginia)",
- "id": 1750,
- "is_active": true,
- "lat": 38.034578,
- "lng": -78.507394,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/libra-46x46.jpg",
- "name": "Libra Data",
- "slug": "libra-data",
- "url": "https://dataverse.lib.virginia.edu/",
- "version": "4.7.1"
- },
- {
- "description": "Open for all researchers worldwide from all disciplines to deposit data. The Odum Institute also offers multiple data curation service levels. For more information, go to http://www.irss.unc.edu/odum/contentPrimary.jsp?nodeid=5.",
- "full_name": "Odum Institute for Research in Social Science",
- "id": 1751,
- "is_active": true,
- "lat": 35.905022,
- "lng": -79.050851,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/unc-46x46.png",
- "name": "UNC Dataverse",
- "slug": "unc-dataverse",
- "url": "https://dataverse.unc.edu/",
- "version": "4.7.1"
- },
- {
- "description": "Peking University Open Research Data Platform",
- "full_name": "Peking University",
- "id": 1752,
- "is_active": true,
- "lat": 39.993923,
- "lng": 116.306539,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/peking-46x46.jpg",
- "name": "Peking University",
- "slug": "peking-university",
- "url": "http://opendata.pku.edu.cn/",
- "version": "4.0"
- },
- {
- "description": "Open for University of Alberta affiliated researchers to deposit data.",
- "full_name": "University of Alberta Libraries Dataverse",
- "id": 1755,
- "is_active": true,
- "lat": 53.494321,
- "lng": -113.549027,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/uOfAlberta-46x46.jpg",
- "name": "UAL Dataverse",
- "slug": "ual-dataverse",
- "url": "https://dataverse.library.ualberta.ca/dvn/",
- "version": "4.5.1"
- },
- {
- "description": "A service brought to you by the ACG@UMaine. The way Supercomputing should be!",
- "full_name": "Maine Dataverse Network",
- "id": 1756,
- "is_active": true,
- "lat": 44.901349,
- "lng": -68.671815,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/uOfMaine-46x46.jpg",
- "name": "Maine Dataverse Network",
- "slug": "maine-dataverse-network",
- "url": "http://dataverse.acg.maine.edu/dvn/",
- "version": "3.5.1"
- },
- {
- "description": "International Crops Research Institute for the Semi-Arid Tropics. Free open data repository of ICRISAT research data including Social science, Phenotypic, Genotypic, Spatial and Soil & Weather data which are linked with open publications.",
- "full_name": "ICRISAT",
- "id": 1757,
- "is_active": true,
- "lat": 17.385,
- "lng": 78.4867,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/icrisat-46x46.png",
- "name": "ICRISAT",
- "slug": "icrisat",
- "url": "http://dataverse.icrisat.org/",
- "version": "4.8.1"
- },
- {
- "description": "Open for researchers and organizations associated with Ontario universities to deposit data.",
- "full_name": "Scholars Portal Dataverse",
- "id": 1758,
- "is_active": true,
- "lat": 43.6532,
- "lng": -79.3832,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/scholarsportal-46x46.jpg",
- "name": "Scholars Portal",
- "slug": "scholars-portal",
- "url": "https://dataverse.scholarsportal.info/",
- "version": "4.10.1"
- },
- {
- "description": "Open for researchers and organizations associated with\r\nFrench universities to deposit data. Hosted by the Center for\r\nSocio-Political Data (Sciences Po and CNRS).",
- "full_name": "Catalogues (CDSP)",
- "id": 1759,
- "is_active": true,
- "lat": 48.854027,
- "lng": 2.328351,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/catalogues-46x46.png",
- "name": "Catalogues (CDSP)",
- "slug": "catalogues-cdsp",
- "url": "https://catalogues.cdsp.sciences-po.fr/",
- "version": "4.6.1"
- },
- {
- "description": "A statewide archive of research data from Texas Digital Library (TDL) member institutions.",
- "full_name": "Texas Data Repository Dataverse",
- "id": 1761,
- "is_active": true,
- "lat": 30.307182,
- "lng": -97.755996,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/tdl-46x46.png",
- "name": "Texas Data Repository Dataverse",
- "slug": "texas-data-repository-dataverse",
- "url": "https://dataverse.tdl.org/",
- "version": "4.7.1"
- },
- {
- "description": "Repositorio de Datos del Consorcio Madro\u00f1o.",
- "full_name": "Dataverse e-cienciaDatos",
- "id": 1762,
- "is_active": true,
- "lat": 40.416775,
- "lng": -3.7492,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/consorciomadrono-46x46.gif",
- "name": "Dataverse e-cienciaDatos",
- "slug": "dataverse-e-cienciadatos",
- "url": "https://edatos.consorciomadrono.es/",
- "version": "4.8.4"
- },
- {
- "description": "Center for International Forestry Research (CIFOR) Dataverse",
- "full_name": "Center for International Forestry Research (CIFOR) Dataverse",
- "id": 1763,
- "is_active": true,
- "lat": -6.594293,
- "lng": 106.806,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/CIFOR-46x46.png",
- "name": "CIFOR",
- "slug": "cifor",
- "url": "https://data.cifor.org",
- "version": "4.6"
- },
- {
- "description": "Organisme fran\u00e7ais de recherche agronomique et de coop\u00e9ration internationale pour le d\u00e9veloppement durable des r\u00e9gions tropicales et m\u00e9diterran\u00e9ennes, les activit\u00e9s du CIRAD rel\u00e8vent des sciences du vivant, des sciences sociales et des sciences de l\u2019ing\u00e9nieur appliqu\u00e9es \u00e0 l\u2019agriculture, \u00e0 l\u2019alimentation, \u00e0 l\u2019environnement et \u00e0 la gestion des territoires.\r\n\r\nFrench agricultural research and international cooperation organization working for the sustainable development of tropical and Mediterranean regions, CIRAD's activities concern the life sciences, social sciences and engineering sciences, applied to agriculture, the environment and territorial management.",
- "full_name": "CIRAD Dataverse",
- "id": 1764,
- "is_active": true,
- "lat": 43.650089,
- "lng": 3.869122,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/cirad-46x46.jpg",
- "name": "CIRAD",
- "slug": "cirad",
- "url": "https://dataverse.cirad.fr/",
- "version": "4.5"
- },
- {
- "description": "",
- "full_name": "University of Manitoba Dataverse",
- "id": 1765,
- "is_active": true,
- "lat": 49.895077,
- "lng": -97.138451,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/manitoba-46x46.jpg",
- "name": "University of Manitoba Dataverse",
- "slug": "university-of-manitoba-dataverse",
- "url": "https://dataverse.lib.umanitoba.ca/",
- "version": "4.8.4"
- },
- {
- "description": "Research data archive open for Norwegian research institutions. Operated by UiT The Arctic University of Norway.",
- "full_name": "Dataverse Network Norway",
- "id": 1767,
- "is_active": true,
- "lat": 69.649208,
- "lng": 18.955324,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/dataverseNO-46x46.png",
- "name": "DataverseNO",
- "slug": "dataverseno",
- "url": "https://dataverse.no/",
- "version": "4.9.4"
- },
- {
- "description": "The institutional open access research data repository for Nanyang Technological University (NTU). NTU researchers are encouraged to use DR-NTU (Data) to deposit, publish and archive their final research data in order to make their research data discoverable, accessible and reusable.",
- "full_name": "Nanyang Technological University",
- "id": 1768,
- "is_active": true,
- "lat": 1.348668,
- "lng": 103.683104,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/NTULogo_2.gif",
- "name": "DR-NTU (Data)",
- "slug": "dr-ntu-data",
- "url": "https://researchdata.ntu.edu.sg",
- "version": "4.7.1"
- },
- {
- "description": "The Australian Data Archive provides a national service for collecting, preserving, publishing and accessing digital research data.",
- "full_name": "Australian Data Archive",
- "id": 1771,
- "is_active": true,
- "lat": -35.343784,
- "lng": 149.082977,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/ada-46x46.jpg",
- "name": "ADA Dataverse",
- "slug": "ada-dataverse",
- "url": "https://dataverse.ada.edu.au/",
- "version": "4.6.1"
- },
- {
- "description": "",
- "full_name": "University of New Brunswick Libraries",
- "id": 1772,
- "is_active": true,
- "lat": 45.964993,
- "lng": -66.646332,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/unbDataverse-46x46.png",
- "name": "UNB Libraries Dataverse",
- "slug": "unb-libraries-dataverse",
- "url": "https://dataverse.lib.unb.ca/",
- "version": "4.8.2"
- },
- {
- "description": "AUSSDA - The Austrian Social Science Data Archive makes social science data accessible, creating opportunities for research and data reuse, benefitting science and society. AUSSDA serves as the Austrian representative in the Consortium of European Social Science Data Archives (CESSDA ERIC).",
- "full_name": "Austrian Social Science Data Archive",
- "id": 1773,
- "is_active": true,
- "lat": 48.210033,
- "lng": 16.363449,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/aussda-46x46.png",
- "name": "AUSSDA Dataverse",
- "slug": "aussda-dataverse",
- "url": "https://data.aussda.at/",
- "version": "4.6.2"
- },
- {
- "description": "Share, publish and get credit for your data. Find and cite research data from across all research fields.",
- "full_name": "Dalhousie University",
- "id": 1774,
- "is_active": true,
- "lat": 44.637484,
- "lng": -63.59122,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/dalhousie-46x46.png",
- "name": "Dalhousie University Dataverse",
- "slug": "dalhousie-university-dataverse",
- "url": "https://dataverse.library.dal.ca",
- "version": "4.7"
- },
- {
- "description": "The University of the West Indies Research Datasets Repository.",
- "full_name": "The University of the West Indies",
- "id": 1775,
- "is_active": true,
- "lat": 18.006372,
- "lng": -76.747148,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/uwi-46x46.png",
- "name": "UWI",
- "slug": "uwi",
- "url": "http://dataverse.sta.uwi.edu/",
- "version": "4.8.4"
- },
- {
- "description": "The Repositori Ilmiah Nasional (RIN) is a means to share, preserve, cite, explore, and analyze research data. RIN increases data availability and allows others to reproduce research more easily. Researchers, data authors, publishers, data distributors, and affiliate institutions all receive academic credit and web visibility. Researchers, agencies, and funders have full control over research data.",
- "full_name": "Lembaga Ilmu Pengetahuan Indonesia (LIPI) Dataverse",
- "id": 1776,
- "is_active": true,
- "lat": -6.228771,
- "lng": 106.818082,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/LIPI-46x46.png",
- "name": "LIPI Dataverse",
- "slug": "lipi-dataverse",
- "url": "https://data.lipi.go.id",
- "version": "4.6.2"
- },
- {
- "description": "INRA is Europe\u2019s top agricultural research institute and the world\u2019s number two centre for the agricultural sciences. Data Inra is offered by INRA as part of its mission to open the results of its research.\r\n\r\nData Inra will share research data in relation with food, nutrition, agriculture and environment. It includes experimental, simulation and observation data, omic data, survey and text data.\r\n\r\nOnly data produced by or in collaboration with INRA will be hosted in the repository, but anyone can access the metadata and the open data.",
- "full_name": "National Institute of Agricultural Research (INRA)",
- "id": 1777,
- "is_active": true,
- "lat": 48.801407,
- "lng": 2.130122,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/inra-46x46-2.jpg",
- "name": "Data Inra",
- "slug": "data-inra",
- "url": "https://data.inra.fr/",
- "version": "4.5.1"
- },
- {
- "description": "The Botswana Harvard AIDS Institute Partneship is a world-renowned educational institution of excellence in research and education pertinent to HIV/AIDS and other emerging public health challenges. Established in 1996, the Botswana Harvard AIDS Institute Partnership (BHP) is a collaborative research and training initiative between Botswana\u2019s Ministry of Health and Wellness and the Harvard T.H. Chan School of Public Health AIDS Initiative. The BHP Dataverse is a data repository for all the research done at BHP. Raw data, anonymised data and final analysis data for every research. This repository will achieve and also easy data sharing within the organisation and outside.",
- "full_name": "Botswana Harvard AIDS Institute Partnership",
- "id": 1778,
- "is_active": true,
- "lat": -24.653257,
- "lng": 25.906792,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/botswana-46x46.png",
- "name": "Botswana Harvard Data",
- "slug": "botswana-harvard-data",
- "url": "https://dataverse.bhp.org.bw/",
- "version": "4.9.4"
- },
- {
- "description": "Transportation data repository maintained by the Virginia Tech Transportation Institute.",
- "full_name": "Virginia Tech Transportation Institute",
- "id": 1779,
- "is_active": true,
- "lat": 37.190102,
- "lng": -80.396776,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/vtti-46x46_Q6PC6r7.png",
- "name": "VTTI",
- "slug": "vtti",
- "url": "https://dataverse.vtti.vt.edu/",
- "version": "4.9.4"
- },
- {
- "description": "",
- "full_name": "Universidade Federal do ABC (UFABC)",
- "id": 1780,
- "is_active": true,
- "lat": -23.643807,
- "lng": -46.528304,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/ufabc-46x46_pWiXhTC.png",
- "name": "Reposit\u00f3rio de Dados de Pesquisa da UFABC",
- "slug": "repositorio-de-dados-de-pesquisa-da-ufabc",
- "url": "http://dataverse.ufabc.edu.br",
- "version": "4.8.5"
- },
- {
- "description": "QDR curates, stores, preserves, publishes, and enables the download of digital data generated through qualitative and multi-method research in the social sciences. The repository develops and disseminates guidance for managing, sharing, citing, and reusing qualitative data, and contributes to the generation of common standards for doing so. QDR\u2019s overarching goals are to make sharing qualitative data customary in the social sciences, to broaden access to social science data, and to strengthen qualitative and multi-method research.",
- "full_name": "Qualitative Data Repository",
- "id": 1781,
- "is_active": true,
- "lat": 43.038013,
- "lng": -76.135566,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/qdr-46x46_kCBOWwk.png",
- "name": "QDR Main Collection",
- "slug": "qdr-main-collection",
- "url": "https://data.qdr.syr.edu",
- "version": "4.10.1"
- },
- {
- "description": "Ifsttar Dataverse is an institutional repository for research data of the French Institute of Science and Technology for Transport, Development and Networks. It catalogues research data in the field of transports, spatial planning and civil engineering.",
- "full_name": "French Institute of Science and Technology for Transport, Development and Networks",
- "id": 1782,
- "is_active": true,
- "lat": 48.8528,
- "lng": 2.6027,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/ifsttar-46x46.jpg",
- "name": "Ifsttar Dataverse",
- "slug": "ifsttar-dataverse",
- "url": "https://research-data.ifsttar.fr/dataverse/data",
- "version": "4.10.1"
- },
- {
- "description": "Centro Internacional De La Papa (International Potato Center) is a member of the CGIAR Consortium, an international organization made up of 15 centers engaged in research for a food secure future.",
- "full_name": "International Potato Center",
- "id": 1783,
- "is_active": true,
- "lat": -12.077791,
- "lng": -76.946888,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/cip-46x46.png",
- "name": "International Potato Center",
- "slug": "international-potato-center",
- "url": "https://data.cipotato.org/dataverse.xhtml",
- "version": "4.8.1"
- },
- {
- "description": "Datasets from the International AAAI Conference on Web and Social Media (ICWSM).",
- "full_name": "International AAAI Conference on Web and Social Media",
- "id": 1784,
- "is_active": true,
- "lat": 37.432057,
- "lng": -122.175297,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/icwsm-46x46.jpg",
- "name": "ICWSM",
- "slug": "icwsm",
- "url": "https://dataverse.mpi-sws.org/dataverse/icwsm",
- "version": "4.8.5"
- },
- {
- "description": "Research data repository of Institute of Linguistics and Literature / Federal University of Uberlandia",
- "full_name": "Institute of Linguistics and Literature (Federal University of Uberl\u00e2ndia)",
- "id": 1785,
- "is_active": true,
- "lat": -18.908702,
- "lng": -48.291944,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/ileel-46x46_JPxxGBm.png",
- "name": "Reposit\u00f3rio de Dados de Pesquisa do ILEEL",
- "slug": "repositorio-de-dados-de-pesquisa-do-ileel",
- "url": "http://dataverse.ileel.ufu.br",
- "version": "4.11"
- },
- {
- "description": "NIE Data Repository is the institutional open access research data repository for National Institute of Education, Singapore. NIE researchers are encouraged to use NIE Data Repository to deposit, publish and archive their final research data in order to make their research data discoverable, accessible and reusable.",
- "full_name": "National Institute of Education",
- "id": 1786,
- "is_active": true,
- "lat": 1.349115,
- "lng": 103.678829,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/nie-46x46.png",
- "name": "NIE Data Repository",
- "slug": "nie-data-repository",
- "url": "https://researchdata.nie.edu.sg",
- "version": "4.10.1"
- },
- {
- "description": "The Dataverse portal of the International Center for Agricultural Research in Dry Ares.",
- "full_name": "International Center for Agriculture Research in the Dry Areas",
- "id": 1787,
- "is_active": true,
- "lat": 33.88863,
- "lng": 35.49548,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/meldata-46x46.png",
- "name": "MELDATA",
- "slug": "meldata",
- "url": "http://data.mel.cgiar.org",
- "version": "4.8.5"
- },
- {
- "description": "",
- "full_name": "UCLA Data Science Center",
- "id": 1788,
- "is_active": true,
- "lat": 34.0689,
- "lng": -118.4452,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/ucla-46x46.png",
- "name": "UCLA Dataverse",
- "slug": "ucla-dataverse",
- "url": "https://dataverse.ucla.edu",
- "version": "4.13"
- },
- {
- "description": "Explore research data from Universidad del Rosario affiliated researchers.",
- "full_name": "Universidad del Rosario",
- "id": 1789,
- "is_active": true,
- "lat": 4.600598,
- "lng": -74.073352,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/ur-46x46_ohTqcfg.png",
- "name": "Repositorio de Datos de Investigaci\u00f3n Universidad del Rosario",
- "slug": "repositorio-de-datos-de-investigacion-universidad-del-rosario",
- "url": "http://research-data.urosario.edu.co/",
- "version": "4.9.4"
- },
- {
- "description": "The University of Milan is home to important research teams operating in the university's extremely rich variety of scientific-disciplinary sectors. Besides taking part in the most relevant national and international research programs, the University is also very active in the field of technology transfer and developing applications for scientific research results. dataverse.unimi.it is the repository for research data offered to all researchers in disciplines from health sciences to laws, from economics to hard sciences, from humanities to mathematics.",
- "full_name": "University of Milan",
- "id": 1790,
- "is_active": true,
- "lat": 45.4601,
- "lng": 9.1946,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/milan-46x46.png",
- "name": "Universit\u00e0 degli Studi di Milano",
- "slug": "universita-degli-studi-di-milano",
- "url": "https://dataverse.unimi.it/",
- "version": null
- },
- {
- "description": "G\u00f6ttingen Research Online is an institutional repository for the publication of research data at the G\u00f6ttingen Campus. It is managed by the G\u00f6ttingen eResearch Alliance, a joint group of the G\u00f6ttingen State and University Library and Gesellschaft f\u00fcr wissenschaftliche Datenverarbeitung mbH G\u00f6ttingen.",
- "full_name": "G\u00f6ttingen eResearch Alliance",
- "id": 1791,
- "is_active": true,
- "lat": 51.533713,
- "lng": 9.932198,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/gottingen-46x46_ZryDdtE.jpg",
- "name": "G\u00f6ttingen Research Online",
- "slug": "gottingen-research-online",
- "url": "https://data.gro.uni-goettingen.de/",
- "version": "4.14"
- },
- {
- "description": "Hosted by IRD, a french public research institution, who defends an original model of equitable scientific partnership with the countries of the South and an interdisciplinary and citizen science, committed to the achievement of the Sustainable Development Goals.",
- "full_name": "French Research Institute for Development",
- "id": 1792,
- "is_active": true,
- "lat": 43.309873,
- "lng": 5.368818,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/ird-46x46.png",
- "name": "Data Suds",
- "slug": "data-suds",
- "url": "https://dataverse.ird.fr",
- "version": "4.14"
- },
- {
- "description": "El Instituto de Opini\u00f3n P\u00fablica de la Pontificia Universidad Cat\u00f3lica del Per\u00fa (IOP) se cre\u00f3 a finales del 2005 con la finalidad de generar un mayor conocimiento acerca de los fen\u00f3menos que interesan, preocupan y afectan a la sociedad peruana. Teniendo como eje metodol\u00f3gico el estudio de la opini\u00f3n p\u00fablica, el IOP investiga las creencias, actitudes y sentimientos que configuran las din\u00e1micas sociales en el Per\u00fa.",
- "full_name": "Pontificia Universidad Cat\u00f3lica del Per\u00fa",
- "id": 1793,
- "is_active": true,
- "lat": -12.068924,
- "lng": -77.079338,
- "logo": "https://dvn-h-prod.hz.lib.harvard.edu/media/logos/pucp-46x46.png",
- "name": "Pontificia Universidad Cat\u00f3lica del Per\u00fa",
- "slug": "pontificia-universidad-catolica-del-peru",
- "url": "http://datos.pucp.edu.pe",
- "version": "4.13"
- },
- {
- "description": "The Jet Propulsion Laboratory is a federally funded research and development center and NASA field center.",
- "full_name": "NASA/Caltech Jet Propulsion Laboratory",
- "id": 1794,
- "is_active": true,
- "lat": 34.201694,
- "lng": -118.171667,
- "logo": "https://www.jpl.nasa.gov/assets/images/logo_nasa_trio_black@2x.png",
- "name": "Jet Propulsion Laboratory",
- "slug": "jpl",
- "url": "https://dataverse.jpl.nasa.gov",
- "version": "5.9"
- },
- {
- "description": "The Research Data Repository for the Max Planck Society.",
- "full_name": "Edmond - Research Data Repository for the Max Planck Society (old domain)",
- "id": 1795,
- "is_active": true,
- "lat": 48.1366,
- "lng": 11.5771,
- "logo": "https://edmond.mpdl.mpg.de/logos/navbar/logo_for_bright.png",
- "name": "Edmond (old domain)",
- "slug": "edmond_mpdl_mpg_de",
- "url": "https://edmond.mpdl.mpg.de",
- "version": "5.14"
- },
- {
- "description": "The Research Data Repository for the Max Planck Society.",
- "full_name": "Edmond - Research Data Repository for the Max Planck Society",
- "id": 1796,
- "is_active": true,
- "lat": 48.1366,
- "lng": 11.5771,
- "logo": "https://edmond.mpg.de/logos/navbar/logo_for_bright.png",
- "name": "Edmond",
- "slug": "edmond_mpg_de",
- "url": "https://edmond.mpg.de",
- "version": "5.14"
- },
- {
- "description": "The institutional research data repository of the National Yang Ming Chiao Tung University",
- "full_name": "National Yang Ming Chiao Tung University",
- "id": 1797,
- "is_active": true,
- "lat": 24.7867714,
- "lng": 120.9984256,
- "logo": "https://dataverse.lib.nycu.edu.tw/logos/navbar/logo.png",
- "name": "NYCU Dataverse",
- "slug": "nycu-dataverse",
- "url": "https://dataverse.lib.nycu.edu.tw",
- "version": "5.10.1"
- }
- ]
-}
diff --git a/repo2docker/contentproviders/dataverse.py b/repo2docker/contentproviders/dataverse.py
index 9054f53ce..35fc3a8ff 100644
--- a/repo2docker/contentproviders/dataverse.py
+++ b/repo2docker/contentproviders/dataverse.py
@@ -1,9 +1,11 @@
-import json
import os
import shutil
-from urllib.parse import parse_qs, urlparse, urlunparse
+from typing import List, Tuple
+from urllib.parse import parse_qs, urlparse
-from ..utils import copytree, deep_get
+import requests
+
+from ..utils import copytree, deep_get, is_doi
from .doi import DoiProvider
@@ -11,22 +13,33 @@ class Dataverse(DoiProvider):
"""
Provide contents of a Dataverse dataset.
- This class loads a a list of existing Dataverse installations from the internal
- file dataverse.json. This file is manually updated with the following command:
-
- python setup.py generate_dataverse_file
+ Dataverse installations are downloaded from
+ https://iqss.github.io/dataverse-installations/data/data.json
+ or $R2D_DATAVERSE_INSTALLATIONS_URL if specified.
"""
- def __init__(self):
- data_file = os.path.join(os.path.dirname(__file__), "dataverse.json")
- with open(data_file) as fp:
- self.hosts = json.load(fp)["installations"]
- super().__init__()
+ dataverse_installations_url = (
+ os.environ.get("R2D_DATAVERSE_INSTALLATIONS_URL")
+ or "https://iqss.github.io/dataverse-installations/data/data.json"
+ )
+
+ hosts = None
- def detect(self, doi, ref=None, extra_args=None):
- """Trigger this provider for things that resolve to a Dataverse dataset.
+ def load_hosts(self):
+ if self.hosts is None:
+ self.log.debug(
+ "Retrieving dataverse installations from %s\n",
+ self.dataverse_installations_url,
+ )
+ r = requests.get(self.dataverse_installations_url)
+ r.raise_for_status()
+ self.hosts = r.json()["installations"]
+
+ def detect(self, spec, ref=None, extra_args=None):
+ """
+ Detect if given spec is hosted on dataverse
- Handles:
+ The spec can be:
- DOI pointing to {siteURL}/dataset.xhtml?persistentId={persistentId}
- DOI pointing to {siteURL}/file.xhtml?persistentId={persistentId}&...
- URL {siteURL}/api/access/datafile/{fileId}
@@ -35,75 +48,154 @@ def detect(self, doi, ref=None, extra_args=None):
- https://dataverse.harvard.edu/api/access/datafile/3323458
- doi:10.7910/DVN/6ZXAGT
- doi:10.7910/DVN/6ZXAGT/3YRRYJ
-
"""
- url = self.doi2url(doi)
+ if is_doi(spec):
+ url = self.doi2url(spec)
+ else:
+ url = spec
# Parse the url, to get the base for later API calls
parsed_url = urlparse(url)
# Check if the url matches any known Dataverse installation, bail if not.
+ self.load_hosts()
host = next(
- (
- host
- for host in self.hosts
- if urlparse(host["url"]).netloc == parsed_url.netloc
- ),
+ (host for host in self.hosts if host["hostname"] == parsed_url.netloc),
None,
)
if host is None:
return
- query_args = parse_qs(parsed_url.query)
- # Corner case handling
- if parsed_url.path.startswith("/file.xhtml"):
- # There's no way of getting file information using its persistentId, the only thing we can do is assume that doi
- # is structured as "doi:/" and try to handle dataset that way.
- new_doi = doi.rsplit("/", 1)[0]
- if new_doi == doi:
- # tough luck :( Avoid inifite recursion and exit.
- return
- return self.detect(new_doi)
- elif parsed_url.path.startswith("/api/access/datafile"):
- # Raw url pointing to a datafile is a typical output from an External Tool integration
- entity_id = os.path.basename(parsed_url.path)
- search_query = "q=entityId:" + entity_id + "&type=file"
- # Knowing the file identifier query search api to get parent dataset
- search_url = urlunparse(
- parsed_url._replace(path="/api/search", query=search_query)
+ # At this point, we *know* this is a dataverse URL, because:
+ # 1. The DOI resolved to a particular host (if using DOI)
+ # 2. The host is in the list of known dataverse installations
+ #
+ # We don't know exactly what kind of dataverse object this is, but
+ # that can be figured out during fetch as needed
+ return url
+
+ def get_dataset_id_from_file_id(self, base_url: str, file_id: str) -> str:
+ """
+ Return the persistent_id (DOI) of a dataset that a given file_id (int or doi) belongs to
+ """
+ if file_id.isdigit():
+ # the file_id is an integer, rather than a persistent id (DOI)
+ api_url = f"{base_url}/api/files/{file_id}?returnDatasetVersion=true"
+ else:
+ # the file_id is a doi itself
+ api_url = f"{base_url}/api/files/:persistentId?persistentId={file_id}&returnDatasetVersion=true"
+
+ resp = self._request(api_url)
+ if resp.status_code == 404:
+ raise ValueError(f"File with id {file_id} not found in {base_url}")
+
+ resp.raise_for_status()
+
+ data = resp.json()["data"]
+ return data["datasetVersion"]["datasetPersistentId"]
+
+ def parse_dataverse_url(self, url: str) -> Tuple[str, bool]:
+ """
+ Parse the persistent id out of a dataverse URL
+
+ persistent_id can point to either a dataset or a file. The second return
+ value is False if we know that the persistent id is a file or a dataset,
+ and True if it is ambiguous.
+
+ Raises a ValueError if we can not parse the url
+ """
+ parsed_url = urlparse(url)
+ path = parsed_url.path
+ qs = parse_qs(parsed_url.query)
+ base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+ is_ambiguous = False
+ # https://dataverse.harvard.edu/citation?persistentId=doi:10.7910/DVN/TJCLKP
+ if path.startswith("/citation"):
+ is_ambiguous = True
+ persistent_id = qs["persistentId"][0]
+ # https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/TJCLKP
+ elif path.startswith("/dataset.xhtml"):
+ # https://dataverse.harvard.edu/api/access/datafile/3323458
+ persistent_id = qs["persistentId"][0]
+ elif path.startswith("/api/access/datafile"):
+ # What we have here is an entity id, which we can use to get a persistentId
+ file_id = os.path.basename(path)
+ persistent_id = self.get_dataset_id_from_file_id(base_url, file_id)
+ elif parsed_url.path.startswith("/file.xhtml"):
+ file_persistent_id = qs["persistentId"][0]
+ persistent_id = self.get_dataset_id_from_file_id(
+ base_url, file_persistent_id
+ )
+ else:
+ raise ValueError(
+ f"Could not determine persistent id for dataverse URL {url}"
+ )
+
+ return persistent_id, is_ambiguous
+
+ def get_datafiles(self, url: str) -> List[dict]:
+ """
+ Return a list of dataFiles for given persistent_id
+
+ Supports the following *dataset* URL styles:
+ - /citation: https://dataverse.harvard.edu/citation?persistentId=doi:10.7910/DVN/TJCLKP
+ - /dataset.xhtml: https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/TJCLKP
+
+ Supports the following *file* URL styles (entire dataset file belongs to will be fetched):
+ - /api/access/datafile: https://dataverse.harvard.edu/api/access/datafile/3323458
+ - /file.xhtml: https://dataverse.harvard.edu/file.xhtml?persistentId=doi:10.7910/DVN/6ZXAGT/3YRRYJ
+ - /citation: https://dataverse.harvard.edu/citation?persistentId=doi:10.7910/DVN/6ZXAGT/3YRRYJ
+
+ If a URL can not be parsed, throw an exception
+ """
+
+ parsed_url = urlparse(url)
+ base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+ persistent_id, is_ambiguous = self.parse_dataverse_url(url)
+
+ dataset_api_url = (
+ f"{base_url}/api/datasets/:persistentId?persistentId={persistent_id}"
+ )
+ resp = self._request(dataset_api_url, headers={"accept": "application/json"})
+ if resp.status_code == 404 and is_ambiguous:
+ # It's possible this is a *file* persistent_id, not a dataset one
+ persistent_id = self.get_dataset_id_from_file_id(base_url, persistent_id)
+ dataset_api_url = (
+ f"{base_url}/api/datasets/:persistentId?persistentId={persistent_id}"
)
- self.log.debug("Querying Dataverse: " + search_url)
- data = self.urlopen(search_url).json()["data"]
- if data["count_in_response"] != 1:
- self.log.debug(
- f"Dataverse search query failed!\n - doi: {doi}\n - url: {url}\n - resp: {json.dump(data)}\n"
- )
- return
-
- self.record_id = deep_get(data, "items.0.dataset_persistent_id")
- elif (
- parsed_url.path.startswith("/dataset.xhtml")
- and "persistentId" in query_args
- ):
- self.record_id = deep_get(query_args, "persistentId.0")
-
- if hasattr(self, "record_id"):
- return {"record": self.record_id, "host": host}
+ resp = self._request(
+ dataset_api_url, headers={"accept": "application/json"}
+ )
+
+ if resp.status_code == 404:
+ # This persistent id is just not here
+ raise ValueError(f"{persistent_id} on {base_url} is not found")
+
+ # We already handled 404, raise error for everything else
+ resp.raise_for_status()
+
+ # We know the exact persistent_id of the dataset we fetched now
+ # Save it for use as content_id
+ self.persistent_id = persistent_id
+
+ data = resp.json()["data"]
+
+ return data["latestVersion"]["files"]
def fetch(self, spec, output_dir, yield_output=False):
"""Fetch and unpack a Dataverse dataset."""
- record_id = spec["record"]
- host = spec["host"]
-
- yield f"Fetching Dataverse record {record_id}.\n"
- url = f'{host["url"]}/api/datasets/:persistentId?persistentId={record_id}'
+ url = spec
+ parsed_url = urlparse(url)
+ # FIXME: Support determining API URL better
+ base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
- resp = self.urlopen(url, headers={"accept": "application/json"})
- record = resp.json()["data"]
+ yield f"Fetching Dataverse record {url}.\n"
- for fobj in deep_get(record, "latestVersion.files"):
+ for fobj in self.get_datafiles(url):
file_url = (
# without format=original you get the preservation format (plain text, tab separated)
- f'{host["url"]}/api/access/datafile/{deep_get(fobj, "dataFile.id")}?format=original'
+ f'{base_url}/api/access/datafile/{deep_get(fobj, "dataFile.id")}?format=original'
)
filename = fobj["label"]
original_filename = fobj["dataFile"].get("originalFileName", None)
@@ -128,5 +220,9 @@ def fetch(self, spec, output_dir, yield_output=False):
@property
def content_id(self):
- """The Dataverse persistent identifier."""
- return self.record_id
+ """
+ The Dataverse persistent identifier.
+
+ Only valid if called after a succesfull fetch
+ """
+ return self.persistent_id
diff --git a/repo2docker/contentproviders/doi.py b/repo2docker/contentproviders/doi.py
index 065602b7a..306248021 100644
--- a/repo2docker/contentproviders/doi.py
+++ b/repo2docker/contentproviders/doi.py
@@ -29,38 +29,32 @@ def _request(self, url, **kwargs):
urlopen = _request
- def _urlopen(self, req, headers=None):
- """A urlopen() helper"""
- # someone passed a string, not a request
- if not isinstance(req, request.Request):
- req = request.Request(req)
-
- req.add_header("User-Agent", f"repo2docker {__version__}")
- if headers is not None:
- for key, value in headers.items():
- req.add_header(key, value)
-
- return request.urlopen(req)
-
def doi2url(self, doi):
# Transform a DOI to a URL
# If not a doi, assume we have a URL and return
if is_doi(doi):
- doi = normalize_doi(doi)
-
- try:
- resp = self._request(f"https://doi.org/{doi}")
- resp.raise_for_status()
- except HTTPError as e:
- # If the DOI doesn't exist, just return URL
- if e.response.status_code == 404:
- return doi
- # Reraise any other errors because if the DOI service is down (or
- # we hit a rate limit) we don't want to silently continue to the
- # default Git provider as this leads to a misleading error.
- logging.error(f"DOI {doi} does not resolve: {e}")
+ normalized_doi = normalize_doi(doi)
+
+ # Use the doi.org resolver API
+ # documented at https://www.doi.org/the-identifier/resources/factsheets/doi-resolution-documentation#5-proxy-server-rest-api
+ req_url = f"https://doi.org/api/handles/{normalized_doi}"
+ resp = self._request(req_url)
+ if resp.status_code == 404:
+ # Not a doi, return what we were passed in
+ return doi
+ elif resp.status_code == 200:
+ data = resp.json()
+ # Pick the first URL we find from the doi response
+ for v in data["values"]:
+ if v["type"] == "URL":
+ return v["data"]["value"]
+
+ # No URLs found for this doi, what do we do?
+ self.log.error("DOI {normalized_doi} doesn't point to any URLs")
+ return doi
+ else:
+ # If we get any other status codes, raise error
raise
- return resp.url
else:
# Just return what is actulally just a URL
return doi
@@ -70,7 +64,7 @@ def fetch_file(self, file_ref, host, output_dir, unzip=False):
# file related to a record
file_url = deep_get(file_ref, host["download"])
fname = deep_get(file_ref, host["filename"])
- logging.debug(f"Downloading file {file_url} as {fname}\n")
+ self.log.debug(f"Downloading file {file_url} as {fname}\n")
yield f"Requesting {file_url}\n"
resp = self._request(file_url, stream=True)
diff --git a/repo2docker/contentproviders/hydroshare.py b/repo2docker/contentproviders/hydroshare.py
index 4104d97a6..c7f8e3e61 100755
--- a/repo2docker/contentproviders/hydroshare.py
+++ b/repo2docker/contentproviders/hydroshare.py
@@ -1,4 +1,3 @@
-import json
import os
import shutil
import time
@@ -33,8 +32,10 @@ def detect(self, doi, ref=None, extra_args=None):
"hostname": [
"https://www.hydroshare.org/resource/",
"http://www.hydroshare.org/resource/",
+ "https://hydroshare.org/resource/",
+ "http://hydroshare.org/resource/",
],
- "django_irods": "https://www.hydroshare.org/django_irods/download/bags/",
+ "django_s3": "https://www.hydroshare.org/django_s3/download/bags/",
"version": "https://www.hydroshare.org/hsapi/resource/{}/scimeta/elements",
}
]
@@ -58,16 +59,15 @@ def fetch(self, spec, output_dir, yield_output=False, timeout=120):
resource_id = spec["resource"]
host = spec["host"]
- bag_url = f'{host["django_irods"]}{resource_id}'
+ bag_url = f'{host["django_s3"]}{resource_id}.zip'
yield f"Downloading {bag_url}.\n"
# bag downloads are prepared on demand and may need some time
conn = self.urlopen(bag_url)
total_wait_time = 0
- while (
- conn.status_code == 200
- and conn.headers["content-type"] != "application/zip"
+ while conn.status_code == 200 and not conn.url.startswith(
+ f"https://s3.hydroshare.org/bags/{resource_id}.zip"
):
wait_time = 10
total_wait_time += wait_time
diff --git a/repo2docker/docker.py b/repo2docker/docker.py
index 830e5c78b..c00cda7b7 100644
--- a/repo2docker/docker.py
+++ b/repo2docker/docker.py
@@ -2,12 +2,38 @@
Docker container engine for repo2docker
"""
+import json
+import os
+import shutil
+import subprocess
+import tarfile
+import tempfile
+from argparse import ArgumentError
+from contextlib import ExitStack, contextmanager
+from pathlib import Path
+
from iso8601 import parse_date
-from traitlets import Dict
+from traitlets import Dict, List, Unicode, default
import docker
-from .engine import Container, ContainerEngine, ContainerEngineException, Image
+from .engine import Container, ContainerEngine, Image
+from .utils import execute_cmd
+
+# The DOCKER_HOST environment variable is used by Docker to set a remote host.
+# The same DOCKER_HOST environment variable is also used
+# by the official Software Development Kit (SDK) and other libraries
+# as a way to select the socket to communicate with.
+# Because repo2docker uses the official Docker SDK,
+# Podman users MUST configure the DOCKER_HOST environment variable.
+DOCKER_HOST = os.getenv("DOCKER_HOST")
+# It is possible for a user to create a Podman socket
+# that does not include the string "podman"
+# but expect the string "podman" is good enough for repo2docker use case.
+if DOCKER_HOST is not None and DOCKER_HOST.find("podman") != -1:
+ DOCKER_CLI = "podman"
+else:
+ DOCKER_CLI = "docker"
class DockerContainer(Container):
@@ -53,7 +79,43 @@ class DockerEngine(ContainerEngine):
https://docker-py.readthedocs.io/en/4.2.0/api.html#module-docker.api.build
"""
- string_output = False
+ string_output = True
+
+ _container_cli = None
+
+ @property
+ def container_cli(self):
+ if self._container_cli is not None:
+ return self._container_cli
+
+ cli = DOCKER_CLI
+
+ cli_version_call = [cli, "version"]
+ try:
+ docker_version = subprocess.run(
+ cli_version_call, stdout=subprocess.DEVNULL, check=True
+ )
+ except (subprocess.CalledProcessError, OSError) as e:
+ raise RuntimeError(
+ f"The {cli} commandline client must be installed: {e}"
+ ) from None
+
+ # docker buildx is based in a plugin that might not be installed
+ # https://github.com/docker/buildx
+ #
+ # podman buildx command is an alias of podman build.
+ # Not all buildx build features are available in Podman.
+ cli_buildx_version_call = [cli, "buildx", "version"]
+ try:
+ docker_buildx_version = subprocess.run(
+ cli_buildx_version_call, stdout=subprocess.DEVNULL, check=True
+ )
+ except (subprocess.CalledProcessError, OSError) as e:
+ raise RuntimeError(f"The buildx plugin must be installed: {e}") from None
+
+ self._container_cli = cli
+
+ return self._container_cli
extra_init_args = Dict(
{},
@@ -69,19 +131,19 @@ class DockerEngine(ContainerEngine):
config=True,
)
- def __init__(self, *, parent):
- super().__init__(parent=parent)
- try:
- kwargs = docker.utils.kwargs_from_env()
- kwargs.update(self.extra_init_args)
- kwargs.setdefault("version", "auto")
- self._apiclient = docker.APIClient(**kwargs)
- except docker.errors.DockerException as e:
- raise ContainerEngineException("Check if docker is running on the host.", e)
+ extra_buildx_build_args = List(
+ Unicode(),
+ help="""
+ Extra commandline arguments to pass to `docker buildx build` when building the image.
+ """,
+ config=True,
+ )
def build(
self,
*,
+ push=False,
+ load=False,
buildargs=None,
cache_from=None,
container_limits=None,
@@ -94,35 +156,116 @@ def build(
platform=None,
**kwargs,
):
- return self._apiclient.build(
- buildargs=buildargs,
- cache_from=cache_from,
- container_limits=container_limits,
- forcerm=True,
- rm=True,
- tag=tag,
- custom_context=custom_context,
- decode=True,
- dockerfile=dockerfile,
- fileobj=fileobj,
- path=path,
- labels=labels,
- platform=platform,
- **kwargs,
- )
+ args = [self.container_cli, "buildx", "build", "--progress", "plain"]
+ if load:
+ if push:
+ raise ValueError(
+ "Setting push=True and load=True is currently not supported"
+ )
+ args.append("--load")
- def images(self):
- images = self._apiclient.images()
- return [Image(tags=image["RepoTags"]) for image in images]
+ if push:
+ args.append("--push")
+
+ if buildargs:
+ for k, v in buildargs.items():
+ args += ["--build-arg", f"{k}={v}"]
+
+ if cache_from:
+ for cf in cache_from:
+ args += ["--cache-from", cf]
+
+ if dockerfile:
+ args += ["--file", dockerfile]
+
+ if tag:
+ args += ["--tag", tag]
+
+ if labels:
+ for k, v in labels.items():
+ args += ["--label", f"{k}={v}"]
+
+ if platform:
+ args += ["--platform", platform]
+
+ # place extra args right *before* the path
+ args += self.extra_buildx_build_args
+
+ with ExitStack() as stack:
+ if self.registry_credentials:
+ stack.enter_context(self.docker_login(**self.registry_credentials))
+ if fileobj:
+ with tempfile.TemporaryDirectory() as d:
+ tarf = tarfile.open(fileobj=fileobj)
+ tarf.extractall(d)
+
+ args += [d]
+
+ yield from execute_cmd(args, True)
+ else:
+ # Assume 'path' is passed in
+ args += [path]
+
+ yield from execute_cmd(args, True)
def inspect_image(self, image):
- image = self._apiclient.inspect_image(image)
- return Image(tags=image["RepoTags"], config=image["Config"])
+ """
+ Return image configuration if it exists, otherwise None
+ """
+ proc = subprocess.run(
+ [self.container_cli, "image", "inspect", image], capture_output=True
+ )
+
+ if proc.returncode != 0:
+ return None
+
+ config = json.loads(proc.stdout.decode())[0]
+ tags = config["RepoTags"]
+ oci_image_configuration = config["Config"]
+
+ # WorkingDir is optional but docker always include it.
+ # https://github.com/containers/podman/discussions/27313
+ if "WorkingDir" not in oci_image_configuration:
+ oci_image_configuration["WorkingDir"] = ""
+
+ return Image(tags=tags, config=oci_image_configuration)
+
+ @contextmanager
+ def docker_login(self, username, password, registry):
+ # Determine existing DOCKER_CONFIG
+ old_dc_path = os.environ.get("DOCKER_CONFIG")
+ if old_dc_path is None:
+ dc_path = Path("~/.docker/config.json").expanduser()
+ else:
+ dc_path = Path(old_dc_path)
+
+ with tempfile.TemporaryDirectory() as d:
+ new_dc_path = Path(d) / "config.json"
+ if dc_path.exists():
+ # If there is an existing DOCKER_CONFIG, copy it to new location so we inherit
+ # whatever configuration the user has already set
+ shutil.copy2(dc_path, new_dc_path)
- def push(self, image_spec):
- if self.registry_credentials:
- self._apiclient.login(**self.registry_credentials)
- return self._apiclient.push(image_spec, stream=True)
+ os.environ["DOCKER_CONFIG"] = d
+ try:
+ subprocess.run(
+ [
+ self.container_cli,
+ "login",
+ "--username",
+ username,
+ "--password-stdin",
+ registry,
+ ],
+ input=password.encode(),
+ check=True,
+ )
+ yield
+ finally:
+ if old_dc_path:
+ os.environ["DOCKER_CONFIG"] = old_dc_path
+ else:
+ del os.environ["DOCKER_CONFIG"]
def run(
self,
diff --git a/repo2docker/engine.py b/repo2docker/engine.py
index a8897a64e..310bddbbf 100644
--- a/repo2docker/engine.py
+++ b/repo2docker/engine.py
@@ -6,7 +6,7 @@
import os
from abc import ABC, abstractmethod
-from traitlets import Dict, default
+from traitlets import Dict, TraitError, default, validate
from traitlets.config import LoggingConfigurable
# Based on https://docker-py.readthedocs.io/en/4.2.0/containers.html
@@ -176,6 +176,17 @@ def _registry_credentials_default(self):
raise
return {}
+ @validate("registry_credentials")
+ def _registry_credentials_validate(self, proposal):
+ """
+ Validate form of registry credentials
+ """
+ new = proposal["value"]
+ if len({"registry", "username", "password"} & new.keys()) != 3:
+ raise TraitError(
+ "registry_credentials must have keys 'registry', 'username' and 'password'"
+ )
+
string_output = True
"""
Whether progress events should be strings or an object.
@@ -202,6 +213,8 @@ def __init__(self, *, parent):
def build(
self,
*,
+ push=False,
+ load=False,
buildargs={},
cache_from=[],
container_limits={},
@@ -219,6 +232,10 @@ def build(
Parameters
----------
+ push: bool
+ Push the resulting image to a registry
+ load: bool
+ Load the resulting image into the container store ready to be run
buildargs : dict
Dictionary of build arguments
cache_from : list[str]
@@ -254,19 +271,9 @@ def build(
"""
raise NotImplementedError("build not implemented")
- def images(self):
- """
- List images
-
- Returns
- -------
- list[Image] : List of Image objects.
- """
- raise NotImplementedError("images not implemented")
-
def inspect_image(self, image):
"""
- Get information about an image
+ Get information about an image, or None if the image does not exist
TODO: This is specific to the engine, can we convert it to a standard format?
@@ -281,27 +288,6 @@ def inspect_image(self, image):
"""
raise NotImplementedError("inspect_image not implemented")
- def push(self, image_spec):
- """
- Push image to a registry
-
- If the registry_credentials traitlets is set it should be used to
- authenticate with the registry before pushing.
-
- Parameters
- ----------
- image_spec : str
- The repository spec to push to
-
- Returns
- -------
- A generator of strings. If an error occurs an exception must be thrown.
-
- If `string_output=True` this should instead be whatever Docker returns:
- https://github.com/jupyter/repo2docker/blob/0.11.0/repo2docker/app.py#L469-L495
- """
- raise NotImplementedError("push not implemented")
-
# Note this is different from the Docker client which has Client.containers.run
def run(
self,
diff --git a/repo2docker/semver.py b/repo2docker/semver.py
index 661829ecc..24dd07319 100644
--- a/repo2docker/semver.py
+++ b/repo2docker/semver.py
@@ -30,7 +30,7 @@ def str_to_version(vstr):
return tuple([int(n) for n in vstr.split(".")])
-@lru_cache()
+@lru_cache
def parse_version(vstr):
"""Convert a simple 'x[.y[.z]]' version string to a comparable VersionInfo
@@ -62,7 +62,7 @@ def patch(v):
return v[2] if len(v) >= 3 else 0
-@lru_cache()
+@lru_cache
def create_semver_matcher(constraint_str):
"""Create a matcher that can be used to match version tuples
diff --git a/repo2docker/utils.py b/repo2docker/utils.py
index 05ba1e535..d3f03aeec 100644
--- a/repo2docker/utils.py
+++ b/repo2docker/utils.py
@@ -1,6 +1,7 @@
import os
import platform
import re
+import socket
import subprocess
import warnings
from contextlib import contextmanager
@@ -537,3 +538,14 @@ def get_platform():
else:
warnings.warn(f"Unexpected platform '{m}', defaulting to linux/amd64")
return "linux/amd64"
+
+
+def get_free_port():
+ """
+ Hacky method to get a free random port on local host
+ """
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.bind(("", 0))
+ port = s.getsockname()[1]
+ s.close()
+ return port
diff --git a/requirements.in b/requirements.in
new file mode 100644
index 000000000..a0a961df6
--- /dev/null
+++ b/requirements.in
@@ -0,0 +1,2 @@
+# freeze current requirements, used in Dockerfile
+.
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000..1ee4ea202
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,87 @@
+#
+# This file is autogenerated by pip-compile with Python 3.12
+# by the following command:
+#
+# ./scripts/refreeze
+#
+aiofiles==25.1.0
+ # via osfclient
+anyio==4.15.1
+ # via httpx
+beautifulsoup4==4.15.0
+ # via jupyter-repo2docker
+certifi==2026.10.23
+ # via
+ # httpcore
+ # httpx
+ # requests
+charset-normalizer==3.4.7
+ # via
+ # jupyter-repo2docker
+ # requests
+docker==7.1.0
+ # via jupyter-repo2docker
+entrypoints==0.4
+ # via jupyter-repo2docker
+escapism==1.1.0
+ # via jupyter-repo2docker
+h11==0.16.0
+ # via httpcore
+httpcore==1.0.9
+ # via httpx
+httpx==0.28.1
+ # via osfclient
+idna==3.11
+ # via
+ # anyio
+ # httpx
+ # requests
+iso8601==2.1.0
+ # via jupyter-repo2docker
+jinja2==3.1.6
+ # via jupyter-repo2docker
+markupsafe==3.0.3
+ # via jinja2
+osfclient @ git+https://github.com/RCOSDP/rdmclient.git@master
+ # via jupyter-repo2docker
+packaging==26.1
+ # via jupyter-repo2docker
+python-dateutil==3.0.0
+ # via osfclient
+python-json-logger==4.1.0
+ # via jupyter-repo2docker
+requests==2.33.1
+ # via
+ # docker
+ # jupyter-repo2docker
+ruamel-yaml==0.19.1
+ # via jupyter-repo2docker
+semver==3.0.4
+ # via jupyter-repo2docker
+six==1.17.0
+ # via
+ # osfclient
+ # python-dateutil
+soupsieve==3.0.4
+ # via beautifulsoup4
+toml==0.10.2
+ # via jupyter-repo2docker
+tqdm==4.70.1
+ # via osfclient
+traitlets==5.14.3
+ # via jupyter-repo2docker
+typing-extensions==4.16.0
+ # via
+ # anyio
+ # beautifulsoup4
+tzdata==2026.5
+ # via python-dateutil
+tzlocal==5.4.5
+ # via osfclient
+urllib3==2.6.3
+ # via
+ # docker
+ # requests
+
+# The following packages are considered to be unsafe in a requirements file:
+# jupyter-repo2docker
diff --git a/scripts/refreeze b/scripts/refreeze
new file mode 100755
index 000000000..8edae5690
--- /dev/null
+++ b/scripts/refreeze
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+"""
+Run pip-compile in docker to generate requirements.txt
+
+Should only need running when requirements change in pyproject.toml, which is uncommon.
+Dependabot should handle updating requirements.txt most of the time.
+"""
+
+from pathlib import Path
+from subprocess import run
+
+repo = Path(__file__).parents[1].resolve()
+
+
+def get_alpine_version():
+ """
+ Get the alpine version from our dockerfile
+
+ make sure we're running with the same version as the docker image.
+ """
+ dockerfile = repo / "Dockerfile"
+ with dockerfile.open() as f:
+ for line in f:
+ if "ALPINE_VERSION" in line:
+ return line.partition("=")[2].strip()
+ raise ValueError(f"Couldn't find ALPINE_VERSION= in {dockerfile}")
+
+
+def refreeze():
+ """
+ Run pip-compile in docker to generate requirements.txt
+ """
+
+ alpine_version = get_alpine_version()
+
+ run(
+ [
+ "docker",
+ "run",
+ "--rm",
+ f"-v{repo}:/tmp/src",
+ "-w/tmp/src",
+ "-eCUSTOM_COMPILE_COMMAND=./scripts/refreeze",
+ f"alpine:{alpine_version}",
+ "sh",
+ "-c",
+ """
+ apk add py3-pip git
+ python3 -m venv /tmp/venv
+ source /tmp/venv/bin/activate
+ pip install pip-tools
+ # exclude repo2docker itself because dependabot messes it up
+ pip-compile --upgrade --no-allow-unsafe --unsafe-package=jupyter-repo2docker
+ """,
+ ]
+ )
+
+
+if __name__ == "__main__":
+ refreeze()
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 42e18cad8..000000000
--- a/setup.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-# used by our travis auto-deployment system
-# needs changing if repo2docker ever stops being
-# a pure python module
-[wheel]
-universal = 1
-
-[metadata]
-license_file = LICENSE
-
-[versioneer]
-VCS = git
-style = pep440
-versionfile_source = repo2docker/_version.py
-versionfile_build = repo2docker/_version.py
-tag_prefix =
-parentdir_prefix = repo2docker-
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 754dc59b4..000000000
--- a/setup.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import sys
-from distutils.cmd import Command
-
-from setuptools import find_packages, setup
-
-import versioneer
-
-if sys.version_info[0] < 3:
- readme = None
-else:
- with open("README.md", encoding="utf8") as f:
- readme = f.read()
-
-
-class GenerateDataverseInstallationsFileCommand(Command):
- description = "Generate Dataverse installations data map"
- user_options = []
-
- def initialize_options(self):
- self.url = (
- "https://services.dataverse.harvard.edu/miniverse/map/installations-json"
- )
-
- def finalize_options(self):
- pass
-
- def run(self):
- import json
- from urllib.request import urlopen
-
- resp = urlopen(self.url, timeout=5)
- resp_body = resp.read()
- data = json.loads(resp_body.decode("utf-8"))
- if "installations" not in data:
- raise ValueError("Malformed installation map.")
-
- def get_identifier(json):
- return int(json["id"])
-
- data["installations"].sort(key=get_identifier)
- with open("repo2docker/contentproviders/dataverse.json", "w") as fp:
- json.dump(data, fp, indent=4, sort_keys=True)
-
-
-__cmdclass = versioneer.get_cmdclass()
-__cmdclass["generate_dataverse_file"] = GenerateDataverseInstallationsFileCommand
-
-setup(
- name="jupyter-repo2docker",
- version=versioneer.get_version(),
- install_requires=[
- "charset-normalizer",
- "docker!=5.0.0",
- "entrypoints",
- "escapism",
- "iso8601",
- "jinja2",
- "python-json-logger",
- "requests",
- "ruamel.yaml>=0.15",
- "semver",
- "osfclient @ git+https://github.com/RCOSDP/rdmclient.git@master",
- "toml",
- "traitlets",
- "beautifulsoup4>=4.10.0",
- ],
- python_requires=">=3.6",
- author="Project Jupyter Contributors",
- author_email="jupyter@googlegroups.com",
- url="https://repo2docker.readthedocs.io/en/latest/",
- project_urls={
- "Documentation": "https://repo2docker.readthedocs.io",
- "Funding": "https://jupyter.org/about",
- "Source": "https://github.com/jupyterhub/repo2docker/",
- "Tracker": "https://github.com/jupyterhub/repo2docker/issues",
- },
- # this should be a whitespace separated string of keywords, not a list
- keywords="reproducible science environments docker",
- description="Repo2docker: Turn code repositories into Jupyter enabled Docker Images",
- long_description=readme,
- long_description_content_type="text/markdown",
- license="BSD",
- classifiers=[
- "Environment :: Console",
- "Intended Audience :: Developers",
- "Intended Audience :: System Administrators",
- "Intended Audience :: Science/Research",
- "License :: OSI Approved :: BSD License",
- "Programming Language :: Python",
- "Programming Language :: Python :: 3",
- ],
- packages=find_packages(),
- include_package_data=True,
- cmdclass=__cmdclass,
- entry_points={
- "console_scripts": [
- "jupyter-repo2docker = repo2docker.__main__:main",
- "repo2docker = repo2docker.__main__:main",
- ],
- "repo2docker.engines": ["docker = repo2docker.docker:DockerEngine"],
- },
-)
diff --git a/tests/base/general/README.md b/tests/base/general/README.md
new file mode 100644
index 000000000..0ddd16903
--- /dev/null
+++ b/tests/base/general/README.md
@@ -0,0 +1,3 @@
+# General tests for the base env
+
+general tests about the OS, base environment, etc.
diff --git a/tests/base/general/test-extra-args.yaml b/tests/base/general/test-extra-args.yaml
new file mode 100644
index 000000000..0e704da26
--- /dev/null
+++ b/tests/base/general/test-extra-args.yaml
@@ -0,0 +1,4 @@
+# make sure we run as uid 1000, user jovyan
+# not the default host uid and username, which vary
+- --user-id=1000
+- --user-name=jovyan
diff --git a/tests/base/general/tests.py b/tests/base/general/tests.py
new file mode 100644
index 000000000..6dbfbfa5a
--- /dev/null
+++ b/tests/base/general/tests.py
@@ -0,0 +1,33 @@
+# this file is not called test_whatever
+# so that the outer test discovery doesn't pick it up
+
+import getpass
+import os
+import shutil
+from pathlib import Path
+from subprocess import check_output
+
+
+def test_node():
+ node = shutil.which("node")
+ assert node is not None
+ node_version = check_output(["node", "--version"], text=True)
+ assert "v20" in node_version
+ npm = shutil.which("npm")
+ # npm version isn't pinned
+ npm_version = check_output(["npm", "--version"], text=True)
+
+
+def test_nb_user():
+ assert os.getuid() == 1000
+ assert os.getgid() == 1000
+ user = getpass.getuser()
+ assert user == "jovyan"
+ home = Path.home()
+ assert home == Path("/home/jovyan")
+ assert home.exists()
+ assert home.owner() == user
+ with (home / "testfile").open("w") as f:
+ f.write("canwrite")
+ for p in home.rglob("*"):
+ assert p.owner() == user
diff --git a/tests/base/general/verify b/tests/base/general/verify
new file mode 100755
index 000000000..0ff935f39
--- /dev/null
+++ b/tests/base/general/verify
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -ex
+
+# install pytest here because we want to invoke the Base BuildPack
+pip install pytest
+pytest -v --color=yes tests.py
diff --git a/tests/base/node/README.md b/tests/base/node/README.md
deleted file mode 100644
index 09007cc86..000000000
--- a/tests/base/node/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Test that node 16 and npm 8 are installed and runnable.
diff --git a/tests/base/node/verify b/tests/base/node/verify
deleted file mode 100755
index 753215533..000000000
--- a/tests/base/node/verify
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-
-set -ex
-
-# check node system package and its version
-which node
-node --version
-node --version | grep v20
-
-# npm comes with the nodejs conda-forge package that we have pinned, but we
-# don't have npm pinned. For example nodejs 18.13.0 brought npm 8.19.3, but
-# nodejs 18.14.0 brought npm 9.3.1.
-#
-# Since we have no npm version pinning, we just test that some version is
-# installed.
-#
-which npm
-npm --version
diff --git a/tests/conda/downgrade/verify b/tests/conda/downgrade/verify
index 2c69b3e81..ebe4c5bee 100755
--- a/tests/conda/downgrade/verify
+++ b/tests/conda/downgrade/verify
@@ -24,7 +24,7 @@ assert pkgs["xeus-cling"]["version"] == "0.6.0"
# this may be brittle, but it's unlikely any of these old versions
# of packages will be rebuilt
# xeus-cling 0.6.0 pins xeus 0.20, which pins openssl 1.1.1,
-# which in turn downgrades Python from >=3.9.16 to 3.9.6
+# which in turn downgrades Python from >=3.9.16 to 3.9.0
assert pkgs["openssl"]["version"].startswith("1.1.1"), pkgs["openssl"]["version"]
assert pkgs["python"]["version"] == "3.9.0", pkgs["python"]["version"]
diff --git a/tests/conda/py35-binder-dir/verify b/tests/conda/py35-binder-dir/verify
index ef166558f..d20cbb03b 100755
--- a/tests/conda/py35-binder-dir/verify
+++ b/tests/conda/py35-binder-dir/verify
@@ -16,13 +16,10 @@ v = out.split()[1]
assert v[:3] == "3.5", out
out = sh(["micromamba", "--version"])
-assert out == "1.5.9", out
+assert out == "2.3.3", out
out = sh(["mamba", "--version"])
-assert (
- out
- == """mamba 1.5.9
-conda 24.5.0"""
-), out
+assert out == "2.3.3", out
+
sh([kernel_python, "-c", "import numpy"])
diff --git a/tests/conda/yaml/environment.yaml b/tests/conda/yaml/environment.yaml
new file mode 100644
index 000000000..94af7b1e3
--- /dev/null
+++ b/tests/conda/yaml/environment.yaml
@@ -0,0 +1,2 @@
+dependencies:
+ - python=3.12
diff --git a/tests/conda/yaml/verify b/tests/conda/yaml/verify
new file mode 100755
index 000000000..bc347d686
--- /dev/null
+++ b/tests/conda/yaml/verify
@@ -0,0 +1,4 @@
+#!/usr/bin/env python
+import sys
+
+assert sys.version_info[:2] == (3, 12), sys.version
diff --git a/tests/conftest.py b/tests/conftest.py
index bade0a1ad..b77bfcb05 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -27,10 +27,13 @@
import requests
import yaml
+import docker
from repo2docker.__main__ import make_r2d
TESTS_DIR = os.path.abspath(os.path.dirname(__file__))
+DOCKER_CLIENT = client = docker.from_env()
+
def pytest_collect_file(parent, file_path):
if file_path.name == "verify":
@@ -39,7 +42,7 @@ def pytest_collect_file(parent, file_path):
return RemoteRepoList.from_parent(parent, path=file_path)
-def make_test_func(args, skip_build=False, extra_run_kwargs=None):
+def make_test_func(args, skip_build=False, extra_run_kwargs=None, external_script=None):
"""Generate a test function that runs repo2docker"""
def test():
@@ -82,10 +85,19 @@ def build_noop():
success = True
break
assert success, f"Notebook never started in {container}"
+
+ if external_script:
+ subprocess.check_call([external_script, f"http://localhost:{port}"])
+
finally:
# stop the container
container.stop()
app.wait_for_container(container)
+ try:
+ container.remove()
+ except:
+ pass
+ DOCKER_CLIENT.images.remove(image=app.output_image_spec)
return test
@@ -202,12 +214,21 @@ class Repo2DockerTest(pytest.Function):
"""A pytest.Item for running repo2docker"""
def __init__(
- self, name, parent, args=None, skip_build=False, extra_run_kwargs=None
+ self,
+ name,
+ parent,
+ args=None,
+ skip_build=False,
+ extra_run_kwargs=None,
+ external_script=None,
):
self.args = args
self.save_cwd = os.getcwd()
f = parent.obj = make_test_func(
- args, skip_build=skip_build, extra_run_kwargs=extra_run_kwargs
+ args,
+ skip_build=skip_build,
+ extra_run_kwargs=extra_run_kwargs,
+ external_script=external_script,
)
super().__init__(name, parent, callobj=f)
@@ -246,6 +267,17 @@ def collect(self):
args.append(str(self.path.parent))
yield Repo2DockerTest.from_parent(self, name="build", args=args)
+ # If external-verify exists it should be run on the host
+ external_verify_script = self.path.parent / "external-verify"
+ if external_verify_script.exists():
+ yield Repo2DockerTest.from_parent(
+ self,
+ name=external_verify_script.name,
+ args=args,
+ skip_build=True,
+ external_script=external_verify_script,
+ )
+
yield Repo2DockerTest.from_parent(
self,
name=self.path.name,
diff --git a/tests/contentproviders/test_dataverse.py b/tests/contentproviders/test_dataverse.py
new file mode 100644
index 000000000..bc3203062
--- /dev/null
+++ b/tests/contentproviders/test_dataverse.py
@@ -0,0 +1,144 @@
+import hashlib
+import os
+from tempfile import TemporaryDirectory
+
+import pytest
+
+from repo2docker.contentproviders import Dataverse
+
+
+@pytest.mark.parametrize(
+ ("doi", "resolved"),
+ [
+ (
+ "doi:10.7910/DVN/6ZXAGT/3YRRYJ",
+ "https://dataverse.harvard.edu/file.xhtml?persistentId=doi:10.7910/DVN/6ZXAGT/3YRRYJ",
+ ),
+ (
+ "10.7910/DVN/6ZXAGT/3YRRYJ",
+ "https://dataverse.harvard.edu/file.xhtml?persistentId=doi:10.7910/DVN/6ZXAGT/3YRRYJ",
+ ),
+ (
+ "10.7910/DVN/TJCLKP",
+ "https://dataverse.harvard.edu/citation?persistentId=doi:10.7910/DVN/TJCLKP",
+ ),
+ (
+ "https://dataverse.harvard.edu/api/access/datafile/3323458",
+ "https://dataverse.harvard.edu/api/access/datafile/3323458",
+ ),
+ (
+ "https://data.cimmyt.org/dataset.xhtml?persistentId=hdl:11529/10016",
+ "https://data.cimmyt.org/dataset.xhtml?persistentId=hdl:11529/10016",
+ ),
+ ("/some/random/string", None),
+ ("https://example.com/path/here", None),
+ # Non dataverse DOIs
+ ("https://doi.org/10.21105/joss.01277", None),
+ ],
+)
+def test_detect(doi, resolved):
+ assert Dataverse().detect(doi) == resolved
+
+
+@pytest.mark.parametrize(
+ ("url", "persistent_id", "is_ambiguous"),
+ [
+ (
+ "https://dataverse.harvard.edu/file.xhtml?persistentId=doi:10.7910/DVN/6ZXAGT/3YRRYJ",
+ "doi:10.7910/DVN/6ZXAGT",
+ False,
+ ),
+ (
+ "https://dataverse.harvard.edu/citation?persistentId=doi:10.7910/DVN/TJCLKP",
+ "doi:10.7910/DVN/TJCLKP",
+ True,
+ ),
+ (
+ "https://dataverse.harvard.edu/api/access/datafile/3323458",
+ "doi:10.7910/DVN/3MJ7IR",
+ False,
+ ),
+ (
+ "https://data.cimmyt.org/dataset.xhtml?persistentId=hdl:11529/10016",
+ "hdl:11529/10016",
+ False,
+ ),
+ ],
+)
+def test_get_persistent_id(url, persistent_id, is_ambiguous):
+ assert Dataverse().parse_dataverse_url(url) == (persistent_id, is_ambiguous)
+
+
+@pytest.mark.parametrize(
+ ("specs", "md5tree"),
+ [
+ (
+ (
+ "https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/TJCLKP&version=5.0",
+ "https://dataverse.harvard.edu/citation?persistentId=doi:10.7910/DVN/TJCLKP",
+ "doi:10.7910/DVN/TJCLKP",
+ ),
+ {
+ "data/primary/primary-data.zip": "f24541c5f89a09bc6db2781220cbbe70",
+ "data/2026-06-24.tsv": "0165275ec1437dd2bcb1c6b4b56ef233",
+ "code/language.py": "9d61582bcf497c83bbd1ed0eed3c772e",
+ },
+ ),
+ (
+ (
+ "https://dataverse.harvard.edu/file.xhtml?persistentId=doi:10.7910/DVN/6ZXAGT/3YRRYJ",
+ "https://dataverse.harvard.edu/citation?persistentId=doi:10.7910/DVN/6ZXAGT/3YRRYJ",
+ "doi:10.7910/DVN/6ZXAGT/3YRRYJ",
+ ),
+ {
+ "ArchaeoGLOBE-master/analysis/figures/1_response_distribution.png": "243c6a3dd66bc3c84102829b277ef333",
+ "ArchaeoGLOBE-master/analysis/figures/2_trends_map_knowledge.png": "2ace6ae9d470dda6cf2f9f9a6588171a",
+ "ArchaeoGLOBE-master/analysis/figures/3_trends_global.png": "63ccd0a7b2d20440cd8f418d4ee88c4d",
+ "ArchaeoGLOBE-master/analysis/figures/4_consensus_transitions.png": "facfaedabeac77c4496d4b9e962a917f",
+ "ArchaeoGLOBE-master/analysis/figures/5_ArchaeoGLOBE_HYDE_comparison.png": "8e002e4d50f179fc1808f562b1353588",
+ "ArchaeoGLOBE-master/apt.txt": "b4224032da6c71d48f46c9b78fc6ed77",
+ "ArchaeoGLOBE-master/analysis/archaeoglobe.pdf": "f575be4790efc963ef1bd40d097cc06d",
+ "ArchaeoGLOBE-master/analysis/archaeoglobe.Rmd": "f37d5f7993fde9ebd64d16b20fc22905",
+ "ArchaeoGLOBE-master/ArchaeoGLOBE.Rproj": "d0250e7918993bab1e707358fe5633e0",
+ "ArchaeoGLOBE-master/CONDUCT.md": "f87ef290340322089c32b4e573d8f1e8",
+ "ArchaeoGLOBE-master/.circleci/config.yml": "6eaa54073a682b3195d8fab3a9dd8344",
+ "ArchaeoGLOBE-master/CONTRIBUTING.md": "b3a6abfc749dd155a3049f94a855bf9f",
+ "ArchaeoGLOBE-master/DESCRIPTION": "745ef979494999e483987de72c0adfbd",
+ "ArchaeoGLOBE-master/dockerfile": "aedce68e5a7d6e79cbb24c9cffeae593",
+ "ArchaeoGLOBE-master/.binder/Dockerfile": "7564a41246ba99b60144afb1d3b6d7de",
+ "ArchaeoGLOBE-master/.gitignore": "62c1482e4febbd35dc02fb7e2a31246b",
+ "ArchaeoGLOBE-master/analysis/data/derived-data/hyde_crop_prop.RDS": "2aea7748b5586923b0de9d13af58e59d",
+ "ArchaeoGLOBE-master/analysis/data/derived-data/kk_anthro_prop.RDS": "145a9e5dd2c95625626a720b52178b70",
+ "ArchaeoGLOBE-master/LICENSE.md": "3aa9d41a92a57944bd4590e004898445",
+ "ArchaeoGLOBE-master/analysis/data/derived-data/placeholder": "d41d8cd98f00b204e9800998ecf8427e",
+ "ArchaeoGLOBE-master/.Rbuildignore": "df15e4fed49abd685b536fef4472b01f",
+ "ArchaeoGLOBE-master/README.md": "0b0faabe580c4d76a0e0d64a4f54bca4",
+ "ArchaeoGLOBE-master/analysis/data/derived-data/README.md": "547fd1a6e874f6178b1cf525b5b9ae72",
+ "ArchaeoGLOBE-master/analysis/figures/S1_FHG_consensus.png": "d2584352e5442b33e4b23e361ca70fe1",
+ "ArchaeoGLOBE-master/analysis/figures/S2_EXAG_consensus.png": "513eddfdad01fd01a20263a55ca6dbe3",
+ "ArchaeoGLOBE-master/analysis/figures/S3_INAG_consensus.png": "b16ba0ecd21b326f873209a7e55a8deb",
+ "ArchaeoGLOBE-master/analysis/figures/S4_PAS_consensus.png": "05695f9412337a00c1cb6d1757d0ec5c",
+ "ArchaeoGLOBE-master/analysis/figures/S5_URBAN_consensus.png": "10119f7495d3b8e7ad7f8a0770574f15",
+ "ArchaeoGLOBE-master/analysis/figures/S6_trends_map_landuse.png": "b1db7c97f39ccfc3a9e094c3e6307af0",
+ "ArchaeoGLOBE-master/analysis/figures/S7_ArchaeoGLOBE_KK10_comparison.png": "30341748324f5f66acadb34c114c3e9d",
+ },
+ ),
+ ],
+)
+def test_fetch(specs: list[str], md5tree):
+ dv = Dataverse()
+
+ for spec in specs:
+ with TemporaryDirectory() as d:
+ output = []
+ for l in dv.fetch(dv.detect(spec), d):
+ output.append(l)
+
+ # Verify md5 sum of the files we expect to find
+ # We are using md5 instead of something more secure because that is what
+ # dataverse itself uses
+ for subpath, expected_sha in md5tree.items():
+ with open(os.path.join(d, subpath), "rb") as f:
+ h = hashlib.md5()
+ h.update(f.read())
+ assert h.hexdigest() == expected_sha
diff --git a/tests/dockerfile/simple/Dockerfile b/tests/dockerfile/simple/Dockerfile
index 492446360..a3e44b5c5 100644
--- a/tests/dockerfile/simple/Dockerfile
+++ b/tests/dockerfile/simple/Dockerfile
@@ -2,11 +2,11 @@ FROM python:3.10
RUN pip install --no-cache notebook
-CMD "/bin/sh"
+CMD ["/bin/sh"]
ADD sayhi.sh /usr/local/bin/sayhi.sh
ADD verify verify
ARG NB_UID
-ENV HOME /tmp
+ENV HOME=/tmp
USER $NB_UID
diff --git a/tests/norun/.gitignore b/tests/norun/.gitignore
new file mode 100644
index 000000000..6cbb595e8
--- /dev/null
+++ b/tests/norun/.gitignore
@@ -0,0 +1 @@
+tmp-certs-*
diff --git a/tests/norun/Dockerfile b/tests/norun/Dockerfile
new file mode 100644
index 000000000..bd083ea7f
--- /dev/null
+++ b/tests/norun/Dockerfile
@@ -0,0 +1,2 @@
+# Smallest possible dockerfile, used only for building images to be tested
+FROM scratch
\ No newline at end of file
diff --git a/tests/norun/test_find.py b/tests/norun/test_find.py
new file mode 100644
index 000000000..4b8d2bd18
--- /dev/null
+++ b/tests/norun/test_find.py
@@ -0,0 +1,23 @@
+import secrets
+from pathlib import Path
+
+from repo2docker.__main__ import make_r2d
+
+HERE = Path(__file__).parent
+
+
+def test_find_image():
+ image_name = f"{secrets.token_hex(8)}:latest"
+ r2d = make_r2d(["--image", image_name, str(HERE)])
+
+ r2d.build()
+
+ assert r2d.find_image()
+
+
+def test_dont_find_image():
+ image_name = f"{secrets.token_hex(8)}:latest"
+ r2d = make_r2d(["--image", image_name, str(HERE)])
+
+ # Just don't actually start the build, so image won't be found
+ assert not r2d.find_image()
diff --git a/tests/norun/test_registry.py b/tests/norun/test_registry.py
new file mode 100644
index 000000000..9be543b54
--- /dev/null
+++ b/tests/norun/test_registry.py
@@ -0,0 +1,247 @@
+import json
+import os
+import secrets
+import shutil
+import socket
+import subprocess
+import time
+from base64 import b64encode
+from pathlib import Path
+from tempfile import TemporaryDirectory
+
+import bcrypt
+import pytest
+import requests
+
+from repo2docker.__main__ import make_r2d
+from repo2docker.docker import DOCKER_CLI
+from repo2docker.utils import get_free_port
+
+HERE = Path(__file__).parent
+
+
+@pytest.fixture(scope="session")
+def dind(registry):
+ port = get_free_port()
+ registry_host, _, _ = registry
+
+ # docker daemon will generate certs here, that we can then use to connect to it.
+ # put it in current dir than in /tmp because on macos, current dir is likely to
+ # shared with docker VM so it can be mounted, unlike /tmp
+ cert_dir = HERE / f"tmp-certs-{secrets.token_hex(8)}"
+ cert_dir.mkdir()
+
+ dind_image = "docker:dind"
+ subprocess.check_call(["docker", "pull", dind_image])
+
+ cmd = [
+ "docker",
+ "run",
+ "-e",
+ "DOCKER_TLS_CERTDIR=/opt/certs",
+ "--privileged",
+ "--mount",
+ f"type=bind,src={cert_dir},dst=/opt/certs",
+ "-p",
+ f"{port}:2376",
+ dind_image,
+ "--host",
+ "0.0.0.0:2376",
+ "--insecure-registry",
+ registry_host,
+ ]
+ proc = subprocess.Popen(cmd)
+ time.sleep(5)
+
+ try:
+ yield f"tcp://127.0.0.1:{port}", cert_dir
+ finally:
+ try:
+ shutil.rmtree(cert_dir)
+ except PermissionError:
+ # Sometimes this is owned by root in CI. is ok, let's let it go
+ pass
+ proc.terminate()
+ proc.wait()
+
+
+@pytest.fixture(scope="session")
+def host_ip():
+ # Get the IP of the current machine, as we need to use the same IP
+ # for all our docker commands, *and* the dind we run needs to reach it
+ # in the same way.
+ # Thanks to https://stackoverflow.com/a/28950776
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.settimeout(0)
+ try:
+ # doesn't even have to be reachable
+ s.connect(("10.254.254.254", 1))
+ host_ip = s.getsockname()[0]
+ finally:
+ s.close()
+
+ return host_ip
+
+
+@pytest.fixture(scope="session")
+def registry(host_ip):
+ port = get_free_port()
+ username = "user"
+ password = secrets.token_hex(16)
+ bcrypted_pw = bcrypt.hashpw(
+ password.encode("utf-8"), bcrypt.gensalt(rounds=12)
+ ).decode("utf-8")
+
+ # We put our password here, and mount it into the container.
+ # put it in current dir than in /tmp because on macos, current dir is likely to
+ # shared with docker VM so it can be mounted, unlike /tmp
+ htpasswd_dir = HERE / f"tmp-certs-{secrets.token_hex(8)}"
+ htpasswd_dir.mkdir()
+ (htpasswd_dir / "htpasswd.conf").write_text(f"{username}:{bcrypted_pw}")
+
+ # Explicitly pull the image first so it runs on time
+ registry_image = "registry:3.0.0-rc.3"
+ subprocess.check_call(["docker", "pull", registry_image])
+
+ cmd = [
+ "docker",
+ "run",
+ "--rm",
+ "-e",
+ "REGISTRY_AUTH=htpasswd",
+ "-e",
+ "REGISTRY_AUTH_HTPASSWD_REALM=basic",
+ "-e",
+ "REGISTRY_AUTH_HTPASSWD_PATH=/opt/htpasswd/htpasswd.conf",
+ "--mount",
+ f"type=bind,src={htpasswd_dir},dst=/opt/htpasswd",
+ "-p",
+ f"{port}:5000",
+ registry_image,
+ ]
+ proc = subprocess.Popen(cmd)
+ health_url = f"http://{host_ip}:{port}/v2"
+ # Wait for the registry to actually come up
+ for i in range(10):
+ try:
+ resp = requests.get(health_url)
+ if resp.status_code in (401, 200):
+ break
+ except requests.ConnectionError:
+ # The service is not up yet
+ pass
+ time.sleep(i)
+ else:
+ raise TimeoutError("Test registry did not come up in time")
+
+ try:
+ yield f"{host_ip}:{port}", username, password
+ finally:
+ proc.terminate()
+ proc.wait()
+
+
+@pytest.mark.skipif(DOCKER_CLI == "podman", reason="Test specific for Docker")
+def test_registry_explicit_creds(registry, dind):
+ """
+ Test that we can push to registry when given explicit credentials
+ """
+ registry_host, username, password = registry
+ image_name = f"{registry_host}/{secrets.token_hex(8)}:latest"
+ r2d = make_r2d(["--image", image_name, "--push", "--no-run", str(HERE)])
+
+ docker_host, cert_dir = dind
+
+ old_environ = os.environ.copy()
+
+ try:
+ os.environ["DOCKER_HOST"] = docker_host
+ os.environ["DOCKER_CERT_PATH"] = str(cert_dir / "client")
+ os.environ["DOCKER_TLS_VERIFY"] = "1"
+ os.environ["CONTAINER_ENGINE_REGISTRY_CREDENTIALS"] = json.dumps(
+ {
+ "registry": f"http://{registry_host}",
+ "username": username,
+ "password": password,
+ }
+ )
+ r2d.start()
+
+ # CONTAINER_ENGINE_REGISTRY_CREDENTIALS unfortunately doesn't propagate to docker manifest, so
+ # let's explicitly set up a docker_config here so we can check if the image exists
+ with TemporaryDirectory() as d:
+ (Path(d) / "config.json").write_text(
+ json.dumps(
+ {
+ "auths": {
+ f"http://{registry_host}": {
+ "auth": b64encode(
+ f"{username}:{password}".encode()
+ ).decode()
+ }
+ }
+ }
+ )
+ )
+ env = os.environ.copy()
+ env["DOCKER_CONFIG"] = d
+ proc = subprocess.run(
+ ["docker", "manifest", "inspect", "--insecure", image_name], env=env
+ )
+ assert proc.returncode == 0
+
+ # Validate that we didn't leak our registry creds into existing docker config
+ docker_config_path = Path(
+ os.environ.get("DOCKER_CONFIG", "~/.docker/config.json")
+ ).expanduser()
+ if docker_config_path.exists():
+ # Just check that our randomly generated password is not in this file
+ # Can this cause a conflict? Sure, if there's a different randomly generated password in here
+ # that matches our own randomly generated password. But if you're that unlucky, take cover from the asteroid.
+ assert password not in docker_config_path.read_text()
+ finally:
+ os.environ.clear()
+ os.environ.update(old_environ)
+
+
+@pytest.mark.skipif(DOCKER_CLI == "podman", reason="Test specific for Docker")
+def test_registry_no_explicit_creds(registry, dind):
+ """
+ Test that we can push to registry *without* explicit credentials but reading from a DOCKER_CONFIG
+ """
+ registry_host, username, password = registry
+ image_name = f"{registry_host}/{secrets.token_hex(8)}:latest"
+ r2d = make_r2d(["--image", image_name, "--push", "--no-run", str(HERE)])
+
+ docker_host, cert_dir = dind
+
+ old_environ = os.environ.copy()
+
+ try:
+ os.environ["DOCKER_HOST"] = docker_host
+ os.environ["DOCKER_CERT_PATH"] = str(cert_dir / "client")
+ os.environ["DOCKER_TLS_VERIFY"] = "1"
+ with TemporaryDirectory() as d:
+ (Path(d) / "config.json").write_text(
+ json.dumps(
+ {
+ "auths": {
+ f"http://{registry_host}": {
+ "auth": b64encode(
+ f"{username}:{password}".encode()
+ ).decode()
+ }
+ }
+ }
+ )
+ )
+ os.environ["DOCKER_CONFIG"] = d
+ r2d.start()
+
+ proc = subprocess.run(
+ ["docker", "manifest", "inspect", "--insecure", image_name]
+ )
+ assert proc.returncode == 0
+ finally:
+ os.environ.clear()
+ os.environ.update(old_environ)
diff --git a/tests/pyproject/pyproject-toml/README.rst b/tests/pyproject/pyproject-toml/README.rst
new file mode 100644
index 000000000..86564f364
--- /dev/null
+++ b/tests/pyproject/pyproject-toml/README.rst
@@ -0,0 +1,4 @@
+Python - pyproject.toml
+-----------------------
+
+``pyproject.toml`` should be used over ``setup.py``.
diff --git a/tests/pyproject/pyproject-toml/mwt/__init__.py b/tests/pyproject/pyproject-toml/mwt/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/pyproject/pyproject-toml/pyproject.toml b/tests/pyproject/pyproject-toml/pyproject.toml
new file mode 100644
index 000000000..530f83977
--- /dev/null
+++ b/tests/pyproject/pyproject-toml/pyproject.toml
@@ -0,0 +1,26 @@
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[project]
+name = "mwt"
+version = "1.0.0"
+dependencies = [
+ "numpy",
+]
+requires-python = ">=3.10"
+authors = [
+ {name = "Project Jupyter Contributors", email = "jupyter@googlegroups.com"},
+]
+maintainers = [
+ {name = "Project Jupyter Contributors", email = "jupyter@googlegroups.com"},
+]
+description = "Test for repo2docker"
+readme = "README.rst"
+license = "MIT"
+
+[project.urls]
+Homepage = "https://repo2docker.readthedocs.io/"
+Documentation = "https://repo2docker.readthedocs.io/"
+Repository = "https://github.com/jupyterhub/repo2docker.git"
+"Bug Tracker" = "https://github.com/jupyterhub/repo2docker/issues"
\ No newline at end of file
diff --git a/tests/pyproject/pyproject-toml/verify b/tests/pyproject/pyproject-toml/verify
new file mode 100755
index 000000000..4d7131e1e
--- /dev/null
+++ b/tests/pyproject/pyproject-toml/verify
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+try:
+ import mwt
+except ImportError:
+ raise Exception("'mwt' shouldn't have been installed!")
diff --git a/tests/r/r-rspm-apt-file/runtime.txt b/tests/r/r-rspm-apt-file/runtime.txt
index aa35fc0b3..fac25eae6 100644
--- a/tests/r/r-rspm-apt-file/runtime.txt
+++ b/tests/r/r-rspm-apt-file/runtime.txt
@@ -1 +1 @@
-r-2022-06-23
+r-2025-07-01
diff --git a/tests/r/r-rspm-apt-file/verify.r b/tests/r/r-rspm-apt-file/verify.r
index 30117134a..ca5b27d14 100755
--- a/tests/r/r-rspm-apt-file/verify.r
+++ b/tests/r/r-rspm-apt-file/verify.r
@@ -2,8 +2,8 @@
library('digest')
-# Fail if version isn't 4.2, the default version for the RBuildPack
+# Fail if version isn't 4.4, the default version for the RBuildPack
print(version)
-if (!(version$major == "4" && as.double(version$minor) >= 2 && as.double(version$minor) < 3)) {
+if (!(version$major == "4" && as.double(version$minor) >= 4 && as.double(version$minor) < 5)) {
quit("yes", 1)
}
diff --git a/tests/r/r-rspm-description-file/DESCRIPTION b/tests/r/r-rspm-description-file/DESCRIPTION
index 2c51fc3c7..a63e8d622 100644
--- a/tests/r/r-rspm-description-file/DESCRIPTION
+++ b/tests/r/r-rspm-description-file/DESCRIPTION
@@ -1,6 +1,6 @@
Package: binderdescription
Version: 0.1
-Date: 2022-06-23
+Date: 2025-07-01
Title: Binder R DESCRIPTION support
Description: Test that automatically building R packages works
Author: Bastian Greshake Tzovaras
diff --git a/tests/r/r4.0-rspm/runtime.txt b/tests/r/r4.0-rspm/runtime.txt
deleted file mode 100644
index e9f265b55..000000000
--- a/tests/r/r4.0-rspm/runtime.txt
+++ /dev/null
@@ -1 +0,0 @@
-r-4.0-2022-01-01
diff --git a/tests/r/r4.0-rspm/install.R b/tests/r/r4.3.2-rspm/install.R
similarity index 100%
rename from tests/r/r4.0-rspm/install.R
rename to tests/r/r4.3.2-rspm/install.R
diff --git a/tests/r/r4.3.2-rspm/runtime.txt b/tests/r/r4.3.2-rspm/runtime.txt
new file mode 100644
index 000000000..cdd616ea8
--- /dev/null
+++ b/tests/r/r4.3.2-rspm/runtime.txt
@@ -0,0 +1 @@
+r-4.3.2-2024-01-10
diff --git a/tests/r/r4.0-rspm/verify b/tests/r/r4.3.2-rspm/verify
similarity index 65%
rename from tests/r/r4.0-rspm/verify
rename to tests/r/r4.3.2-rspm/verify
index 835f141c0..c46a402ac 100755
--- a/tests/r/r4.0-rspm/verify
+++ b/tests/r/r4.3.2-rspm/verify
@@ -1,9 +1,9 @@
#!/usr/bin/env Rscript
library('digest')
-# Fail if version is not 4.0
+# Fail if version is not 4.3.2
print(version)
-if (!(version$major == "4" && as.double(version$minor) >= 0 && as.double(version$minor) < 1)) {
+if (!(version$major == "4" && version$minor == "3.2")) {
quit("yes", 1)
}
diff --git a/tests/r/r4.5-rspm/install.R b/tests/r/r4.5-rspm/install.R
new file mode 100644
index 000000000..f609959ef
--- /dev/null
+++ b/tests/r/r4.5-rspm/install.R
@@ -0,0 +1 @@
+install.packages("digest")
diff --git a/tests/r/r4.5-rspm/runtime.txt b/tests/r/r4.5-rspm/runtime.txt
new file mode 100644
index 000000000..a57181976
--- /dev/null
+++ b/tests/r/r4.5-rspm/runtime.txt
@@ -0,0 +1 @@
+r-4.5.1-2025-06-13
diff --git a/tests/r/r4.5-rspm/verify b/tests/r/r4.5-rspm/verify
new file mode 100755
index 000000000..6d9130610
--- /dev/null
+++ b/tests/r/r4.5-rspm/verify
@@ -0,0 +1,13 @@
+#!/usr/bin/env Rscript
+library('digest')
+
+# Fail if version is not 4.5
+print(version)
+if (!(version$major == "4" && as.double(version$minor) >= 5 && as.double(version$minor) < 6)) {
+ quit("yes", 1)
+}
+
+# The date we have chosen should give us an rspm mirror
+if (!(startsWith(options()$repos["CRAN"], "https://packagemanager.posit.co"))) {
+ quit("yes", 1)
+}
diff --git a/tests/ui/README.md b/tests/ui/README.md
new file mode 100644
index 000000000..04adc855b
--- /dev/null
+++ b/tests/ui/README.md
@@ -0,0 +1,9 @@
+# User interface tests
+
+This contains very basic [Playwright](https://playwright.dev/python/) tests to check the
+
+- JupyterLab
+- RStudio
+- RShiny
+
+interfaces can be accessed.
diff --git a/tests/ui/browser/environment.yml b/tests/ui/browser/environment.yml
new file mode 100644
index 000000000..9f6f2aaba
--- /dev/null
+++ b/tests/ui/browser/environment.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - r-base
diff --git a/tests/ui/browser/external-verify b/tests/ui/browser/external-verify
new file mode 100755
index 000000000..1e78c86f4
--- /dev/null
+++ b/tests/ui/browser/external-verify
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+# This script is run outside the container
+
+set -eux
+
+export TEST_REPO2DOCKER_URL="${1}/?token=token"
+pytest --verbose --color=yes --browser=firefox tests/ui/browser/external-verify.py
diff --git a/tests/ui/browser/external-verify.py b/tests/ui/browser/external-verify.py
new file mode 100755
index 000000000..bbeec18bb
--- /dev/null
+++ b/tests/ui/browser/external-verify.py
@@ -0,0 +1,41 @@
+import os
+from subprocess import check_output
+from urllib.parse import urlsplit
+
+import pytest
+from playwright.sync_api import Page, expect
+
+
+# To run this test manually:
+# - Run: repo2docker tests/ui/browser/
+# - Run: TEST_REPO2DOCKER_URL= python -mpytest --browser=firefox tests/ui/browser/external-verify.py [--headed]
+def test_user_interfaces(page: Page) -> None:
+ url = os.getenv("TEST_REPO2DOCKER_URL")
+ u = urlsplit(url)
+
+ # Includes token
+ page.goto(url)
+
+ # Initial page should be Jupyter Notebook
+ page.wait_for_url(f"{u.scheme}://{u.netloc}/tree")
+
+ # Check JupyterLab
+ page.goto(f"{u.scheme}://{u.netloc}/lab")
+ expect(page.get_by_text("Python 3 (ipykernel)").nth(1)).to_be_visible()
+
+ # Check JupyterLab RStudio launcher
+ with page.expect_popup() as page1_info:
+ page.get_by_text("RStudio [↗]").click()
+ page1 = page1_info.value
+ page1.wait_for_url(f"{u.scheme}://{u.netloc}/rstudio/")
+ # Top-left logo
+ expect(page1.locator("#rstudio_rstudio_logo")).to_be_visible()
+ # Initial RStudio console text
+ expect(page1.get_by_text("R version ")).to_be_visible()
+
+ # Check JupyterLab RShiny launcher
+ with page.expect_popup() as page2_info:
+ page.get_by_text("Shiny [↗]").click()
+ page2 = page2_info.value
+ page2.wait_for_url(f"{u.scheme}://{u.netloc}/shiny/")
+ expect(page2.get_by_text("Index of /")).to_be_visible()
diff --git a/tests/ui/browser/test-extra-args.yaml b/tests/ui/browser/test-extra-args.yaml
new file mode 100644
index 000000000..574c469a7
--- /dev/null
+++ b/tests/ui/browser/test-extra-args.yaml
@@ -0,0 +1,2 @@
+- --env
+- JUPYTER_TOKEN=token
diff --git a/tests/ui/browser/verify b/tests/ui/browser/verify
new file mode 100755
index 000000000..1a2485251
--- /dev/null
+++ b/tests/ui/browser/verify
@@ -0,0 +1 @@
+#!/bin/sh
diff --git a/tests/unit/contentproviders/test_dataverse.py b/tests/unit/contentproviders/test_dataverse.py
deleted file mode 100644
index abdfe456d..000000000
--- a/tests/unit/contentproviders/test_dataverse.py
+++ /dev/null
@@ -1,160 +0,0 @@
-import json
-import os
-import re
-from io import BytesIO
-from tempfile import TemporaryDirectory
-from unittest.mock import patch
-from urllib.parse import urlsplit
-from urllib.request import Request, urlopen
-
-import pytest
-
-from repo2docker.contentproviders import Dataverse
-
-test_dv = Dataverse()
-harvard_dv = next(_ for _ in test_dv.hosts if _["name"] == "Harvard Dataverse")
-cimmyt_dv = next(_ for _ in test_dv.hosts if _["name"] == "CIMMYT Research Data")
-test_hosts = [
- (
- [
- "doi:10.7910/DVN/6ZXAGT/3YRRYJ",
- "10.7910/DVN/6ZXAGT",
- "https://dataverse.harvard.edu/api/access/datafile/3323458",
- "https://data.cimmyt.org/dataset.xhtml?persistentId=hdl:11529/10016",
- ],
- [
- {"host": harvard_dv, "record": "doi:10.7910/DVN/6ZXAGT"},
- {"host": cimmyt_dv, "record": "hdl:11529/10016"},
- ],
- )
-]
-doi_responses = {
- "https://doi.org/10.7910/DVN/6ZXAGT/3YRRYJ": (
- "https://dataverse.harvard.edu/file.xhtml"
- "?persistentId=doi:10.7910/DVN/6ZXAGT/3YRRYJ"
- ),
- "https://doi.org/10.7910/DVN/6ZXAGT": (
- "https://dataverse.harvard.edu/dataset.xhtml"
- "?persistentId=doi:10.7910/DVN/6ZXAGT"
- ),
- "https://dataverse.harvard.edu/api/access/datafile/3323458": (
- "https://dataverse.harvard.edu/api/access/datafile/3323458"
- ),
- "https://doi.org/10.21105/joss.01277": (
- "https://joss.theoj.org/papers/10.21105/joss.01277"
- ),
-}
-
-
-@pytest.mark.parametrize("test_input, expected", test_hosts)
-def test_detect_dataverse(test_input, expected, requests_mock):
- def doi_resolver(req, context):
- resp = doi_responses.get(req.url)
- # doi responses are redirects
- if resp is not None:
- context.status_code = 302
- context.headers["Location"] = resp
- return resp
-
- requests_mock.get(re.compile("https://"), json=doi_resolver)
- requests_mock.get(
- "https://dataverse.harvard.edu/api/search?q=entityId:3323458&type=file",
- json={
- "data": {
- "count_in_response": 1,
- "items": [{"dataset_persistent_id": "doi:10.7910/DVN/6ZXAGT"}],
- }
- },
- )
-
- assert requests_mock.call_count == 0
- # valid Dataverse DOIs trigger this content provider
- assert Dataverse().detect(test_input[0]) == expected[0]
- # 4: doi resolution (302), File, doi resolution (302), then dataset
- assert requests_mock.call_count == 4
- requests_mock.reset_mock()
-
- assert Dataverse().detect(test_input[1]) == expected[0]
- # 2: doi (302), dataset
- assert requests_mock.call_count == 2
- requests_mock.reset_mock()
-
- assert Dataverse().detect(test_input[2]) == expected[0]
- # 1: datafile (search dataverse for the file id)
- assert requests_mock.call_count == 1
- requests_mock.reset_mock()
-
- assert Dataverse().detect(test_input[3]) == expected[1]
- requests_mock.reset_mock()
-
- # Don't trigger the Dataverse content provider
- assert Dataverse().detect("/some/path/here") is None
- assert Dataverse().detect("https://example.com/path/here") is None
- # don't handle DOIs that aren't from Dataverse
- assert Dataverse().detect("https://doi.org/10.21105/joss.01277") is None
-
-
-@pytest.fixture
-def dv_files(tmpdir):
- f1 = tmpdir.join("some-file.txt")
- f1.write("some content")
-
- f2 = tmpdir.mkdir("directory").join("some-other-file.txt")
- f2.write("some other content")
-
- f3 = tmpdir.join("directory").mkdir("subdirectory").join("the-other-file.txt")
- f3.write("yet another content")
-
- return [f1, f2, f3]
-
-
-def test_dataverse_fetch(dv_files, requests_mock):
- mock_response = {
- "data": {
- "latestVersion": {
- "files": [
- {"dataFile": {"id": 1}, "label": "some-file.txt"},
- {
- "dataFile": {"id": 2},
- "label": "some-other-file.txt",
- "directoryLabel": "directory",
- },
- {
- "dataFile": {"id": 3},
- "label": "the-other-file.txt",
- "directoryLabel": "directory/subdirectory",
- },
- ]
- }
- }
- }
-
- spec = {"host": harvard_dv, "record": "doi:10.7910/DVN/6ZXAGT"}
-
- def mock_filecontent(req, context):
- parts = urlsplit(req.url)
- file_no = int(parts.path.split("/")[-1]) - 1
- return open(dv_files[file_no], "rb").read()
-
- requests_mock.get(
- "https://dataverse.harvard.edu/api/datasets/"
- ":persistentId?persistentId=doi:10.7910/DVN/6ZXAGT",
- json=mock_response,
- )
- requests_mock.get(
- re.compile("https://dataverse.harvard.edu/api/access/datafile"),
- content=mock_filecontent,
- )
-
- dv = Dataverse()
-
- with TemporaryDirectory() as d:
- output = []
- for l in dv.fetch(spec, d):
- output.append(l)
- unpacked_files = set(os.listdir(d))
- expected = {"directory", "some-file.txt"}
- assert expected == unpacked_files
- assert os.path.isfile(
- os.path.join(d, "directory", "subdirectory", "the-other-file.txt")
- )
diff --git a/tests/unit/contentproviders/test_doi.py b/tests/unit/contentproviders/test_doi.py
index 301b50beb..58cae5f5b 100644
--- a/tests/unit/contentproviders/test_doi.py
+++ b/tests/unit/contentproviders/test_doi.py
@@ -33,7 +33,7 @@ def test_url_headers(requests_mock):
@pytest.mark.parametrize(
"requested_doi, expected",
[
- ("10.5281/zenodo.3242074", "https://zenodo.org/records/3242074"),
+ ("10.5281/zenodo.3242074", "https://zenodo.org/record/3242074"),
# Unresolving DOI:
("10.1/1234", "10.1/1234"),
],
diff --git a/tests/unit/contentproviders/test_figshare.py b/tests/unit/contentproviders/test_figshare.py
index b4f42de31..9b8229e46 100644
--- a/tests/unit/contentproviders/test_figshare.py
+++ b/tests/unit/contentproviders/test_figshare.py
@@ -21,16 +21,9 @@
@pytest.mark.parametrize("link,expected", test_content_ids)
-def test_content_id(link, expected, requests_mock):
- def mocked_get(req, context):
- if req.url.startswith("https://doi.org"):
- context.status_code = 302
- context.headers["Location"] = link
- return link
-
- requests_mock.get(re.compile("https://"), text=mocked_get)
+def test_content_id(link, expected):
fig = Figshare()
- fig.detect("10.6084/m9.figshare.9782777")
+ fig.detect(link)
assert fig.content_id == expected
@@ -136,7 +129,6 @@ def test_fetch_zip(requests_mock):
)
requests_mock.get(f"file://{fig_path}", content=open(fig_path, "rb").read())
- # with patch.object(Figshare, "urlopen", new=mock_urlopen):
with TemporaryDirectory() as d:
output = []
for l in test_fig.fetch(test_spec, d):
diff --git a/tests/unit/contentproviders/test_hydroshare.py b/tests/unit/contentproviders/test_hydroshare.py
index 0c8acfd14..1288010a7 100755
--- a/tests/unit/contentproviders/test_hydroshare.py
+++ b/tests/unit/contentproviders/test_hydroshare.py
@@ -1,5 +1,4 @@
import os
-import re
from contextlib import contextmanager
from tempfile import NamedTemporaryFile, TemporaryDirectory
from unittest.mock import patch
@@ -34,50 +33,40 @@ def doi_resolver(req, context):
}
-def test_content_id(requests_mock):
- requests_mock.get(re.compile("https://"), json=hydroshare_data)
- requests_mock.get(re.compile("https://doi.org"), json=doi_resolver)
-
+def test_content_id():
hydro = Hydroshare()
hydro.detect("10.4211/hs.b8f6eae9d89241cf8b5904033460af61")
- assert hydro.content_id == "b8f6eae9d89241cf8b5904033460af61.v1569427757"
-
+ assert hydro.content_id == "b8f6eae9d89241cf8b5904033460af61.v1783452415"
-def test_detect_hydroshare(requests_mock):
- requests_mock.get(re.compile("https://"), json=hydroshare_data)
- requests_mock.get(re.compile("https://doi.org"), json=doi_resolver)
+def test_detect_hydroshare():
# valid Hydroshare DOIs trigger this content provider
expected = {
"host": {
"hostname": [
"https://www.hydroshare.org/resource/",
"http://www.hydroshare.org/resource/",
+ "https://hydroshare.org/resource/",
+ "http://hydroshare.org/resource/",
],
- "django_irods": "https://www.hydroshare.org/django_irods/download/bags/",
+ "django_s3": "https://www.hydroshare.org/django_s3/download/bags/",
"version": "https://www.hydroshare.org/hsapi/resource/{}/scimeta/elements",
},
"resource": "b8f6eae9d89241cf8b5904033460af61",
- "version": "1569427757",
+ "version": "1783452415",
}
assert (
Hydroshare().detect(
- "https://www.hydroshare.org/resource/b8f6eae9d89241cf8b5904033460af61"
+ "https://hydroshare.org/resource/b8f6eae9d89241cf8b5904033460af61"
)
== expected
)
- # assert a call to urlopen was called to fetch version
- assert requests_mock.call_count == 1
- requests_mock.reset_mock()
assert (
Hydroshare().detect("10.4211/hs.b8f6eae9d89241cf8b5904033460af61") == expected
)
- # assert 3 calls were made, 2 to resolve the DOI (302 + 200) and another to fetch the version
- assert requests_mock.call_count == 3
- requests_mock.reset_mock()
assert (
Hydroshare().detect(
@@ -85,9 +74,6 @@ def test_detect_hydroshare(requests_mock):
)
== expected
)
- # assert 3 more calls were made, 2 to resolve the DOI and another to fetch the version
- assert requests_mock.call_count == 3
- requests_mock.reset_mock()
# Don't trigger the Hydroshare content provider
assert Hydroshare().detect("/some/path/here") is None
@@ -98,7 +84,7 @@ def test_detect_hydroshare(requests_mock):
@contextmanager
-def hydroshare_archive(prefix="b8f6eae9d89241cf8b5904033460af61/data/contents"):
+def hydroshare_archive(prefix="123456789/data/contents"):
with NamedTemporaryFile(suffix=".zip") as zfile:
with ZipFile(zfile.name, mode="w") as zip:
zip.writestr(f"{prefix}/some-file.txt", "some content")
@@ -108,10 +94,9 @@ def hydroshare_archive(prefix="b8f6eae9d89241cf8b5904033460af61/data/contents"):
class MockResponse:
- def __init__(self, content_type, status_code):
+ def __init__(self, url, status_code):
self.status_code = status_code
- self.headers = dict()
- self.headers["content-type"] = content_type
+ self.url = url
def test_fetch_bag():
@@ -121,22 +106,27 @@ def test_fetch_bag():
Hydroshare,
"urlopen",
side_effect=[
- MockResponse("application/html", 200),
- MockResponse("application/zip", 200),
+ MockResponse(
+ "https://www.hydroshare.org/django_s3/download/bags/123456789.zip",
+ 200,
+ ),
+ MockResponse("https://s3.hydroshare.org/bags/123456789.zip", 200),
],
):
with patch.object(
Hydroshare, "_urlretrieve", side_effect=[(hydro_path, None)]
):
hydro = Hydroshare()
- hydro.resource_id = "b8f6eae9d89241cf8b5904033460af61"
+ hydro.resource_id = "123456789"
spec = {
"host": {
"hostname": [
"https://www.hydroshare.org/resource/",
"http://www.hydroshare.org/resource/",
+ "https://hydroshare.org/resource/",
+ "http://hydroshare.org/resource/",
],
- "django_irods": "https://www.hydroshare.org/django_irods/download/bags/",
+ "django_s3": "https://www.hydroshare.org/django_s3/download/bags/",
},
"resource": "123456789",
}
@@ -162,8 +152,10 @@ def test_fetch_bag_failure():
"hostname": [
"https://www.hydroshare.org/resource/",
"http://www.hydroshare.org/resource/",
+ "https://hydroshare.org/resource/",
+ "http://hydroshare.org/resource/",
],
- "django_irods": "https://www.hydroshare.org/django_irods/download/bags/",
+ "django_s3": "https://www.hydroshare.org/django_s3/download/bags/",
},
"resource": "123456789",
}
@@ -188,8 +180,10 @@ def test_fetch_bag_timeout():
"hostname": [
"https://www.hydroshare.org/resource/",
"http://www.hydroshare.org/resource/",
+ "https://hydroshare.org/resource/",
+ "http://hydroshare.org/resource/",
],
- "django_irods": "https://www.hydroshare.org/django_irods/download/bags/",
+ "django_s3": "https://www.hydroshare.org/django_s3/download/bags/",
},
"resource": "123456789",
}
diff --git a/tests/unit/contentproviders/test_swhid.py b/tests/unit/contentproviders/test_swhid.py
index 56fee1dfc..edf68af4b 100644
--- a/tests/unit/contentproviders/test_swhid.py
+++ b/tests/unit/contentproviders/test_swhid.py
@@ -56,11 +56,6 @@ def test_detect(swhid, expected):
assert provider.detect(swhid) == expected
-def fake_urlopen(req):
- print(req)
- return req.headers
-
-
def test_unresolving_swhid():
provider = Swhid()
diff --git a/tests/unit/contentproviders/test_zenodo.py b/tests/unit/contentproviders/test_zenodo.py
index 38737ae1a..a1e1226fb 100644
--- a/tests/unit/contentproviders/test_zenodo.py
+++ b/tests/unit/contentproviders/test_zenodo.py
@@ -21,18 +21,7 @@
}
-def doi_resolver(req, context):
- resp = doi_responses.get(req.url)
- # doi responses are redirects
- if resp is not None:
- context.status_code = 302
- context.headers["Location"] = resp
- return resp
-
-
-def test_content_id(requests_mock):
- requests_mock.get(re.compile("https://"), json=doi_resolver)
-
+def test_content_id():
zen = Zenodo()
zen.detect("10.5281/zenodo.3232985")
assert zen.content_id == "3232985"
@@ -60,15 +49,11 @@ def test_content_id(requests_mock):
@pytest.mark.parametrize("test_input,expected", test_hosts)
-def test_detect_zenodo(test_input, expected, requests_mock):
- requests_mock.get(re.compile("https://"), json=doi_resolver)
+def test_detect_zenodo(test_input, expected):
# valid Zenodo DOIs trigger this content provider
assert Zenodo().detect(test_input[0]) == expected
assert Zenodo().detect(test_input[1]) == expected
assert Zenodo().detect(test_input[2]) == expected
- # only two of the three calls above have to resolve a DOI (2 req per doi resolution)
- assert requests_mock.call_count == 4
- requests_mock.reset_mock()
# Don't trigger the Zenodo content provider
assert Zenodo().detect("/some/path/here") is None
diff --git a/tests/unit/test_app.py b/tests/unit/test_app.py
index d37103e60..834a0ae94 100644
--- a/tests/unit/test_app.py
+++ b/tests/unit/test_app.py
@@ -1,4 +1,4 @@
-import errno
+import os
from tempfile import TemporaryDirectory
from unittest.mock import patch
@@ -8,35 +8,6 @@
import docker
from repo2docker.__main__ import make_r2d
from repo2docker.app import Repo2Docker
-from repo2docker.utils import chdir
-
-
-def test_find_image():
- images = [{"RepoTags": ["some-org/some-repo:latest"]}]
-
- with patch("repo2docker.docker.docker.APIClient") as FakeDockerClient:
- instance = FakeDockerClient.return_value
- instance.images.return_value = images
-
- r2d = Repo2Docker()
- r2d.output_image_spec = "some-org/some-repo"
- assert r2d.find_image()
-
- instance.images.assert_called_with()
-
-
-def test_dont_find_image():
- images = [{"RepoTags": ["some-org/some-image-name:latest"]}]
-
- with patch("repo2docker.docker.docker.APIClient") as FakeDockerClient:
- instance = FakeDockerClient.return_value
- instance.images.return_value = images
-
- r2d = Repo2Docker()
- r2d.output_image_spec = "some-org/some-other-image-name"
- assert not r2d.find_image()
-
- instance.images.assert_called_with()
def test_image_name_remains_unchanged():
@@ -78,19 +49,22 @@ def test_local_dir_image_name(repo_with_content):
)
-def test_build_kwargs(repo_with_content):
+def test_extra_buildx_build_args(repo_with_content):
upstream, sha1 = repo_with_content
- argv = [upstream]
+ argv = ["--DockerEngine.extra_buildx_build_args=--check", upstream]
app = make_r2d(argv)
- app.extra_build_kwargs = {"somekey": "somevalue"}
-
- with patch.object(docker.APIClient, "build") as builds:
- builds.return_value = []
+ with patch("repo2docker.docker.execute_cmd") as execute_cmd:
app.build()
- builds.assert_called_once()
- args, kwargs = builds.call_args
- assert "somekey" in kwargs
- assert kwargs["somekey"] == "somevalue"
+
+ args, kwargs = execute_cmd.call_args
+ cmd = args[0]
+ docker_host = os.environ.get("DOCKER_HOST")
+ if docker_host and docker_host.find("podman") != -1:
+ assert cmd[:3] == ["podman", "buildx", "build"]
+ else:
+ assert cmd[:3] == ["docker", "buildx", "build"]
+ # make sure it's inserted before the end
+ assert "--check" in cmd[:-1]
def test_run_kwargs(repo_with_content):
@@ -107,43 +81,12 @@ def test_run_kwargs(repo_with_content):
assert kwargs["somekey"] == "somevalue"
-def test_root_not_allowed():
- with TemporaryDirectory() as src, patch("os.geteuid") as geteuid:
- geteuid.return_value = 0
- argv = [src]
- with pytest.raises(SystemExit) as exc:
- app = make_r2d(argv)
- assert exc.code == 1
-
- with pytest.raises(ValueError):
- app = Repo2Docker(repo=src, run=False)
- app.build()
-
- app = Repo2Docker(repo=src, user_id=1000, user_name="jovyan", run=False)
- app.initialize()
- with patch.object(docker.APIClient, "build") as builds:
- builds.return_value = []
- app.build()
- builds.assert_called_once()
-
-
-def test_dryrun_works_without_docker(tmpdir, capsys):
- with chdir(tmpdir):
- with patch.object(docker, "APIClient") as client:
- client.side_effect = docker.errors.DockerException("Error: no Docker")
- app = Repo2Docker(dry_run=True)
- app.build()
- captured = capsys.readouterr()
- assert "Error: no Docker" not in captured.err
-
-
-def test_error_log_without_docker(tmpdir, capsys):
- with chdir(tmpdir):
- with patch.object(docker, "APIClient") as client:
- client.side_effect = docker.errors.DockerException("Error: no Docker")
- app = Repo2Docker()
-
- with pytest.raises(SystemExit):
- app.build()
- captured = capsys.readouterr()
- assert "Error: no Docker" in captured.err
+def test_no_default(tmp_path, capsys):
+ empty = tmp_path / "empty"
+ empty.mkdir()
+ argv = ["--Repo2Docker.default_buildpack=None", str(empty)]
+ app = make_r2d(argv)
+ with pytest.raises(SystemExit):
+ app.build()
+ captured = capsys.readouterr()
+ assert "No environment specification found" in captured.err
diff --git a/tests/unit/test_args.py b/tests/unit/test_args.py
index d3e304f00..a116a9884 100644
--- a/tests/unit/test_args.py
+++ b/tests/unit/test_args.py
@@ -50,11 +50,11 @@ def test_mem_limit():
"""
Test various ways of passing --build-memory-limit
"""
- r2d = make_r2d(["--build-memory-limit", "1024", "."])
- assert int(r2d.build_memory_limit) == 1024
+ with pytest.raises(SystemExit):
+ r2d = make_r2d(["--build-memory-limit", "1024", "."])
- r2d = make_r2d(["--build-memory-limit", "3K", "."])
- assert int(r2d.build_memory_limit) == 1024 * 3
+ with pytest.raises(SystemExit):
+ r2d = make_r2d(["--Repo2Docker.build_memory_limit", "1024", "."])
def test_run_required():
diff --git a/tests/unit/test_argumentvalidation.py b/tests/unit/test_argumentvalidation.py
index 78b964649..1439a6533 100644
--- a/tests/unit/test_argumentvalidation.py
+++ b/tests/unit/test_argumentvalidation.py
@@ -212,34 +212,6 @@ def test_invalid_container_port_protocol_mapping_fail(temp_cwd):
assert not validate_arguments(builddir, args_list, "Port specification")
-def test_docker_handle_fail(temp_cwd):
- """
- Test to check if r2d fails with minimal error message on not being able to connect to docker daemon
- """
- args_list = []
-
- assert not validate_arguments(
- builddir,
- args_list,
- "Check if docker is running on the host.",
- disable_dockerd=True,
- )
-
-
-def test_docker_handle_debug_fail(temp_cwd):
- """
- Test to check if r2d fails with helpful error message on not being able to connect to docker daemon and debug enabled
- """
- args_list = ["--debug"]
-
- assert not validate_arguments(
- builddir,
- args_list,
- "Check if docker is running on the host.",
- disable_dockerd=True,
- )
-
-
def test_docker_no_build_success(temp_cwd):
"""
Test to check if r2d succeeds with --no-build argument with not being able to connect to docker daemon
diff --git a/tests/unit/test_buildpack.py b/tests/unit/test_buildpack.py
index 38fc5c237..ce84df2fc 100644
--- a/tests/unit/test_buildpack.py
+++ b/tests/unit/test_buildpack.py
@@ -1,9 +1,14 @@
+from datetime import date
from os.path import join as pjoin
from tempfile import TemporaryDirectory
import pytest
-from repo2docker.buildpacks import LegacyBinderDockerBuildPack, PythonBuildPack
+from repo2docker.buildpacks import (
+ BaseImage,
+ LegacyBinderDockerBuildPack,
+ PythonBuildPack,
+)
from repo2docker.utils import chdir
@@ -46,3 +51,45 @@ def test_unsupported_python(tmpdir, python_version, base_image):
assert bp.python_version == python_version
with pytest.raises(ValueError):
bp.render()
+
+
+@pytest.mark.parametrize(
+ "runtime_txt, expected",
+ [
+ (None, (None, None, None)),
+ ("abc-001", ("abc", "001", None)),
+ ("abc-001-2025-06-22", ("abc", "001", date(2025, 6, 22))),
+ ("abc-2025-06-22", ("abc", None, date(2025, 6, 22))),
+ ("a_b/c-0.0.1-2025-06-22", ("a_b/c", "0.0.1", date(2025, 6, 22))),
+ ],
+)
+def test_runtime(tmpdir, runtime_txt, expected, base_image):
+ tmpdir.chdir()
+
+ if runtime_txt is not None:
+ with open("runtime.txt", "w") as f:
+ f.write(runtime_txt)
+
+ base = BaseImage(base_image)
+ assert base.runtime == expected
+
+
+@pytest.mark.parametrize(
+ "runtime_txt",
+ [
+ "",
+ "abc",
+ "abc-001-25-06-22",
+ ],
+)
+def test_invalid_runtime(tmpdir, runtime_txt, base_image):
+ tmpdir.chdir()
+
+ if runtime_txt is not None:
+ with open("runtime.txt", "w") as f:
+ f.write(runtime_txt)
+
+ base = BaseImage(base_image)
+
+ with pytest.raises(ValueError, match=r"^Invalid runtime.txt.*"):
+ base.runtime
diff --git a/tests/unit/test_docker.py b/tests/unit/test_docker.py
index c47aa4fa0..2fe0b6f5e 100644
--- a/tests/unit/test_docker.py
+++ b/tests/unit/test_docker.py
@@ -2,9 +2,6 @@
import os
from subprocess import check_output
-from unittest.mock import Mock, patch
-
-from repo2docker.docker import DockerEngine
repo_root = os.path.abspath(
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
@@ -22,43 +19,3 @@ def test_git_credential_env():
.strip()
)
assert out == credential_env
-
-
-class MockDockerEngine(DockerEngine):
- def __init__(self, *args, **kwargs):
- self._apiclient = Mock()
-
-
-def test_docker_push_no_credentials():
- engine = MockDockerEngine()
-
- engine.push("image")
-
- assert len(engine._apiclient.method_calls) == 1
- engine._apiclient.push.assert_called_once_with("image", stream=True)
-
-
-def test_docker_push_dict_credentials():
- engine = MockDockerEngine()
- engine.registry_credentials = {"username": "abc", "password": "def"}
-
- engine.push("image")
-
- assert len(engine._apiclient.method_calls) == 2
- engine._apiclient.login.assert_called_once_with(username="abc", password="def")
- engine._apiclient.push.assert_called_once_with("image", stream=True)
-
-
-def test_docker_push_env_credentials():
- engine = MockDockerEngine()
- with patch.dict(
- "os.environ",
- {
- "CONTAINER_ENGINE_REGISTRY_CREDENTIALS": '{"username": "abc", "password": "def"}'
- },
- ):
- engine.push("image")
-
- assert len(engine._apiclient.method_calls) == 2
- engine._apiclient.login.assert_called_once_with(username="abc", password="def")
- engine._apiclient.push.assert_called_once_with("image", stream=True)
diff --git a/tests/unit/test_editable.py b/tests/unit/test_editable.py
index de4d6261e..d0d261755 100644
--- a/tests/unit/test_editable.py
+++ b/tests/unit/test_editable.py
@@ -3,11 +3,15 @@
import tempfile
import time
+import pytest
+
from repo2docker.__main__ import make_r2d
+from repo2docker.docker import DOCKER_CLI
DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "dockerfile", "editable")
+@pytest.mark.skipif(DOCKER_CLI == "podman", reason="Podman does NOT support bind mount")
def test_editable(run_repo2docker):
"""Run a local repository in edit mode. Verify a new file has been
created afterwards"""
@@ -28,6 +32,7 @@ def test_editable(run_repo2docker):
os.remove(newfile)
+@pytest.mark.skipif(DOCKER_CLI == "podman", reason="Podman does NOT support bind mount")
def test_editable_by_host():
"""Test whether a new file created by the host environment, is
detected in the container"""
@@ -38,7 +43,11 @@ def test_editable_by_host():
container = app.start_container()
# give the container a chance to start
+ waiting_container_counter = 0
while container.status != "running":
+ if waiting_container_counter >= 60:
+ assert container.status == "running"
+ waiting_container_counter = waiting_container_counter + 1
time.sleep(1)
try:
diff --git a/tests/unit/test_engine.py b/tests/unit/test_engine.py
new file mode 100644
index 000000000..1ccf1dc28
--- /dev/null
+++ b/tests/unit/test_engine.py
@@ -0,0 +1,18 @@
+import pytest
+from traitlets import TraitError
+
+from repo2docker.engine import ContainerEngine
+
+
+def test_registry_credentials():
+ e = ContainerEngine(parent=None)
+
+ # This should be fine
+ e.registry_credentials = {
+ "registry": "something",
+ "username": "something",
+ "password": "something",
+ }
+
+ with pytest.raises(TraitError):
+ e.registry_credentials = {"hi": "bye"}
diff --git a/tests/unit/test_matlab.py b/tests/unit/test_matlab.py
new file mode 100644
index 000000000..7009f428b
--- /dev/null
+++ b/tests/unit/test_matlab.py
@@ -0,0 +1,76 @@
+"""
+Test MATLAB installation scripts
+"""
+
+from unittest.mock import patch
+
+import pytest
+from requests.models import Response
+
+from repo2docker.app import Repo2Docker
+from repo2docker.buildpacks import CondaBuildPack
+from repo2docker.buildpacks.conda.matlab import matlab_requirements_scripts
+
+DEPS_URL = "https://raw.githubusercontent.com/mathworks-ref-arch/container-images/main/matlab-deps/r2025b/{}/base-dependencies.txt"
+
+
+def deps_response():
+ r = Response()
+ r.status_code = 200
+ r._content = b"# comment\nlibasound2t64\nlibxt6t64\n"
+ return r
+
+
+@pytest.mark.parametrize(
+ "base_image, ubuntu_name",
+ [
+ (Repo2Docker.base_image.default_value, "ubuntu24.04"),
+ ("docker.io/library/buildpack-deps:jammy", "ubuntu22.04"),
+ ],
+)
+def test_requirements_for_base_image(base_image, ubuntu_name):
+ with patch("requests.get", return_value=deps_response()) as get:
+ scripts = matlab_requirements_scripts("R2025b", base_image)
+ get.assert_called_once_with(DEPS_URL.format(ubuntu_name))
+ [(user, script)] = scripts
+ assert user == "root"
+ assert "libasound2t64 libxt6t64" in script
+ assert "# comment" not in script
+
+
+def test_unknown_base_image():
+ with pytest.raises(ValueError):
+ matlab_requirements_scripts("R2025b", "buildpack-deps:focal")
+
+
+def test_conda_assemble_scripts_with_mpm_yml(tmpdir):
+ tmpdir.chdir()
+ with open("environment.yml", "w") as f:
+ f.write("dependencies:\n - numpy\n")
+ with open("mpm.yml", "w") as f:
+ f.write("release: R2025b\nproducts:\n - Simulink\n")
+
+ bp = CondaBuildPack(Repo2Docker.base_image.default_value)
+ with patch("requests.get", return_value=deps_response()) as get:
+ scripts = bp._get_assemble_scripts_for_matlab()
+ get.assert_called_once_with(DEPS_URL.format("ubuntu24.04"))
+ joined = "\n".join(script for _, script in scripts)
+ assert "--release=R2025b" in joined
+ assert "--products MATLAB Simulink" in joined
+ assert "python3-distutils" not in joined
+ assert "jupyter-matlab-proxy" in joined
+ assert bp._get_env_for_matlab() == [
+ ("MW_CONTEXT_TAGS", "MATLAB_PROXY:JUPYTER:MPM:V1")
+ ]
+
+
+def test_mpm_yml_requires_release(tmpdir):
+ tmpdir.chdir()
+ with open("environment.yml", "w") as f:
+ f.write("dependencies:\n - numpy\n")
+ with open("mpm.yml", "w") as f:
+ f.write("products:\n - Simulink\n")
+
+ bp = CondaBuildPack(Repo2Docker.base_image.default_value)
+ with pytest.raises(ValueError):
+ bp._get_assemble_scripts_for_matlab()
diff --git a/tests/unit/test_ports.py b/tests/unit/test_ports.py
index aaebcdab1..43fd990b5 100644
--- a/tests/unit/test_ports.py
+++ b/tests/unit/test_ports.py
@@ -11,7 +11,6 @@
import pytest
import requests
-import docker
from repo2docker.__main__ import make_r2d
from repo2docker.app import Repo2Docker
@@ -69,10 +68,7 @@ def _cleanup():
container.reload()
if container.status == "running":
container.kill()
- try:
container.remove()
- except docker.errors.NotFound:
- pass
request.addfinalizer(_cleanup)
diff --git a/tests/unit/test_r.py b/tests/unit/test_r.py
index a7028d829..24c486093 100644
--- a/tests/unit/test_r.py
+++ b/tests/unit/test_r.py
@@ -8,7 +8,7 @@
@pytest.mark.parametrize(
- "runtime_version, expected", [("", "4.2"), ("3.6", "3.6"), ("3.5.1", "3.5")]
+ "runtime_version, expected", [("", "4.4"), ("3.6", "3.6"), ("3.5.1", "3.5")]
)
def test_version_specification(tmpdir, runtime_version, expected, base_image):
tmpdir.chdir()
diff --git a/tests/unit/test_users.py b/tests/unit/test_users.py
index 89bee96f9..074ea4aaf 100644
--- a/tests/unit/test_users.py
+++ b/tests/unit/test_users.py
@@ -9,7 +9,10 @@
from getpass import getuser
from unittest import mock
+import pytest
+
from repo2docker import Repo2Docker
+from repo2docker.docker import DOCKER_CLI
def test_automatic_username_deduction():
@@ -22,6 +25,7 @@ def test_automatic_username_deduction():
assert r2d.user_name == expected
+@pytest.mark.skipif(DOCKER_CLI == "podman", reason="Podman does NOT support bind mount")
def test_user():
"""
Validate user id and name setting
diff --git a/tests/unit/test_volumes.py b/tests/unit/test_volumes.py
index 9e108ec82..b9a550c41 100644
--- a/tests/unit/test_volumes.py
+++ b/tests/unit/test_volumes.py
@@ -8,7 +8,12 @@
import time
from getpass import getuser
+import pytest
+from repo2docker.docker import DOCKER_CLI
+
+
+@pytest.mark.skipif(DOCKER_CLI == "podman", reason="Podman does NOT support bind mount")
def test_volume_abspath():
"""
Validate that you can bind mount a volume onto an absolute dir & write to it
@@ -39,6 +44,7 @@ def test_volume_abspath():
assert f.read() == ts
+@pytest.mark.skipif(DOCKER_CLI == "podman", reason="Podman does NOT support bind mount")
def test_volume_relpath():
"""
Validate that you can bind mount a volume onto an relative path & write to it
diff --git a/tests/venv/start/postBuild/README.rst b/tests/venv/start/postBuild/README.rst
index ecfd428eb..81b2e86cd 100644
--- a/tests/venv/start/postBuild/README.rst
+++ b/tests/venv/start/postBuild/README.rst
@@ -3,3 +3,5 @@ postBuild and start
This test checks that we can use a postBuild and start script
at the same time.
+
+It also checks that exit on error (set -e) has not leaked into the main shell.
diff --git a/tests/venv/start/postBuild/verify b/tests/venv/start/postBuild/verify
index 5fd16f8d0..a8dbc5e33 100755
--- a/tests/venv/start/postBuild/verify
+++ b/tests/venv/start/postBuild/verify
@@ -1,4 +1,9 @@
-#!/bin/bash
+#!/bin/bash -il
+# Run this as an interactive login shell so that the conda profile is sourced
+
+# Test that `set -e` isn't incorrectly set in a profile
+false
+
set -euo pipefail
grep 'Done!' $HOME/postbuild.txt
# set value of TEST_START_VAR to empty string when it is not defined
diff --git a/versioneer.py b/versioneer.py
deleted file mode 100644
index 5d28b0d69..000000000
--- a/versioneer.py
+++ /dev/null
@@ -1,2333 +0,0 @@
-# Version: 0.29
-
-"""The Versioneer - like a rocketeer, but for versions.
-
-The Versioneer
-==============
-
-* like a rocketeer, but for versions!
-* https://github.com/python-versioneer/python-versioneer
-* Brian Warner
-* License: Public Domain (Unlicense)
-* Compatible with: Python 3.7, 3.8, 3.9, 3.10, 3.11 and pypy3
-* [![Latest Version][pypi-image]][pypi-url]
-* [![Build Status][travis-image]][travis-url]
-
-This is a tool for managing a recorded version number in setuptools-based
-python projects. The goal is to remove the tedious and error-prone "update
-the embedded version string" step from your release process. Making a new
-release should be as easy as recording a new tag in your version-control
-system, and maybe making new tarballs.
-
-
-## Quick Install
-
-Versioneer provides two installation modes. The "classic" vendored mode installs
-a copy of versioneer into your repository. The experimental build-time dependency mode
-is intended to allow you to skip this step and simplify the process of upgrading.
-
-### Vendored mode
-
-* `pip install versioneer` to somewhere in your $PATH
- * A [conda-forge recipe](https://github.com/conda-forge/versioneer-feedstock) is
- available, so you can also use `conda install -c conda-forge versioneer`
-* add a `[tool.versioneer]` section to your `pyproject.toml` or a
- `[versioneer]` section to your `setup.cfg` (see [Install](INSTALL.md))
- * Note that you will need to add `tomli; python_version < "3.11"` to your
- build-time dependencies if you use `pyproject.toml`
-* run `versioneer install --vendor` in your source tree, commit the results
-* verify version information with `python setup.py version`
-
-### Build-time dependency mode
-
-* `pip install versioneer` to somewhere in your $PATH
- * A [conda-forge recipe](https://github.com/conda-forge/versioneer-feedstock) is
- available, so you can also use `conda install -c conda-forge versioneer`
-* add a `[tool.versioneer]` section to your `pyproject.toml` or a
- `[versioneer]` section to your `setup.cfg` (see [Install](INSTALL.md))
-* add `versioneer` (with `[toml]` extra, if configuring in `pyproject.toml`)
- to the `requires` key of the `build-system` table in `pyproject.toml`:
- ```toml
- [build-system]
- requires = ["setuptools", "versioneer[toml]"]
- build-backend = "setuptools.build_meta"
- ```
-* run `versioneer install --no-vendor` in your source tree, commit the results
-* verify version information with `python setup.py version`
-
-## Version Identifiers
-
-Source trees come from a variety of places:
-
-* a version-control system checkout (mostly used by developers)
-* a nightly tarball, produced by build automation
-* a snapshot tarball, produced by a web-based VCS browser, like github's
- "tarball from tag" feature
-* a release tarball, produced by "setup.py sdist", distributed through PyPI
-
-Within each source tree, the version identifier (either a string or a number,
-this tool is format-agnostic) can come from a variety of places:
-
-* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows
- about recent "tags" and an absolute revision-id
-* the name of the directory into which the tarball was unpacked
-* an expanded VCS keyword ($Id$, etc)
-* a `_version.py` created by some earlier build step
-
-For released software, the version identifier is closely related to a VCS
-tag. Some projects use tag names that include more than just the version
-string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool
-needs to strip the tag prefix to extract the version identifier. For
-unreleased software (between tags), the version identifier should provide
-enough information to help developers recreate the same tree, while also
-giving them an idea of roughly how old the tree is (after version 1.2, before
-version 1.3). Many VCS systems can report a description that captures this,
-for example `git describe --tags --dirty --always` reports things like
-"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the
-0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has
-uncommitted changes).
-
-The version identifier is used for multiple purposes:
-
-* to allow the module to self-identify its version: `myproject.__version__`
-* to choose a name and prefix for a 'setup.py sdist' tarball
-
-## Theory of Operation
-
-Versioneer works by adding a special `_version.py` file into your source
-tree, where your `__init__.py` can import it. This `_version.py` knows how to
-dynamically ask the VCS tool for version information at import time.
-
-`_version.py` also contains `$Revision$` markers, and the installation
-process marks `_version.py` to have this marker rewritten with a tag name
-during the `git archive` command. As a result, generated tarballs will
-contain enough information to get the proper version.
-
-To allow `setup.py` to compute a version too, a `versioneer.py` is added to
-the top level of your source tree, next to `setup.py` and the `setup.cfg`
-that configures it. This overrides several distutils/setuptools commands to
-compute the version when invoked, and changes `setup.py build` and `setup.py
-sdist` to replace `_version.py` with a small static file that contains just
-the generated version data.
-
-## Installation
-
-See [INSTALL.md](./INSTALL.md) for detailed installation instructions.
-
-## Version-String Flavors
-
-Code which uses Versioneer can learn about its version string at runtime by
-importing `_version` from your main `__init__.py` file and running the
-`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can
-import the top-level `versioneer.py` and run `get_versions()`.
-
-Both functions return a dictionary with different flavors of version
-information:
-
-* `['version']`: A condensed version string, rendered using the selected
- style. This is the most commonly used value for the project's version
- string. The default "pep440" style yields strings like `0.11`,
- `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section
- below for alternative styles.
-
-* `['full-revisionid']`: detailed revision identifier. For Git, this is the
- full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac".
-
-* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the
- commit date in ISO 8601 format. This will be None if the date is not
- available.
-
-* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that
- this is only accurate if run in a VCS checkout, otherwise it is likely to
- be False or None
-
-* `['error']`: if the version string could not be computed, this will be set
- to a string describing the problem, otherwise it will be None. It may be
- useful to throw an exception in setup.py if this is set, to avoid e.g.
- creating tarballs with a version string of "unknown".
-
-Some variants are more useful than others. Including `full-revisionid` in a
-bug report should allow developers to reconstruct the exact code being tested
-(or indicate the presence of local changes that should be shared with the
-developers). `version` is suitable for display in an "about" box or a CLI
-`--version` output: it can be easily compared against release notes and lists
-of bugs fixed in various releases.
-
-The installer adds the following text to your `__init__.py` to place a basic
-version in `YOURPROJECT.__version__`:
-
- from ._version import get_versions
- __version__ = get_versions()['version']
- del get_versions
-
-## Styles
-
-The setup.cfg `style=` configuration controls how the VCS information is
-rendered into a version string.
-
-The default style, "pep440", produces a PEP440-compliant string, equal to the
-un-prefixed tag name for actual releases, and containing an additional "local
-version" section with more detail for in-between builds. For Git, this is
-TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags
---dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the
-tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and
-that this commit is two revisions ("+2") beyond the "0.11" tag. For released
-software (exactly equal to a known tag), the identifier will only contain the
-stripped tag, e.g. "0.11".
-
-Other styles are available. See [details.md](details.md) in the Versioneer
-source tree for descriptions.
-
-## Debugging
-
-Versioneer tries to avoid fatal errors: if something goes wrong, it will tend
-to return a version of "0+unknown". To investigate the problem, run `setup.py
-version`, which will run the version-lookup code in a verbose mode, and will
-display the full contents of `get_versions()` (including the `error` string,
-which may help identify what went wrong).
-
-## Known Limitations
-
-Some situations are known to cause problems for Versioneer. This details the
-most significant ones. More can be found on Github
-[issues page](https://github.com/python-versioneer/python-versioneer/issues).
-
-### Subprojects
-
-Versioneer has limited support for source trees in which `setup.py` is not in
-the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are
-two common reasons why `setup.py` might not be in the root:
-
-* Source trees which contain multiple subprojects, such as
- [Buildbot](https://github.com/buildbot/buildbot), which contains both
- "master" and "slave" subprojects, each with their own `setup.py`,
- `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI
- distributions (and upload multiple independently-installable tarballs).
-* Source trees whose main purpose is to contain a C library, but which also
- provide bindings to Python (and perhaps other languages) in subdirectories.
-
-Versioneer will look for `.git` in parent directories, and most operations
-should get the right version string. However `pip` and `setuptools` have bugs
-and implementation details which frequently cause `pip install .` from a
-subproject directory to fail to find a correct version string (so it usually
-defaults to `0+unknown`).
-
-`pip install --editable .` should work correctly. `setup.py install` might
-work too.
-
-Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in
-some later version.
-
-[Bug #38](https://github.com/python-versioneer/python-versioneer/issues/38) is tracking
-this issue. The discussion in
-[PR #61](https://github.com/python-versioneer/python-versioneer/pull/61) describes the
-issue from the Versioneer side in more detail.
-[pip PR#3176](https://github.com/pypa/pip/pull/3176) and
-[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve
-pip to let Versioneer work correctly.
-
-Versioneer-0.16 and earlier only looked for a `.git` directory next to the
-`setup.cfg`, so subprojects were completely unsupported with those releases.
-
-### Editable installs with setuptools <= 18.5
-
-`setup.py develop` and `pip install --editable .` allow you to install a
-project into a virtualenv once, then continue editing the source code (and
-test) without re-installing after every change.
-
-"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a
-convenient way to specify executable scripts that should be installed along
-with the python package.
-
-These both work as expected when using modern setuptools. When using
-setuptools-18.5 or earlier, however, certain operations will cause
-`pkg_resources.DistributionNotFound` errors when running the entrypoint
-script, which must be resolved by re-installing the package. This happens
-when the install happens with one version, then the egg_info data is
-regenerated while a different version is checked out. Many setup.py commands
-cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into
-a different virtualenv), so this can be surprising.
-
-[Bug #83](https://github.com/python-versioneer/python-versioneer/issues/83) describes
-this one, but upgrading to a newer version of setuptools should probably
-resolve it.
-
-
-## Updating Versioneer
-
-To upgrade your project to a new release of Versioneer, do the following:
-
-* install the new Versioneer (`pip install -U versioneer` or equivalent)
-* edit `setup.cfg` and `pyproject.toml`, if necessary,
- to include any new configuration settings indicated by the release notes.
- See [UPGRADING](./UPGRADING.md) for details.
-* re-run `versioneer install --[no-]vendor` in your source tree, to replace
- `SRC/_version.py`
-* commit any changed files
-
-## Future Directions
-
-This tool is designed to make it easily extended to other version-control
-systems: all VCS-specific components are in separate directories like
-src/git/ . The top-level `versioneer.py` script is assembled from these
-components by running make-versioneer.py . In the future, make-versioneer.py
-will take a VCS name as an argument, and will construct a version of
-`versioneer.py` that is specific to the given VCS. It might also take the
-configuration arguments that are currently provided manually during
-installation by editing setup.py . Alternatively, it might go the other
-direction and include code from all supported VCS systems, reducing the
-number of intermediate scripts.
-
-## Similar projects
-
-* [setuptools_scm](https://github.com/pypa/setuptools_scm/) - a non-vendored build-time
- dependency
-* [minver](https://github.com/jbweston/miniver) - a lightweight reimplementation of
- versioneer
-* [versioningit](https://github.com/jwodder/versioningit) - a PEP 518-based setuptools
- plugin
-
-## License
-
-To make Versioneer easier to embed, all its code is dedicated to the public
-domain. The `_version.py` that it creates is also in the public domain.
-Specifically, both are released under the "Unlicense", as described in
-https://unlicense.org/.
-
-[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg
-[pypi-url]: https://pypi.python.org/pypi/versioneer/
-[travis-image]:
-https://img.shields.io/travis/com/python-versioneer/python-versioneer.svg
-[travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer
-
-"""
-# pylint:disable=invalid-name,import-outside-toplevel,missing-function-docstring
-# pylint:disable=missing-class-docstring,too-many-branches,too-many-statements
-# pylint:disable=raise-missing-from,too-many-lines,too-many-locals,import-error
-# pylint:disable=too-few-public-methods,redefined-outer-name,consider-using-with
-# pylint:disable=attribute-defined-outside-init,too-many-arguments
-
-import configparser
-import errno
-import functools
-import json
-import os
-import re
-import subprocess
-import sys
-from pathlib import Path
-from typing import Any, Callable, Dict, List, NoReturn, Optional, Tuple, Union, cast
-
-have_tomllib = True
-if sys.version_info >= (3, 11):
- import tomllib
-else:
- try:
- import tomli as tomllib
- except ImportError:
- have_tomllib = False
-
-
-class VersioneerConfig:
- """Container for Versioneer configuration parameters."""
-
- VCS: str
- style: str
- tag_prefix: str
- versionfile_source: str
- versionfile_build: Optional[str]
- parentdir_prefix: Optional[str]
- verbose: Optional[bool]
-
-
-def get_root() -> str:
- """Get the project root directory.
-
- We require that all commands are run from the project root, i.e. the
- directory that contains setup.py, setup.cfg, and versioneer.py .
- """
- root = os.path.realpath(os.path.abspath(os.getcwd()))
- setup_py = os.path.join(root, "setup.py")
- pyproject_toml = os.path.join(root, "pyproject.toml")
- versioneer_py = os.path.join(root, "versioneer.py")
- if not (
- os.path.exists(setup_py)
- or os.path.exists(pyproject_toml)
- or os.path.exists(versioneer_py)
- ):
- # allow 'python path/to/setup.py COMMAND'
- root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0])))
- setup_py = os.path.join(root, "setup.py")
- pyproject_toml = os.path.join(root, "pyproject.toml")
- versioneer_py = os.path.join(root, "versioneer.py")
- if not (
- os.path.exists(setup_py)
- or os.path.exists(pyproject_toml)
- or os.path.exists(versioneer_py)
- ):
- err = (
- "Versioneer was unable to run the project root directory. "
- "Versioneer requires setup.py to be executed from "
- "its immediate directory (like 'python setup.py COMMAND'), "
- "or in a way that lets it use sys.argv[0] to find the root "
- "(like 'python path/to/setup.py COMMAND')."
- )
- raise VersioneerBadRootError(err)
- try:
- # Certain runtime workflows (setup.py install/develop in a setuptools
- # tree) execute all dependencies in a single python process, so
- # "versioneer" may be imported multiple times, and python's shared
- # module-import table will cache the first one. So we can't use
- # os.path.dirname(__file__), as that will find whichever
- # versioneer.py was first imported, even in later projects.
- my_path = os.path.realpath(os.path.abspath(__file__))
- me_dir = os.path.normcase(os.path.splitext(my_path)[0])
- vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0])
- if me_dir != vsr_dir and "VERSIONEER_PEP518" not in globals():
- print(
- "Warning: build in %s is using versioneer.py from %s"
- % (os.path.dirname(my_path), versioneer_py)
- )
- except NameError:
- pass
- return root
-
-
-def get_config_from_root(root: str) -> VersioneerConfig:
- """Read the project setup.cfg file to determine Versioneer config."""
- # This might raise OSError (if setup.cfg is missing), or
- # configparser.NoSectionError (if it lacks a [versioneer] section), or
- # configparser.NoOptionError (if it lacks "VCS="). See the docstring at
- # the top of versioneer.py for instructions on writing your setup.cfg .
- root_pth = Path(root)
- pyproject_toml = root_pth / "pyproject.toml"
- setup_cfg = root_pth / "setup.cfg"
- section: Union[Dict[str, Any], configparser.SectionProxy, None] = None
- if pyproject_toml.exists() and have_tomllib:
- try:
- with open(pyproject_toml, "rb") as fobj:
- pp = tomllib.load(fobj)
- section = pp["tool"]["versioneer"]
- except (tomllib.TOMLDecodeError, KeyError) as e:
- print(f"Failed to load config from {pyproject_toml}: {e}")
- print("Try to load it from setup.cfg")
- if not section:
- parser = configparser.ConfigParser()
- with open(setup_cfg) as cfg_file:
- parser.read_file(cfg_file)
- parser.get("versioneer", "VCS") # raise error if missing
-
- section = parser["versioneer"]
-
- # `cast`` really shouldn't be used, but its simplest for the
- # common VersioneerConfig users at the moment. We verify against
- # `None` values elsewhere where it matters
-
- cfg = VersioneerConfig()
- cfg.VCS = section["VCS"]
- cfg.style = section.get("style", "")
- cfg.versionfile_source = cast(str, section.get("versionfile_source"))
- cfg.versionfile_build = section.get("versionfile_build")
- cfg.tag_prefix = cast(str, section.get("tag_prefix"))
- if cfg.tag_prefix in ("''", '""', None):
- cfg.tag_prefix = ""
- cfg.parentdir_prefix = section.get("parentdir_prefix")
- if isinstance(section, configparser.SectionProxy):
- # Make sure configparser translates to bool
- cfg.verbose = section.getboolean("verbose")
- else:
- cfg.verbose = section.get("verbose")
-
- return cfg
-
-
-class NotThisMethod(Exception):
- """Exception raised if a method is not valid for the current scenario."""
-
-
-# these dictionaries contain VCS-specific tools
-LONG_VERSION_PY: Dict[str, str] = {}
-HANDLERS: Dict[str, Dict[str, Callable]] = {}
-
-
-def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator
- """Create decorator to mark a method as the handler of a VCS."""
-
- def decorate(f: Callable) -> Callable:
- """Store f in HANDLERS[vcs][method]."""
- HANDLERS.setdefault(vcs, {})[method] = f
- return f
-
- return decorate
-
-
-def run_command(
- commands: List[str],
- args: List[str],
- cwd: Optional[str] = None,
- verbose: bool = False,
- hide_stderr: bool = False,
- env: Optional[Dict[str, str]] = None,
-) -> Tuple[Optional[str], Optional[int]]:
- """Call the given command(s)."""
- assert isinstance(commands, list)
- process = None
-
- popen_kwargs: Dict[str, Any] = {}
- if sys.platform == "win32":
- # This hides the console window if pythonw.exe is used
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- popen_kwargs["startupinfo"] = startupinfo
-
- for command in commands:
- try:
- dispcmd = str([command] + args)
- # remember shell=False, so use git.cmd on windows, not just git
- process = subprocess.Popen(
- [command] + args,
- cwd=cwd,
- env=env,
- stdout=subprocess.PIPE,
- stderr=(subprocess.PIPE if hide_stderr else None),
- **popen_kwargs,
- )
- break
- except OSError as e:
- if e.errno == errno.ENOENT:
- continue
- if verbose:
- print("unable to run %s" % dispcmd)
- print(e)
- return None, None
- else:
- if verbose:
- print(f"unable to find command, tried {commands}")
- return None, None
- stdout = process.communicate()[0].strip().decode()
- if process.returncode != 0:
- if verbose:
- print("unable to run %s (error)" % dispcmd)
- print("stdout was %s" % stdout)
- return None, process.returncode
- return stdout, process.returncode
-
-
-LONG_VERSION_PY[
- "git"
-] = r'''
-# This file helps to compute a version number in source trees obtained from
-# git-archive tarball (such as those provided by githubs download-from-tag
-# feature). Distribution tarballs (built by setup.py sdist) and build
-# directories (produced by setup.py build) will contain a much shorter file
-# that just contains the computed version number.
-
-# This file is released into the public domain.
-# Generated by versioneer-0.29
-# https://github.com/python-versioneer/python-versioneer
-
-"""Git implementation of _version.py."""
-
-import errno
-import os
-import re
-import subprocess
-import sys
-from typing import Any, Callable, Dict, List, Optional, Tuple
-import functools
-
-
-def get_keywords() -> Dict[str, str]:
- """Get the keywords needed to look up the version information."""
- # these strings will be replaced by git during git-archive.
- # setup.py/versioneer.py will grep for the variable names, so they must
- # each be defined on a line of their own. _version.py will just call
- # get_keywords().
- git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s"
- git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s"
- git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s"
- keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
- return keywords
-
-
-class VersioneerConfig:
- """Container for Versioneer configuration parameters."""
-
- VCS: str
- style: str
- tag_prefix: str
- parentdir_prefix: str
- versionfile_source: str
- verbose: bool
-
-
-def get_config() -> VersioneerConfig:
- """Create, populate and return the VersioneerConfig() object."""
- # these strings are filled in when 'setup.py versioneer' creates
- # _version.py
- cfg = VersioneerConfig()
- cfg.VCS = "git"
- cfg.style = "%(STYLE)s"
- cfg.tag_prefix = "%(TAG_PREFIX)s"
- cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s"
- cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s"
- cfg.verbose = False
- return cfg
-
-
-class NotThisMethod(Exception):
- """Exception raised if a method is not valid for the current scenario."""
-
-
-LONG_VERSION_PY: Dict[str, str] = {}
-HANDLERS: Dict[str, Dict[str, Callable]] = {}
-
-
-def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator
- """Create decorator to mark a method as the handler of a VCS."""
- def decorate(f: Callable) -> Callable:
- """Store f in HANDLERS[vcs][method]."""
- if vcs not in HANDLERS:
- HANDLERS[vcs] = {}
- HANDLERS[vcs][method] = f
- return f
- return decorate
-
-
-def run_command(
- commands: List[str],
- args: List[str],
- cwd: Optional[str] = None,
- verbose: bool = False,
- hide_stderr: bool = False,
- env: Optional[Dict[str, str]] = None,
-) -> Tuple[Optional[str], Optional[int]]:
- """Call the given command(s)."""
- assert isinstance(commands, list)
- process = None
-
- popen_kwargs: Dict[str, Any] = {}
- if sys.platform == "win32":
- # This hides the console window if pythonw.exe is used
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- popen_kwargs["startupinfo"] = startupinfo
-
- for command in commands:
- try:
- dispcmd = str([command] + args)
- # remember shell=False, so use git.cmd on windows, not just git
- process = subprocess.Popen([command] + args, cwd=cwd, env=env,
- stdout=subprocess.PIPE,
- stderr=(subprocess.PIPE if hide_stderr
- else None), **popen_kwargs)
- break
- except OSError as e:
- if e.errno == errno.ENOENT:
- continue
- if verbose:
- print("unable to run %%s" %% dispcmd)
- print(e)
- return None, None
- else:
- if verbose:
- print("unable to find command, tried %%s" %% (commands,))
- return None, None
- stdout = process.communicate()[0].strip().decode()
- if process.returncode != 0:
- if verbose:
- print("unable to run %%s (error)" %% dispcmd)
- print("stdout was %%s" %% stdout)
- return None, process.returncode
- return stdout, process.returncode
-
-
-def versions_from_parentdir(
- parentdir_prefix: str,
- root: str,
- verbose: bool,
-) -> Dict[str, Any]:
- """Try to determine the version from the parent directory name.
-
- Source tarballs conventionally unpack into a directory that includes both
- the project name and a version string. We will also support searching up
- two directory levels for an appropriately named parent directory
- """
- rootdirs = []
-
- for _ in range(3):
- dirname = os.path.basename(root)
- if dirname.startswith(parentdir_prefix):
- return {"version": dirname[len(parentdir_prefix):],
- "full-revisionid": None,
- "dirty": False, "error": None, "date": None}
- rootdirs.append(root)
- root = os.path.dirname(root) # up a level
-
- if verbose:
- print("Tried directories %%s but none started with prefix %%s" %%
- (str(rootdirs), parentdir_prefix))
- raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
-
-
-@register_vcs_handler("git", "get_keywords")
-def git_get_keywords(versionfile_abs: str) -> Dict[str, str]:
- """Extract version information from the given file."""
- # the code embedded in _version.py can just fetch the value of these
- # keywords. When used from setup.py, we don't want to import _version.py,
- # so we do it with a regexp instead. This function is not used from
- # _version.py.
- keywords: Dict[str, str] = {}
- try:
- with open(versionfile_abs, "r") as fobj:
- for line in fobj:
- if line.strip().startswith("git_refnames ="):
- mo = re.search(r'=\s*"(.*)"', line)
- if mo:
- keywords["refnames"] = mo.group(1)
- if line.strip().startswith("git_full ="):
- mo = re.search(r'=\s*"(.*)"', line)
- if mo:
- keywords["full"] = mo.group(1)
- if line.strip().startswith("git_date ="):
- mo = re.search(r'=\s*"(.*)"', line)
- if mo:
- keywords["date"] = mo.group(1)
- except OSError:
- pass
- return keywords
-
-
-@register_vcs_handler("git", "keywords")
-def git_versions_from_keywords(
- keywords: Dict[str, str],
- tag_prefix: str,
- verbose: bool,
-) -> Dict[str, Any]:
- """Get version information from git keywords."""
- if "refnames" not in keywords:
- raise NotThisMethod("Short version file found")
- date = keywords.get("date")
- if date is not None:
- # Use only the last line. Previous lines may contain GPG signature
- # information.
- date = date.splitlines()[-1]
-
- # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant
- # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601
- # -like" string, which we must then edit to make compliant), because
- # it's been around since git-1.5.3, and it's too difficult to
- # discover which version we're using, or to work around using an
- # older one.
- date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
- refnames = keywords["refnames"].strip()
- if refnames.startswith("$Format"):
- if verbose:
- print("keywords are unexpanded, not using")
- raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
- refs = {r.strip() for r in refnames.strip("()").split(",")}
- # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
- # just "foo-1.0". If we see a "tag: " prefix, prefer those.
- TAG = "tag: "
- tags = {r[len(TAG):] for r in refs if r.startswith(TAG)}
- if not tags:
- # Either we're using git < 1.8.3, or there really are no tags. We use
- # a heuristic: assume all version tags have a digit. The old git %%d
- # expansion behaves like git log --decorate=short and strips out the
- # refs/heads/ and refs/tags/ prefixes that would let us distinguish
- # between branches and tags. By ignoring refnames without digits, we
- # filter out many common branch names like "release" and
- # "stabilization", as well as "HEAD" and "master".
- tags = {r for r in refs if re.search(r'\d', r)}
- if verbose:
- print("discarding '%%s', no digits" %% ",".join(refs - tags))
- if verbose:
- print("likely tags: %%s" %% ",".join(sorted(tags)))
- for ref in sorted(tags):
- # sorting will prefer e.g. "2.0" over "2.0rc1"
- if ref.startswith(tag_prefix):
- r = ref[len(tag_prefix):]
- # Filter out refs that exactly match prefix or that don't start
- # with a number once the prefix is stripped (mostly a concern
- # when prefix is '')
- if not re.match(r'\d', r):
- continue
- if verbose:
- print("picking %%s" %% r)
- return {"version": r,
- "full-revisionid": keywords["full"].strip(),
- "dirty": False, "error": None,
- "date": date}
- # no suitable tags, so version is "0+unknown", but full hex is still there
- if verbose:
- print("no suitable tags, using unknown + full revision id")
- return {"version": "0+unknown",
- "full-revisionid": keywords["full"].strip(),
- "dirty": False, "error": "no suitable tags", "date": None}
-
-
-@register_vcs_handler("git", "pieces_from_vcs")
-def git_pieces_from_vcs(
- tag_prefix: str,
- root: str,
- verbose: bool,
- runner: Callable = run_command
-) -> Dict[str, Any]:
- """Get version from 'git describe' in the root of the source tree.
-
- This only gets called if the git-archive 'subst' keywords were *not*
- expanded, and _version.py hasn't already been rewritten with a short
- version string, meaning we're inside a checked out source tree.
- """
- GITS = ["git"]
- if sys.platform == "win32":
- GITS = ["git.cmd", "git.exe"]
-
- # GIT_DIR can interfere with correct operation of Versioneer.
- # It may be intended to be passed to the Versioneer-versioned project,
- # but that should not change where we get our version from.
- env = os.environ.copy()
- env.pop("GIT_DIR", None)
- runner = functools.partial(runner, env=env)
-
- _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
- hide_stderr=not verbose)
- if rc != 0:
- if verbose:
- print("Directory %%s not under git control" %% root)
- raise NotThisMethod("'git rev-parse --git-dir' returned error")
-
- # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
- # if there isn't one, this yields HEX[-dirty] (no NUM)
- describe_out, rc = runner(GITS, [
- "describe", "--tags", "--dirty", "--always", "--long",
- "--match", f"{tag_prefix}[[:digit:]]*"
- ], cwd=root)
- # --long was added in git-1.5.5
- if describe_out is None:
- raise NotThisMethod("'git describe' failed")
- describe_out = describe_out.strip()
- full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
- if full_out is None:
- raise NotThisMethod("'git rev-parse' failed")
- full_out = full_out.strip()
-
- pieces: Dict[str, Any] = {}
- pieces["long"] = full_out
- pieces["short"] = full_out[:7] # maybe improved later
- pieces["error"] = None
-
- branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"],
- cwd=root)
- # --abbrev-ref was added in git-1.6.3
- if rc != 0 or branch_name is None:
- raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
- branch_name = branch_name.strip()
-
- if branch_name == "HEAD":
- # If we aren't exactly on a branch, pick a branch which represents
- # the current commit. If all else fails, we are on a branchless
- # commit.
- branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
- # --contains was added in git-1.5.4
- if rc != 0 or branches is None:
- raise NotThisMethod("'git branch --contains' returned error")
- branches = branches.split("\n")
-
- # Remove the first line if we're running detached
- if "(" in branches[0]:
- branches.pop(0)
-
- # Strip off the leading "* " from the list of branches.
- branches = [branch[2:] for branch in branches]
- if "master" in branches:
- branch_name = "master"
- elif not branches:
- branch_name = None
- else:
- # Pick the first branch that is returned. Good or bad.
- branch_name = branches[0]
-
- pieces["branch"] = branch_name
-
- # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
- # TAG might have hyphens.
- git_describe = describe_out
-
- # look for -dirty suffix
- dirty = git_describe.endswith("-dirty")
- pieces["dirty"] = dirty
- if dirty:
- git_describe = git_describe[:git_describe.rindex("-dirty")]
-
- # now we have TAG-NUM-gHEX or HEX
-
- if "-" in git_describe:
- # TAG-NUM-gHEX
- mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
- if not mo:
- # unparsable. Maybe git-describe is misbehaving?
- pieces["error"] = ("unable to parse git-describe output: '%%s'"
- %% describe_out)
- return pieces
-
- # tag
- full_tag = mo.group(1)
- if not full_tag.startswith(tag_prefix):
- if verbose:
- fmt = "tag '%%s' doesn't start with prefix '%%s'"
- print(fmt %% (full_tag, tag_prefix))
- pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'"
- %% (full_tag, tag_prefix))
- return pieces
- pieces["closest-tag"] = full_tag[len(tag_prefix):]
-
- # distance: number of commits since tag
- pieces["distance"] = int(mo.group(2))
-
- # commit: short hex revision ID
- pieces["short"] = mo.group(3)
-
- else:
- # HEX: no tags
- pieces["closest-tag"] = None
- out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root)
- pieces["distance"] = len(out.split()) # total number of commits
-
- # commit date: see ISO-8601 comment in git_versions_from_keywords()
- date = runner(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip()
- # Use only the last line. Previous lines may contain GPG signature
- # information.
- date = date.splitlines()[-1]
- pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
-
- return pieces
-
-
-def plus_or_dot(pieces: Dict[str, Any]) -> str:
- """Return a + if we don't already have one, else return a ."""
- if "+" in pieces.get("closest-tag", ""):
- return "."
- return "+"
-
-
-def render_pep440(pieces: Dict[str, Any]) -> str:
- """Build up version string, with post-release "local version identifier".
-
- Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
- get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
-
- Exceptions:
- 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += plus_or_dot(pieces)
- rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- else:
- # exception #1
- rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"],
- pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- return rendered
-
-
-def render_pep440_branch(pieces: Dict[str, Any]) -> str:
- """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
-
- The ".dev0" means not master branch. Note that .dev0 sorts backwards
- (a feature branch will appear "older" than the master branch).
-
- Exceptions:
- 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += plus_or_dot(pieces)
- rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- else:
- # exception #1
- rendered = "0"
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += "+untagged.%%d.g%%s" %% (pieces["distance"],
- pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- return rendered
-
-
-def pep440_split_post(ver: str) -> Tuple[str, Optional[int]]:
- """Split pep440 version string at the post-release segment.
-
- Returns the release segments before the post-release and the
- post-release version number (or -1 if no post-release segment is present).
- """
- vc = str.split(ver, ".post")
- return vc[0], int(vc[1] or 0) if len(vc) == 2 else None
-
-
-def render_pep440_pre(pieces: Dict[str, Any]) -> str:
- """TAG[.postN.devDISTANCE] -- No -dirty.
-
- Exceptions:
- 1: no tags. 0.post0.devDISTANCE
- """
- if pieces["closest-tag"]:
- if pieces["distance"]:
- # update the post release segment
- tag_version, post_version = pep440_split_post(pieces["closest-tag"])
- rendered = tag_version
- if post_version is not None:
- rendered += ".post%%d.dev%%d" %% (post_version + 1, pieces["distance"])
- else:
- rendered += ".post0.dev%%d" %% (pieces["distance"])
- else:
- # no commits, use the tag as the version
- rendered = pieces["closest-tag"]
- else:
- # exception #1
- rendered = "0.post0.dev%%d" %% pieces["distance"]
- return rendered
-
-
-def render_pep440_post(pieces: Dict[str, Any]) -> str:
- """TAG[.postDISTANCE[.dev0]+gHEX] .
-
- The ".dev0" means dirty. Note that .dev0 sorts backwards
- (a dirty tree will appear "older" than the corresponding clean one),
- but you shouldn't be releasing software with -dirty anyways.
-
- Exceptions:
- 1: no tags. 0.postDISTANCE[.dev0]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += ".post%%d" %% pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- rendered += plus_or_dot(pieces)
- rendered += "g%%s" %% pieces["short"]
- else:
- # exception #1
- rendered = "0.post%%d" %% pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- rendered += "+g%%s" %% pieces["short"]
- return rendered
-
-
-def render_pep440_post_branch(pieces: Dict[str, Any]) -> str:
- """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
-
- The ".dev0" means not master branch.
-
- Exceptions:
- 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += ".post%%d" %% pieces["distance"]
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += plus_or_dot(pieces)
- rendered += "g%%s" %% pieces["short"]
- if pieces["dirty"]:
- rendered += ".dirty"
- else:
- # exception #1
- rendered = "0.post%%d" %% pieces["distance"]
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += "+g%%s" %% pieces["short"]
- if pieces["dirty"]:
- rendered += ".dirty"
- return rendered
-
-
-def render_pep440_old(pieces: Dict[str, Any]) -> str:
- """TAG[.postDISTANCE[.dev0]] .
-
- The ".dev0" means dirty.
-
- Exceptions:
- 1: no tags. 0.postDISTANCE[.dev0]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += ".post%%d" %% pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- else:
- # exception #1
- rendered = "0.post%%d" %% pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- return rendered
-
-
-def render_git_describe(pieces: Dict[str, Any]) -> str:
- """TAG[-DISTANCE-gHEX][-dirty].
-
- Like 'git describe --tags --dirty --always'.
-
- Exceptions:
- 1: no tags. HEX[-dirty] (note: no 'g' prefix)
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"]:
- rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
- else:
- # exception #1
- rendered = pieces["short"]
- if pieces["dirty"]:
- rendered += "-dirty"
- return rendered
-
-
-def render_git_describe_long(pieces: Dict[str, Any]) -> str:
- """TAG-DISTANCE-gHEX[-dirty].
-
- Like 'git describe --tags --dirty --always -long'.
- The distance/hash is unconditional.
-
- Exceptions:
- 1: no tags. HEX[-dirty] (note: no 'g' prefix)
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
- else:
- # exception #1
- rendered = pieces["short"]
- if pieces["dirty"]:
- rendered += "-dirty"
- return rendered
-
-
-def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]:
- """Render the given version pieces into the requested style."""
- if pieces["error"]:
- return {"version": "unknown",
- "full-revisionid": pieces.get("long"),
- "dirty": None,
- "error": pieces["error"],
- "date": None}
-
- if not style or style == "default":
- style = "pep440" # the default
-
- if style == "pep440":
- rendered = render_pep440(pieces)
- elif style == "pep440-branch":
- rendered = render_pep440_branch(pieces)
- elif style == "pep440-pre":
- rendered = render_pep440_pre(pieces)
- elif style == "pep440-post":
- rendered = render_pep440_post(pieces)
- elif style == "pep440-post-branch":
- rendered = render_pep440_post_branch(pieces)
- elif style == "pep440-old":
- rendered = render_pep440_old(pieces)
- elif style == "git-describe":
- rendered = render_git_describe(pieces)
- elif style == "git-describe-long":
- rendered = render_git_describe_long(pieces)
- else:
- raise ValueError("unknown style '%%s'" %% style)
-
- return {"version": rendered, "full-revisionid": pieces["long"],
- "dirty": pieces["dirty"], "error": None,
- "date": pieces.get("date")}
-
-
-def get_versions() -> Dict[str, Any]:
- """Get version information or return default if unable to do so."""
- # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
- # __file__, we can work backwards from there to the root. Some
- # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
- # case we can only use expanded keywords.
-
- cfg = get_config()
- verbose = cfg.verbose
-
- try:
- return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
- verbose)
- except NotThisMethod:
- pass
-
- try:
- root = os.path.realpath(__file__)
- # versionfile_source is the relative path from the top of the source
- # tree (where the .git directory might live) to this file. Invert
- # this to find the root from __file__.
- for _ in cfg.versionfile_source.split('/'):
- root = os.path.dirname(root)
- except NameError:
- return {"version": "0+unknown", "full-revisionid": None,
- "dirty": None,
- "error": "unable to find root of source tree",
- "date": None}
-
- try:
- pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
- return render(pieces, cfg.style)
- except NotThisMethod:
- pass
-
- try:
- if cfg.parentdir_prefix:
- return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
- except NotThisMethod:
- pass
-
- return {"version": "0+unknown", "full-revisionid": None,
- "dirty": None,
- "error": "unable to compute version", "date": None}
-'''
-
-
-@register_vcs_handler("git", "get_keywords")
-def git_get_keywords(versionfile_abs: str) -> Dict[str, str]:
- """Extract version information from the given file."""
- # the code embedded in _version.py can just fetch the value of these
- # keywords. When used from setup.py, we don't want to import _version.py,
- # so we do it with a regexp instead. This function is not used from
- # _version.py.
- keywords: Dict[str, str] = {}
- try:
- with open(versionfile_abs) as fobj:
- for line in fobj:
- if line.strip().startswith("git_refnames ="):
- mo = re.search(r'=\s*"(.*)"', line)
- if mo:
- keywords["refnames"] = mo.group(1)
- if line.strip().startswith("git_full ="):
- mo = re.search(r'=\s*"(.*)"', line)
- if mo:
- keywords["full"] = mo.group(1)
- if line.strip().startswith("git_date ="):
- mo = re.search(r'=\s*"(.*)"', line)
- if mo:
- keywords["date"] = mo.group(1)
- except OSError:
- pass
- return keywords
-
-
-@register_vcs_handler("git", "keywords")
-def git_versions_from_keywords(
- keywords: Dict[str, str],
- tag_prefix: str,
- verbose: bool,
-) -> Dict[str, Any]:
- """Get version information from git keywords."""
- if "refnames" not in keywords:
- raise NotThisMethod("Short version file found")
- date = keywords.get("date")
- if date is not None:
- # Use only the last line. Previous lines may contain GPG signature
- # information.
- date = date.splitlines()[-1]
-
- # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
- # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
- # -like" string, which we must then edit to make compliant), because
- # it's been around since git-1.5.3, and it's too difficult to
- # discover which version we're using, or to work around using an
- # older one.
- date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
- refnames = keywords["refnames"].strip()
- if refnames.startswith("$Format"):
- if verbose:
- print("keywords are unexpanded, not using")
- raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
- refs = {r.strip() for r in refnames.strip("()").split(",")}
- # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
- # just "foo-1.0". If we see a "tag: " prefix, prefer those.
- TAG = "tag: "
- tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)}
- if not tags:
- # Either we're using git < 1.8.3, or there really are no tags. We use
- # a heuristic: assume all version tags have a digit. The old git %d
- # expansion behaves like git log --decorate=short and strips out the
- # refs/heads/ and refs/tags/ prefixes that would let us distinguish
- # between branches and tags. By ignoring refnames without digits, we
- # filter out many common branch names like "release" and
- # "stabilization", as well as "HEAD" and "master".
- tags = {r for r in refs if re.search(r"\d", r)}
- if verbose:
- print("discarding '%s', no digits" % ",".join(refs - tags))
- if verbose:
- print("likely tags: %s" % ",".join(sorted(tags)))
- for ref in sorted(tags):
- # sorting will prefer e.g. "2.0" over "2.0rc1"
- if ref.startswith(tag_prefix):
- r = ref[len(tag_prefix) :]
- # Filter out refs that exactly match prefix or that don't start
- # with a number once the prefix is stripped (mostly a concern
- # when prefix is '')
- if not re.match(r"\d", r):
- continue
- if verbose:
- print("picking %s" % r)
- return {
- "version": r,
- "full-revisionid": keywords["full"].strip(),
- "dirty": False,
- "error": None,
- "date": date,
- }
- # no suitable tags, so version is "0+unknown", but full hex is still there
- if verbose:
- print("no suitable tags, using unknown + full revision id")
- return {
- "version": "0+unknown",
- "full-revisionid": keywords["full"].strip(),
- "dirty": False,
- "error": "no suitable tags",
- "date": None,
- }
-
-
-@register_vcs_handler("git", "pieces_from_vcs")
-def git_pieces_from_vcs(
- tag_prefix: str, root: str, verbose: bool, runner: Callable = run_command
-) -> Dict[str, Any]:
- """Get version from 'git describe' in the root of the source tree.
-
- This only gets called if the git-archive 'subst' keywords were *not*
- expanded, and _version.py hasn't already been rewritten with a short
- version string, meaning we're inside a checked out source tree.
- """
- GITS = ["git"]
- if sys.platform == "win32":
- GITS = ["git.cmd", "git.exe"]
-
- # GIT_DIR can interfere with correct operation of Versioneer.
- # It may be intended to be passed to the Versioneer-versioned project,
- # but that should not change where we get our version from.
- env = os.environ.copy()
- env.pop("GIT_DIR", None)
- runner = functools.partial(runner, env=env)
-
- _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=not verbose)
- if rc != 0:
- if verbose:
- print("Directory %s not under git control" % root)
- raise NotThisMethod("'git rev-parse --git-dir' returned error")
-
- # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
- # if there isn't one, this yields HEX[-dirty] (no NUM)
- describe_out, rc = runner(
- GITS,
- [
- "describe",
- "--tags",
- "--dirty",
- "--always",
- "--long",
- "--match",
- f"{tag_prefix}[[:digit:]]*",
- ],
- cwd=root,
- )
- # --long was added in git-1.5.5
- if describe_out is None:
- raise NotThisMethod("'git describe' failed")
- describe_out = describe_out.strip()
- full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
- if full_out is None:
- raise NotThisMethod("'git rev-parse' failed")
- full_out = full_out.strip()
-
- pieces: Dict[str, Any] = {}
- pieces["long"] = full_out
- pieces["short"] = full_out[:7] # maybe improved later
- pieces["error"] = None
-
- branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root)
- # --abbrev-ref was added in git-1.6.3
- if rc != 0 or branch_name is None:
- raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
- branch_name = branch_name.strip()
-
- if branch_name == "HEAD":
- # If we aren't exactly on a branch, pick a branch which represents
- # the current commit. If all else fails, we are on a branchless
- # commit.
- branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
- # --contains was added in git-1.5.4
- if rc != 0 or branches is None:
- raise NotThisMethod("'git branch --contains' returned error")
- branches = branches.split("\n")
-
- # Remove the first line if we're running detached
- if "(" in branches[0]:
- branches.pop(0)
-
- # Strip off the leading "* " from the list of branches.
- branches = [branch[2:] for branch in branches]
- if "master" in branches:
- branch_name = "master"
- elif not branches:
- branch_name = None
- else:
- # Pick the first branch that is returned. Good or bad.
- branch_name = branches[0]
-
- pieces["branch"] = branch_name
-
- # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
- # TAG might have hyphens.
- git_describe = describe_out
-
- # look for -dirty suffix
- dirty = git_describe.endswith("-dirty")
- pieces["dirty"] = dirty
- if dirty:
- git_describe = git_describe[: git_describe.rindex("-dirty")]
-
- # now we have TAG-NUM-gHEX or HEX
-
- if "-" in git_describe:
- # TAG-NUM-gHEX
- mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe)
- if not mo:
- # unparsable. Maybe git-describe is misbehaving?
- pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out
- return pieces
-
- # tag
- full_tag = mo.group(1)
- if not full_tag.startswith(tag_prefix):
- if verbose:
- fmt = "tag '%s' doesn't start with prefix '%s'"
- print(fmt % (full_tag, tag_prefix))
- pieces["error"] = "tag '{}' doesn't start with prefix '{}'".format(
- full_tag,
- tag_prefix,
- )
- return pieces
- pieces["closest-tag"] = full_tag[len(tag_prefix) :]
-
- # distance: number of commits since tag
- pieces["distance"] = int(mo.group(2))
-
- # commit: short hex revision ID
- pieces["short"] = mo.group(3)
-
- else:
- # HEX: no tags
- pieces["closest-tag"] = None
- out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root)
- pieces["distance"] = len(out.split()) # total number of commits
-
- # commit date: see ISO-8601 comment in git_versions_from_keywords()
- date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip()
- # Use only the last line. Previous lines may contain GPG signature
- # information.
- date = date.splitlines()[-1]
- pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
-
- return pieces
-
-
-def do_vcs_install(versionfile_source: str, ipy: Optional[str]) -> None:
- """Git-specific installation logic for Versioneer.
-
- For Git, this means creating/changing .gitattributes to mark _version.py
- for export-subst keyword substitution.
- """
- GITS = ["git"]
- if sys.platform == "win32":
- GITS = ["git.cmd", "git.exe"]
- files = [versionfile_source]
- if ipy:
- files.append(ipy)
- if "VERSIONEER_PEP518" not in globals():
- try:
- my_path = __file__
- if my_path.endswith((".pyc", ".pyo")):
- my_path = os.path.splitext(my_path)[0] + ".py"
- versioneer_file = os.path.relpath(my_path)
- except NameError:
- versioneer_file = "versioneer.py"
- files.append(versioneer_file)
- present = False
- try:
- with open(".gitattributes") as fobj:
- for line in fobj:
- if line.strip().startswith(versionfile_source):
- if "export-subst" in line.strip().split()[1:]:
- present = True
- break
- except OSError:
- pass
- if not present:
- with open(".gitattributes", "a+") as fobj:
- fobj.write(f"{versionfile_source} export-subst\n")
- files.append(".gitattributes")
- run_command(GITS, ["add", "--"] + files)
-
-
-def versions_from_parentdir(
- parentdir_prefix: str,
- root: str,
- verbose: bool,
-) -> Dict[str, Any]:
- """Try to determine the version from the parent directory name.
-
- Source tarballs conventionally unpack into a directory that includes both
- the project name and a version string. We will also support searching up
- two directory levels for an appropriately named parent directory
- """
- rootdirs = []
-
- for _ in range(3):
- dirname = os.path.basename(root)
- if dirname.startswith(parentdir_prefix):
- return {
- "version": dirname[len(parentdir_prefix) :],
- "full-revisionid": None,
- "dirty": False,
- "error": None,
- "date": None,
- }
- rootdirs.append(root)
- root = os.path.dirname(root) # up a level
-
- if verbose:
- print(
- "Tried directories %s but none started with prefix %s"
- % (str(rootdirs), parentdir_prefix)
- )
- raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
-
-
-SHORT_VERSION_PY = """
-# This file was generated by 'versioneer.py' (0.29) from
-# revision-control system data, or from the parent directory name of an
-# unpacked source archive. Distribution tarballs contain a pre-generated copy
-# of this file.
-
-import json
-
-version_json = '''
-%s
-''' # END VERSION_JSON
-
-
-def get_versions():
- return json.loads(version_json)
-"""
-
-
-def versions_from_file(filename: str) -> Dict[str, Any]:
- """Try to determine the version from _version.py if present."""
- try:
- with open(filename) as f:
- contents = f.read()
- except OSError:
- raise NotThisMethod("unable to read _version.py")
- mo = re.search(
- r"version_json = '''\n(.*)''' # END VERSION_JSON", contents, re.M | re.S
- )
- if not mo:
- mo = re.search(
- r"version_json = '''\r\n(.*)''' # END VERSION_JSON", contents, re.M | re.S
- )
- if not mo:
- raise NotThisMethod("no version_json in _version.py")
- return json.loads(mo.group(1))
-
-
-def write_to_version_file(filename: str, versions: Dict[str, Any]) -> None:
- """Write the given version number to the given _version.py file."""
- contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": "))
- with open(filename, "w") as f:
- f.write(SHORT_VERSION_PY % contents)
-
- print("set {} to '{}'".format(filename, versions["version"]))
-
-
-def plus_or_dot(pieces: Dict[str, Any]) -> str:
- """Return a + if we don't already have one, else return a ."""
- if "+" in pieces.get("closest-tag", ""):
- return "."
- return "+"
-
-
-def render_pep440(pieces: Dict[str, Any]) -> str:
- """Build up version string, with post-release "local version identifier".
-
- Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
- get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
-
- Exceptions:
- 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += plus_or_dot(pieces)
- rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- else:
- # exception #1
- rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- return rendered
-
-
-def render_pep440_branch(pieces: Dict[str, Any]) -> str:
- """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
-
- The ".dev0" means not master branch. Note that .dev0 sorts backwards
- (a feature branch will appear "older" than the master branch).
-
- Exceptions:
- 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += plus_or_dot(pieces)
- rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- else:
- # exception #1
- rendered = "0"
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
- if pieces["dirty"]:
- rendered += ".dirty"
- return rendered
-
-
-def pep440_split_post(ver: str) -> Tuple[str, Optional[int]]:
- """Split pep440 version string at the post-release segment.
-
- Returns the release segments before the post-release and the
- post-release version number (or -1 if no post-release segment is present).
- """
- vc = str.split(ver, ".post")
- return vc[0], int(vc[1] or 0) if len(vc) == 2 else None
-
-
-def render_pep440_pre(pieces: Dict[str, Any]) -> str:
- """TAG[.postN.devDISTANCE] -- No -dirty.
-
- Exceptions:
- 1: no tags. 0.post0.devDISTANCE
- """
- if pieces["closest-tag"]:
- if pieces["distance"]:
- # update the post release segment
- tag_version, post_version = pep440_split_post(pieces["closest-tag"])
- rendered = tag_version
- if post_version is not None:
- rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"])
- else:
- rendered += ".post0.dev%d" % (pieces["distance"])
- else:
- # no commits, use the tag as the version
- rendered = pieces["closest-tag"]
- else:
- # exception #1
- rendered = "0.post0.dev%d" % pieces["distance"]
- return rendered
-
-
-def render_pep440_post(pieces: Dict[str, Any]) -> str:
- """TAG[.postDISTANCE[.dev0]+gHEX] .
-
- The ".dev0" means dirty. Note that .dev0 sorts backwards
- (a dirty tree will appear "older" than the corresponding clean one),
- but you shouldn't be releasing software with -dirty anyways.
-
- Exceptions:
- 1: no tags. 0.postDISTANCE[.dev0]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += ".post%d" % pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- rendered += plus_or_dot(pieces)
- rendered += "g%s" % pieces["short"]
- else:
- # exception #1
- rendered = "0.post%d" % pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- rendered += "+g%s" % pieces["short"]
- return rendered
-
-
-def render_pep440_post_branch(pieces: Dict[str, Any]) -> str:
- """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
-
- The ".dev0" means not master branch.
-
- Exceptions:
- 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += ".post%d" % pieces["distance"]
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += plus_or_dot(pieces)
- rendered += "g%s" % pieces["short"]
- if pieces["dirty"]:
- rendered += ".dirty"
- else:
- # exception #1
- rendered = "0.post%d" % pieces["distance"]
- if pieces["branch"] != "master":
- rendered += ".dev0"
- rendered += "+g%s" % pieces["short"]
- if pieces["dirty"]:
- rendered += ".dirty"
- return rendered
-
-
-def render_pep440_old(pieces: Dict[str, Any]) -> str:
- """TAG[.postDISTANCE[.dev0]] .
-
- The ".dev0" means dirty.
-
- Exceptions:
- 1: no tags. 0.postDISTANCE[.dev0]
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"] or pieces["dirty"]:
- rendered += ".post%d" % pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- else:
- # exception #1
- rendered = "0.post%d" % pieces["distance"]
- if pieces["dirty"]:
- rendered += ".dev0"
- return rendered
-
-
-def render_git_describe(pieces: Dict[str, Any]) -> str:
- """TAG[-DISTANCE-gHEX][-dirty].
-
- Like 'git describe --tags --dirty --always'.
-
- Exceptions:
- 1: no tags. HEX[-dirty] (note: no 'g' prefix)
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- if pieces["distance"]:
- rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
- else:
- # exception #1
- rendered = pieces["short"]
- if pieces["dirty"]:
- rendered += "-dirty"
- return rendered
-
-
-def render_git_describe_long(pieces: Dict[str, Any]) -> str:
- """TAG-DISTANCE-gHEX[-dirty].
-
- Like 'git describe --tags --dirty --always -long'.
- The distance/hash is unconditional.
-
- Exceptions:
- 1: no tags. HEX[-dirty] (note: no 'g' prefix)
- """
- if pieces["closest-tag"]:
- rendered = pieces["closest-tag"]
- rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
- else:
- # exception #1
- rendered = pieces["short"]
- if pieces["dirty"]:
- rendered += "-dirty"
- return rendered
-
-
-def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]:
- """Render the given version pieces into the requested style."""
- if pieces["error"]:
- return {
- "version": "unknown",
- "full-revisionid": pieces.get("long"),
- "dirty": None,
- "error": pieces["error"],
- "date": None,
- }
-
- if not style or style == "default":
- style = "pep440" # the default
-
- if style == "pep440":
- rendered = render_pep440(pieces)
- elif style == "pep440-branch":
- rendered = render_pep440_branch(pieces)
- elif style == "pep440-pre":
- rendered = render_pep440_pre(pieces)
- elif style == "pep440-post":
- rendered = render_pep440_post(pieces)
- elif style == "pep440-post-branch":
- rendered = render_pep440_post_branch(pieces)
- elif style == "pep440-old":
- rendered = render_pep440_old(pieces)
- elif style == "git-describe":
- rendered = render_git_describe(pieces)
- elif style == "git-describe-long":
- rendered = render_git_describe_long(pieces)
- else:
- raise ValueError("unknown style '%s'" % style)
-
- return {
- "version": rendered,
- "full-revisionid": pieces["long"],
- "dirty": pieces["dirty"],
- "error": None,
- "date": pieces.get("date"),
- }
-
-
-class VersioneerBadRootError(Exception):
- """The project root directory is unknown or missing key files."""
-
-
-def get_versions(verbose: bool = False) -> Dict[str, Any]:
- """Get the project version from whatever source is available.
-
- Returns dict with two keys: 'version' and 'full'.
- """
- if "versioneer" in sys.modules:
- # see the discussion in cmdclass.py:get_cmdclass()
- del sys.modules["versioneer"]
-
- root = get_root()
- cfg = get_config_from_root(root)
-
- assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg"
- handlers = HANDLERS.get(cfg.VCS)
- assert handlers, "unrecognized VCS '%s'" % cfg.VCS
- verbose = verbose or bool(cfg.verbose) # `bool()` used to avoid `None`
- assert (
- cfg.versionfile_source is not None
- ), "please set versioneer.versionfile_source"
- assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix"
-
- versionfile_abs = os.path.join(root, cfg.versionfile_source)
-
- # extract version from first of: _version.py, VCS command (e.g. 'git
- # describe'), parentdir. This is meant to work for developers using a
- # source checkout, for users of a tarball created by 'setup.py sdist',
- # and for users of a tarball/zipball created by 'git archive' or github's
- # download-from-tag feature or the equivalent in other VCSes.
-
- get_keywords_f = handlers.get("get_keywords")
- from_keywords_f = handlers.get("keywords")
- if get_keywords_f and from_keywords_f:
- try:
- keywords = get_keywords_f(versionfile_abs)
- ver = from_keywords_f(keywords, cfg.tag_prefix, verbose)
- if verbose:
- print("got version from expanded keyword %s" % ver)
- return ver
- except NotThisMethod:
- pass
-
- try:
- ver = versions_from_file(versionfile_abs)
- if verbose:
- print(f"got version from file {versionfile_abs} {ver}")
- return ver
- except NotThisMethod:
- pass
-
- from_vcs_f = handlers.get("pieces_from_vcs")
- if from_vcs_f:
- try:
- pieces = from_vcs_f(cfg.tag_prefix, root, verbose)
- ver = render(pieces, cfg.style)
- if verbose:
- print("got version from VCS %s" % ver)
- return ver
- except NotThisMethod:
- pass
-
- try:
- if cfg.parentdir_prefix:
- ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
- if verbose:
- print("got version from parentdir %s" % ver)
- return ver
- except NotThisMethod:
- pass
-
- if verbose:
- print("unable to compute version")
-
- return {
- "version": "0+unknown",
- "full-revisionid": None,
- "dirty": None,
- "error": "unable to compute version",
- "date": None,
- }
-
-
-def get_version() -> str:
- """Get the short version string for this project."""
- return get_versions()["version"]
-
-
-def get_cmdclass(cmdclass: Optional[Dict[str, Any]] = None):
- """Get the custom setuptools subclasses used by Versioneer.
-
- If the package uses a different cmdclass (e.g. one from numpy), it
- should be provide as an argument.
- """
- if "versioneer" in sys.modules:
- del sys.modules["versioneer"]
- # this fixes the "python setup.py develop" case (also 'install' and
- # 'easy_install .'), in which subdependencies of the main project are
- # built (using setup.py bdist_egg) in the same python process. Assume
- # a main project A and a dependency B, which use different versions
- # of Versioneer. A's setup.py imports A's Versioneer, leaving it in
- # sys.modules by the time B's setup.py is executed, causing B to run
- # with the wrong versioneer. Setuptools wraps the sub-dep builds in a
- # sandbox that restores sys.modules to it's pre-build state, so the
- # parent is protected against the child's "import versioneer". By
- # removing ourselves from sys.modules here, before the child build
- # happens, we protect the child from the parent's versioneer too.
- # Also see https://github.com/python-versioneer/python-versioneer/issues/52
-
- cmds = {} if cmdclass is None else cmdclass.copy()
-
- # we add "version" to setuptools
- from setuptools import Command
-
- class cmd_version(Command):
- description = "report generated version string"
- user_options: List[Tuple[str, str, str]] = []
- boolean_options: List[str] = []
-
- def initialize_options(self) -> None:
- pass
-
- def finalize_options(self) -> None:
- pass
-
- def run(self) -> None:
- vers = get_versions(verbose=True)
- print("Version: %s" % vers["version"])
- print(" full-revisionid: %s" % vers.get("full-revisionid"))
- print(" dirty: %s" % vers.get("dirty"))
- print(" date: %s" % vers.get("date"))
- if vers["error"]:
- print(" error: %s" % vers["error"])
-
- cmds["version"] = cmd_version
-
- # we override "build_py" in setuptools
- #
- # most invocation pathways end up running build_py:
- # distutils/build -> build_py
- # distutils/install -> distutils/build ->..
- # setuptools/bdist_wheel -> distutils/install ->..
- # setuptools/bdist_egg -> distutils/install_lib -> build_py
- # setuptools/install -> bdist_egg ->..
- # setuptools/develop -> ?
- # pip install:
- # copies source tree to a tempdir before running egg_info/etc
- # if .git isn't copied too, 'git describe' will fail
- # then does setup.py bdist_wheel, or sometimes setup.py install
- # setup.py egg_info -> ?
-
- # pip install -e . and setuptool/editable_wheel will invoke build_py
- # but the build_py command is not expected to copy any files.
-
- # we override different "build_py" commands for both environments
- if "build_py" in cmds:
- _build_py: Any = cmds["build_py"]
- else:
- from setuptools.command.build_py import build_py as _build_py
-
- class cmd_build_py(_build_py):
- def run(self) -> None:
- root = get_root()
- cfg = get_config_from_root(root)
- versions = get_versions()
- _build_py.run(self)
- if getattr(self, "editable_mode", False):
- # During editable installs `.py` and data files are
- # not copied to build_lib
- return
- # now locate _version.py in the new build/ directory and replace
- # it with an updated value
- if cfg.versionfile_build:
- target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build)
- print("UPDATING %s" % target_versionfile)
- write_to_version_file(target_versionfile, versions)
-
- cmds["build_py"] = cmd_build_py
-
- if "build_ext" in cmds:
- _build_ext: Any = cmds["build_ext"]
- else:
- from setuptools.command.build_ext import build_ext as _build_ext
-
- class cmd_build_ext(_build_ext):
- def run(self) -> None:
- root = get_root()
- cfg = get_config_from_root(root)
- versions = get_versions()
- _build_ext.run(self)
- if self.inplace:
- # build_ext --inplace will only build extensions in
- # build/lib<..> dir with no _version.py to write to.
- # As in place builds will already have a _version.py
- # in the module dir, we do not need to write one.
- return
- # now locate _version.py in the new build/ directory and replace
- # it with an updated value
- if not cfg.versionfile_build:
- return
- target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build)
- if not os.path.exists(target_versionfile):
- print(
- f"Warning: {target_versionfile} does not exist, skipping "
- "version update. This can happen if you are running build_ext "
- "without first running build_py."
- )
- return
- print("UPDATING %s" % target_versionfile)
- write_to_version_file(target_versionfile, versions)
-
- cmds["build_ext"] = cmd_build_ext
-
- if "cx_Freeze" in sys.modules: # cx_freeze enabled?
- from cx_Freeze.dist import build_exe as _build_exe # type: ignore
-
- # nczeczulin reports that py2exe won't like the pep440-style string
- # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g.
- # setup(console=[{
- # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION
- # "product_version": versioneer.get_version(),
- # ...
-
- class cmd_build_exe(_build_exe):
- def run(self) -> None:
- root = get_root()
- cfg = get_config_from_root(root)
- versions = get_versions()
- target_versionfile = cfg.versionfile_source
- print("UPDATING %s" % target_versionfile)
- write_to_version_file(target_versionfile, versions)
-
- _build_exe.run(self)
- os.unlink(target_versionfile)
- with open(cfg.versionfile_source, "w") as f:
- LONG = LONG_VERSION_PY[cfg.VCS]
- f.write(
- LONG
- % {
- "DOLLAR": "$",
- "STYLE": cfg.style,
- "TAG_PREFIX": cfg.tag_prefix,
- "PARENTDIR_PREFIX": cfg.parentdir_prefix,
- "VERSIONFILE_SOURCE": cfg.versionfile_source,
- }
- )
-
- cmds["build_exe"] = cmd_build_exe
- del cmds["build_py"]
-
- if "py2exe" in sys.modules: # py2exe enabled?
- try:
- from py2exe.setuptools_buildexe import py2exe as _py2exe # type: ignore
- except ImportError:
- from py2exe.distutils_buildexe import py2exe as _py2exe # type: ignore
-
- class cmd_py2exe(_py2exe):
- def run(self) -> None:
- root = get_root()
- cfg = get_config_from_root(root)
- versions = get_versions()
- target_versionfile = cfg.versionfile_source
- print("UPDATING %s" % target_versionfile)
- write_to_version_file(target_versionfile, versions)
-
- _py2exe.run(self)
- os.unlink(target_versionfile)
- with open(cfg.versionfile_source, "w") as f:
- LONG = LONG_VERSION_PY[cfg.VCS]
- f.write(
- LONG
- % {
- "DOLLAR": "$",
- "STYLE": cfg.style,
- "TAG_PREFIX": cfg.tag_prefix,
- "PARENTDIR_PREFIX": cfg.parentdir_prefix,
- "VERSIONFILE_SOURCE": cfg.versionfile_source,
- }
- )
-
- cmds["py2exe"] = cmd_py2exe
-
- # sdist farms its file list building out to egg_info
- if "egg_info" in cmds:
- _egg_info: Any = cmds["egg_info"]
- else:
- from setuptools.command.egg_info import egg_info as _egg_info
-
- class cmd_egg_info(_egg_info):
- def find_sources(self) -> None:
- # egg_info.find_sources builds the manifest list and writes it
- # in one shot
- super().find_sources()
-
- # Modify the filelist and normalize it
- root = get_root()
- cfg = get_config_from_root(root)
- self.filelist.append("versioneer.py")
- if cfg.versionfile_source:
- # There are rare cases where versionfile_source might not be
- # included by default, so we must be explicit
- self.filelist.append(cfg.versionfile_source)
- self.filelist.sort()
- self.filelist.remove_duplicates()
-
- # The write method is hidden in the manifest_maker instance that
- # generated the filelist and was thrown away
- # We will instead replicate their final normalization (to unicode,
- # and POSIX-style paths)
- from setuptools import unicode_utils
-
- normalized = [
- unicode_utils.filesys_decode(f).replace(os.sep, "/")
- for f in self.filelist.files
- ]
-
- manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
- with open(manifest_filename, "w") as fobj:
- fobj.write("\n".join(normalized))
-
- cmds["egg_info"] = cmd_egg_info
-
- # we override different "sdist" commands for both environments
- if "sdist" in cmds:
- _sdist: Any = cmds["sdist"]
- else:
- from setuptools.command.sdist import sdist as _sdist
-
- class cmd_sdist(_sdist):
- def run(self) -> None:
- versions = get_versions()
- self._versioneer_generated_versions = versions
- # unless we update this, the command will keep using the old
- # version
- self.distribution.metadata.version = versions["version"]
- return _sdist.run(self)
-
- def make_release_tree(self, base_dir: str, files: List[str]) -> None:
- root = get_root()
- cfg = get_config_from_root(root)
- _sdist.make_release_tree(self, base_dir, files)
- # now locate _version.py in the new base_dir directory
- # (remembering that it may be a hardlink) and replace it with an
- # updated value
- target_versionfile = os.path.join(base_dir, cfg.versionfile_source)
- print("UPDATING %s" % target_versionfile)
- write_to_version_file(
- target_versionfile, self._versioneer_generated_versions
- )
-
- cmds["sdist"] = cmd_sdist
-
- return cmds
-
-
-CONFIG_ERROR = """
-setup.cfg is missing the necessary Versioneer configuration. You need
-a section like:
-
- [versioneer]
- VCS = git
- style = pep440
- versionfile_source = src/myproject/_version.py
- versionfile_build = myproject/_version.py
- tag_prefix =
- parentdir_prefix = myproject-
-
-You will also need to edit your setup.py to use the results:
-
- import versioneer
- setup(version=versioneer.get_version(),
- cmdclass=versioneer.get_cmdclass(), ...)
-
-Please read the docstring in ./versioneer.py for configuration instructions,
-edit setup.cfg, and re-run the installer or 'python versioneer.py setup'.
-"""
-
-SAMPLE_CONFIG = """
-# See the docstring in versioneer.py for instructions. Note that you must
-# re-run 'versioneer.py setup' after changing this section, and commit the
-# resulting files.
-
-[versioneer]
-#VCS = git
-#style = pep440
-#versionfile_source =
-#versionfile_build =
-#tag_prefix =
-#parentdir_prefix =
-
-"""
-
-OLD_SNIPPET = """
-from ._version import get_versions
-__version__ = get_versions()['version']
-del get_versions
-"""
-
-INIT_PY_SNIPPET = """
-from . import {0}
-__version__ = {0}.get_versions()['version']
-"""
-
-
-def do_setup() -> int:
- """Do main VCS-independent setup function for installing Versioneer."""
- root = get_root()
- try:
- cfg = get_config_from_root(root)
- except (OSError, configparser.NoSectionError, configparser.NoOptionError) as e:
- if isinstance(e, (OSError, configparser.NoSectionError)):
- print("Adding sample versioneer config to setup.cfg", file=sys.stderr)
- with open(os.path.join(root, "setup.cfg"), "a") as f:
- f.write(SAMPLE_CONFIG)
- print(CONFIG_ERROR, file=sys.stderr)
- return 1
-
- print(" creating %s" % cfg.versionfile_source)
- with open(cfg.versionfile_source, "w") as f:
- LONG = LONG_VERSION_PY[cfg.VCS]
- f.write(
- LONG
- % {
- "DOLLAR": "$",
- "STYLE": cfg.style,
- "TAG_PREFIX": cfg.tag_prefix,
- "PARENTDIR_PREFIX": cfg.parentdir_prefix,
- "VERSIONFILE_SOURCE": cfg.versionfile_source,
- }
- )
-
- ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py")
- maybe_ipy: Optional[str] = ipy
- if os.path.exists(ipy):
- try:
- with open(ipy) as f:
- old = f.read()
- except OSError:
- old = ""
- module = os.path.splitext(os.path.basename(cfg.versionfile_source))[0]
- snippet = INIT_PY_SNIPPET.format(module)
- if OLD_SNIPPET in old:
- print(" replacing boilerplate in %s" % ipy)
- with open(ipy, "w") as f:
- f.write(old.replace(OLD_SNIPPET, snippet))
- elif snippet not in old:
- print(" appending to %s" % ipy)
- with open(ipy, "a") as f:
- f.write(snippet)
- else:
- print(" %s unmodified" % ipy)
- else:
- print(" %s doesn't exist, ok" % ipy)
- maybe_ipy = None
-
- # Make VCS-specific changes. For git, this means creating/changing
- # .gitattributes to mark _version.py for export-subst keyword
- # substitution.
- do_vcs_install(cfg.versionfile_source, maybe_ipy)
- return 0
-
-
-def scan_setup_py() -> int:
- """Validate the contents of setup.py against Versioneer's expectations."""
- found = set()
- setters = False
- errors = 0
- with open("setup.py") as f:
- for line in f.readlines():
- if "import versioneer" in line:
- found.add("import")
- if "versioneer.get_cmdclass()" in line:
- found.add("cmdclass")
- if "versioneer.get_version()" in line:
- found.add("get_version")
- if "versioneer.VCS" in line:
- setters = True
- if "versioneer.versionfile_source" in line:
- setters = True
- if len(found) != 3:
- print("")
- print("Your setup.py appears to be missing some important items")
- print("(but I might be wrong). Please make sure it has something")
- print("roughly like the following:")
- print("")
- print(" import versioneer")
- print(" setup( version=versioneer.get_version(),")
- print(" cmdclass=versioneer.get_cmdclass(), ...)")
- print("")
- errors += 1
- if setters:
- print("You should remove lines like 'versioneer.VCS = ' and")
- print("'versioneer.versionfile_source = ' . This configuration")
- print("now lives in setup.cfg, and should be removed from setup.py")
- print("")
- errors += 1
- return errors
-
-
-def setup_command() -> NoReturn:
- """Set up Versioneer and exit with appropriate error code."""
- errors = do_setup()
- errors += scan_setup_py()
- sys.exit(1 if errors else 0)
-
-
-if __name__ == "__main__":
- cmd = sys.argv[1]
- if cmd == "setup":
- setup_command()