name: "test"
on:
  workflow_dispatch:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Actionlint
        uses: eifinger/actionlint-action@23c85443d840cd73bbecb9cddfc933cc21649a38  # v1.9.1
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
      - run: |
          npm install
      - run: |
          npm run all
      - name: Check all jobs are in all-tests-passed.needs
        run: |
          tsc check-all-tests-passed-needs.ts
          node check-all-tests-passed-needs.js
        working-directory: .github/scripts
      - name: Make sure no changes from linters are detected
        run: |
          git diff --exit-code || (echo "::error::Please run 'npm run all' to fix the issues" && exit 1)

  test-default-version:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, macos-14, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - name: Install latest version
        id: setup-uv
        uses: ./
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project
        shell: bash
      - name: Check uv-path is set
        run: ${{ steps.setup-uv.outputs.uv-path }} --version
      - name: Check uvx-path is set
        run: ${{ steps.setup-uv.outputs.uvx-path }} --version

  test-specific-version:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        uv-version: ["0.3.0", "0.3.2", "0.3", "0.3.x", ">=0.3.0"]
    steps:
      - uses: actions/checkout@v4
      - name: Install version ${{ matrix.uv-version }}
        uses: ./
        with:
          version: ${{ matrix.uv-version }}
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project

  test-semver-range:
    strategy:
      matrix:
        os: [ ubuntu-latest, selfhosted-ubuntu-arm64 ]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Install version 0.3
        id: setup-uv
        uses: ./
        with:
          version: "0.3"
      - name: Correct version gets installed
        run: |
          if [ "$(uv --version)" != "uv 0.3.5" ]; then
            echo "Wrong uv version: $(uv --version)"
            exit 1
          fi
      - name: Output has correct version
        run: |
          if [ "$UV_VERSION" != "0.3.5" ]; then
            exit 1
          fi
        env:
          UV_VERSION: ${{ steps.setup-uv.outputs.uv-version }}

  test-pep440-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install version 0.4.30
        id: setup-uv
        uses: ./
        with:
          version: ">=0.4.25,<0.5"
      - name: Correct version gets installed
        run: |
          if [ "$(uv --version)" != "uv 0.4.30" ]; then
            echo "Wrong uv version: $(uv --version)"
            exit 1
          fi

  test-pyproject-file-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install version 0.5.14
        id: setup-uv
        uses: ./
        with:
          working-directory: "__tests__/fixtures/pyproject-toml-project"
      - name: Correct version gets installed
        run: |
          if [ "$(uv --version)" != "uv 0.5.14" ]; then
            echo "Wrong uv version: $(uv --version)"
            exit 1
          fi

  test-malformed-pyproject-file-fallback:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install using malformed pyproject.toml
        id: setup-uv
        uses: ./
        with:
          working-directory: "__tests__/fixtures/malformed-pyproject-toml-project"
      - run: uv --help

  test-uv-file-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install version 0.5.15
        id: setup-uv
        uses: ./
        with:
          working-directory: "__tests__/fixtures/uv-toml-project"
      - name: Correct version gets installed
        run: |
          if [ "$(uv --version)" != "uv 0.5.15" ]; then
            echo "Wrong uv version: $(uv --version)"
            exit 1
          fi

  test-version-file-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install from requirements file
        id: setup-uv
        uses: ./
        with:
          version-file: "__tests__/fixtures/uv-in-requirements-txt-project/requirements.txt"
      - name: Correct version gets installed
        run: |
          if [ "$(uv --version)" != "uv 0.6.17" ]; then
            echo "Wrong uv version: $(uv --version)"
            exit 1
          fi

  test-checksum:
    runs-on: ${{ matrix.inputs.os }}
    strategy:
      matrix:
        inputs:
          - os: ubuntu-latest
            checksum: "4d9279ad5ca596b1e2d703901d508430eb07564dc4d8837de9e2fca9c90f8ecd"
          - os: macos-latest
            checksum: "a70cbfbf3bb5c08b2f84963b4f12c94e08fbb2468ba418a3bfe1066fbe9e7218"
    steps:
      - uses: actions/checkout@v4
      - name: Checksum matches expected
        uses: ./
        with:
          version: "0.3.2"
          checksum: ${{ matrix.inputs.checksum }}
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project

  test-with-explicit-token:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install default version
        uses: ./
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project

  test-uvx:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install default version
        uses: ./
      - run: uvx ruff --version

  test-tool-install:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os:
          [
            ubuntu-latest,
            macos-latest,
            macos-14,
            windows-latest,
          ]
    steps:
      - uses: actions/checkout@v4
      - name: Install default version
        uses: ./
      - run: uv tool install ruff
      - run: ruff --version

  test-tilde-expansion-tool-dirs:
    runs-on: selfhosted-ubuntu-arm64
    steps:
      - uses: actions/checkout@v4
      - name: Setup with cache
        uses: ./
        with:
          tool-bin-dir: "~/tool-bin-dir"
          tool-dir: "~/tool-dir"
      - name: "Check if tool dirs are expanded"
        run: |
          if ! echo "$PATH" | grep -q "/home/ubuntu/tool-bin-dir"; then
              echo "PATH does not contain /home/ubuntu/tool-bin-dir: $PATH"
              exit 1
          fi
          if [ "$UV_TOOL_DIR" != "/home/ubuntu/tool-dir" ]; then
              echo "UV_TOOL_DIR does not contain /home/ubuntu/tool-dir: $UV_TOOL_DIR"
              exit 1
          fi

  test-python-version:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - name: Install latest version
        uses: ./
        with:
          python-version: 3.13.1t
      - name: Verify UV_PYTHON is set to correct version
        run: |
          echo "$UV_PYTHON"
          if [ "$UV_PYTHON" != "3.13.1t" ]; then
            exit 1
          fi
        shell: bash
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project

  test-activate-environment:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ ubuntu-latest, macos-latest, windows-latest ]
    steps:
      - uses: actions/checkout@v4
      - name: Install latest version
        uses: ./
        with:
          python-version: 3.13.1t
          activate-environment: true
      - name: Verify packages can be installed
        run: uv pip install pip
        shell: bash
      - name: Verify python version is correct
        run: |
          python --version
          if [ "$(python --version)" != "Python 3.13.1" ]; then
            exit 1
          fi
        shell: bash

  test-musl:
    runs-on: ubuntu-latest
    container: alpine
    steps:
      - uses: actions/checkout@v4
      - name: Install latest version
        uses: ./
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project

  test-setup-cache:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        enable-cache: [ "true", "false", "auto" ]
        os: [ "ubuntu-latest", "selfhosted-ubuntu-arm64", "windows-latest" ]
    steps:
      - uses: actions/checkout@v4
      - name: Setup with cache
        uses: ./
        with:
          enable-cache: ${{ matrix.enable-cache }}
          cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}-test-setup-cache-${{ matrix.os }}-${{ matrix.enable-cache }}
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project
        shell: bash
  test-restore-cache:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        enable-cache: [ "true", "false", "auto" ]
        os: [ "ubuntu-latest", "selfhosted-ubuntu-arm64", "windows-latest" ]
    needs: test-setup-cache
    steps:
      - uses: actions/checkout@v4
      - name: Restore with cache
        id: restore
        uses: ./
        with:
          enable-cache: ${{ matrix.enable-cache }}
          cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}-test-setup-cache-${{ matrix.os }}-${{ matrix.enable-cache }}
      - name: Cache was hit
        if: ${{ matrix.enable-cache == 'true' || (matrix.enable-cache == 'auto' && matrix.os == 'ubuntu-latest') }}
        run: |
          if [ "$CACHE_HIT" != "true" ]; then
            exit 1
          fi
        env:
          CACHE_HIT: ${{ steps.restore.outputs.cache-hit }}
        shell: bash
      - name: Cache was not hit
        if: ${{ matrix.enable-cache == 'false' || (matrix.enable-cache == 'auto' && matrix.os == 'selfhosted-ubuntu-arm64') }}
        run: |
          if [ "$CACHE_HIT" == "true" ]; then
            exit 1
          fi
        env:
          CACHE_HIT: ${{ steps.restore.outputs.cache-hit }}
        shell: bash
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project
        shell: bash

  test-setup-cache-requirements-txt:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup with cache
        uses: ./
        with:
          enable-cache: true
          cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}-test-setup-cache-requirements-txt
      - run: |
          uv venv
          uv pip install -r requirements.txt
        working-directory: __tests__/fixtures/requirements-txt-project
  test-restore-cache-requirements-txt:
    runs-on: ubuntu-latest
    needs: test-setup-cache
    steps:
      - uses: actions/checkout@v4
      - name: Restore with cache
        id: restore
        uses: ./
        with:
          enable-cache: true
          cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}-test-setup-cache-requirements-txt
      - name: Cache was hit
        run: |
          if [ "$CACHE_HIT" != "true" ]; then
            exit 1
          fi
        env:
          CACHE_HIT: ${{ steps.restore.outputs.cache-hit }}
      - run: |
          uv venv
          uv pip install -r requirements.txt
        working-directory: __tests__/fixtures/requirements-txt-project

  test-setup-cache-dependency-glob:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup with cache
        uses: ./
        with:
          enable-cache: true
          cache-dependency-glob: |
            __tests__/fixtures/uv-project/uv.lock
            **/pyproject.toml
          cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}-test-setup-cache-dependency-glob
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project
  test-restore-cache-dependency-glob:
    runs-on: ubuntu-latest
    needs: test-setup-cache-dependency-glob
    steps:
      - uses: actions/checkout@v4
      - name: Change pyproject.toml
        run: |
          echo '[tool.uv]' >> __tests__/fixtures/uv-project/pyproject.toml
          echo 'dev-dependencies = []' >> __tests__/fixtures/uv-project/pyproject.toml
      - name: Restore with cache
        id: restore
        uses: ./
        with:
          enable-cache: true
          cache-dependency-glob: |
            __tests__/fixtures/uv-project/uv.lock
            **/pyproject.toml
          cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}-test-setup-cache-dependency-glob
          ignore-nothing-to-cache: true
      - name: Cache was not hit
        run: |
          if [ "$CACHE_HIT" == "true" ]; then
            exit 1
          fi
        env:
          CACHE_HIT: ${{ steps.restore.outputs.cache-hit }}

  test-cache-local:
    strategy:
      matrix:
        inputs:
          - os: ubuntu-latest
            expected-cache-dir: "/home/runner/work/_temp/setup-uv-cache"
          - os: windows-latest
            expected-cache-dir: "D:\\a\\_temp\\setup-uv-cache"
          - os: selfhosted-ubuntu-arm64
            expected-cache-dir: "/home/ubuntu/.cache/uv"
    runs-on: ${{ matrix.inputs.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Setup with cache
        uses: ./
        with:
          cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}-test-cache-local
      - run: |
          if [ "$UV_CACHE_DIR" != "${{ matrix.inputs.expected-cache-dir }}" ]; then
            echo "UV_CACHE_DIR is not set to the expected value: $UV_CACHE_DIR"
            exit 1
          fi
        shell: bash

  test-setup-cache-local:
    runs-on: selfhosted-ubuntu-arm64
    steps:
      - uses: actions/checkout@v4
      - name: Setup with cache
        uses: ./
        with:
          enable-cache: true
          cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}-test-setup-cache-local
          cache-local-path: /tmp/uv-cache
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project
  test-restore-cache-local:
    runs-on: selfhosted-ubuntu-arm64
    needs: test-setup-cache-local
    steps:
      - uses: actions/checkout@v4
      - name: Restore with cache
        id: restore
        uses: ./
        with:
          enable-cache: true
          cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}-test-setup-cache-local
          cache-local-path: /tmp/uv-cache
      - name: Cache was hit
        run: |
          if [ "$CACHE_HIT" != "true" ]; then
            exit 1
          fi
        env:
          CACHE_HIT: ${{ steps.restore.outputs.cache-hit }}
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project

  test-tilde-expansion-cache-local-path:
    runs-on: selfhosted-ubuntu-arm64
    steps:
      - uses: actions/checkout@v4
      - name: Create cache directory
        run: mkdir -p ~/uv-cache
        shell: bash
      - name: Setup with cache
        uses: ./
        with:
          cache-local-path: ~/uv-cache/cache-local-path
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project

  test-tilde-expansion-cache-dependency-glob:
    runs-on: selfhosted-ubuntu-arm64
    steps:
      - uses: actions/checkout@v4
      - name: Create cache directory
        run: mkdir -p ~/uv-cache
        shell: bash
      - name: Create cache dependency glob file
        run: touch ~/uv-cache.glob
        shell: bash
      - name: Setup with cache
        uses: ./
        with:
          enable-cache: true
          cache-local-path: ~/uv-cache/cache-dependency-glob
          cache-dependency-glob: "~/uv-cache.glob"
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project

  cleanup-tilde-expansion-tests:
    needs:
      - test-tilde-expansion-cache-local-path
      - test-tilde-expansion-cache-dependency-glob
    if: always()
    runs-on: selfhosted-ubuntu-arm64
    steps:
      - name: Remove cache directory
        run: rm -rf ~/uv-cache
        shell: bash
      - name: Remove cache dependency glob file
        run: rm -f ~/uv-cache.glob
        shell: bash

  test-no-python-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Fake pyproject.toml at root
        run: cp __tests__/fixtures/old-python-constraint-project/pyproject.toml pyproject.toml
      - name: Setup with cache
        uses: ./
        with:
          enable-cache: true
      - run: uv sync
        working-directory: __tests__/fixtures/old-python-constraint-project

  test-custom-manifest-file:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install from custom manifest file
        uses: ./
        with:
          manifest-file: "https://raw.githubusercontent.com/astral-sh/setup-uv/${{ github.ref }}/__tests__/download/custom-manifest.json"
      - run: uv sync
        working-directory: __tests__/fixtures/uv-project
      - name: Correct version gets installed
        run: |
          if [ "$(uv --version)" != "uv 0.7.12-alpha.1" ]; then
            echo "Wrong uv version: $(uv --version)"
            exit 1
          fi

  all-tests-passed:
    runs-on: ubuntu-latest
    needs:
      - lint
      - test-default-version
      - test-specific-version
      - test-semver-range
      - test-pep440-version
      - test-pyproject-file-version
      - test-malformed-pyproject-file-fallback
      - test-uv-file-version
      - test-version-file-version
      - test-checksum
      - test-with-explicit-token
      - test-uvx
      - test-tool-install
      - test-tilde-expansion-tool-dirs
      - test-python-version
      - test-activate-environment
      - test-musl
      - test-cache-local
      - test-setup-cache
      - test-restore-cache
      - test-setup-cache-requirements-txt
      - test-restore-cache-requirements-txt
      - test-setup-cache-dependency-glob
      - test-restore-cache-dependency-glob
      - test-setup-cache-local
      - test-restore-cache-local
      - test-tilde-expansion-cache-local-path
      - test-tilde-expansion-cache-dependency-glob
      - cleanup-tilde-expansion-tests
      - test-no-python-version
      - test-custom-manifest-file
    if: always()
    steps:
      - name: All tests passed
        run: |
          echo "All jobs passed: ${{ !contains(needs.*.result, 'failure') }}"
          # shellcheck disable=SC2242
          exit ${{ contains(needs.*.result, 'failure') && 1 || 0 }}
