name: CI

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

permissions: {}

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
  PYTHON_VERSION: "3.12"
  RUSTUP_MAX_RETRIES: 10
  RUST_BACKTRACE: 1

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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: "Determine changed files"
        id: changed
        shell: bash
        run: |
          CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD)

          CODE_CHANGED=false

          while IFS= read -r file; do
            # Generated markdown and JSON files are checked during test runs.
            if [[ "${file}" =~ ^docs/ && ! "${file}" =~ ^docs/reference/(cli|settings).md && ! "${file}" =~ ^docs/reference/environment.md ]]; then
              echo "Skipping ${file} (matches docs/ pattern)"
              continue
            fi
            if [[ "${file}" =~ ^mkdocs.*\.yml$ ]]; then
              echo "Skipping ${file} (matches mkdocs*.yml pattern)"
              continue
            fi
            if [[ "${file}" =~ \.md$ ]]; then
              echo "Skipping ${file} (matches *.md pattern)"
              continue
            fi
            if [[ "${file}" =~ ^bin/ ]]; then
              echo "Skipping ${file} (matches bin/ pattern)"
              continue
            fi
            if [[ "${file}" =~ ^assets/ ]]; then
              echo "Skipping ${file} (matches assets/ pattern)"
              continue
            fi

            echo "Detected code change in: ${file}"
            CODE_CHANGED=true
            break

          done <<< "${CHANGED_FILES}"
          echo "code_any_changed=${CODE_CHANGED}" >> "${GITHUB_OUTPUT}"
  lint:
    timeout-minutes: 10
    name: "lint"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: 3.12

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

      - name: "Install uv"
        uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3

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

      - name: "Prettier"
        run: |
          npx prettier --check "**/*.{json5,yaml,yml}"
          npx prettier --prose-wrap always --check "**/*.md"

      - name: "README check"
        run: python scripts/transform_readme.py --target pypi

      - name: "Python format"
        run: uvx ruff format --diff .

      - name: "Python lint"
        run: uvx ruff check .

      - name: "Python type check"
        run: uvx mypy

      - name: "Validate project metadata"
        run: uvx --from 'validate-pyproject[all,store]' validate-pyproject pyproject.toml

      - name: "Lint shell scripts"
        uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0
        env:
          # renovate: datasource=github-tags depName=koalaman/shellcheck
          SHELLCHECK_VERSION: "v0.10.0"
          SHELLCHECK_OPTS: --shell bash
        with:
          version: ${{ env.SHELLCHECK_VERSION }}
          severity: style
          check_together: "yes"

  cargo-clippy:
    timeout-minutes: 10
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: ubuntu-latest
    name: "cargo clippy | ubuntu"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
        with:
          save-if: ${{ github.ref == 'refs/heads/main' }}
      - name: "Check uv_build dependencies"
        uses: EmbarkStudios/cargo-deny-action@f2ba7abc2abebaf185c833c3961145a3c275caad # v2.0.13
        with:
          command: check bans
          manifest-path: crates/uv-build/Cargo.toml
      - name: "Install Rust toolchain"
        run: rustup component add clippy
      - name: "Clippy"
        run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings

  cargo-clippy-windows:
    timeout-minutes: 15
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: windows-latest
    name: "cargo clippy | windows"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: Setup Dev Drive
        run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1

      # 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:UV_WORKSPACE" -Recurse

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
        with:
          workspaces: ${{ env.UV_WORKSPACE }}

      - name: "Install Rust toolchain"
        run: rustup component add clippy

      - name: "Clippy"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings

  cargo-dev-generate-all:
    timeout-minutes: 10
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: ubuntu-latest
    name: "cargo dev generate-all"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
        with:
          save-if: ${{ github.ref == 'refs/heads/main' }}
      - name: "Generate all"
        run: cargo dev generate-all --mode check

  cargo-shear:
    timeout-minutes: 10
    name: "cargo shear"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - name: "Install cargo shear"
        uses: taiki-e/install-action@a416ddeedbd372e614cc1386e8b642692f66865e # v2.57.1
        with:
          tool: 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:
    timeout-minutes: 10
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: depot-ubuntu-22.04-16
    name: "cargo test | ubuntu"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0

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

      - uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
      - name: "Install required Python versions"
        run: uv python install

      - name: "Install secret service"
        run: |
          sudo apt update -y
          sudo apt install -y gnome-keyring

      - name: "Start gnome-keyring"
        # run gnome-keyring with 'foobar' as password for the login keyring
        # this will create a new login keyring and unlock it
        # the login password doesn't matter, but the keyring must be unlocked for the tests to work
        run: gnome-keyring-daemon --components=secrets --daemonize --unlock <<< 'foobar'

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

      - name: "Cargo test"
        env:
          # Retry more than default to reduce flakes in CI
          UV_HTTP_RETRIES: 5
        run: |
          cargo nextest run \
            --features python-patch,native-auth,secret-service \
            --workspace \
            --status-level skip --failure-output immediate-final --no-fail-fast -j 20 --final-status-level slow

  cargo-test-macos:
    timeout-minutes: 15
    needs: determine_changes
    # Only run macOS tests on main without opt-in
    if: ${{ contains(github.event.pull_request.labels.*.name, 'test:macos') || github.ref == 'refs/heads/main' }}
    runs-on: depot-macos-14
    name: "cargo test | macos"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0

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

      - uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
      - name: "Install required Python versions"
        run: uv python install

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

      - name: "Cargo test"
        env:
          # Retry more than default to reduce flakes in CI
          UV_HTTP_RETRIES: 5
        run: |
          cargo nextest run \
            --no-default-features \
            --features python,python-managed,pypi,git,performance,crates-io,native-auth,apple-native \
            --workspace \
            --status-level skip --failure-output immediate-final --no-fail-fast -j 12 --final-status-level slow

  cargo-test-windows:
    timeout-minutes: 15
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: depot-windows-2022-16
    name: "cargo test | windows"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: Setup Dev Drive
        run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1

      # 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:UV_WORKSPACE" -Recurse

      - uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
      - name: "Install required Python versions"
        run: uv python install

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
        with:
          workspaces: ${{ env.UV_WORKSPACE }}

      - name: "Install Rust toolchain"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: rustup show

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

      - name: "Cargo test"
        working-directory: ${{ env.UV_WORKSPACE }}
        env:
          # Retry more than default to reduce flakes in CI
          UV_HTTP_RETRIES: 5
          # Avoid permission errors during concurrent tests
          # See https://github.com/astral-sh/uv/issues/6940
          UV_LINK_MODE: copy
        shell: bash
        run: |
          cargo nextest run \
            --no-default-features \
            --features python,pypi,python-managed,native-auth,windows-native \
            --workspace \
            --status-level skip --failure-output immediate-final --no-fail-fast -j 20 --final-status-level slow

  # Separate jobs for the nightly crate
  windows-trampoline-check:
    timeout-minutes: 15
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: windows-latest
    name: "check windows trampoline | ${{ matrix.target-arch }}"
    strategy:
      fail-fast: false
      matrix:
        target-arch: ["x86_64", "i686", "aarch64"]
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: Setup Dev Drive
        run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1

      # 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:UV_WORKSPACE" -Recurse

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
        with:
          workspaces: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline

      - name: "Install Rust toolchain"
        working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
        run: |
          rustup target add ${{ matrix.target-arch }}-pc-windows-msvc
          rustup component add rust-src --target ${{ matrix.target-arch }}-pc-windows-msvc

      - name: "Install cargo-bloat"
        uses: taiki-e/install-action@a416ddeedbd372e614cc1386e8b642692f66865e # v2.57.1
        with:
          tool: cargo-bloat

      - name: "rustfmt"
        working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
        run: cargo fmt --all --check

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

      - name: "Bloat Check"
        working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
        run: |
          $output = cargo bloat --release --target x86_64-pc-windows-msvc
          $filteredOutput = $output | Select-String -Pattern 'core::fmt::write|core::fmt::getcount' -NotMatch
          $containsPatterns = $filteredOutput | Select-String -Pattern 'core::fmt|std::panicking|std::backtrace_rs'

          if ($containsPatterns) {
              Exit 1
          } else {
              Exit 0
          }

  # Separate jobs for the nightly crate
  windows-trampoline-test:
    timeout-minutes: 10
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: ${{ matrix.runner }}
    name: "test windows trampoline | ${{ matrix.target-arch }}"
    strategy:
      fail-fast: false
      matrix:
        include:
          - { runner: windows-latest, target-arch: "x86_64" }
          - { runner: windows-latest, target-arch: "i686" }
          - { runner: windows-11-arm, target-arch: "aarch64" }
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - name: Setup Dev Drive
        run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1
      # 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:UV_WORKSPACE" -Recurse
      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
        with:
          workspaces: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
      - name: "Install Rust toolchain"
        working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
        run: |
          rustup target add ${{ matrix.target-arch }}-pc-windows-msvc
          rustup component add rust-src --target ${{ matrix.target-arch }}-pc-windows-msvc
      - name: "Test committed binaries"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: |
          rustup target add ${{ matrix.target-arch }}-pc-windows-msvc
          cargo test -p uv-trampoline-builder --target ${{ matrix.target-arch }}-pc-windows-msvc
      # Build and copy the new binaries
      - name: "Build"
        working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
        run: |
          cargo build --target ${{ matrix.target-arch }}-pc-windows-msvc
          cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-console.exe trampolines/uv-trampoline-${{ matrix.target-arch }}-console.exe
          cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-gui.exe trampolines/uv-trampoline-${{ matrix.target-arch }}-gui.exe
      - name: "Test new binaries"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: |
          # We turn off the default "production" test feature since these are debug binaries
          cargo test -p uv-trampoline-builder --target ${{ matrix.target-arch }}-pc-windows-msvc --no-default-features

  typos:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - uses: crate-ci/typos@392b78fe18a52790c53f42456e46124f77346842 # v1.34.0

  docs:
    timeout-minutes: 10
    name: "mkdocs"
    runs-on: ubuntu-latest
    env:
      MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          fetch-depth: 0
          persist-credentials: false
      - uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
      - name: "Add SSH key"
        if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
        uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
        with:
          ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}

      - name: "Build docs (public)"
        run: uvx --with-requirements docs/requirements.txt mkdocs build --strict -f mkdocs.public.yml

      - name: "Build docs (insiders)"
        if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
        run: uvx --with-requirements docs/requirements-insiders.txt mkdocs build --strict -f mkdocs.insiders.yml

  build-binary-linux-libc:
    timeout-minutes: 10
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: github-ubuntu-24.04-x86_64-8
    name: "build binary | linux libc"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0

      - name: "Build"
        run: cargo build

      - name: "Upload binary"
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: uv-linux-libc-${{ github.sha }}
          path: |
            ./target/debug/uv
            ./target/debug/uvx
          retention-days: 1

  build-binary-linux-aarch64:
    timeout-minutes: 10
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: github-ubuntu-24.04-aarch64-4
    name: "build binary | linux aarch64"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0

      - name: "Build"
        run: cargo build

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

  build-binary-linux-musl:
    timeout-minutes: 10
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: github-ubuntu-24.04-x86_64-8
    name: "build binary | linux musl"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1

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

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0

      - name: "Build"
        run: cargo build --target x86_64-unknown-linux-musl --bin uv --bin uvx

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

  build-binary-macos-aarch64:
    timeout-minutes: 10
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: macos-14 # github-macos-14-aarch64-3
    name: "build binary | macos aarch64"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
      - name: "Build"
        run: cargo build --bin uv --bin uvx

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

  build-binary-macos-x86_64:
    timeout-minutes: 10
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: macos-latest-large # github-macos-14-x86_64-12
    name: "build binary | macos x86_64"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
      - name: "Build"
        run: cargo build --bin uv --bin uvx

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

  build-binary-windows-x86_64:
    needs: determine_changes
    timeout-minutes: 10
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: windows-latest
    name: "build binary | windows x86_64"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: Setup Dev Drive
        run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1

      # 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:UV_WORKSPACE" -Recurse

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
        with:
          workspaces: ${{ env.UV_WORKSPACE }}

      - name: "Build"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: cargo build --bin uv --bin uvx

      - name: "Upload binary"
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: uv-windows-x86_64-${{ github.sha }}
          path: |
            ${{ env.UV_WORKSPACE }}/target/debug/uv.exe
            ${{ env.UV_WORKSPACE }}/target/debug/uvx.exe
          retention-days: 1

  build-binary-windows-aarch64:
    needs: determine_changes
    timeout-minutes: 25
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on:
      labels: windows-latest
    name: "build binary | windows aarch64"
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: Create Dev Drive using ReFS
        run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1

      # 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:UV_WORKSPACE" -Recurse

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
        with:
          workspaces: ${{ env.UV_WORKSPACE }}

      - name: "Install cross target"
        run: rustup target add aarch64-pc-windows-msvc

      - name: "Build"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: cargo build --target aarch64-pc-windows-msvc

      - name: "Upload binary"
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: uv-windows-aarch64-${{ github.sha }}
          path: |
            ${{ env.UV_WORKSPACE }}/target/aarch64-pc-windows-msvc/debug/uv.exe
            ${{ env.UV_WORKSPACE }}/target/aarch64-pc-windows-msvc/debug/uvx.exe
          retention-days: 1

  build-binary-msrv:
    name: "build binary | msrv"
    needs: determine_changes
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: github-ubuntu-24.04-x86_64-8
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - uses: SebRollen/toml-action@b1b3628f55fc3a28208d4203ada8b737e9687876 # v1.2.0
        id: msrv
        with:
          file: "Cargo.toml"
          field: "workspace.package.rust-version"
      - name: "Install Rust toolchain"
        run: rustup default ${MSRV}
        env:
          MSRV: ${{ steps.msrv.outputs.value }}
      - name: "Install mold"
        uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1
      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
      - run: cargo +${MSRV} build
        env:
          MSRV: ${{ steps.msrv.outputs.value }}
      - run: ./target/debug/uv --version

  build-binary-freebsd:
    needs: determine_changes
    timeout-minutes: 10
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    runs-on: ubuntu-latest
    name: "build binary | freebsd"

    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
      - name: "Cross build"
        run: |
          # Install cross from `freebsd-firecracker`
          wget -q -O cross https://github.com/acj/freebsd-firecracker/releases/download/v0.0.10/cross
          chmod +x cross
          mv cross /usr/local/bin/cross

          cross build --target x86_64-unknown-freebsd

      - name: Test in Firecracker VM
        uses: acj/freebsd-firecracker-action@e8e9155e944111ea65be7a606c69b32092f3c4c8 # v0.5.2
        with:
          verbose: false
          checkout: false
          pre-run: |
            # The exclude `*` prevents examination of directories so we need to
            # include each parent directory of the binary
            include_path="$(mktemp)"
            cat <<EOF > $include_path
            target
            target/x86_64-unknown-freebsd
            target/x86_64-unknown-freebsd/debug
            target/x86_64-unknown-freebsd/debug/uv
            EOF

            rsync -r -e "ssh" \
              --relative \
              --copy-links \
              --include-from "$include_path" \
              --exclude "*" \
              . firecracker:
          run-in-vm: |
            mv target/x86_64-unknown-freebsd/debug/uv uv
            chmod +x uv
            ./uv --version

  ecosystem-test:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "ecosystem test | ${{ matrix.repo }}"
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - repo: "prefecthq/prefect"
            ref: "7f25bbdf45fc81cca6dc23fb6a7377d436b70c83"
            commands:
              - "uv venv"
              - "uv pip install -e '.[dev]'"
            python: "3.9"
          - repo: "pallets/flask"
            ref: "b78b5a210bde49e7e04b62a2a4f453ca10e0048c"
            commands:
              - "uv venv"
              - "uv pip install -r requirements/dev.txt"
            python: "3.12"
          - repo: "pydantic/pydantic-core"
            ref: "d03bf4a01ca3b378cc8590bd481f307e82115bc6"
            commands:
              - "uv sync --group all"
              - "uv lock --upgrade"
            python: "3.12"
      fail-fast: false
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          repository: ${{ matrix.repo }}
          ref: ${{ matrix.ref }}
          persist-credentials: false

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

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

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

      - name: "Test"
        run: |
          echo '${{ toJSON(matrix.commands) }}' | jq -r '.[]' | while read cmd; do
            echo "+ $cmd" >&2
            if [[ $cmd == uv* ]]; then
              ./$cmd
            else
              $cmd
            fi
          done

  smoke-test-linux:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "smoke test | linux"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

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

      - name: "Smoke test"
        run: |
          ./uv run scripts/smoke-test

      - name: "Test shell completions"
        run: |
          eval "$(./uv generate-shell-completion bash)"
          eval "$(./uvx --generate-shell-completion bash)"

  smoke-test-linux-aarch64:
    timeout-minutes: 10
    needs: build-binary-linux-aarch64
    name: "smoke test | linux aarch64"
    runs-on: github-ubuntu-24.04-aarch64-2
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

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

      - name: "Smoke test"
        run: |
          ./uv run scripts/smoke-test

      - name: "Test shell completions"
        run: |
          eval "$(./uv generate-shell-completion bash)"
          eval "$(./uvx --generate-shell-completion bash)"

  smoke-test-linux-musl:
    timeout-minutes: 10
    needs: build-binary-linux-musl
    name: "check system | alpine"
    runs-on: ubuntu-latest
    container: alpine:latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

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

      - name: "Smoke test"
        run: |
          ./uv run scripts/smoke-test

  smoke-test-macos:
    timeout-minutes: 10
    needs: build-binary-macos-x86_64
    name: "smoke test | macos"
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

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

      - name: "Smoke test"
        run: |
          ./uv run scripts/smoke-test

      - name: "Test shell completions"
        run: |
          eval "$(./uv generate-shell-completion bash)"
          eval "$(./uvx --generate-shell-completion bash)"

  smoke-test-windows-x86_64:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "smoke test | windows x86_64"
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Smoke test"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: |
          ./uv run scripts/smoke-test

      - name: "Test uv shell completions"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: |
          (& ./uv generate-shell-completion powershell) | Out-String | Invoke-Expression

      - name: "Test uvx shell completions"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: |
          (& ./uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression

  smoke-test-windows-aarch64:
    timeout-minutes: 10
    needs: build-binary-windows-aarch64
    name: "smoke test | windows aarch64"
    runs-on: windows-11-arm
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Smoke test"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: |
          ./uv run scripts/smoke-test

      - name: "Test uv shell completions"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: |
          (& ./uv generate-shell-completion powershell) | Out-String | Invoke-Expression

      - name: "Test uvx shell completions"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: |
          (& ./uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression

  integration-test-conda:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "integration test | conda on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - uses: conda-incubator/setup-miniconda@835234971496cad1653abb28a638a281cf32541f # v3.2.0
        with:
          miniconda-version: latest
          activate-environment: uv
          python-version: "3.12"

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-libc-${{ 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-deadsnakes-39-linux:
    timeout-minutes: 15
    needs: build-binary-linux-libc
    name: "integration test | deadsnakes python3.9 on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - name: "Install python3.9"
        run: |
          for i in {1..5}; do
            sudo add-apt-repository ppa:deadsnakes && break || { echo "Attempt $i failed, retrying in 10 seconds..."; sleep 10; }
            if [ $i -eq 5 ]; then
              echo "Failed to add repository after 5 attempts"
              exit 1
            fi
          done
          sudo apt-get update
          sudo apt-get install python3.9

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

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

      - name: "Check missing distutils"
        run: |
          ./uv venv -p 3.9 --python-preference only-system -v 2>&1 | tee log.txt || true
          # We should report that distutils is missing
          grep 'Python installation is missing `distutils`' log.txt

      - name: "Install distutils"
        run: |
          sudo apt-get install python3.9-distutils

      - name: "Create a virtualenv"
        run: |
          ./uv venv -p 3.9 --python-preference only-system -v

      - name: "Check version"
        run: |
          .venv/bin/python --version

      - name: "Check install"
        run: |
          ./uv pip install -v anyio

  integration-test-free-threaded-windows-x86_64:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "integration test | free-threaded on windows"
    runs-on: windows-latest

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

      - name: "Install free-threaded Python via uv"
        run: |
          ./uv python install -v 3.13t

      - name: "Create a virtual environment (stdlib)"
        run: |
          & (./uv python find 3.13t) -m venv .venv

      - name: "Check version (stdlib)"
        run: |
          .venv/Scripts/python --version

      - name: "Create a virtual environment (uv)"
        run: |
          ./uv venv -c -p 3.13t --managed-python

      - name: "Check version (uv)"
        run: |
          .venv/Scripts/python --version

      - name: "Check is free-threaded"
        run: |
          .venv/Scripts/python -c "import sys; exit(1) if sys._is_gil_enabled() else exit(0)"

      - name: "Check install"
        run: |
          ./uv pip install -v anyio

      - name: "Check uv run"
        run: |
          ./uv run python -c ""
          ./uv run -p 3.13t python -c ""

  integration-test-windows-aarch64-implicit:
    timeout-minutes: 10
    needs: build-binary-windows-aarch64
    name: "integration test | aarch64 windows implicit"
    runs-on: windows-11-arm

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

      - name: "Install Python via uv (implicitly select x64)"
        run: |
          ./uv python install -v 3.13

      - name: "Create a virtual environment (stdlib)"
        run: |
          & (./uv python find 3.13) -m venv .venv

      - name: "Check version (stdlib)"
        run: |
          .venv/Scripts/python --version

      - name: "Create a virtual environment (uv)"
        run: |
          ./uv venv -c -p 3.13 --managed-python

      - name: "Check version (uv)"
        run: |
          .venv/Scripts/python --version

      - name: "Check is x64"
        run: |
          .venv/Scripts/python -c "import sys; exit(1) if 'AMD64' not in sys.version else exit(0)"

      - name: "Check install"
        run: |
          ./uv pip install -v anyio

      - name: "Check uv run"
        run: |
          ./uv run python -c ""
          ./uv run -p 3.13 python -c ""

  integration-test-windows-aarch64-explicit:
    timeout-minutes: 10
    needs: build-binary-windows-aarch64
    name: "integration test | aarch64 windows explicit"
    runs-on: windows-11-arm

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

      - name: "Install Python via uv (explicitly select aarch64)"
        run: |
          ./uv python install -v cpython-3.13-windows-aarch64-none

      - name: "Create a virtual environment (stdlib)"
        run: |
          & (./uv python find 3.13) -m venv .venv

      - name: "Check version (stdlib)"
        run: |
          .venv/Scripts/python --version

      - name: "Create a virtual environment (uv)"
        run: |
          ./uv venv -c -p 3.13 --managed-python

      - name: "Check version (uv)"
        run: |
          .venv/Scripts/python --version

      - name: "Check is NOT x64"
        run: |
          .venv/Scripts/python -c "import sys; exit(1) if 'AMD64' in sys.version else exit(0)"

      - name: "Check install"
        run: |
          ./uv pip install -v anyio

      - name: "Check uv run"
        run: |
          ./uv run python -c ""
          ./uv run -p 3.13 python -c ""

  integration-test-pypy-linux:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "integration test | pypy on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

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

      - name: "Install PyPy"
        run: ./uv python install -v pypy3.9

      - name: "Create a virtual environment"
        run: |
          ./uv venv -p pypy3.9 --managed-python

      - name: "Check for executables"
        run: |
          check_in_bin() {
              local executable_name=$1
              local bin_path=".venv/bin"

              if [[ -x "$bin_path/$executable_name" ]]; then
                  return 0
              else
                  echo "Executable '$executable_name' not found in folder '$bin_path'."
                  return 1
              fi
          }

          executables=("pypy" "pypy3" "python")

          all_found=true
          for executable_name in "${executables[@]}"; do
              check_in_bin "$executable_name" "$folder_path"
              result=$?

              if [[ $result -ne 0 ]]; then
                  all_found=false
              fi
          done

          if ! $all_found; then
            echo "One or more expected executables were not found."
            exit 1
          fi

      - name: "Check version"
        run: |
          .venv/bin/pypy --version
          .venv/bin/pypy3 --version
          .venv/bin/python --version

      - name: "Check install"
        run: |
          ./uv pip install anyio

  integration-test-pypy-windows-x86_64:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "integration test | pypy on windows"
    runs-on: windows-latest

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

      - name: "Install PyPy"
        run: .\uv.exe python install pypy3.9

      - name: "Create a virtual environment"
        run: |
          .\uv.exe venv -p pypy3.9 --managed-python

      - name: "Check for executables"
        shell: python
        run: |
          import sys
          from pathlib import Path

          def binary_exist(binary):
            binaries_path = Path(".venv\\Scripts")
            if (binaries_path / binary).exists():
              return True
            print(f"Executable '{binary}' not found in folder '{binaries_path}'.")

          all_found = True
          expected_binaries = [
              "pypy3.9.exe",
              "pypy3.9w.exe",
              "pypy3.exe",
              "pypyw.exe",
              "python.exe",
              "python3.9.exe",
              "python3.exe",
              "pythonw.exe",
          ]
          for binary in expected_binaries:
            if not binary_exist(binary):
              all_found = False

          if not all_found:
            print("One or more expected executables were not found.")
            sys.exit(1)

      - name: "Check version"
        run: |
          & .venv\Scripts\pypy3.9.exe --version
          & .venv\Scripts\pypy3.exe --version
          & .venv\Scripts\python.exe --version

      - name: "Check install"
        run: |
          .\uv.exe pip install anyio

  integration-test-graalpy-linux:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "integration test | graalpy on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

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

      - name: "Install GraalPy"
        run: ./uv python install -v graalpy

      - name: "Create a virtual environment"
        run: |
          ./uv venv -p graalpy --managed-python

      - name: "Check for executables"
        run: |
          check_in_bin() {
              local executable_name=$1
              local bin_path=".venv/bin"

              if [[ -x "$bin_path/$executable_name" ]]; then
                  return 0
              else
                  echo "Executable '$executable_name' not found in folder '$bin_path'."
                  return 1
              fi
          }

          executables=("graalpy" "python3" "python")

          all_found=true
          for executable_name in "${executables[@]}"; do
              check_in_bin "$executable_name" "$folder_path"
              result=$?

              if [[ $result -ne 0 ]]; then
                  all_found=false
              fi
          done

          if ! $all_found; then
            echo "One or more expected executables were not found."
            exit 1
          fi

      - name: "Check version"
        run: |
          .venv/bin/graalpy --version
          .venv/bin/python3 --version
          .venv/bin/python --version

      - name: "Check install"
        run: |
          ./uv pip install anyio

      - name: "Check a GraalPy dev version (different version parsing)"
        run: |
          curl -sLf https://github.com/graalvm/graal-languages-ea-builds/releases/download/graalpy-25.0.0-ea.31/graalpy-25.0.0-ea.31-linux-amd64.tar.gz | tar xz
          ./uv run -p ./graalpy-25.0.0-dev-linux-amd64/bin/python python --version

  integration-test-graalpy-windows-x86_64:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "integration test | graalpy on windows"
    runs-on: windows-latest

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

      - name: "Install GraalPy"
        run: .\uv.exe python install graalpy

      - name: "Create a virtual environment"
        run: |
          .\uv.exe venv -p graalpy --managed-python

      - name: "Check for executables"
        shell: python
        run: |
          import sys
          from pathlib import Path

          def binary_exist(binary):
            binaries_path = Path(".venv\\Scripts")
            if (binaries_path / binary).exists():
              return True
            print(f"Executable '{binary}' not found in folder '{binaries_path}'.")

          all_found = True
          expected_binaries = [
              "graalpy.exe",
              "python.exe",
              "python3.exe",
          ]
          for binary in expected_binaries:
            if not binary_exist(binary):
              all_found = False

          if not all_found:
            print("One or more expected executables were not found.")
            sys.exit(1)

      - name: "Check version"
        run: |
          & .venv\Scripts\graalpy.exe --version
          & .venv\Scripts\python3.exe --version
          & .venv\Scripts\python.exe --version

      - name: "Check install"
        run: |
          .\uv.exe pip install anyio

  integration-test-pyodide-linux:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "integration test | pyodide on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

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

      - name: "Create a native virtual environment"
        run: |
          ./uv venv venv-native -p 3.12
          # We use features added in 0.30.3 but there is no known breakage in
          # newer versions.
          ./uv pip install -p venv-native/bin/python pyodide-build==0.30.7 pip

      - name: "Install Pyodide interpreter"
        run: |
          source ./venv-native/bin/activate
          pyodide xbuildenv install 0.27.5
          PYODIDE_PYTHON=$(pyodide config get interpreter)
          PYODIDE_INDEX=$(pyodide config get package_index)
          echo "PYODIDE_PYTHON=$PYODIDE_PYTHON" >> $GITHUB_ENV
          echo "PYODIDE_INDEX=$PYODIDE_INDEX" >> $GITHUB_ENV

      - name: "Create Pyodide virtual environment"
        run: |
          ./uv venv -p $PYODIDE_PYTHON venv-pyodide
          source ./venv-pyodide/bin/activate
          ./uv pip install --extra-index-url=$PYODIDE_INDEX --no-build numpy
          python -c 'import numpy'

      - name: "Install Pyodide with uv python"
        run: |
          ./uv python install cpython-3.13.2-emscripten-wasm32-musl

      - name: "Create a Pyodide virtual environment using uv installed Python"
        run: |
          ./uv venv -p cpython-3.13.2-emscripten-wasm32-musl venv-pyodide2
          # TODO: be able to install Emscripten wheels here...
          source ./venv-pyodide2/bin/activate
          ./uv pip install packaging
          python -c 'import packaging'

  integration-test-github-actions:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "integration test | github actions"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.12.7"

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-libc-${{ 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 system opt-in via the environment"
        run: |
          ./uv pip install anyio --reinstall

      - name: "Create a project"
        run: |
          # Use Python 3.11 as the minimum required version
          ./uv init --python 3.11
          ./uv add anyio

      - name: "Sync to the system Python"
        run: ./uv sync -v --python 3.12
        env:
          UV_PROJECT_ENVIRONMENT: "/opt/hostedtoolcache/Python/3.12.7/x64"

      - name: "Attempt to sync to the system Python with an incompatible version"
        run: |
          ./uv sync -v --python 3.11 && { echo "ci: Error; should not succeed"; exit 1; } || { echo "ci: Ok; expected failure"; exit 0; }
        env:
          UV_PROJECT_ENVIRONMENT: "/opt/hostedtoolcache/Python/3.12.7/x64"

      - name: "Attempt to sync to a non-Python environment directory"
        run: |
          mkdir -p /home/runner/example
          touch /home/runner/example/some-file
          ./uv sync -v && { echo "ci: Error; should not succeed"; exit 1; } || { echo "ci: Ok; expected failure"; exit 0; }
        env:
          UV_PROJECT_ENVIRONMENT: "/home/runner/example"

  integration-test-github-actions-freethreaded:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "integration test | free-threaded python on github actions"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.13t"

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-libc-${{ 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 but without free-threaded opt-in"
        run: |
          ./uv pip install anyio --system --python 3.13 || echo "Failed as expected"
          # (we need to request 3.13 or we'll discover 3.12 on the system)

      - name: "Install a package with system and free-threaded opt-in"
        run: |
          ./uv pip install anyio --system --python 3.13t

      - name: "Create a virtual environment"
        run: |
          ./uv venv -p 3.13t --python-preference only-system

      - name: "Check is free-threaded"
        run: |
          .venv/bin/python -c "import sys; exit(1) if sys._is_gil_enabled() else exit(0)"

  integration-test-wsl:
    timeout-minutes: 15
    needs: build-binary-linux-musl
    name: "integration test | pyenv on wsl x86-64"
    runs-on: windows-latest
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event.pull_request.head.repo.fork != true }}
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Setup WSL"
        uses: Vampire/setup-wsl@6a8db447be7ed35f2f499c02c6e60ff77ef11278 # v6
        with:
          distribution: Ubuntu-22.04

      - name: "Install pyenv-win"
        shell: pwsh
        run: |
          Write-Host "Installing pyenv-win..."
          Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"
          .\install-pyenv-win.ps1

          # Add pyenv-win to PATH for this session
          $env:PYENV = "$env:USERPROFILE\.pyenv\pyenv-win"
          $env:PATH = "$env:PYENV\bin;$env:PYENV\shims;$env:PATH"

          # Add to GITHUB_PATH so WSL can find the shims
          echo "$env:PYENV\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
          echo "$env:PYENV\shims" | Out-File -FilePath $env:GITHUB_PATH -Append

          Write-Host "Installing Python 3.11.9 via pyenv-win..."
          & "$env:PYENV\bin\pyenv.bat" install 3.11.9
          & "$env:PYENV\bin\pyenv.bat" global 3.11.9

          Write-Host "Verifying pyenv-win installation..."
          & "$env:PYENV\bin\pyenv.bat" versions

      - name: "Test uv"
        shell: wsl-bash {0}
        run: |
          set -x
          chmod +x ./uv

          # Check that we don't fail on `pyenv-win` shims
          ./uv python list -v

  integration-test-publish-changed:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "integration test | determine publish changes"
    runs-on: ubuntu-latest
    outputs:
      # Flag that is raised when any code is changed
      code: ${{ steps.changed.outputs.code_any_changed }}
    # Only the main repository is a trusted publisher
    if: github.repository == 'astral-sh/uv' && !contains(github.event.pull_request.labels.*.name, 'no-test')
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          fetch-depth: 0
          persist-credentials: false
      # Only publish a new release if the publishing code changed
      - name: "Determine changed files"
        id: changed
        shell: bash
        run: |
          CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD)

          CODE_CHANGED=false

          while IFS= read -r file; do
            if [[ "${file}" =~ ^crates/uv-publish/ || "${file}" =~ ^scripts/publish/ || "${file}" == ".github/workflows/ci.yml" ]]; then
              echo "Detected code change in: ${file}"
              CODE_CHANGED=true
              break
            fi

            echo "Skipping ${file} (not in watched paths)"
            continue

          done <<< "${CHANGED_FILES}"
          echo "code_any_changed=${CODE_CHANGED}" >> "${GITHUB_OUTPUT}"

  integration-test-registries:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "integration test | registries"
    runs-on: ubuntu-latest
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event.pull_request.head.repo.fork != true }}
    environment: uv-test-registries
    env:
      PYTHON_VERSION: 3.12
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          fetch-depth: 0
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "${{ env.PYTHON_VERSION }}"

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

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

      - name: "Configure AWS credentials"
        uses: aws-actions/configure-aws-credentials@c5a43c32e1873343614c533eb83ffabbe5bc53bc
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: "Get AWS CodeArtifact token"
        run: |
          UV_TEST_AWS_TOKEN=$(aws codeartifact get-authorization-token \
            --domain tests \
            --domain-owner ${{ secrets.AWS_ACCOUNT_ID }} \
            --region us-east-1 \
            --query authorizationToken \
            --output text)
          echo "::add-mask::$UV_TEST_AWS_TOKEN"
          echo "UV_TEST_AWS_TOKEN=$UV_TEST_AWS_TOKEN" >> $GITHUB_ENV

      - name: "Authenticate with GCP"
        id: "auth"
        uses: "google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5"
        with:
          credentials_json: "${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}"

      - name: "Set up GCP SDK"
        uses: "google-github-actions/setup-gcloud@26f734c2779b00b7dda794207734c511110a4368"

      - name: "Get GCP Artifact Registry token"
        id: get_token
        run: |
          UV_TEST_GCP_TOKEN=$(gcloud auth print-access-token)
          echo "::add-mask::$UV_TEST_GCP_TOKEN"
          echo "UV_TEST_GCP_TOKEN=$UV_TEST_GCP_TOKEN" >> $GITHUB_ENV

      - name: "Run registry tests with environment variable backend"
        run: ./uv run -p "${PYTHON_VERSION}" scripts/registries-test.py --uv ./uv --color always --all --auth-method env
        env:
          RUST_LOG: uv=debug
          UV_TEST_ARTIFACTORY_TOKEN: ${{ secrets.UV_TEST_ARTIFACTORY_TOKEN }}
          UV_TEST_ARTIFACTORY_URL: ${{ secrets.UV_TEST_ARTIFACTORY_URL }}
          UV_TEST_ARTIFACTORY_USERNAME: ${{ secrets.UV_TEST_ARTIFACTORY_USERNAME }}
          UV_TEST_AWS_URL: ${{ secrets.UV_TEST_AWS_URL }}
          UV_TEST_AWS_USERNAME: aws
          UV_TEST_AZURE_TOKEN: ${{ secrets.UV_TEST_AZURE_TOKEN }}
          UV_TEST_AZURE_URL: ${{ secrets.UV_TEST_AZURE_URL }}
          UV_TEST_AZURE_USERNAME: dummy
          UV_TEST_CLOUDSMITH_TOKEN: ${{ secrets.UV_TEST_CLOUDSMITH_TOKEN }}
          UV_TEST_CLOUDSMITH_URL: ${{ secrets.UV_TEST_CLOUDSMITH_URL }}
          UV_TEST_CLOUDSMITH_USERNAME: ${{ secrets.UV_TEST_CLOUDSMITH_USERNAME }}
          UV_TEST_GCP_URL: ${{ secrets.UV_TEST_GCP_URL }}
          UV_TEST_GCP_USERNAME: oauth2accesstoken
          UV_TEST_GEMFURY_TOKEN: ${{ secrets.UV_TEST_GEMFURY_TOKEN }}
          UV_TEST_GEMFURY_URL: ${{ secrets.UV_TEST_GEMFURY_URL }}
          UV_TEST_GEMFURY_USERNAME: ${{ secrets.UV_TEST_GEMFURY_USERNAME }}
          UV_TEST_GITLAB_TOKEN: ${{ secrets.UV_TEST_GITLAB_TOKEN }}
          UV_TEST_GITLAB_URL: ${{ secrets.UV_TEST_GITLAB_URL }}
          UV_TEST_GITLAB_USERNAME: token

      - name: "Run registry tests with text store backend"
        run: ./uv run -p "${PYTHON_VERSION}" scripts/registries-test.py --uv ./uv --color always --all --auth-method text-store
        env:
          RUST_LOG: uv=debug
          UV_TEST_ARTIFACTORY_TOKEN: ${{ secrets.UV_TEST_ARTIFACTORY_TOKEN }}
          UV_TEST_ARTIFACTORY_URL: ${{ secrets.UV_TEST_ARTIFACTORY_URL }}
          UV_TEST_ARTIFACTORY_USERNAME: ${{ secrets.UV_TEST_ARTIFACTORY_USERNAME }}
          UV_TEST_AWS_URL: ${{ secrets.UV_TEST_AWS_URL }}
          UV_TEST_AWS_USERNAME: aws
          UV_TEST_AZURE_TOKEN: ${{ secrets.UV_TEST_AZURE_TOKEN }}
          UV_TEST_AZURE_URL: ${{ secrets.UV_TEST_AZURE_URL }}
          UV_TEST_AZURE_USERNAME: dummy
          UV_TEST_CLOUDSMITH_TOKEN: ${{ secrets.UV_TEST_CLOUDSMITH_TOKEN }}
          UV_TEST_CLOUDSMITH_URL: ${{ secrets.UV_TEST_CLOUDSMITH_URL }}
          UV_TEST_CLOUDSMITH_USERNAME: ${{ secrets.UV_TEST_CLOUDSMITH_USERNAME }}
          UV_TEST_GCP_URL: ${{ secrets.UV_TEST_GCP_URL }}
          UV_TEST_GCP_USERNAME: oauth2accesstoken
          UV_TEST_GEMFURY_TOKEN: ${{ secrets.UV_TEST_GEMFURY_TOKEN }}
          UV_TEST_GEMFURY_URL: ${{ secrets.UV_TEST_GEMFURY_URL }}
          UV_TEST_GEMFURY_USERNAME: ${{ secrets.UV_TEST_GEMFURY_USERNAME }}
          UV_TEST_GITLAB_TOKEN: ${{ secrets.UV_TEST_GITLAB_TOKEN }}
          UV_TEST_GITLAB_URL: ${{ secrets.UV_TEST_GITLAB_URL }}
          UV_TEST_GITLAB_USERNAME: token

  integration-test-publish:
    timeout-minutes: 20
    needs: integration-test-publish-changed
    name: "integration test | uv publish"
    runs-on: ubuntu-latest
    if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event.pull_request.head.repo.fork != true && (needs.integration-test-publish-changed.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    environment: uv-test-publish
    env:
      # No dbus in GitHub Actions
      PYTHON_KEYRING_BACKEND: keyrings.alt.file.PlaintextKeyring
      PYTHON_VERSION: 3.12
    permissions:
      # For trusted publishing
      id-token: write
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          fetch-depth: 0
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "${{ env.PYTHON_VERSION }}"

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

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

      - name: "Build astral-test-pypa-gh-action"
        run: |
          # Build a yet unused version of `astral-test-pypa-gh-action`
          mkdir astral-test-pypa-gh-action
          cd astral-test-pypa-gh-action
          ../uv init --package
          # Get the latest patch version
          patch_version=$(curl https://test.pypi.org/simple/astral-test-pypa-gh-action/?format=application/vnd.pypi.simple.v1+json | jq --raw-output '.files[-1].filename' | sed 's/astral_test_pypa_gh_action-0\.1\.\([0-9]\+\)\.tar\.gz/\1/')
          # Set the current version to one higher (which should be unused)
          sed -i "s/0.1.0/0.1.$((patch_version + 1))/g" pyproject.toml
          ../uv build

      - name: "Publish astral-test-pypa-gh-action"
        uses: pypa/gh-action-pypi-publish@db8f07d3871a0a180efa06b95d467625c19d5d5f # release/v1
        with:
          # With this GitHub action, we can't do as rigid checks as with our custom Python script, so we publish more
          # leniently
          skip-existing: "true"
          verbose: "true"
          repository-url: "https://test.pypi.org/legacy/"
          packages-dir: "astral-test-pypa-gh-action/dist"

      - name: "Add password to keyring"
        run: |
          # `keyrings.alt` contains the plaintext keyring
          ./uv tool install --with keyrings.alt keyring
          echo $UV_TEST_PUBLISH_KEYRING | keyring set https://test.pypi.org/legacy/?astral-test-keyring __token__
        env:
          UV_TEST_PUBLISH_KEYRING: ${{ secrets.UV_TEST_PUBLISH_KEYRING }}

      - name: "Add password to uv text store"
        run: |
          ./uv auth login https://test.pypi.org/legacy/?astral-test-text-store --token ${UV_TEST_PUBLISH_TEXT_STORE}
        env:
          UV_TEST_PUBLISH_TEXT_STORE: ${{ secrets.UV_TEST_PUBLISH_TEXT_STORE }}

      - name: "Publish test packages"
        # `-p 3.12` prefers the python we just installed over the one locked in `.python_version`.
        run: ./uv run -p "${PYTHON_VERSION}" scripts/publish/test_publish.py --uv ./uv all
        env:
          RUST_LOG: uv=debug,uv_publish=trace
          UV_TEST_PUBLISH_TOKEN: ${{ secrets.UV_TEST_PUBLISH_TOKEN }}
          UV_TEST_PUBLISH_PASSWORD: ${{ secrets.UV_TEST_PUBLISH_PASSWORD }}
          UV_TEST_PUBLISH_GITLAB_PAT: ${{ secrets.UV_TEST_PUBLISH_GITLAB_PAT }}
          UV_TEST_PUBLISH_CODEBERG_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CODEBERG_TOKEN }}
          UV_TEST_PUBLISH_CLOUDSMITH_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CLOUDSMITH_TOKEN }}
          UV_TEST_PUBLISH_PYTHON_VERSION: ${{ env.PYTHON_VERSION }}

  integration-uv-build-backend:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "integration test | uv_build"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "${{ env.PYTHON_VERSION }}"

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

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

      - name: "Test uv_build package"
        run: |
          # Build the Python package, which is not covered by uv's integration tests since they can't depend on having
          # a Python package (only the binary itself is built before running Rust's tests)
          ./uv build -v crates/uv-build

          # Test the main path (`build_wheel`) through pip
          ./uv venv -v --seed
          ./uv run --no-project python -m pip install -v scripts/packages/built-by-uv --find-links crates/uv-build/dist --no-index --no-deps
          ./uv run --no-project python -c "from built_by_uv import greet; print(greet())"

          # Test both `build_wheel` and `build_sdist` through uv
          ./uv venv -c -v
          ./uv build -v --force-pep517 scripts/packages/built-by-uv --find-links crates/uv-build/dist --offline
          ./uv pip install -v scripts/packages/built-by-uv/dist/*.tar.gz --find-links crates/uv-build/dist --offline --no-deps
          ./uv run --no-project python -c "from built_by_uv import greet; print(greet())"

          # Test both `build_wheel` and `build_sdist` through the official `build`
          rm -rf scripts/packages/built-by-uv/dist/
          ./uv venv -c -v
          ./uv pip install build
          # Add the uv binary to PATH for `build` to find
          PATH="$(pwd):$PATH" UV_OFFLINE=1 UV_FIND_LINKS=crates/uv-build/dist ./uv run --no-project python -m build -v --installer uv scripts/packages/built-by-uv
          ./uv pip install -v scripts/packages/built-by-uv/dist/*.tar.gz --find-links crates/uv-build/dist --offline --no-deps
          ./uv run --no-project python -c "from built_by_uv import greet; print(greet())"

  cache-test-ubuntu:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "check cache | ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.12"

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-libc-${{ 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:
    timeout-minutes: 10
    needs: build-binary-macos-aarch64
    name: "check cache | macos aarch64"
    runs-on: macos-14 # github-macos-14-aarch64-3
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        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:
    timeout-minutes: 10
    needs: build-binary-linux-musl
    name: "check system | python on debian"
    runs-on: ubuntu-latest
    container: debian:bookworm
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-musl-${{ 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

      - name: "Test `uv run` with system Python"
        run: |
          ./uv run -p python3.11 -v python -c "import debian"
          ./uv run -p python3.11 -v --with anyio python -c "import debian"

  system-test-fedora:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "check system | python on fedora"
    runs-on: ubuntu-latest
    container: fedora:43
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-libc-${{ 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:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "check system | python on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.12"

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-libc-${{ 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

  # Currently failing, see https://github.com/astral-sh/uv/issues/13811
  # system-test-opensuse:
  #   timeout-minutes: 5
  #   needs: build-binary-linux-libc
  #   name: "check system | python on opensuse"
  #   runs-on: ubuntu-latest
  #   container: opensuse/tumbleweed
  #   steps:
  #     - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

  #     - 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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
  #       with:
  #         name: uv-linux-libc-${{ 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:
    timeout-minutes: 10
    needs: build-binary-linux-musl
    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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - 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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-musl-${{ github.sha }}

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

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

      # Needed for building Pydantic
      - name: "Install build tools"
        run: dnf install -y gcc

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

  system-test-graalpy:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "check system | graalpy on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "graalpy24.1"

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

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

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

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

  system-test-pypy:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "check system | pypy on ubuntu"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-libc-${{ 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:
    timeout-minutes: 10
    needs: build-binary-linux-musl
    name: "check system | pyston"
    runs-on: ubuntu-latest
    container: pyston/pyston:2.3.5
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-musl-${{ 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:
    timeout-minutes: 10
    needs: build-binary-linux-musl
    name: "check system | alpine"
    runs-on: ubuntu-latest
    container: alpine:latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-musl-${{ 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:
    timeout-minutes: 10
    needs: build-binary-macos-aarch64
    name: "check system | python on macos aarch64"
    runs-on: macos-14 # github-macos-14-aarch64-3
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        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:
    timeout-minutes: 10
    needs: build-binary-macos-aarch64
    name: "check system | homebrew python on macos aarch64"
    runs-on: macos-14 # github-macos-14-aarch64-3
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        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:
    timeout-minutes: 10
    needs: build-binary-macos-x86_64
    name: "check system | python on macos x86-64"
    runs-on: macos-13 # github-macos-13-x86_64-4
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        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:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "check system | python3.10 on windows x86-64"
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.10"

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-windows-x86_64-${{ 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_64-python-310:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "check system | python3.10 on windows x86"
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.10"
          architecture: "x86"

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-windows-x86_64-${{ 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-x86_64-python-313:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "check system | python3.13 on windows x86-64"
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.13"
          allow-prereleases: true

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-windows-x86_64-${{ 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-windows-aarch64-x86-python-313:
    timeout-minutes: 10
    needs: build-binary-windows-aarch64
    name: "check system | x86-64 python3.13 on windows aarch64"
    runs-on: windows-11-arm
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.13"
          architecture: "x64"
          allow-prereleases: true

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

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

  system-test-windows-aarch64-aarch64-python-313:
    timeout-minutes: 10
    needs: build-binary-windows-aarch64
    name: "check system | aarch64 python3.13 on windows aarch64"
    runs-on: windows-11-arm
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.13"
          architecture: "arm64"
          allow-prereleases: true

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

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

  # Test our PEP 514 integration that installs Python into the Windows registry.
  system-test-windows-registry:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "check system | windows registry"
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      # NB: Run this last, we are modifying the registry
      - name: "Test PEP 514 registration"
        run: python ./scripts/check_registry.py --uv ./uv.exe

  system-test-choco:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "check system | python3.12 via chocolatey"
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

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

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-windows-x86_64-${{ 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:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "check system | python3.9 via pyenv"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: "Install pyenv"
        run: |
          # Install pyenv
          curl https://pyenv.run | bash

          # Set up environment variables for current step
          export PYENV_ROOT="$HOME/.pyenv"
          export PATH="$PYENV_ROOT/bin:$PATH"
          eval "$(pyenv init -)"

          # Install Python 3.9
          pyenv install 3.9
          pyenv global 3.9

          # Make environment variables persist across steps
          echo "PYENV_ROOT=$HOME/.pyenv" >> $GITHUB_ENV
          echo "$HOME/.pyenv/bin" >> $GITHUB_PATH
          echo "$HOME/.pyenv/shims" >> $GITHUB_PATH

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-linux-libc-${{ 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-linux-313:
    timeout-minutes: 10
    needs: build-binary-linux-libc
    name: "check system | python3.13"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: 3.13
          allow-prereleases: true

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

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

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

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

  system-test-conda:
    timeout-minutes: 10
    needs:
      [
        build-binary-windows-x86_64,
        build-binary-macos-aarch64,
        build-binary-linux-libc,
      ]
    name: check system | conda${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.arch }}
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        os: ["linux", "windows", "macos"]
        python-version: ["3.8", "3.11"]
        include:
          - {
              os: "linux",
              target: "linux-libc",
              runner: "ubuntu-latest",
              arch: "x86-64",
            }
          - {
              os: "windows",
              target: "windows-x86_64",
              runner: "windows-latest",
              arch: "x86-64",
            }
          - {
              os: "macos",
              target: "macos-aarch64",
              runner: "macos-14",
              arch: "aarch64",
            }
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: conda-incubator/setup-miniconda@835234971496cad1653abb28a638a281cf32541f # v3.2.0
        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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        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}
        run: python ./scripts/check_system_python.py --uv ./uv

  system-test-amazonlinux:
    timeout-minutes: 10
    needs: build-binary-linux-musl
    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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - name: "Install Python"
        run: yum install python3 python3-pip -y

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

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

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

      - name: Install build tools
        run: yum install -y gcc

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

  system-test-windows-embedded-python-310:
    timeout-minutes: 10
    needs: build-binary-windows-x86_64
    name: "check system | embedded python3.10 on windows x86-64"
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - name: "Download binary"
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: uv-windows-x86_64-${{ 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-walltime:
    name: "benchmarks | walltime aarch64 linux"
    runs-on: codspeed-macro
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    timeout-minutes: 20
    steps:
      - name: "Checkout Branch"
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0

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

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

      - name: "Install requirements and prime cache"
        run: |
          sudo apt-get update
          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/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
          cargo run --bin uv -- pip compile scripts/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache

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

      - name: "Run benchmarks"
        uses: CodSpeedHQ/action@0b6e7a3d96c9d2a6057e7bcea6b45aaf2f7ce60b # v3.8.0
        with:
          run: cargo codspeed run
          token: ${{ secrets.CODSPEED_TOKEN }}

  benchmarks-instrumented:
    name: "benchmarks | instrumented"
    runs-on: ubuntu-latest
    needs: determine_changes
    if: ${{ github.repository == 'astral-sh/uv' && !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
    timeout-minutes: 20
    steps:
      - name: "Checkout Branch"
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false

      - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0

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

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

      - name: "Install requirements and prime cache"
        run: |
          sudo apt-get update
          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/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
          cargo run --bin uv -- pip compile scripts/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache

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

      - name: "Run benchmarks"
        uses: CodSpeedHQ/action@0b6e7a3d96c9d2a6057e7bcea6b45aaf2f7ce60b # v3.8.0
        with:
          run: cargo codspeed run
          token: ${{ secrets.CODSPEED_TOKEN }}
