diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml new file mode 100644 index 000000000..43066223b --- /dev/null +++ b/.github/workflows/bench.yml @@ -0,0 +1,89 @@ +on: + workflow_call: + +permissions: {} + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUSTUP_MAX_RETRIES: 10 + +jobs: + benchmarks-walltime: + name: "walltime on aarch64 linux" + runs-on: codspeed-macro + if: ${{ github.repository == 'astral-sh/uv' }} + timeout-minutes: 60 + steps: + - name: "Checkout Branch" + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + + - name: "Install Rust toolchain" + run: rustup show + + - name: "Install codspeed" + uses: taiki-e/install-action@d850aa816998e5cf15f67a78c7b933f2a5033f8a # v2.63.3 + 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 test/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache + cargo run --bin uv -- pip compile test/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache + + - name: "Build benchmarks" + run: cargo codspeed build --profile profiling -p uv-bench + + - name: "Run benchmarks" + uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1 + with: + run: cargo codspeed run + mode: walltime + token: ${{ secrets.CODSPEED_TOKEN }} + + benchmarks-simulated: + name: "simulated" + runs-on: ubuntu-latest + if: ${{ github.repository == 'astral-sh/uv' }} + timeout-minutes: 20 + steps: + - name: "Checkout Branch" + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + + - name: "Install Rust toolchain" + run: rustup show + + - name: "Install codspeed" + uses: taiki-e/install-action@d850aa816998e5cf15f67a78c7b933f2a5033f8a # v2.63.3 + 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 test/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache + cargo run --bin uv -- pip compile test/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache + + - name: "Build benchmarks" + run: cargo codspeed build --profile profiling -p uv-bench + + - name: "Run benchmarks" + uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1 + with: + run: cargo codspeed run + mode: simulation + token: ${{ secrets.CODSPEED_TOKEN }} diff --git a/.github/workflows/build-dev-binaries.yml b/.github/workflows/build-dev-binaries.yml new file mode 100644 index 000000000..bb0337a89 --- /dev/null +++ b/.github/workflows/build-dev-binaries.yml @@ -0,0 +1,281 @@ +on: + workflow_call: + +permissions: {} + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUSTUP_MAX_RETRIES: 10 + +jobs: + build-binary-linux-libc: + name: "linux libc" + timeout-minutes: 10 + runs-on: github-ubuntu-24.04-x86_64-8 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + + - name: "Build" + run: cargo build --profile no-debug + + - name: "Upload binary" + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: uv-linux-libc-${{ github.sha }} + path: | + ./target/no-debug/uv + ./target/no-debug/uvx + retention-days: 1 + + build-binary-linux-aarch64: + name: "linux aarch64" + timeout-minutes: 10 + runs-on: github-ubuntu-24.04-aarch64-4 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + + - name: "Build" + run: cargo build --profile no-debug + + - name: "Upload binary" + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: uv-linux-aarch64-${{ github.sha }} + path: | + ./target/no-debug/uv + ./target/no-debug/uvx + retention-days: 1 + + build-binary-linux-musl: + name: "linux musl" + timeout-minutes: 10 + runs-on: github-ubuntu-24.04-x86_64-8 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + + - name: "Setup musl" + run: | + sudo apt-get install musl-tools + rustup target add x86_64-unknown-linux-musl + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + + - name: "Build" + run: cargo build --profile no-debug --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/no-debug/uv + ./target/x86_64-unknown-linux-musl/no-debug/uvx + retention-days: 1 + + build-binary-macos-aarch64: + name: "macos aarch64" + timeout-minutes: 10 + runs-on: macos-14 # github-macos-14-aarch64-3 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + - name: "Build" + run: cargo build --profile no-debug --bin uv --bin uvx + + - name: "Upload binary" + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: uv-macos-aarch64-${{ github.sha }} + path: | + ./target/no-debug/uv + ./target/no-debug/uvx + retention-days: 1 + + build-binary-macos-x86_64: + name: "macos x86_64" + timeout-minutes: 10 + runs-on: macos-latest-large # github-macos-14-x86_64-12 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + - name: "Build" + run: cargo build --profile no-debug --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/no-debug/uv + ./target/no-debug/uvx + retention-days: 1 + + build-binary-windows-x86_64: + name: "windows x86_64" + timeout-minutes: 10 + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + workspaces: ${{ env.UV_WORKSPACE }} + + - name: "Build" + working-directory: ${{ env.UV_WORKSPACE }} + run: cargo build --profile no-debug --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/no-debug/uv.exe + ${{ env.UV_WORKSPACE }}/target/no-debug/uvx.exe + retention-days: 1 + + build-binary-windows-aarch64: + name: "windows aarch64" + timeout-minutes: 25 + runs-on: + labels: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + 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 --profile no-debug --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/no-debug/uv.exe + ${{ env.UV_WORKSPACE }}/target/aarch64-pc-windows-msvc/no-debug/uvx.exe + retention-days: 1 + + build-binary-msrv: + name: "msrv" + runs-on: github-ubuntu-24.04-x86_64-8 + timeout-minutes: 10 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + - run: cargo +${MSRV} build --profile no-debug + env: + MSRV: ${{ steps.msrv.outputs.value }} + - run: ./target/no-debug/uv --version + + build-binary-freebsd: + name: "freebsd" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + - 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 --profile no-debug + + - name: Test in Firecracker VM + uses: acj/freebsd-firecracker-action@a5a3fc1709c5b5368141a5699f10259aca3cd965 # v0.6.0 + 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 < $include_path + target + target/x86_64-unknown-freebsd + target/x86_64-unknown-freebsd/no-debug + target/x86_64-unknown-freebsd/no-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/no-debug/uv uv + chmod +x uv + ./uv --version diff --git a/.github/workflows/build-binaries.yml b/.github/workflows/build-release-binaries.yml similarity index 98% rename from .github/workflows/build-binaries.yml rename to .github/workflows/build-release-binaries.yml index 936a9b42e..abe3e4884 100644 --- a/.github/workflows/build-binaries.yml +++ b/.github/workflows/build-release-binaries.yml @@ -2,30 +2,17 @@ # # Generates both wheels (for PyPI) and archived binaries (for GitHub releases). # -# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a local -# artifacts job within `cargo-dist`. +# Called from: +# - .github/workflows/ci.yml (when release-relevant files change) +# - .github/workflows/release.yml (as a local artifacts job within `cargo-dist`) name: "Build release binaries" on: workflow_call: inputs: plan: - required: true + required: false type: string - pull_request: - paths: - # We want to ensure that the maturin builds still work when we change - # Project metadata - - pyproject.toml - - Cargo.toml - - .cargo/config.toml - - crates/uv-build/Cargo.toml - - crates/uv-build/pyproject.toml - # Toolchain or dependency versions - - Cargo.lock - - rust-toolchain.toml - # And the workflow itself - - .github/workflows/build-binaries.yml concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/check-docs.yml b/.github/workflows/check-docs.yml new file mode 100644 index 000000000..986c086d1 --- /dev/null +++ b/.github/workflows/check-docs.yml @@ -0,0 +1,42 @@ +on: + workflow_call: + +permissions: {} + +jobs: + 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + with: + version: "0.9.24" + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} + - name: "Generate reference documentation" + run: | + cargo dev generate-options-reference + cargo dev generate-cli-reference + cargo dev generate-env-vars-reference + - 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 diff --git a/.github/workflows/check-fmt.yml b/.github/workflows/check-fmt.yml new file mode 100644 index 000000000..1d1b22789 --- /dev/null +++ b/.github/workflows/check-fmt.yml @@ -0,0 +1,38 @@ +on: + workflow_call: + +permissions: {} + +jobs: + rust: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: "Install Rustfmt" + run: rustup component add rustfmt + - run: cargo fmt --all --check + + prettier: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - run: npx prettier --check . + + python: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: "Install uv" + uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + with: + version: "0.9.24" + - run: uvx ruff format --diff . diff --git a/.github/workflows/check-generated-files.yml b/.github/workflows/check-generated-files.yml new file mode 100644 index 000000000..07efff0e6 --- /dev/null +++ b/.github/workflows/check-generated-files.yml @@ -0,0 +1,34 @@ +on: + workflow_call: + inputs: + schema-changed: + required: true + type: string + +permissions: {} + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUSTUP_MAX_RETRIES: 10 + +jobs: + cargo-dev-generate-all: + timeout-minutes: 10 + runs-on: ubuntu-latest + name: "cargo dev generate-all" + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} + - name: "Generate all" + run: cargo dev generate-all --mode dry-run + - name: "Check sysconfig mappings" + run: cargo dev generate-sysconfig-metadata --mode check + - name: "Check JSON schema" + if: ${{ inputs.schema-changed == 'true' }} + run: cargo dev generate-json-schema --mode check diff --git a/.github/workflows/check-lint.yml b/.github/workflows/check-lint.yml new file mode 100644 index 000000000..f3029eace --- /dev/null +++ b/.github/workflows/check-lint.yml @@ -0,0 +1,161 @@ +on: + workflow_call: + inputs: + code-changed: + required: true + type: string + +permissions: {} + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUSTUP_MAX_RETRIES: 10 + +jobs: + ruff: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: "Install uv" + uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + with: + version: "0.9.24" + - run: uvx ruff check . + + ty: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: "Install uv" + uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + with: + version: "0.9.24" + - run: | + uvx ty check python/uv + uvx \ + --directory crates/uv-python \ + --with-requirements fetch-download-metadata.py \ + ty check --python-version 3.13 fetch-download-metadata.py + + shellcheck: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 + env: + # renovate: datasource=github-tags depName=koalaman/shellcheck + SHELLCHECK_VERSION: "v0.11.0" + SHELLCHECK_OPTS: --shell bash + with: + version: ${{ env.SHELLCHECK_VERSION }} + severity: style + check_together: "yes" + + validate-pyproject: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: "Install uv" + uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + with: + version: "0.9.24" + - run: uvx --from 'validate-pyproject[all,store]' validate-pyproject pyproject.toml + + readme: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: 3.12 + - run: python scripts/transform_readme.py --target pypi + + clippy-ubuntu: + name: "clippy on linux" + timeout-minutes: 10 + if: ${{ inputs.code-changed == 'true' || github.ref == 'refs/heads/main' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} + - name: "Check uv_build dependencies" + uses: EmbarkStudios/cargo-deny-action@76cd80eb775d7bbbd2d80292136d74d39e1b4918 # v2.0.14 + 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 + + clippy-windows: + name: "clippy on windows" + timeout-minutes: 15 + if: ${{ inputs.code-changed == 'true' || github.ref == 'refs/heads/main' }} + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + 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 + + shear: + name: "cargo shear" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: "Install cargo shear" + uses: taiki-e/install-action@d850aa816998e5cf15f67a78c7b933f2a5033f8a # v2.63.3 + with: + tool: cargo-shear + - run: cargo shear + + typos: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - uses: crate-ci/typos@64e4db431eb262bb5c6baa19dce280d78532830c # v1.37.3 diff --git a/.github/workflows/check-publish.yml b/.github/workflows/check-publish.yml new file mode 100644 index 000000000..58ef298e1 --- /dev/null +++ b/.github/workflows/check-publish.yml @@ -0,0 +1,25 @@ +on: + workflow_call: + +permissions: {} + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUSTUP_MAX_RETRIES: 10 + +jobs: + cargo-publish-dry-run: + timeout-minutes: 20 + runs-on: depot-ubuntu-22.04-8 + name: "cargo publish dry-run" + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} + - name: "cargo publish dry-run" + run: cargo publish --workspace --dry-run diff --git a/.github/workflows/check-zizmor.yml b/.github/workflows/check-zizmor.yml new file mode 100644 index 000000000..3fa3d60f1 --- /dev/null +++ b/.github/workflows/check-zizmor.yml @@ -0,0 +1,16 @@ +on: + workflow_call: + +permissions: {} + +jobs: + zizmor: + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ffb2b904..4615cbef3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,25 +12,22 @@ 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" + plan: runs-on: ubuntu-latest outputs: - # Flag that is raised when any code is changed - code: ${{ steps.changed.outputs.code_any_changed }} - # Flag that is raised when uv.schema.json is changed (e.g., in a release PR) - schema: ${{ steps.changed.outputs.schema_changed }} - # Flag that is raised when trampoline-related code is changed - trampoline: ${{ steps.changed.outputs.trampoline_any_changed }} + # Run checks/tests if no-test label is not present and code changed (or on main) + test-code: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (steps.changed.outputs.code_any_changed == 'true' || github.ref == 'refs/heads/main') }} + # Run schema check if schema file changed + check-schema: ${{ steps.changed.outputs.schema_changed == 'true' }} + # Run release build test if release files changed + build-release-binaries: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && steps.changed.outputs.release_build_changed == 'true' }} + # Run format/lint checks (always unless no-test label) + run-checks: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') }} + # Run publish test if publish-related files changed + test-publish: ${{ steps.changed.outputs.publish_changed == 'true' || github.ref == 'refs/heads/main' }} + # Run trampoline checks if trampoline-related code changed + test-windows-trampoline: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (steps.changed.outputs.trampoline_any_changed == 'true' || github.ref == 'refs/heads/main') }} steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: @@ -45,6 +42,8 @@ jobs: CODE_CHANGED=false SCHEMA_CHANGED=false + RELEASE_BUILD_CHANGED=false + PUBLISH_CHANGED=false TRAMPOLINE_CHANGED=false while IFS= read -r file; do @@ -54,6 +53,18 @@ jobs: SCHEMA_CHANGED=true fi + # Check if release build files changed (pyproject.toml, Cargo.toml, etc.) + if [[ "${file}" == "pyproject.toml" || "${file}" == "Cargo.toml" || "${file}" == "Cargo.lock" || "${file}" == "rust-toolchain.toml" || "${file}" == ".cargo/config.toml" || "${file}" == "crates/uv-build/Cargo.toml" || "${file}" == "crates/uv-build/pyproject.toml" || "${file}" == ".github/workflows/build-release-binaries.yml" ]]; then + echo "Detected release build change: ${file}" + RELEASE_BUILD_CHANGED=true + fi + + # Check if publish-related files changed + if [[ "${file}" =~ ^crates/uv-publish/ || "${file}" =~ ^scripts/publish/ || "${file}" == ".github/workflows/ci.yml" ]]; then + echo "Detected publish change: ${file}" + PUBLISH_CHANGED=true + fi + # Check if trampoline-related files changed if [[ "${file}" =~ ^crates/uv-trampoline/ ]] || [[ "${file}" =~ ^crates/uv-trampoline-builder/ ]]; then echo "Detected trampoline change: ${file}" @@ -87,1891 +98,110 @@ jobs: done <<< "${CHANGED_FILES}" echo "code_any_changed=${CODE_CHANGED}" >> "${GITHUB_OUTPUT}" echo "schema_changed=${SCHEMA_CHANGED}" >> "${GITHUB_OUTPUT}" + echo "release_build_changed=${RELEASE_BUILD_CHANGED}" >> "${GITHUB_OUTPUT}" + echo "publish_changed=${PUBLISH_CHANGED}" >> "${GITHUB_OUTPUT}" echo "trampoline_any_changed=${TRAMPOLINE_CHANGED}" >> "${GITHUB_OUTPUT}" - lint: - timeout-minutes: 10 - name: "lint" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 - with: - python-version: 3.12 + check-fmt: + uses: ./.github/workflows/check-fmt.yml - - name: "Install Rustfmt" - run: rustup component add rustfmt + check-lint: + needs: plan + uses: ./.github/workflows/check-lint.yml + with: + code-changed: ${{ needs.plan.outputs.test-code }} - - name: "Install uv" - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 - with: - version: "0.9.24" + check-docs: + needs: plan + if: ${{ needs.plan.outputs.run-checks == 'true' }} + uses: ./.github/workflows/check-docs.yml + secrets: inherit - - name: "rustfmt" - run: cargo fmt --all --check + check-zizmor: + needs: plan + if: ${{ needs.plan.outputs.run-checks == 'true' }} + uses: ./.github/workflows/check-zizmor.yml + permissions: + contents: read + security-events: write - - name: "Prettier" - run: npx prettier --check . + check-publish: + needs: plan + if: ${{ needs.plan.outputs.test-code == 'true' }} + uses: ./.github/workflows/check-publish.yml - - name: "README check" - run: python scripts/transform_readme.py --target pypi + check-generated-files: + needs: plan + if: ${{ needs.plan.outputs.test-code == 'true' }} + uses: ./.github/workflows/check-generated-files.yml + with: + schema-changed: ${{ needs.plan.outputs.check-schema }} - - name: "Python format" - run: uvx ruff format --diff . + test: + needs: plan + if: ${{ needs.plan.outputs.test-code == 'true' }} + uses: ./.github/workflows/test.yml - - name: "Python lint" - run: uvx ruff check . + test-windows-trampolines: + needs: plan + if: ${{ needs.plan.outputs.test-windows-trampoline == 'true' }} + uses: ./.github/workflows/test-windows-trampolines.yml - - name: "Python type check" - run: | - uvx ty check python/uv - uvx \ - --directory crates/uv-python \ - --with-requirements fetch-download-metadata.py \ - ty check --python-version 3.13 fetch-download-metadata.py + build-dev-binaries: + needs: plan + if: ${{ needs.plan.outputs.test-code == 'true' }} + uses: ./.github/workflows/build-dev-binaries.yml - - name: "Validate project metadata" - run: uvx --from 'validate-pyproject[all,store]' validate-pyproject pyproject.toml + test-smoke: + needs: build-dev-binaries + uses: ./.github/workflows/test-smoke.yml + with: + sha: ${{ github.sha }} - - name: "Lint shell scripts" - uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 - env: - # renovate: datasource=github-tags depName=koalaman/shellcheck - SHELLCHECK_VERSION: "v0.11.0" - SHELLCHECK_OPTS: --shell bash - with: - version: ${{ env.SHELLCHECK_VERSION }} - severity: style - check_together: "yes" + test-integration: + needs: build-dev-binaries + uses: ./.github/workflows/test-integration.yml + secrets: inherit + permissions: + id-token: write + with: + sha: ${{ github.sha }} - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - with: - save-if: ${{ github.ref == 'refs/heads/main' }} - - name: "Check uv_build dependencies" - uses: EmbarkStudios/cargo-deny-action@76cd80eb775d7bbbd2d80292136d74d39e1b4918 # v2.0.14 - 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 + test-system: + needs: build-dev-binaries + uses: ./.github/workflows/test-system.yml + with: + sha: ${{ github.sha }} - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false + test-ecosystem: + needs: build-dev-binaries + uses: ./.github/workflows/test-ecosystem.yml + with: + sha: ${{ github.sha }} - - name: Setup Dev Drive - run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1 + build-release-binaries: + needs: plan + if: ${{ needs.plan.outputs.build-release-binaries == 'true' }} + uses: ./.github/workflows/build-release-binaries.yml + secrets: inherit - # 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 + bench: + needs: plan + if: ${{ needs.plan.outputs.test-code == 'true' }} + uses: ./.github/workflows/bench.yml + secrets: inherit - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - 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-publish-dry-run: + # This job cannot be moved into a reusable workflow because it includes coverage for uploading + # attestations and PyPI does not support attestations in reusable workflows. + test-publish: + name: "test uv publish" timeout-minutes: 20 - 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-8 - name: "cargo publish dry-run" - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - with: - save-if: ${{ github.ref == 'refs/heads/main' }} - - name: "cargo publish dry-run" - run: cargo publish --workspace --dry-run - - 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') }} + needs: + - plan + - build-dev-binaries runs-on: ubuntu-latest - name: "cargo dev generate-all" - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - with: - save-if: ${{ github.ref == 'refs/heads/main' }} - - name: "Generate all" - run: cargo dev generate-all --mode dry-run - - name: "Check sysconfig mappings" - run: cargo dev generate-sysconfig-metadata --mode check - - name: "Check JSON schema" - if: ${{ needs.determine_changes.outputs.schema == 'true' }} - run: cargo dev generate-json-schema --mode check - - cargo-shear: - timeout-minutes: 10 - name: "cargo shear" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - name: "Install cargo shear" - uses: taiki-e/install-action@d850aa816998e5cf15f67a78c7b933f2a5033f8a # v2.63.3 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - - name: "Install Rust toolchain" - run: rustup show - - - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 - with: - version: "0.9.24" - - - 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 \ - --cargo-profile fast-build \ - --features python-patch,native-auth,secret-service \ - --workspace \ - --profile ci-linux - - cargo-test-macos: - timeout-minutes: 20 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - - name: "Install Rust toolchain" - run: rustup show - - - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 - with: - version: "0.9.24" - - - 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 \ - --cargo-profile fast-build \ - --no-default-features \ - --features python,python-managed,pypi,git,git-lfs,performance,crates-io,native-auth,apple-native \ - --workspace \ - --profile ci-macos - - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 - with: - version: "0.9.24" - - - name: "Install required Python versions" - run: uv python install - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - 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 \ - --cargo-profile fast-build \ - --no-default-features \ - --features python,pypi,python-managed,native-auth,windows-native \ - --workspace \ - --profile ci-windows - - # 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.trampoline == '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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - 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 --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.trampoline == '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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - 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 ../uv-trampoline-builder/trampolines/uv-trampoline-${{ matrix.target-arch }}-console.exe - cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-gui.exe ../uv-trampoline-builder/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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - uses: crate-ci/typos@64e4db431eb262bb5c6baa19dce280d78532830c # v1.37.3 - - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 - with: - version: "0.9.24" - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 - - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0 - with: - save-if: ${{ github.ref == 'refs/heads/main' }} - - name: "Generate reference documentation" - run: | - cargo dev generate-options-reference - cargo dev generate-cli-reference - cargo dev generate-env-vars-reference - - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - - name: "Build" - run: cargo build --profile no-debug - - - name: "Upload binary" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: uv-linux-libc-${{ github.sha }} - path: | - ./target/no-debug/uv - ./target/no-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - - name: "Build" - run: cargo build --profile no-debug - - - name: "Upload binary" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: uv-linux-aarch64-${{ github.sha }} - path: | - ./target/no-debug/uv - ./target/no-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - - name: "Setup musl" - run: | - sudo apt-get install musl-tools - rustup target add x86_64-unknown-linux-musl - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - - name: "Build" - run: cargo build --profile no-debug --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/no-debug/uv - ./target/x86_64-unknown-linux-musl/no-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - name: "Build" - run: cargo build --profile no-debug --bin uv --bin uvx - - - name: "Upload binary" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: uv-macos-aarch64-${{ github.sha }} - path: | - ./target/no-debug/uv - ./target/no-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - name: "Build" - run: cargo build --profile no-debug --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/no-debug/uv - ./target/no-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - with: - workspaces: ${{ env.UV_WORKSPACE }} - - - name: "Build" - working-directory: ${{ env.UV_WORKSPACE }} - run: cargo build --profile no-debug --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/no-debug/uv.exe - ${{ env.UV_WORKSPACE }}/target/no-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - 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 --profile no-debug --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/no-debug/uv.exe - ${{ env.UV_WORKSPACE }}/target/aarch64-pc-windows-msvc/no-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - run: cargo +${MSRV} build --profile no-debug - env: - MSRV: ${{ steps.msrv.outputs.value }} - - run: ./target/no-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - 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 --profile no-debug - - - name: Test in Firecracker VM - uses: acj/freebsd-firecracker-action@a5a3fc1709c5b5368141a5699f10259aca3cd965 # v0.6.0 - 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 < $include_path - target - target/x86_64-unknown-freebsd - target/x86_64-unknown-freebsd/no-debug - target/x86_64-unknown-freebsd/no-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/no-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - repository: ${{ matrix.repo }} - ref: ${{ matrix.ref }} - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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-nushell: - timeout-minutes: 10 - needs: build-binary-linux-libc - name: "integration test | activate nushell venv" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - name: Install nushell - env: - # This token only needs read access to the GitHub repository nushell/nushell. - # This token is used (via gh-cli) to avoid hitting GitHub REST API rate limits. - GITHUB_TOKEN: ${{ github.token }} - run: |- - # get latest nushell tag name - nu_latest=$(gh release list --repo nushell/nushell --limit 1 --exclude-pre-releases --exclude-drafts --json "tagName" --jq '.[0].tagName') - # trim any trailing whitespace from output - nu_tag=${nu_latest%%[[:space:]]*} - - # download binary for x86_64-unknown-linux-gnu target - gh release download ${nu_tag} --repo nushell/nushell --pattern "nu-${nu_tag}-x86_64-unknown-linux-gnu.tar.gz" - - # extract nu binary from tar.gz - tar -xf "nu-${nu_tag}-x86_64-unknown-linux-gnu.tar.gz" - # make the binary executable - chmod +x "./nu-${nu_tag}-x86_64-unknown-linux-gnu/nu" - # add it to PATH - echo "${{ github.workspace }}/nu-${nu_tag}-x86_64-unknown-linux-gnu" >> "${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: Create venv - # The python version is arbitrary for this test. - # We only want to ensure the activation script behaves properly - run: ./uv venv - - - name: Activate venv - shell: nu {0} - run: overlay use ${{ github.workspace }}/.venv/bin/activate.nu - - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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-windows-python-install-manager: - timeout-minutes: 10 - needs: build-binary-windows-x86_64 - name: "integration test | windows python install manager" - 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 Python via Python Install manager" - run: | - # https://www.python.org/downloads/release/pymanager-250/ - winget install --accept-package-agreements --accept-source-agreements 9NQ7512CXL7T - # Call Python Install Manager's py.exe by full path to avoid legacy py.exe - & "$env:LOCALAPPDATA\Microsoft\WindowsApps\py.exe" install 3.14 - - # https://github.com/astral-sh/uv/issues/16204 - - name: "Check temporary environment creation" - run: | - ./uv run -p $env:LOCALAPPDATA\Python\pythoncore-3.14-64\python.exe --with numpy python -c "import sys; print(sys.executable)" - - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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.0.0 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 - 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@fc2174804b84f912b1f6d334e9463f484f1c552d" - with: - credentials_json: "${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}" - - - name: "Set up GCP SDK" - uses: "google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db" - - - 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') }} + if: ${{ github.repository == 'astral-sh/uv' && github.event.pull_request.head.repo.fork != true && needs.plan.outputs.test-publish == 'true' }} environment: uv-test-publish env: # No dbus in GitHub Actions @@ -2047,966 +277,17 @@ jobs: UV_TEST_PUBLISH_PYX_TOKEN: ${{ secrets.UV_TEST_PUBLISH_PYX_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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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 test/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 test/packages/built-by-uv --find-links crates/uv-build/dist --offline - ./uv pip install -v test/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 test/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 test/packages/built-by-uv - ./uv pip install -v test/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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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/rockylinux:${{ matrix.rocky-version }} - strategy: - fail-fast: false - matrix: - rocky-version: ["8", "9", "10"] - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - name: "Install Python" - if: matrix.rocky-version == '8' - run: | - for i in {1..5}; do - dnf install python39 python39-pip which -y && break || { echo "Attempt $i failed, retrying in 10 seconds..."; sleep 10; } - if [ $i -eq 5 ]; then - echo "Failed to install Python after 5 attempts" - exit 1 - fi - done - - - name: "Install Python" - if: matrix.rocky-version == '9' - run: | - for i in {1..5}; do - dnf install python3.9 python3.9-pip which -y && break || { echo "Attempt $i failed, retrying in 10 seconds..."; sleep 10; } - if [ $i -eq 5 ]; then - echo "Failed to install Python after 5 attempts" - exit 1 - fi - done - - - name: "Install Python" - if: matrix.rocky-version == '10' - run: | - dnf install python3 python3-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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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: "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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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-aarch64-emulated: - timeout-minutes: 10 - needs: build-binary-macos-aarch64 - name: "check system | x86-64 python on macos aarch64" - runs-on: macos-14 # github-macos-14-aarch64-3 - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 - with: - python-version: 3.13 - architecture: x64 - - - 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-15-intel # github-macos-15-x86_64-4 - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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 --externally-managed - - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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 + required-checks-passed: + name: "all required jobs passed" + if: always() 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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" + - check-fmt + - check-lint + - check-docs + - check-generated-files + - test + - build-dev-binaries 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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: 25 - steps: - - name: "Checkout Branch" - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - - 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 test/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache - cargo run --bin uv -- pip compile test/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache - - - name: "Build benchmarks" - run: cargo codspeed build --profile profiling -p uv-bench - - - name: "Run benchmarks" - uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1 - with: - run: cargo codspeed run - mode: walltime - token: ${{ secrets.CODSPEED_TOKEN }} - - benchmarks-simulated: - name: "benchmarks | simulated" - 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - - - 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 test/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache - cargo run --bin uv -- pip compile test/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache - - - name: "Build benchmarks" - run: cargo codspeed build --profile profiling -p uv-bench - - - name: "Run benchmarks" - uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1 - with: - run: cargo codspeed run - mode: simulation - token: ${{ secrets.CODSPEED_TOKEN }} + - name: "Check required jobs passed" + run: echo '${{ toJSON(needs) }}' | jq -e 'all(.[]; .result == "success" or .result == "skipped")' > /dev/null diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index b6873cf29..bd1a878f8 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -36,7 +36,7 @@ jobs: with: python-version: 3.12 - - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - name: "Generate reference documentation" run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96f130720..bb947f671 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -95,7 +95,7 @@ jobs: needs: - plan if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }} - uses: ./.github/workflows/build-binaries.yml + uses: ./.github/workflows/build-release-binaries.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit diff --git a/.github/workflows/test-ecosystem.yml b/.github/workflows/test-ecosystem.yml new file mode 100644 index 000000000..d389e7e2b --- /dev/null +++ b/.github/workflows/test-ecosystem.yml @@ -0,0 +1,65 @@ +on: + workflow_call: + inputs: + sha: + required: true + type: string + +permissions: {} + +jobs: + ecosystem-test: + name: "${{ matrix.repo }}" + timeout-minutes: 10 + 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + repository: ${{ matrix.repo }} + ref: ${{ matrix.ref }} + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: ${{ matrix.python }} + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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 diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml new file mode 100644 index 000000000..29cfbac6f --- /dev/null +++ b/.github/workflows/test-integration.yml @@ -0,0 +1,951 @@ +on: + workflow_call: + inputs: + sha: + description: "The commit SHA to use for artifact names" + required: true + type: string + +permissions: {} + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + PYTHON_VERSION: "3.12" + RUSTUP_MAX_RETRIES: 10 + +jobs: + integration-test-nushell: + name: "nushell" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: Install nushell + env: + # This token only needs read access to the GitHub repository nushell/nushell. + # This token is used (via gh-cli) to avoid hitting GitHub REST API rate limits. + GITHUB_TOKEN: ${{ github.token }} + run: |- + # get latest nushell tag name + nu_latest=$(gh release list --repo nushell/nushell --limit 1 --exclude-pre-releases --exclude-drafts --json "tagName" --jq '.[0].tagName') + # trim any trailing whitespace from output + nu_tag=${nu_latest%%[[:space:]]*} + + # download binary for x86_64-unknown-linux-gnu target + gh release download ${nu_tag} --repo nushell/nushell --pattern "nu-${nu_tag}-x86_64-unknown-linux-gnu.tar.gz" + + # extract nu binary from tar.gz + tar -xf "nu-${nu_tag}-x86_64-unknown-linux-gnu.tar.gz" + # make the binary executable + chmod +x "./nu-${nu_tag}-x86_64-unknown-linux-gnu/nu" + # add it to PATH + echo "${{ github.workspace }}/nu-${nu_tag}-x86_64-unknown-linux-gnu" >> "${GITHUB_PATH}" + + - name: Download binary + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.sha }} + + - name: Prepare binary + run: chmod +x ./uv + + - name: Create venv + # The python version is arbitrary for this test. + # We only want to ensure the activation script behaves properly + run: ./uv venv + + - name: Activate venv + shell: nu {0} + run: overlay use ${{ github.workspace }}/.venv/bin/activate.nu + + integration-test-conda: + name: "conda on linux" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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-${{ inputs.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: + name: "deadsnakes python3.9 on ubuntu" + timeout-minutes: 15 + 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-${{ inputs.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: + name: "free-threaded on windows" + timeout-minutes: 10 + runs-on: windows-latest + + steps: + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-x86_64-${{ inputs.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: + name: "aarch64 windows implicit" + timeout-minutes: 10 + runs-on: windows-11-arm + + steps: + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-aarch64-${{ inputs.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: + name: "aarch64 windows explicit" + timeout-minutes: 10 + runs-on: windows-11-arm + + steps: + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-aarch64-${{ inputs.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-windows-python-install-manager: + name: "windows python install manager" + timeout-minutes: 10 + runs-on: windows-latest + + steps: + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-x86_64-${{ inputs.sha }} + + - name: "Install Python via Python Install manager" + run: | + # https://www.python.org/downloads/release/pymanager-250/ + winget install --accept-package-agreements --accept-source-agreements 9NQ7512CXL7T + # Call Python Install Manager's py.exe by full path to avoid legacy py.exe + & "$env:LOCALAPPDATA\Microsoft\WindowsApps\py.exe" install 3.14 + + # https://github.com/astral-sh/uv/issues/16204 + - name: "Check temporary environment creation" + run: | + ./uv run -p $env:LOCALAPPDATA\Python\pythoncore-3.14-64\python.exe --with numpy python -c "import sys; print(sys.executable)" + + integration-test-pypy-linux: + name: "pypy on linux" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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: + name: "pypy on windows" + timeout-minutes: 10 + runs-on: windows-latest + + steps: + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-x86_64-${{ inputs.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: + name: "graalpy on linux" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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: + name: "graalpy on windows" + timeout-minutes: 10 + runs-on: windows-latest + + steps: + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-x86_64-${{ inputs.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: + name: "pyodide on linux" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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: + name: "github actions" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "3.12.7" + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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: + name: "free-threaded on github actions" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "3.13t" + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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: + name: "pyenv on wsl" + timeout-minutes: 15 + runs-on: windows-latest + if: ${{ github.event.pull_request.head.repo.fork != true }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-musl-${{ inputs.sha }} + + - name: "Setup WSL" + uses: Vampire/setup-wsl@6a8db447be7ed35f2f499c02c6e60ff77ef11278 # v6.0.0 + 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-registries: + name: "registries" + timeout-minutes: 10 + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.head.repo.fork != true }} + environment: uv-test-registries + env: + PYTHON_VERSION: 3.12 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "${{ env.PYTHON_VERSION }}" + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.sha }} + + - name: "Prepare binary" + run: chmod +x ./uv + + - name: "Configure AWS credentials" + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 + 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@fc2174804b84f912b1f6d334e9463f484f1c552d" + with: + credentials_json: "${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}" + + - name: "Set up GCP SDK" + uses: "google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db" + + - 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-uv-build-backend: + name: "uv_build" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "${{ env.PYTHON_VERSION }}" + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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 test/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 test/packages/built-by-uv --find-links crates/uv-build/dist --offline + ./uv pip install -v test/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 test/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 test/packages/built-by-uv + ./uv pip install -v test/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: + name: "cache on linux" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "3.12" + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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: + name: "cache on macos aarch64" + timeout-minutes: 10 + runs-on: macos-14 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-macos-aarch64-${{ inputs.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 diff --git a/.github/workflows/test-smoke.yml b/.github/workflows/test-smoke.yml new file mode 100644 index 000000000..5f5c504d6 --- /dev/null +++ b/.github/workflows/test-smoke.yml @@ -0,0 +1,182 @@ +on: + workflow_call: + inputs: + sha: + description: "The commit SHA to use for artifact names" + required: true + type: string + +permissions: {} + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUSTUP_MAX_RETRIES: 10 + +jobs: + smoke-test-linux: + name: "linux" + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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: + name: "linux aarch64" + timeout-minutes: 10 + runs-on: github-ubuntu-24.04-aarch64-2 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-aarch64-${{ inputs.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: + name: "linux musl" + timeout-minutes: 10 + runs-on: ubuntu-latest + container: alpine:latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-musl-${{ inputs.sha }} + + - name: "Prepare binary" + run: | + chmod +x ./uv + chmod +x ./uvx + + - name: "Smoke test" + run: | + ./uv run scripts/smoke-test + + smoke-test-macos: + name: "macos" + timeout-minutes: 10 + runs-on: macos-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-macos-x86_64-${{ inputs.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: + name: "windows x86_64" + timeout-minutes: 10 + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-x86_64-${{ inputs.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: + name: "windows aarch64" + timeout-minutes: 10 + runs-on: windows-11-arm + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-aarch64-${{ inputs.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 diff --git a/.github/workflows/test-system.yml b/.github/workflows/test-system.yml new file mode 100644 index 000000000..9bba3b71c --- /dev/null +++ b/.github/workflows/test-system.yml @@ -0,0 +1,762 @@ +on: + workflow_call: + inputs: + sha: + description: "The commit SHA to use for artifact names" + required: true + type: string + +permissions: {} + +jobs: + system-test-debian: + timeout-minutes: 10 + name: "python on debian" + runs-on: ubuntu-latest + container: debian:bookworm + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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-${{ inputs.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 + name: "python on fedora" + runs-on: ubuntu-latest + container: fedora:43 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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-${{ inputs.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 + name: "python3.12 via setup-python" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "3.12" + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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 + # name: "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-${{ inputs.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 + name: "python on rocky linux ${{ matrix.rocky-version }}" + runs-on: ubuntu-latest + container: rockylinux/rockylinux:${{ matrix.rocky-version }} + strategy: + fail-fast: false + matrix: + rocky-version: ["8", "9", "10"] + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Install Python" + if: matrix.rocky-version == '8' + run: | + for i in {1..5}; do + dnf install python39 python39-pip which -y && break || { echo "Attempt $i failed, retrying in 10 seconds..."; sleep 10; } + if [ $i -eq 5 ]; then + echo "Failed to install Python after 5 attempts" + exit 1 + fi + done + + - name: "Install Python" + if: matrix.rocky-version == '9' + run: | + for i in {1..5}; do + dnf install python3.9 python3.9-pip which -y && break || { echo "Attempt $i failed, retrying in 10 seconds..."; sleep 10; } + if [ $i -eq 5 ]; then + echo "Failed to install Python after 5 attempts" + exit 1 + fi + done + + - name: "Install Python" + if: matrix.rocky-version == '10' + run: | + dnf install python3 python3-pip which -y + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-musl-${{ inputs.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 + name: "graalpy on linux" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "graalpy24.1" + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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 + name: "pypy on linux" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "pypy3.9" + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ inputs.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 + name: "pyston on linux" + runs-on: ubuntu-latest + container: pyston/pyston:2.3.5 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-musl-${{ inputs.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 + name: "python on alpine" + runs-on: ubuntu-latest + container: alpine:latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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-${{ inputs.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 + name: "python on macos aarch64" + runs-on: macos-14 # github-macos-14-aarch64-3 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-macos-aarch64-${{ inputs.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-homebrew: + timeout-minutes: 10 + name: "homebrew python on macos aarch64" + runs-on: macos-14 # github-macos-14-aarch64-3 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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-${{ inputs.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-emulated: + timeout-minutes: 10 + name: "x86-64 python on macos aarch64" + runs-on: macos-14 # github-macos-14-aarch64-3 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: 3.13 + architecture: x64 + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-macos-aarch64-${{ inputs.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 + name: "python on macos x86-64" + runs-on: macos-15-intel # github-macos-15-x86_64-4 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-macos-x86_64-${{ inputs.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-windows-python-310: + timeout-minutes: 10 + name: "python3.10 on windows x86-64" + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "3.10" + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-x86_64-${{ inputs.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 + name: "python3.10 on windows x86" + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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-${{ inputs.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 + name: "python3.13 on windows x86-64" + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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-${{ inputs.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 + name: "x86-64 python3.13 on windows aarch64" + runs-on: windows-11-arm + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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-${{ inputs.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 + name: "aarch64 python3.13 on windows aarch64" + runs-on: windows-11-arm + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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-${{ inputs.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 + name: "windows registry" + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-x86_64-${{ inputs.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 + name: "python3.9 via chocolatey" + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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-${{ inputs.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 + name: "python3.9 via pyenv" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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-${{ inputs.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 + name: "python3.13 via setup-python" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.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-${{ inputs.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 + name: "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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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 }}-${{ inputs.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 + name: "python on 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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-${{ inputs.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 + name: "embedded python3.10 on windows x86-64" + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Download binary" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-windows-x86_64-${{ inputs.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 diff --git a/.github/workflows/test-windows-trampolines.yml b/.github/workflows/test-windows-trampolines.yml new file mode 100644 index 000000000..f5c6512ce --- /dev/null +++ b/.github/workflows/test-windows-trampolines.yml @@ -0,0 +1,117 @@ +on: + workflow_call: + +permissions: {} + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUSTUP_MAX_RETRIES: 10 + +jobs: + # Separate jobs for the nightly crate + windows-trampoline-check: + timeout-minutes: 15 + runs-on: windows-latest + name: "check on ${{ matrix.target-arch }}" + strategy: + fail-fast: false + matrix: + target-arch: ["x86_64", "i686", "aarch64"] + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + 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@d850aa816998e5cf15f67a78c7b933f2a5033f8a # v2.63.3 + with: + tool: cargo-bloat + + - name: "rustfmt" + working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline + run: cargo fmt --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 + runs-on: ${{ matrix.runner }} + name: "test on ${{ 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + 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 ../uv-trampoline-builder/trampolines/uv-trampoline-${{ matrix.target-arch }}-console.exe + cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-gui.exe ../uv-trampoline-builder/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 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..43db00f93 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,163 @@ +on: + workflow_call: + +permissions: {} + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + PYTHON_VERSION: "3.12" + RUSTUP_MAX_RETRIES: 10 + RUST_BACKTRACE: 1 + +jobs: + # 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 + runs-on: depot-ubuntu-22.04-16 + name: "cargo test on linux" + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + + - name: "Install Rust toolchain" + run: rustup show + + - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + with: + version: "0.9.24" + + - 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@d850aa816998e5cf15f67a78c7b933f2a5033f8a # v2.63.3 + 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 \ + --cargo-profile fast-build \ + --features python-patch,native-auth,secret-service \ + --workspace \ + --profile ci-linux + + cargo-test-macos: + timeout-minutes: 20 + # 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 on macos" + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + + - name: "Install Rust toolchain" + run: rustup show + + - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + with: + version: "0.9.24" + + - name: "Install required Python versions" + run: uv python install + + - name: "Install cargo nextest" + uses: taiki-e/install-action@d850aa816998e5cf15f67a78c7b933f2a5033f8a # v2.63.3 + 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 \ + --cargo-profile fast-build \ + --no-default-features \ + --features python,python-managed,pypi,git,git-lfs,performance,crates-io,native-auth,apple-native \ + --workspace \ + --profile ci-macos + + cargo-test-windows: + timeout-minutes: 15 + runs-on: depot-windows-2022-16 + name: "cargo test on windows" + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + 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@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + with: + version: "0.9.24" + + - name: "Install required Python versions" + run: uv python install + + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + 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@d850aa816998e5cf15f67a78c7b933f2a5033f8a # v2.63.3 + 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 \ + --cargo-profile fast-build \ + --no-default-features \ + --features python,pypi,python-managed,native-auth,windows-native \ + --workspace \ + --profile ci-windows diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml deleted file mode 100644 index 4115dafac..000000000 --- a/.github/workflows/zizmor.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: zizmor - -on: - push: - branches: ["main"] - pull_request: - branches: ["**"] - -permissions: {} - -jobs: - zizmor: - name: Run zizmor - runs-on: ubuntu-latest - permissions: - security-events: write - steps: - - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - - name: Run zizmor - uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0