name: CI

on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

env:
  CARGO_INCREMENTAL: 0
  CARGO_NET_RETRY: 10
  CARGO_TERM_COLOR: always
  RUSTUP_MAX_RETRIES: 10
  PYTHON_VERSION: "3.12"

jobs:
  determine_changes:
    name: "Determine changes"
    runs-on: ubuntu-latest
    outputs:
      # Flag that is raised when any code is changed
      code: ${{ steps.changed.outputs.code_any_changed }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: tj-actions/changed-files@v44
        id: changed
        with:
          files_yaml: |
            code:
              - "**/*"
              - "!docs/**/*"
              - "!**/*.md"
              - "!bin/**"
              - "!assets/**"
  cargo-fmt:
    name: "cargo fmt"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: "Install Rustfmt"
        run: rustup component add rustfmt

      - name: "rustfmt"
        run: cargo fmt --all --check

      - name: "Prettier"
        run: npx prettier --check "**/*.{json5,yaml,yml}"

      - name: "Ruff format"
        run: pipx run ruff format --diff .

      - name: "Ruff check"
        run: pipx run ruff check .

  cargo-clippy:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: ubuntu-latest
    name: "cargo clippy | ubuntu"
    steps:
      - uses: actions/checkout@v4
      - name: "Install Rust toolchain"
        run: rustup component add clippy
      - uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/main' }}
      - name: "Clippy"
        run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings

  cargo-clippy-xwin:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: ubuntu-latest
    name: "cargo clippy | windows"
    steps:
      - uses: actions/checkout@v4
      - name: "Install Rust toolchain"
        run: rustup target add x86_64-pc-windows-msvc
      - name: Load xwin cache
        uses: actions/cache@v4
        with:
          path: "${{ github.workspace}}/.xwin"
          key: cargo-xwin
      - name: Load rust cache
        uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/main' }}
      - name: "Install cargo-xwin"
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-xwin
      - name: Install xwin dependencies
        run: sudo apt-get install --no-install-recommends -y llvm clang cmake ninja-build
      - name: "Clippy"
        run: cargo xwin clippy --target x86_64-pc-windows-msvc --workspace --all-targets --all-features --locked --profile fast-build -- -D warnings
        env:
          XWIN_ARCH: "x86_64"
          XWIN_CACHE_DIR: "${{ github.workspace}}/.xwin"

  cargo-shear:
    name: "cargo shear"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: cargo-bins/cargo-binstall@main
      - run: cargo binstall --no-confirm cargo-shear
      - run: cargo shear

  # We use the large GitHub actions runners
  # For Ubuntu and Windows, this requires Organization-level configuration
  # See: https://docs.github.com/en/actions/using-github-hosted-runners/about-larger-runners/about-larger-runners#about-ubuntu-and-windows-larger-runners

  cargo-test-linux:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on:
      labels: "ubuntu-latest-large"
    name: "cargo test | ubuntu"
    steps:
      - uses: actions/checkout@v4
      - name: "Install Rust toolchain"
        run: rustup show

      - uses: rui314/setup-mold@v1

      - uses: Swatinem/rust-cache@v2

      - name: "Install required Python versions"
        run: |
          cargo run toolchain install

      - name: "Install cargo nextest"
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-nextest

      - name: "Cargo test"
        run: |
          cargo nextest run \
            --features python-patch \
            --workspace \
            --status-level skip --failure-output immediate-final --no-fail-fast -j 12 --final-status-level slow

      - name: "Smoke test"
        run: |
          uv="./target/debug/uv"
          $uv venv
          $uv pip install ruff

  cargo-test-macos:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on:
      labels: "macos-14"
    name: "cargo test | macos"
    steps:
      - uses: actions/checkout@v4
      - name: "Install Rust toolchain"
        run: rustup show

      - uses: rui314/setup-mold@v1

      - uses: Swatinem/rust-cache@v2

      - name: "Install required Python versions"
        run: |
          cargo run toolchain install

      - name: "Install cargo nextest"
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-nextest

      - name: "Cargo test"
        run: |
          cargo nextest run \
            --features python-patch \
            --workspace \
            --status-level skip --failure-output immediate-final --no-fail-fast -j 12 --final-status-level slow

      - name: "Smoke test"
        run: |
          uv="./target/debug/uv"
          $uv venv
          $uv pip install ruff

  cargo-test-windows:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on:
      labels: "windows-latest-large"
    name: "cargo test | windows"
    steps:
      - name: Create Dev Drive using ReFS
        run: |
          $Volume = New-VHD -Path C:/uv_dev_drive.vhdx -SizeBytes 12GB |
                    Mount-VHD -Passthru |
                    Initialize-Disk -Passthru |
                    New-Partition -AssignDriveLetter -UseMaximumSize |
                    Format-Volume -FileSystem ReFS -Confirm:$false -Force
          Write-Output $Volume
          Write-Output "DEV_DRIVE=$($Volume.DriveLetter):" >> $env:GITHUB_ENV

      - uses: actions/checkout@v4

      # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone...
      - name: Copy Git Repo to Dev Drive
        run: |
          Copy-Item -Path "${{ github.workspace }}" -Destination "${{ env.DEV_DRIVE }}/uv" -Recurse

      - name: "Install Rust toolchain"
        working-directory: ${{ env.DEV_DRIVE }}/uv
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
        run: rustup show

      # We do not test with Python patch versions on Windows
      # so we can use `setup-python` instead of our bootstrapping code
      # this is much faster on the extremely slow GitHub Windows runners.
      - uses: actions/setup-python@v5
        with:
          python-version: |
            3.8
            3.9
            3.10
            3.11
            3.12

      - uses: Swatinem/rust-cache@v2
        with:
          workspaces: ${{ env.DEV_DRIVE }}/uv
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup

      - name: "Install cargo nextest"
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-nextest
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup

      - name: "Cargo test"
        working-directory: ${{ env.DEV_DRIVE }}/uv
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
        run: |
          cargo nextest run --no-default-features --features python,pypi,git --workspace --status-level skip --failure-output immediate-final --no-fail-fast -j 12 --final-status-level slow

      - name: "Smoke test"
        working-directory: ${{ env.DEV_DRIVE }}/uv
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
          # Avoid debug build stack overflows.
          UV_STACK_SIZE: 2000000 # 2 megabyte, double the default on windows
        run: |
          Set-Alias -Name uv -Value ./target/debug/uv
          uv venv
          uv pip install ruff

  # Separate job for the nightly crate
  windows-trampoline:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: windows-latest
    name: "check windows trampoline"
    steps:
      - name: Create Dev Drive using ReFS
        run: |
          $Volume = New-VHD -Path C:/uv_dev_drive.vhdx -SizeBytes 10GB |
                    Mount-VHD -Passthru |
                    Initialize-Disk -Passthru |
                    New-Partition -AssignDriveLetter -UseMaximumSize |
                    Format-Volume -FileSystem ReFS -Confirm:$false -Force
          Write-Output $Volume
          Write-Output "DEV_DRIVE=$($Volume.DriveLetter):" >> $env:GITHUB_ENV

      - uses: actions/checkout@v4

      # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone...
      - name: Copy Git Repo to Dev Drive
        run: |
          Copy-Item -Path "${{ github.workspace }}" -Destination "${{ env.DEV_DRIVE }}/uv" -Recurse

      - name: "Install Rust toolchain"
        working-directory: ${{ env.DEV_DRIVE }}/uv/crates/uv-trampoline
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
        run: |
          rustup target add x86_64-pc-windows-msvc
          rustup component add clippy rust-src --toolchain nightly-2024-05-27-x86_64-pc-windows-msvc

      - uses: rui314/setup-mold@v1

      - uses: Swatinem/rust-cache@v2
        with:
          workspaces: ${{ env.DEV_DRIVE }}/uv/crates/uv-trampoline
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup

      - name: "Clippy"
        working-directory: ${{ env.DEV_DRIVE }}/uv/crates/uv-trampoline
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
        run: cargo clippy --all-features --locked --target x86_64-pc-windows-msvc -- -D warnings

      - name: "Build"
        working-directory: ${{ env.DEV_DRIVE }}/uv/crates/uv-trampoline
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
        run: cargo build --release --target x86_64-pc-windows-msvc

  typos:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: crate-ci/typos@master

  build-binary-linux:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on:
      labels: ubuntu-latest-large
    name: "build binary | linux"
    steps:
      - uses: actions/checkout@v4

      - uses: rui314/setup-mold@v1

      - name: "Setup musl"
        run: |
          sudo apt-get install musl-tools
          rustup target add x86_64-unknown-linux-musl

      - uses: Swatinem/rust-cache@v2
      - name: "Build"
        run: cargo build --target x86_64-unknown-linux-musl

      - name: "Upload binary"
        uses: actions/upload-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}
          path: ./target/x86_64-unknown-linux-musl/debug/uv
          retention-days: 1

  build-binary-macos-aarch64:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on:
      labels: macos-14
    name: "build binary | macos aarch64"
    steps:
      - uses: actions/checkout@v4

      - uses: rui314/setup-mold@v1

      - uses: Swatinem/rust-cache@v2
      - name: "Build"
        run: cargo build

      - name: "Upload binary"
        uses: actions/upload-artifact@v4
        with:
          name: uv-macos-aarch64-${{ github.sha }}
          path: ./target/debug/uv
          retention-days: 1

  build-binary-macos-x86_64:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on:
      labels: macos-14-large
    name: "build binary | macos x86_64"
    steps:
      - uses: actions/checkout@v4

      - uses: rui314/setup-mold@v1

      - uses: Swatinem/rust-cache@v2
      - name: "Build"
        run: cargo build

      - name: "Upload binary"
        uses: actions/upload-artifact@v4
        with:
          name: uv-macos-x86_64-${{ github.sha }}
          path: ./target/debug/uv
          retention-days: 1

  build-binary-windows:
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on:
      labels: windows-latest-large
    name: "build binary | windows"
    steps:
      - name: Create Dev Drive using ReFS
        run: |
          $Volume = New-VHD -Path C:/uv_dev_drive.vhdx -SizeBytes 10GB |
                    Mount-VHD -Passthru |
                    Initialize-Disk -Passthru |
                    New-Partition -AssignDriveLetter -UseMaximumSize |
                    Format-Volume -FileSystem ReFS -Confirm:$false -Force
          Write-Output $Volume
          Write-Output "DEV_DRIVE=$($Volume.DriveLetter):" >> $env:GITHUB_ENV

      - uses: actions/checkout@v4

      # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone...
      - name: Copy Git Repo to Dev Drive
        run: |
          Copy-Item -Path "${{ github.workspace }}" -Destination "${{ env.DEV_DRIVE }}/uv" -Recurse

      - uses: rui314/setup-mold@v1

      - uses: Swatinem/rust-cache@v2
        with:
          workspaces: ${{ env.DEV_DRIVE }}/uv
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup

      - name: "Build"
        working-directory: ${{ env.DEV_DRIVE }}/uv
        env:
          CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
          RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
        run: cargo build

      - name: "Upload binary"
        uses: actions/upload-artifact@v4
        with:
          name: uv-windows-${{ github.sha }}
          path: ${{ env.DEV_DRIVE }}/uv/target/debug/uv.exe
          retention-days: 1

  ecosystem-test:
    needs: build-binary-linux
    name: "ecosystem test | ${{ matrix.repo }}"
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - repo: "prefecthq/prefect"
            command: "uv pip install -e '.[dev]'"
            python: "3.9"
          - repo: "pallets/flask"
            command: "uv pip install -r requirements/dev.txt"
            python: "3.12"
      fail-fast: false
    steps:
      - uses: actions/checkout@v4
        with:
          repository: ${{ matrix.repo }}

      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Test"
        run: |
          ./uv venv
          ./${{ matrix.command }}

  integration-test-conda:
    needs: build-binary-linux
    name: "integration test | conda on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: conda-incubator/setup-miniconda@v3
        with:
          miniconda-version: latest
          activate-environment: uv
          python-version: "3.12"

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: Conda info
        shell: bash -el {0}
        run: conda info

      - name: "Install a package"
        shell: bash -el {0}
        run: |
          echo "$CONDA_PREFIX"
          ./uv pip install anyio

  integration-test-github-actions:
    needs: build-binary-linux
    name: "integration test | github actions"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Install a package without system opt-in"
        run: |
          ./uv pip install anyio && exit 1 || echo "Failed as expected"

      - name: "Install a package with system opt-in"
        run: |
          ./uv pip install anyio --system

      - name: Configure uv to use the system Python by default
        run: echo "UV_SYSTEM_PYTHON=1" >> $GITHUB_ENV

      - name: "Install a package with environment system opt-in"
        run: |
          ./uv pip install anyio --reinstall

  cache-test-ubuntu:
    needs: build-binary-linux
    name: "check cache | ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Download binary for last version"
        run: curl -LsSf "https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-unknown-linux-gnu.tar.gz" | tar -xvz

      - name: "Check cache compatibility"
        run: python scripts/check_cache_compat.py --uv-current ./uv --uv-previous ./uv-x86_64-unknown-linux-gnu/uv

  cache-test-macos-aarch64:
    needs: build-binary-macos-aarch64
    name: "check cache | macos aarch64"
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-macos-aarch64-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Download binary for last version"
        run: curl -LsSf "https://github.com/astral-sh/uv/releases/latest/download/uv-aarch64-apple-darwin.tar.gz" | tar -xvz

      - name: "Check cache compatibility"
        run: python scripts/check_cache_compat.py --uv-current ./uv --uv-previous ./uv-aarch64-apple-darwin/uv

  system-test-debian:
    needs: build-binary-linux
    name: "check system | python on debian"
    runs-on: ubuntu-latest
    container: debian:bookworm
    steps:
      - uses: actions/checkout@v4

      - name: "Install Python"
        run: apt-get update && apt-get install -y python3.11 python3-pip python3.11-venv

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python3.11)

      - name: "Validate global Python install"
        run: python3.11 scripts/check_system_python.py --uv ./uv --externally-managed

  system-test-fedora:
    needs: build-binary-linux
    name: "check system | python on fedora"
    runs-on: ubuntu-latest
    container: fedora:41
    steps:
      - uses: actions/checkout@v4

      - name: "Install Python"
        run: dnf install which -y && python3 -m ensurepip

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python3)

      - name: "Validate global Python install"
        run: python3 scripts/check_system_python.py --uv ./uv

  system-test-ubuntu:
    needs: build-binary-linux
    name: "check system | python on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python)

      - name: "Validate global Python install"
        run: python scripts/check_system_python.py --uv ./uv

  system-test-opensuse:
    needs: build-binary-linux
    name: "check system | python on opensuse"
    runs-on: ubuntu-latest
    container: opensuse/tumbleweed
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v4

      - name: "Install Python"
        run: >
          until
          zypper install -y python310 which && python3.10 -m ensurepip && mv /usr/bin/python3.10 /usr/bin/python3;
          do sleep 10;
          done

          # We retry because `zypper` can fail during remote repository updates
          # The above will not sleep forever due to the job level timeout

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python3)

      - name: "Validate global Python install"
        run: python3 scripts/check_system_python.py --uv ./uv

  # Note: rockylinux is a 1-1 code compatible distro to rhel
  # rockylinux mimics centos but with added maintenance stability
  # and avoids issues with centos stream uptime concerns
  system-test-rocky-linux:
    needs: build-binary-linux
    name: "check system | python on rocky linux ${{ matrix.rocky-version }}"
    runs-on: ubuntu-latest
    container: rockylinux:${{ matrix.rocky-version }}
    strategy:
      fail-fast: false
      matrix:
        rocky-version: ["8", "9"]
    steps:
      - uses: actions/checkout@v4

      - name: "Install Python"
        if: matrix.rocky-version == '8'
        run: |
          dnf install python39 python39-pip which -y

      - name: "Install Python"
        if: matrix.rocky-version == '9'
        run: |
          dnf install python3.9 python3.9-pip which -y

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python3)

      - name: "Validate global Python install"
        run: python3 scripts/check_system_python.py --uv ./uv

  system-test-pypy:
    needs: build-binary-linux
    name: "check system | pypy on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "pypy3.9"

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which pypy)

      - name: "Validate global Python install"
        run: pypy scripts/check_system_python.py --uv ./uv

  system-test-pyston:
    needs: build-binary-linux
    name: "check system | pyston"
    runs-on: ubuntu-latest
    container: pyston/pyston:2.3.5
    steps:
      - uses: actions/checkout@v4

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which pyston)

      - name: "Validate global Python install"
        run: pyston scripts/check_system_python.py --uv ./uv

  system-test-alpine:
    needs: build-binary-linux
    name: "check system | alpine"
    runs-on: ubuntu-latest
    container: alpine:latest
    steps:
      - uses: actions/checkout@v4

      - name: "Install Python"
        run: apk add --update --no-cache python3 py3-pip

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python3)

      - name: "Validate global Python install"
        run: python3 scripts/check_system_python.py --uv ./uv --externally-managed

  system-test-macos-aarch64:
    needs: build-binary-macos-aarch64
    name: "check system | python on macos aarch64"
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-macos-aarch64-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      # This should be the macOS system Python
      # We'd like to test with Homebrew but this Python takes precedence in system Python discovery
      - name: "Print Python path"
        run: echo $(which python3)

      - name: "Validate global Python install"
        run: python3 scripts/check_system_python.py --uv ./uv --externally-managed

  system-test-macos-aarch64-homebrew:
    needs: build-binary-macos-aarch64
    name: "check system | homebrew python on macos aarch64"
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4

      - name: "Install Python"
        run: brew install python3

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-macos-aarch64-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python3)

      - name: "Validate global Python install"
        run: python3 scripts/check_system_python.py --uv ./uv --externally-managed

  system-test-macos-x86_64:
    needs: build-binary-macos-x86_64
    name: "check system | python on macos x86_64"
    runs-on: macos-12
    steps:
      - uses: actions/checkout@v4

      # We test with GitHub's Python as a regression test for
      # https://github.com/astral-sh/uv/issues/2450
      - uses: actions/setup-python@v5

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-macos-x86_64-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python3)

      - name: "Validate global Python install"
        run: python3 scripts/check_system_python.py --uv ./uv

  system-test-windows-python-310:
    needs: build-binary-windows
    name: "check system | python3.10 on windows"
    runs-on: windows-latest
    env:
      # Avoid debug build stack overflows.
      UV_STACK_SIZE: 2000000 # 2 megabyte, double the default on windows
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-windows-${{ github.sha }}

      - name: "Print Python path"
        run: echo $(which python)

      - name: "Validate global Python install"
        run: py -3.10 ./scripts/check_system_python.py --uv ./uv.exe

  system-test-windows-x86-python-310:
    needs: build-binary-windows
    name: "check system | python3.10 on windows x86"
    runs-on: windows-latest
    env:
      # Avoid debug build stack overflows.
      UV_STACK_SIZE: 2000000 # 2 megabyte, double the default on windows
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.10"
          architecture: "x86"

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-windows-${{ github.sha }}

      - name: "Print Python path"
        run: echo $(which python)

      - name: "Validate global Python install"
        run: python ./scripts/check_system_python.py --uv ./uv.exe

  system-test-windows-python-313:
    needs: build-binary-windows
    name: "check system | python3.13 on windows"
    runs-on: windows-latest
    env:
      # Avoid debug build stack overflows.
      UV_STACK_SIZE: 2000000 # 2 megabyte, double the default on windows
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          # TODO(konsti): https://github.com/actions/setup-python/issues/888
          python-version: "3.13.0-beta.1"
          allow-prereleases: true
          cache: pip

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-windows-${{ github.sha }}

      - name: "Print Python path"
        run: echo $(which python)

      - name: "Validate global Python install"
        run: py -3.13 ./scripts/check_system_python.py --uv ./uv.exe

  system-test-choco:
    needs: build-binary-windows
    name: "check system | python3.12 via chocolatey"
    runs-on: windows-latest
    env:
      # Avoid debug build stack overflows.
      UV_STACK_SIZE: 2000000 # 2 megabyte, double the default on windows
    steps:
      - uses: actions/checkout@v4

      - name: "Install Python"
        run: choco install python3 --verbose --version=3.9.13

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-windows-${{ github.sha }}

      - name: "Print Python path"
        run: echo $(which python3)

      - name: "Validate global Python install"
        run: py -3.9 ./scripts/check_system_python.py --uv ./uv.exe

  system-test-pyenv:
    needs: build-binary-linux
    name: "check system | python via pyenv"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: "Install pyenv"
        uses: "gabrielfalcao/pyenv-action@v18"
        with:
          default: 3.9.7

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python3.9)

      - name: "Validate global Python install"
        run: python3.9 scripts/check_system_python.py --uv ./uv

  system-test-conda:
    needs:
      [build-binary-windows, build-binary-macos-aarch64, build-binary-linux]
    name: check system | conda${{ matrix.python-version }} on ${{ matrix.os }}
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        os: ["linux", "windows", "macos"]
        python-version: ["3.8", "3.11"]
        include:
          - { os: "linux", target: "linux", runner: "ubuntu-latest" }
          - { os: "windows", target: "windows", runner: "windows-latest" }
          - { os: "macos", target: "macos-aarch64", runner: "macos-14" }
    steps:
      - uses: actions/checkout@v4

      - uses: conda-incubator/setup-miniconda@v3
        with:
          miniconda-version: "latest"
          activate-environment: uv
          python-version: ${{ matrix.python-version }}

      - name: Conda info
        shell: bash -el {0}
        run: conda info

      - name: Conda list
        shell: pwsh
        run: conda list

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-${{ matrix.target }}-${{ github.sha }}

      - name: "Prepare binary"
        if: ${{ matrix.os != 'windows' }}
        run: chmod +x ./uv

      - name: "Print Python path"
        shell: bash -el {0}
        run: echo $(which python)

      - name: "Validate global Python install"
        shell: bash -el {0}
        env:
          # Avoid debug build stack overflows.
          UV_STACK_SIZE: 2000000 # 2 megabyte, double the default on windows
        run: python ./scripts/check_system_python.py --uv ./uv

  system-test-amazonlinux:
    needs: build-binary-linux
    name: "check system | amazonlinux"
    runs-on: ubuntu-latest
    container: amazonlinux:2023
    steps:
      - name: "Install base requirements"
        run: |
          # Needed for `actions/checkout`
          yum install tar gzip which -y
      - uses: actions/checkout@v4
      - name: "Install Python"
        run: yum install python3 python3-pip -y

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-linux-${{ github.sha }}

      - name: "Prepare binary"
        run: chmod +x ./uv

      - name: "Print Python path"
        run: echo $(which python3)

      - name: "Validate global Python install"
        run: python3 scripts/check_system_python.py --uv ./uv

  system-test-windows-embedded-python-310:
    needs: build-binary-windows
    name: "check system | embedded python3.10 on windows"
    runs-on: windows-latest
    env:
      # Avoid debug build stack overflows.
      UV_STACK_SIZE: 2000000 # 2 megabyte, double the default on windows
    steps:
      - uses: actions/checkout@v4

      - name: "Download binary"
        uses: actions/download-artifact@v4
        with:
          name: uv-windows-${{ github.sha }}

      # Download embedded Python.
      - name: "Download embedded Python"
        run: curl -LsSf https://www.python.org/ftp/python/3.11.8/python-3.11.8-embed-amd64.zip -o python-3.11.8-embed-amd64.zip

      - name: "Unzip embedded Python"
        run: 7z x python-3.11.8-embed-amd64.zip -oembedded-python

      - name: "Show embedded Python contents"
        run: ls embedded-python

      - name: "Set PATH"
        run: echo "${{ github.workspace }}\embedded-python" >> $env:GITHUB_PATH

      - name: "Print Python path"
        run: echo $(which python)

      - name: "Validate embedded Python install"
        run: python ./scripts/check_embedded_python.py --uv ./uv.exe

  benchmarks:
    runs-on: ubuntu-latest
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    timeout-minutes: 20
    steps:
      - name: "Checkout Branch"
        uses: actions/checkout@v4

      - name: "Install Rust toolchain"
        run: rustup show

      - name: "Install codspeed"
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-codspeed

      - uses: Swatinem/rust-cache@v2

      - name: "Install requirements and prime cache"
        run: |
          sudo apt-get install -y libsasl2-dev libldap2-dev libkrb5-dev
          cargo run --bin uv -- venv --cache-dir .cache
          cargo run --bin uv -- pip compile scripts/requirements/airflow.in --cache-dir .cache

      - name: "Build benchmarks"
        run: cargo codspeed build --features codspeed -p bench

      - name: "Run benchmarks"
        uses: CodSpeedHQ/action@v2
        with:
          run: cargo codspeed run
          token: ${{ secrets.CODSPEED_TOKEN }}
