diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 797d85e0a..a02b042a0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -417,3 +417,9 @@ jobs: needs: - build uses: ./.github/workflows/test_downstream.yml + + test_common_wheels: + name: "Test installation of common wheels" + needs: + - build + uses: ./.github/workflows/test_common_wheels.yml diff --git a/.github/workflows/test_common_wheels.yml b/.github/workflows/test_common_wheels.yml new file mode 100644 index 000000000..997bc0da3 --- /dev/null +++ b/.github/workflows/test_common_wheels.yml @@ -0,0 +1,77 @@ +name: "Test common wheel files for installation with pixi" + +on: + workflow_call: + +jobs: + test_common_wheels: + name: ${{ matrix.arch.name }} - Test Installation of Common Wheels + runs-on: ${{ matrix.arch.os }} + env: + TARGET_RELEASE: "${{ github.workspace }}/.pixi/target/release" + LOGS_DIR: "${{ github.workspace }}/tests/wheel_tests/.logs" + SUMMARY_FILE: "${{ github.workspace }}/tests/wheel_tests/.summary.md" + PYTHONIOENCODING: utf-8 + strategy: + fail-fast: false + matrix: + arch: + # Linux + - { + target: x86_64-unknown-linux-musl, + os: 8core_ubuntu_latest_runner, + name: "Linux", + } + # MacOS + - { target: x86_64-apple-darwin, os: macos-13, name: "MacOS-x86" } + - { target: aarch64-apple-darwin, os: macos-14, name: "MacOS-Arm" } # macOS-14 is the ARM chipset + # Windows + - { + target: x86_64-pc-windows-msvc, + os: windows-latest, + extension: .exe, + name: "Windows", + } + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Download binary from build + uses: actions/download-artifact@v4 + with: + name: pixi-${{ matrix.arch.target }}${{ matrix.arch.extension }} + path: pixi_bin + - name: Debug + run: | + pwd + - name: Create Directory and Move Executable to TARGET_RELEASE + if: matrix.arch.name != 'Windows' + run: | + mkdir -p ${{ env.TARGET_RELEASE }} + mv pixi_bin/pixi-${{ matrix.arch.target }} ${{ env.TARGET_RELEASE }}/pixi + chmod a+x ${{ env.TARGET_RELEASE }}/pixi + - name: Create Directory and Move Executable to TARGET_RELEASE + if: matrix.arch.name == 'Windows' && matrix.arch.target == 'x86_64-pc-windows-msvc' + run: | + New-Item -ItemType Directory -Force -Path "${{ env.TARGET_RELEASE }}" + Move-Item -Path "pixi_bin/pixi-${{ matrix.arch.target }}${{ matrix.arch.extension }}" -Destination "${{ env.TARGET_RELEASE }}/pixi.exe" + shell: pwsh + - name: Test common wheels + run: ${{ env.TARGET_RELEASE }}/pixi${{ matrix.arch.extension }} run test-common-wheels-ci + - name: Write .summary.md to Github Summary + if: ${{ matrix.arch.name != 'Windows' && always() }} + shell: bash + run: | + cat ${{ env.SUMMARY_FILE }} >> $GITHUB_STEP_SUMMARY + - name: Write .summary.md to GitHub Summary (Windows) + if: ${{ matrix.arch.name == 'Windows' && always() }} + shell: pwsh + run: | + $resolvedPath = Resolve-Path $env:SUMMARY_FILE + Get-Content $resolvedPath | Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + - name: Upload Logs + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: wheel-tests-logs-${{ matrix.arch.name }} + include-hidden-files: true + path: ${{ env.LOGS_DIR }} diff --git a/pixi.lock b/pixi.lock index 2e2f9b202..38c5f6bdf 100644 --- a/pixi.lock +++ b/pixi.lock @@ -33,6 +33,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/docopt-0.6.2-py_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.7.0-heb67821_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-12.4.0-h236703b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-12.4.0-hb2e57f8_1.conda @@ -83,6 +84,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.7-he7c6b58_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/make-4.4.1-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mimalloc-2.1.7-hac33072_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mold-2.33.0-h3b4bb38_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.11.2-py312h66e93f0_0.conda @@ -98,10 +101,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.16.3-py312h4b3b743_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pykwalify-1.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyrsistent-0.20.0-py312h98912ed_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-rerunfailures-14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.3-hab00c5b_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda @@ -109,6 +114,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h41a817b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.6-py312h98912ed_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.8-py312h98912ed_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rust-1.80.1-h0a17960_0.conda @@ -121,6 +127,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2021.12.0-h84d6215_4.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tbump-6.9.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomlkit-0.13.2-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda @@ -151,6 +158,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/docopt-0.6.2-py_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/git-2.46.0-pl5321h9b6aa9b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2 @@ -172,6 +180,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.0-h1b8f9f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.0-hd019ec5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-h87427d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/mypy-1.11.2-py312hb553811_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-hf036a51_1.conda @@ -185,10 +195,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pydantic-core-2.16.3-py312h1b0e595_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pykwalify-1.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyrsistent-0.20.0-py312h41838bb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-rerunfailures-14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.3-h1411813_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda @@ -196,6 +208,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.2-py312hbd25219_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h9e318b2_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ruamel.yaml-0.18.6-py312h41838bb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ruamel.yaml.clib-0.2.8-py312h41838bb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/rust-1.80.1-h6c54e5d_0.conda @@ -206,6 +219,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tbump-6.9.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomlkit-0.13.2-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda @@ -236,6 +250,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/docopt-0.6.2-py_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/git-2.46.0-pl5321h41514c7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2 @@ -258,6 +273,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.46.0-hfb93653_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.0-h7a5bd25_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-hfb2fe0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.11.2-py312h024a12e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda @@ -271,10 +288,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.16.3-py312h5280bc4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pykwalify-1.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyrsistent-0.20.0-py312he37b823_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-rerunfailures-14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.3-h4a7b5fc_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda @@ -282,6 +301,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h7e5086c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.6-py312he37b823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.8-py312he37b823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rust-1.80.1-h4ff7c5d_0.conda @@ -292,6 +312,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tbump-6.9.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomlkit-0.13.2-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda @@ -321,6 +342,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/docopt-0.6.2-py_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/git-2.46.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2 @@ -336,6 +358,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.46.0-h2466b09_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.11.2-py312h4389bb4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda @@ -347,16 +371,19 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.16.3-py312hfccd98a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pykwalify-1.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyrsistent-0.20.0-py312he70551f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh0701188_6.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-rerunfailures-14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.3-h2628c8c_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.2-py312h4389bb4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.6-py312he70551f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.8-py312he70551f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/rust-1.80.1-hf8d6059_0.conda @@ -367,6 +394,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tbump-6.9.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h5226925_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomlkit-0.13.2-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda @@ -854,7 +882,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py312h06ac9bb_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.8-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.15.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.2-h59595ed_0.conda @@ -899,7 +927,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-1.17.0-py312hf857d28_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.8-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.15.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-18.1.8-hd876a4e_6.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.2-h73e2aa4_0.conda @@ -936,7 +964,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.0-py312h0fad829_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.8-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.15.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-18.1.8-h3ed4263_6.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.2-hebf3989_0.conda @@ -973,7 +1001,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-1.17.0-py312h4389bb4_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.8-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.15.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.6.2-h63175ca_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.4.2-h8ffe710_5.tar.bz2 @@ -1144,6 +1172,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-3.2.0-pyhd8ed1ab_3.tar.bz2 @@ -1158,6 +1187,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.11.2-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda @@ -1167,16 +1198,20 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-6.0.0-py312h66e93f0_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.16.3-py312h4b3b743_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyrsistent-0.20.0-py312h98912ed_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-rerunfailures-14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.3-hab00c5b_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h41a817b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda @@ -1192,6 +1227,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-3.2.0-pyhd8ed1ab_3.tar.bz2 @@ -1199,6 +1235,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.4.2-h0d85af4_5.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.0-h1b8f9f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-h87427d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/mypy-1.11.2-py312hb553811_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-hf036a51_1.conda @@ -1208,16 +1246,20 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-6.0.0-py312hb553811_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pydantic-core-2.16.3-py312h1b0e595_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyrsistent-0.20.0-py312h41838bb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-rerunfailures-14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.3-h1411813_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.2-py312hbd25219_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h9e318b2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda @@ -1233,6 +1275,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-3.2.0-pyhd8ed1ab_3.tar.bz2 @@ -1240,6 +1283,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.46.0-hfb93653_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-hfb2fe0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.11.2-py312h024a12e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda @@ -1249,16 +1294,20 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-6.0.0-py312h024a12e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.16.3-py312h5280bc4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyrsistent-0.20.0-py312he37b823_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-rerunfailures-14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.3-h4a7b5fc_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h7e5086c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda @@ -1274,6 +1323,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.4.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-3.2.0-pyhd8ed1ab_3.tar.bz2 @@ -1281,6 +1331,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.4.2-h8ffe710_5.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.46.0-h2466b09_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.11.2-py312h4389bb4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda @@ -1289,15 +1341,19 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-6.0.0-py312h4389bb4_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.16.3-py312hfccd98a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyrsistent-0.20.0-py312he70551f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-rerunfailures-14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.3-h2628c8c_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.2-py312h4389bb4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-72.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h5226925_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda @@ -2299,18 +2355,18 @@ packages: timestamp: 1710362455984 - kind: conda name: filelock - version: 3.15.4 + version: 3.16.0 build: pyhd8ed1ab_0 subdir: noarch noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/filelock-3.15.4-pyhd8ed1ab_0.conda - sha256: f78d9c0be189a77cb0c67d02f33005f71b89037a85531996583fb79ff3fe1a0a - md5: 0e7e4388e9d5283e22b35a9443bdbcc9 + url: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.0-pyhd8ed1ab_0.conda + sha256: f55c9af3d92a363fa9e4f164038db85a028befb65d56df0b2cb34911eba8a37a + md5: ec288789b07ae3be555046e099798a56 depends: - python >=3.7 license: Unlicense - size: 17592 - timestamp: 1719088395353 + size: 17402 + timestamp: 1725740654220 - kind: conda name: font-ttf-dejavu-sans-mono version: '2.37' @@ -5060,6 +5116,22 @@ packages: license_family: BSD size: 78331 timestamp: 1710435316163 +- kind: conda + name: markdown-it-py + version: 3.0.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + sha256: c041b0eaf7a6af3344d5dd452815cdc148d6284fec25a4fa3f4263b3a021e962 + md5: 93a8e71256479c62074356ef6ebf501b + depends: + - mdurl >=0.1,<1 + - python >=3.8 + license: MIT + license_family: MIT + size: 64356 + timestamp: 1686175179621 - kind: conda name: markupsafe version: 2.1.5 @@ -5140,6 +5212,21 @@ packages: license_family: BSD size: 25414 timestamp: 1724959688117 +- kind: conda + name: mdurl + version: 0.1.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda + sha256: 64073dfb6bb429d52fff30891877b48c7ec0f89625b1bf844905b66a81cce6e1 + md5: 776a8dd9e824f77abac30e6ef43a8f7a + depends: + - python >=3.6 + license: MIT + license_family: MIT + size: 14680 + timestamp: 1704317789138 - kind: conda name: mdx_truly_sane_lists version: '1.3' @@ -6586,6 +6673,25 @@ packages: license_family: MIT size: 257671 timestamp: 1721923749407 +- kind: conda + name: pytest-rerunfailures + version: '14.0' + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pytest-rerunfailures-14.0-pyhd8ed1ab_0.conda + sha256: 08fb77f313c5739a3526a07c865671f6e36d1458d073c1b23b4f0b66501138bd + md5: 0696324a8c882d182485f20368d21583 + depends: + - importlib-metadata >=1 + - packaging >=17.1 + - pytest >=7.2 + - python >=3.8 + - setuptools + license: MPL-2.0 + license_family: OTHER + size: 17835 + timestamp: 1710357496750 - kind: conda name: pytest-xdist version: 3.6.1 @@ -7161,6 +7267,24 @@ packages: license_family: APACHE size: 58810 timestamp: 1717057174842 +- kind: conda + name: rich + version: 13.7.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-13.7.1-pyhd8ed1ab_0.conda + sha256: 2b26d58aa59e46f933c3126367348651b0dab6e0bf88014e857415bb184a4667 + md5: ba445bf767ae6f0d959ff2b40c20912b + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.7.0 + - typing_extensions >=4.0.0,<5.0.0 + license: MIT + license_family: MIT + size: 184347 + timestamp: 1709150578093 - kind: conda name: ruamel.yaml version: 0.18.6 @@ -7763,6 +7887,21 @@ packages: license_family: BSD size: 3318875 timestamp: 1699202167581 +- kind: conda + name: toml + version: 0.10.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2 + sha256: f0f3d697349d6580e4c2f35ba9ce05c65dc34f9f049e85e45da03800b46139c1 + md5: f832c45a477c78bebd107098db465095 + depends: + - python >=2.7 + license: MIT + license_family: MIT + size: 18433 + timestamp: 1604308660817 - kind: conda name: tomli version: 2.0.1 diff --git a/pixi.toml b/pixi.toml index 7e2a7fdb2..0c308e963 100644 --- a/pixi.toml +++ b/pixi.toml @@ -12,7 +12,7 @@ channels = ["https://fast.prefix.dev/conda-forge"] platforms = ["linux-64", "win-64", "osx-64", "osx-arm64"] [dependencies] -python = ">=3.12.3,<4" +python = "3.12.*" [tasks] build = "cargo build --release" @@ -31,9 +31,17 @@ mypy = ">=1.11,<1.12" # For detecting cpu cores with pytest-xdist psutil = ">=6.0.0,<7" # For running tests in parallel, use this instead of regular pytest +filelock = ">=3.16.0,<4" +pytest-rerunfailures = ">=14.0,<15" pytest-xdist = ">=3.6.1,<4" +rich = ">=13.7.1,<14" +toml = ">=0.10.2,<0.11" [feature.pytest.tasks] +test-common-wheels-ci = { cmd = "pytest -n logical tests/wheel_tests/" } +test-common-wheels-dev = { cmd = "pytest -n logical tests/wheel_tests/", depends-on = [ + "build", +] } test-integration-ci = "pytest -n logical tests/integration" test-integration-dev = { cmd = "pytest -n logical tests/integration", depends-on = [ "build", diff --git a/tests/wheel_tests/.gitignore b/tests/wheel_tests/.gitignore new file mode 100644 index 000000000..1e641ca21 --- /dev/null +++ b/tests/wheel_tests/.gitignore @@ -0,0 +1,4 @@ +.wheel_test_results.toml +.logs/** +.summary.md +.wheel_test_results.lock diff --git a/tests/wheel_tests/conftest.py b/tests/wheel_tests/conftest.py new file mode 100644 index 000000000..21456f0f0 --- /dev/null +++ b/tests/wheel_tests/conftest.py @@ -0,0 +1,26 @@ +from generate_summaries import terminal_summary, markdown_summary +from helpers import setup_stdout_stderr_logging + + +def pytest_configure(config): + setup_stdout_stderr_logging() + + +def pytest_addoption(parser): + # Used to override the default path to the pixi executable + parser.addoption("--pixi-exec", action="store", help="Path to the pixi executable") + + +def pytest_terminal_summary(terminalreporter, exitstatus, config): + """ + At the end of the test session, generate a summary report. + """ + terminal_summary() + + +def pytest_sessionfinish(session, exitstatus): + """ + At the end of the test session, generate a `.summary.md` report. That contains the + same information as the terminal summary. + """ + markdown_summary() diff --git a/tests/wheel_tests/generate_summaries.py b/tests/wheel_tests/generate_summaries.py new file mode 100644 index 000000000..1634c9c8d --- /dev/null +++ b/tests/wheel_tests/generate_summaries.py @@ -0,0 +1,116 @@ +from record_results import RESULTS_FILE +import toml +from rich.console import Console +from rich.table import Table +from rich.text import Text +from rich.panel import Panel +from pathlib import Path + +from read_wheels import read_wheel_file + + +def terminal_summary(): + # Read aggregated results from the shared file + results_file = RESULTS_FILE + if not results_file.exists(): + print("Error: No test results found.") + return + + with results_file.open("r") as f: + results = toml.load(f)["results"] + + packages = read_wheel_file() + + console = Console() + table = Table(title="Test Results", show_header=True, header_style="bold magenta") + table.add_column("Test Name", style="dim") + table.add_column("Outcome", justify="right") + table.add_column("Duration (s)", justify="right") + table.add_column("Error Details") + + # Populate the table with collected results + names = [] + for result in sorted(results, key=lambda r: r["name"]): + outcome_color = "green" if result["outcome"] == "passed" else "red" + error_details = result["longrepr"] if result["outcome"] == "failed" else "" + table.add_row( + Text(result["name"]), + Text(result["outcome"], style=outcome_color), + f"{result['duration']:.2f}", + error_details, + ) + # Record name + names.append(result["name"]) + + for package in packages: + if package.to_add_cmd() not in names: + table.add_row( + Text(package.to_add_cmd()), + Text("N/A", style="dim"), + Text("N/A", style="dim"), + Text("N/A", style="dim"), + ) + + # Display the table in the terminal + console.print(table) + + # Add a summary box with instructions + summary_text = ( + "[bold]Summary:[/bold]\n\n" + f"- Total tests run: {len(results)}\n" + f"- Passed: {sum(1 for r in results if r['outcome'] == 'passed')}\n" + f"- Failed: {sum(1 for r in results if r['outcome'] == 'failed')}\n\n" + "To filter tests by a specific wheel, use the command:\n" + "[bold green]pytest -k ''[/]\n\n" + "Replace [bold][/] with the desired wheel's name to run only tests for that wheel.\n" + r'E.g use [magenta] pixi r test-common-wheels-dev -k "jax\[cuda12]"[/] to run tests for the [bold]jax\[cuda12][/] wheel.' + "\n\n" + "[bold yellow]Note:[/]\n" + "Any [italic]failed[/] tests will have recorded their output to the [bold].log/[/] directory, which" + " resides next to to `wheels.toml` file.\n" + ) + + # Create a Rich panel (box) for the summary text + summary_panel = Panel( + summary_text, title="Test Debrief", title_align="left", border_style="bright_blue" + ) + + # Display the summary box in the terminal + console.print(summary_panel) + + +def markdown_summary(): + if not RESULTS_FILE.exists(): + return + + summary_file = Path(__file__).parent / ".summary.md" + with summary_file.open("w") as f: + # Read the RESULTS_FILE and generate a markdown summary + f.write("# Test Summary\n\n") + f.write(""" +This document contains a summary of the test results for the wheels in the `wheels.toml` file. +You can use the following command, in the pixi repository, to filter tests by a specific wheel: +```bash +pixi r test-common-wheels -k "" +# E.g +pixi r test-common-wheels-dev -k "jax[cuda12]" +``` + +""") + f.write("## Test Results\n\n") + f.write("\n") + f.write("| Test Name | Outcome | Duration (s) | Error Details |\n") + f.write("| :--- | ---: | ---: | --- |\n") + + results_file = RESULTS_FILE + with results_file.open("r") as r: + results = toml.load(r)["results"] + for result in results: + outcome = ( + 'Passed' + if result["outcome"] == "passed" + else 'Failed' + ) + error_details = result["longrepr"] if result["outcome"] == "failed" else "" + f.write(f"|{result["name"]}|{outcome}|{result['duration']:.2f}|{error_details}|\n") + f.write("\n") diff --git a/tests/wheel_tests/helpers.py b/tests/wheel_tests/helpers.py new file mode 100644 index 000000000..0cd42c261 --- /dev/null +++ b/tests/wheel_tests/helpers.py @@ -0,0 +1,61 @@ +import os +import subprocess +import pathlib +from typing import Any +import toml + +StrPath = str | os.PathLike[str] +LOG_DIR = pathlib.Path(__file__).parent / ".logs" + + +def run(args: list[StrPath], cwd: StrPath | None = None) -> None: + """ + Run a subprocess and check the return code + """ + proc: subprocess.CompletedProcess[bytes] = subprocess.run( + args, cwd=cwd, capture_output=True, check=False + ) + proc.check_returncode() + + +def add_system_requirements(manifest_path: pathlib.Path, system_requirements: dict[str, Any]): + """ + Add system requirements to the manifest file + add something like this: + [system-requirements] + libc = { family = "glibc", version = "2.17" } + to the manifest file. + """ + with manifest_path.open("r") as f: + manifest = toml.load(f) + manifest["system-requirements"] = system_requirements + with manifest_path.open("w") as f: + toml.dump(manifest, f) + + +def setup_stdout_stderr_logging(): + """ + Set up the logging directory + """ + if not LOG_DIR.exists(): + LOG_DIR.mkdir() + for file in LOG_DIR.iterdir(): + file.unlink() + + +def log_called_process_error(name: str, err: subprocess.CalledProcessError, std_err_only=False): + """ + Log the output of a subprocess that failed + has the option to log only the stderr + """ + if not LOG_DIR.exists(): + raise RuntimeError("Call setup_stdout_stderr_logging before logging") + + std_out_log = LOG_DIR / f"{name}.stdout" + std_err_log = LOG_DIR / f"{name}.stderr" + if err.returncode != 0: + if not std_err_only: + with std_out_log.open("w", encoding="utf-8") as f: + f.write(err.stdout.decode("uft-8")) + with std_err_log.open("w", encoding="utf-8") as f: + f.write(err.stderr.decode("utf-8")) diff --git a/tests/wheel_tests/read_wheels.py b/tests/wheel_tests/read_wheels.py new file mode 100644 index 000000000..6f809b019 --- /dev/null +++ b/tests/wheel_tests/read_wheels.py @@ -0,0 +1,120 @@ +from dataclasses import dataclass +from pathlib import Path +import tomllib +from typing import Literal, Iterable, Self, Any + + +@dataclass +class PackageSpec: + """ + A class to represent the package specification in the `wheels.toml` file. + A single Package Name can have multiple specifications. + """ + + version: Literal["*"] | str = "*" + extras: str | None = None + target: str | list[str] | None = None + system_requirements: dict[str, Any] | None = None + + def target_iter(self) -> Iterable[str]: + """ + Returns an iterable of the target platforms + """ + if isinstance(self.target, str): + return [self.target] + elif isinstance(self.target, list): + return self.target + return [] + + @classmethod + def __from_toml(cls, spec: dict[str, str] | str) -> Self: + if isinstance(spec, str): + return cls(version=spec, extras=None, target=None, system_requirements=None) + if isinstance(spec, dict): + return cls( + spec.get("version", "*"), + spec.get("extras"), + spec.get("target"), + spec.get("system-requirements"), + ) + + @classmethod + def from_toml(cls, spec: dict[str, str] | list[dict[str, str]] | str) -> Self | list[Self]: + if isinstance(spec, list): + return [cls.__from_toml(s) for s in spec] + else: + return cls.__from_toml(spec) + + +@dataclass +class Package: + """ + Specifies a package which is a name and a specification + on how to install it. + """ + + # Name of the package + name: str + # Specification of the package + spec: PackageSpec + + def to_add_cmd(self) -> str: + """ + Converts the package to a command that can be consumed with the + `pixi add` command. + """ + cmd = f"{self.name}" + if self.spec.extras: + cmd = f"{cmd}[{self.spec.extras}]" + if self.spec.version and self.spec.version != "*": + cmd = f"{cmd}=={self.spec.version}" + return cmd + + +@dataclass +class WheelTest: + """ + A class to represent the `wheels.toml` file + """ + + # Mapping of wheel names to installation specifications + name: dict[str, list[PackageSpec] | PackageSpec] + + def to_packages(self) -> Iterable[Package]: + """ + Converts to a list of installable packages + """ + for name, specs in self.name.items(): + if isinstance(specs, PackageSpec): + yield Package(name, specs) + else: + yield from [Package(name, spec) for spec in specs] + + @classmethod + def from_toml(cls, file: Path) -> Self: + """ + Read the wheels from the toml file and return the instance + """ + with file.open("rb") as f: + toml = tomllib.load(f) + if not isinstance(toml, dict): + raise ValueError("Expected a dictionary") + wheels = toml + return cls({name: PackageSpec.from_toml(spec) for name, spec in wheels.items()}) + + @classmethod + def from_str(cls, s: str) -> Self: + """ + Read the wheels from the toml string and return the instance + """ + toml = tomllib.loads(s) + return cls({name: PackageSpec.from_toml(spec) for name, spec in toml.items()}) + + +def read_wheel_file() -> Iterable[Package]: + """ + Read the wheel file `wheels.toml` and return the package + instances. + """ + wheel_path = Path(__file__).parent / Path("wheels.toml") + return WheelTest.from_toml(wheel_path).to_packages() diff --git a/tests/wheel_tests/record_results.py b/tests/wheel_tests/record_results.py new file mode 100644 index 000000000..eb7200572 --- /dev/null +++ b/tests/wheel_tests/record_results.py @@ -0,0 +1,42 @@ +from pathlib import Path +import toml +from filelock import FileLock + +# Path to the results file, containing test outcomes +RESULTS_FILE = Path(__file__).parent / ".wheel_test_results.toml" +# Lock file to ensure process-safe write access to the results file +LOCK_FILE = RESULTS_FILE.with_suffix(".lock") + + +def record_result(test_id: str, name: str, outcome: str, duration: float, details: str): + """ + Collects test status after each test run, compatible with pytest-xdist. + """ + result = {"name": name, "outcome": outcome, "duration": duration, "longrepr": details} + + # Use file lock for process-safe write access to the results file + lock = FileLock(str(LOCK_FILE)) + + with lock: + test = {"id": test_id, "results": []} + + # Get the existing results + if RESULTS_FILE.exists(): + with RESULTS_FILE.open("r") as f: + data = toml.load(f) + # If this doesn't hold, don't use the recorded data + if "id" in data and data["id"] == test_id: + test = data + + # Append the new result + # if we are in the same session + if test["id"] == test_id: + test["results"].append(result) + # The data is from a different session + # so we overwrite the data + else: + test["results"] = [result] + + # Write the results back to the file + with RESULTS_FILE.open("w") as f: + toml.dump(test, f) diff --git a/tests/wheel_tests/test_common_wheels.py b/tests/wheel_tests/test_common_wheels.py new file mode 100644 index 000000000..74bb23310 --- /dev/null +++ b/tests/wheel_tests/test_common_wheels.py @@ -0,0 +1,95 @@ +import pytest +import os +import pathlib +import subprocess +from read_wheels import Package, read_wheel_file +import time +from record_results import record_result +from helpers import add_system_requirements, log_called_process_error, run +import sys + + +@pytest.mark.flaky(reruns=5, reruns_delay=1, condition=sys.platform.startswith("win32")) +def test_wheel(pixi: str, package: Package, testrun_uid: str, tmp_path: pathlib.Path): + """ + Create a temporary directory and install the wheel in it. + The `testrun_uid` is a unique identifier for the test run + this is created by pytest-xdist + """ + start = time.perf_counter() + try: + # Path to the manifest file + manifest_path = tmp_path / "pixi.toml" + run([pixi, "init"], cwd=tmp_path) + + # Check if we need to add system-requirements + # There is no CLI for it currently so we need to manually edit the file + if package.spec.system_requirements: + add_system_requirements(manifest_path, package.spec.system_requirements) + + # Add python to the project + run([pixi, "add", "--no-progress", "--manifest-path", manifest_path, "python==3.12.*"]) + + # Add the wheel to the project + run_args = [ + pixi, + "-vvv", + "add", + "--no-progress", + "--manifest-path", + manifest_path, + "--pypi", + package.to_add_cmd(), + ] + + # Add for another platform, if specified + for platform in package.spec.target_iter(): + run_args.extend(["--platform", package.spec.target]) + + run(run_args) + # Record the success of the test + record_result(testrun_uid, package.to_add_cmd(), "passed", time.perf_counter() - start, "") + except subprocess.CalledProcessError as e: + # Record the failure details + record_result( + testrun_uid, package.to_add_cmd(), "failed", time.perf_counter() - start, str(e) + ) + # Log the error + log_called_process_error(package.to_add_cmd(), e, std_err_only=True) + # Re-raise the exception to fail the test + raise e + + +def pytest_generate_tests(metafunc): + """ + This generates the test for the wheels by reading the wheels from the toml specification + creates a test for each entry in the toml file + """ + if "package" in metafunc.fixturenames: + packages = read_wheel_file() + metafunc.parametrize("package", [pytest.param(w, id=f"{w.to_add_cmd()}") for w in packages]) + + +@pytest.fixture(scope="session") +def pixi(pytestconfig): + # The command line argument overrides the default path + if pytestconfig.getoption("pixi_exec"): + return pytestconfig.getoption("pixi_exec") + + # Check pixi environment variable + project_root = os.environ.get("PIXI_PROJECT_ROOT") + if not project_root: + pytest.exit("PROJECT_ROOT environment variable is not set, run from pixi task") + + # Check if the target directory exists + # This assertion is for the type checker + assert project_root + project_root = pathlib.Path(project_root) + target_dir = project_root.joinpath(".pixi/target/release") + if not target_dir.exists(): + pytest.exit("pixi executable not found, run `pixi r build` first") + + if sys.platform.startswith("win"): + return target_dir.joinpath("pixi.exe") + else: + return target_dir.joinpath("pixi") diff --git a/tests/wheel_tests/test_read_wheels.py b/tests/wheel_tests/test_read_wheels.py new file mode 100644 index 000000000..0e7e9970f --- /dev/null +++ b/tests/wheel_tests/test_read_wheels.py @@ -0,0 +1,21 @@ +from read_wheels import Package, PackageSpec, WheelTest + + +def test_spec_to_add_cmd(): + assert Package("foo", PackageSpec()).to_add_cmd() == "foo" + assert Package("foo", PackageSpec("1.0")).to_add_cmd() == "foo==1.0" + assert Package("foo", PackageSpec("1.0", "bar")).to_add_cmd() == "foo[bar]==1.0" + + +def test_wheel_test_from_str(): + toml = """ + foo = "*" + bar = { version = "1.0", extras = "baz", target = "linux-64" } + laz = ["*", { version = "1.0", extras = "baz" }] + """ + wt = WheelTest.from_str(toml) + assert len(list(wt.to_packages())) == 4 + assert Package("foo", PackageSpec()) in wt.to_packages() + assert Package("bar", PackageSpec("1.0", "baz", "linux-64")) in wt.to_packages() + assert Package("laz", PackageSpec()) in wt.to_packages() + assert Package("laz", PackageSpec("1.0", "baz")) in wt.to_packages() diff --git a/tests/wheel_tests/wheels.toml b/tests/wheel_tests/wheels.toml new file mode 100644 index 000000000..711b22b80 --- /dev/null +++ b/tests/wheel_tests/wheels.toml @@ -0,0 +1,67 @@ +# This file defines the wheels that are used to test common wheels and adding them using pixi +# Add the top-level wheels, for where you want a simple `name` = `version` mapping +# Add more complex mappings using the [[name]] table, where you can specify extras, version, and more +# Or a single [name] for a more complex version +confz = "*" +dvc = "*" +dvclive = "*" +icecream = "*" +matplotlib = "*" +mkdocs = "*" +mkdocs-material = "*" +numpy = "*" +pymeshfix = "*" +pyright = "*" +pytest = "*" +pytest-benchmark = "*" +pyvista = "*" +ruff = "*" +scipy = "*" +taichi = "*" +trimesh = "*" +typeguard = "*" + +## Add more complex dependencies after this line: + +## Failing for windows (add for linux until the issue is resolved) +[meshpy] +target = "linux-64" +version = "*" + +[meshtaichi-patcher] +target = "linux-64" +version = "*" + +[meshtools] +target = "linux-64" +version = "*" + +[rerun-sdk] +target = "linux-64" +version = "*" +# Use these system requirements +# because the wheel requires a higher version of glibc than the conda counterpart +[rerun-sdk.system-requirements] +libc = { family = "glibc", version = "2.31" } + +[mkdocstrings] +extras = "python" +version = "*" + +[meshio] +extras = "all" +version = "*" +[meshio.system-requirements] +macos = "14.0" + +[[jax]] +version = "*" +[[jax]] +extras = "cuda12" +version = "*" + +[nvidia-cuda-nvrtc-cu11] +target = "linux-64" +version = "*" +[nvidia-cuda-nvrtc-cu11.system-requirements] +cuda = "11.0"