mirror of
https://github.com/astral-sh/uv.git
synced 2026-05-06 08:56:53 -04:00
Upgrade reqwest to 0.13 (#18550)
The following user-facing changes are included here: - `aws-lc` is used instead of `ring` for a cryptography backend - Expands our certificate signature algorithm support to include ECDSA_P256_SHA512, ECDSA_P384_SHA512, ECDSA_P521_SHA256, ECDSA_P521_SHA384, and ECDSA_P521_SHA512 - `--native-tls` is deprecated in favor of a new `--system-certs` flag, avoiding confusion with the TLS implementation used (we use `rustls` not `native-tls`, see prior confusion at https://github.com/astral-sh/uv/issues/11595) - NASM is a new build requirement on Windows, it is required by `aws-lc` on x86-64 and i386 - `rustls-platform-verifier` is used instead of `rustls-native-certs` for system certificate verification - On macOS, certificate validation is now delegated to `Security.framework` (`SecTrust`). Performance when using `--system-certs` is improved by avoiding exporting and parsing all the certificates from the keychain at startup. - On Windows, certificate validation is now delegated to `CertGetCertificateChain` and `CertVerifyCertificateChainPolicy` - On Linux, certificate validation should be approximately unchanged - Some previously failing chains may succeed, and some previously accepted chains may fail; generally, this should result in behavior closer matching browsers and other native applications - macOS and Windows may now perform live OCSP fetches for early revocation, which could add latency to some requests - Empty `SSL_CERT_FILE` values are ignored (for consistency with `SSL_CERT_DIR`) The following internal changes are included here: - Certificate loading has been refactored to use a newtype with helper methods - The certificate tests have been rewritten - We use `webpki-root-certs` instead of `webpki-roots`, see https://github.com/astral-sh/uv/pull/17543#discussion_r2820187691 - We request `identity` encoding for range requests, see https://github.com/astral-sh/async_http_range_reader/pull/3#discussion_r2700194798 - Various dependencies (including forks) updates to versions which use reqwest 0.13+ This is a replacement of #17543 with an updated description. See that pull request for prior discussion. I've made the following changes from the initial approach there: - Previously, the `native-tls` TLS implementation was added which included an OpenSSL build. We don't currently use the `native-tls` implementation, but the `--native-tls` flag there was erroneously updated to enable it. - Previously, there was a `--tls-backend` flag to toggle between `native-tls` and `rustls`. Since we currently always use `rustls`, this is deferred to future work (if we need it at all). - Previously, there were unintentional breaking changes to `SSL_CERT_FILE` and `SSL_CERT_DIR` handling, including merging with the base certificates instead of replacing them, dropping support for OpenSSL hash-named certificate files, skipping deduplication of certificates. Here, we retain use of `rustls-native-certs` for loading certificates from the system as it handles these edge cases. Closes https://github.com/astral-sh/uv/issues/17427 --------- Co-authored-by: salmonsd <22984014+salmonsd@users.noreply.github.com>
This commit is contained in:
@@ -307,7 +307,9 @@ jobs:
|
|||||||
echo "CC_aarch64_linux_android=${TOOLCHAIN}/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV"
|
echo "CC_aarch64_linux_android=${TOOLCHAIN}/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV"
|
||||||
echo "CXX_aarch64_linux_android=${TOOLCHAIN}/bin/aarch64-linux-android24-clang++" >> "$GITHUB_ENV"
|
echo "CXX_aarch64_linux_android=${TOOLCHAIN}/bin/aarch64-linux-android24-clang++" >> "$GITHUB_ENV"
|
||||||
echo "AR_aarch64_linux_android=${TOOLCHAIN}/bin/llvm-ar" >> "$GITHUB_ENV"
|
echo "AR_aarch64_linux_android=${TOOLCHAIN}/bin/llvm-ar" >> "$GITHUB_ENV"
|
||||||
|
echo "RANLIB_aarch64_linux_android=${TOOLCHAIN}/bin/llvm-ranlib" >> "$GITHUB_ENV"
|
||||||
echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=${TOOLCHAIN}/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV"
|
echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=${TOOLCHAIN}/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV"
|
||||||
|
echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_RANLIB=${TOOLCHAIN}/bin/llvm-ranlib" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
# NDK 23+ removed libgcc, provide a stub that redirects to libunwind
|
# NDK 23+ removed libgcc, provide a stub that redirects to libunwind
|
||||||
LIBDIR=$(echo "${TOOLCHAIN}"/lib/clang/*/lib/linux/aarch64)
|
LIBDIR=$(echo "${TOOLCHAIN}"/lib/clang/*/lib/linux/aarch64)
|
||||||
|
|||||||
@@ -256,7 +256,14 @@ jobs:
|
|||||||
- name: "Install cargo extensions"
|
- name: "Install cargo extensions"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: scripts/install-cargo-extensions.sh
|
run: scripts/install-cargo-extensions.sh
|
||||||
|
- name: "Install NASM"
|
||||||
|
# NASM is required for x86/x86-64 Windows targets by aws-lc-sys.
|
||||||
|
# On aarch64-pc-windows-msvc, it uses clang-cl instead.
|
||||||
|
# See: https://aws.github.io/aws-lc-rs/requirements/windows.html#build-requirements
|
||||||
|
if: contains(matrix.platform.target, 'x86') || contains(matrix.platform.target, 'i686')
|
||||||
|
run: |
|
||||||
|
winget install NASM.NASM --accept-source-agreements --accept-package-agreements
|
||||||
|
echo "C:\Program Files\NASM" | Out-File -FilePath $env:GITHUB_PATH -Append
|
||||||
# uv
|
# uv
|
||||||
- name: "Build wheels"
|
- name: "Build wheels"
|
||||||
uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1
|
uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1
|
||||||
@@ -266,6 +273,8 @@ jobs:
|
|||||||
args: --release --locked --out dist --features self-update,windows-gui-bin --compatibility pypi
|
args: --release --locked --out dist --features self-update,windows-gui-bin --compatibility pypi
|
||||||
env:
|
env:
|
||||||
CARGO: ${{ github.workspace }}/scripts/cargo.cmd
|
CARGO: ${{ github.workspace }}/scripts/cargo.cmd
|
||||||
|
# Disable prebuilt NASM objects so we always compile assembly from source.
|
||||||
|
AWS_LC_SYS_PREBUILT_NASM: "0"
|
||||||
- name: "Test wheel"
|
- name: "Test wheel"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -351,25 +360,25 @@ jobs:
|
|||||||
manylinux: 2_17
|
manylinux: 2_17
|
||||||
docker-options: -e CARGO
|
docker-options: -e CARGO
|
||||||
args: --release --locked --out dist --features self-update --compatibility pypi
|
args: --release --locked --out dist --features self-update --compatibility pypi
|
||||||
# See: https://github.com/sfackler/rust-openssl/issues/2036#issuecomment-1724324145
|
|
||||||
before-script-linux: |
|
before-script-linux: |
|
||||||
# Install the 32-bit cross target on 64-bit (noop if we're already on 64-bit)
|
# Install the 32-bit cross target on 64-bit (noop if we're already on 64-bit)
|
||||||
rustup target add ${{ matrix.target }}
|
rustup target add ${{ matrix.target }}
|
||||||
# If we're running on rhel centos, install needed packages.
|
# If we're running on rhel centos, install needed packages.
|
||||||
if command -v yum &> /dev/null; then
|
if command -v yum &> /dev/null; then
|
||||||
yum update -y && yum install -y perl-core openssl openssl-devel pkgconfig libatomic
|
yum update -y && yum install -y pkgconfig libatomic
|
||||||
|
|
||||||
# If we're running on i686 we need to symlink libatomic
|
# Install cross build requirements
|
||||||
# in order to build openssl with -latomic flag.
|
if [[ "${{ matrix.target }}" == "i686-unknown-linux-gnu" ]]; then
|
||||||
if [[ ! -d "/usr/lib64" ]]; then
|
yum install -y glibc-devel.i686 libstdc++-devel.i686 libatomic.i686
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Symlink libatomic so the linker can find it with -latomic.
|
||||||
|
if [[ -f "/usr/lib/libatomic.so.1" && ! -f "/usr/lib/libatomic.so" ]]; then
|
||||||
ln -s /usr/lib/libatomic.so.1 /usr/lib/libatomic.so
|
ln -s /usr/lib/libatomic.so.1 /usr/lib/libatomic.so
|
||||||
else
|
|
||||||
# Support cross-compiling from 64-bit to 32-bit
|
|
||||||
yum install -y glibc-devel.i686 libstdc++-devel.i686
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# If we're running on debian-based system.
|
# If we're running on debian-based system.
|
||||||
apt update -y && apt-get install -y libssl-dev openssl pkg-config
|
apt update -y && apt-get install -y pkg-config
|
||||||
fi
|
fi
|
||||||
# Install cargo extensions as a static musl binary so it runs in any container.
|
# Install cargo extensions as a static musl binary so it runs in any container.
|
||||||
scripts/install-cargo-extensions.sh
|
scripts/install-cargo-extensions.sh
|
||||||
@@ -595,8 +604,12 @@ jobs:
|
|||||||
rust-toolchain: ${{ matrix.platform.toolchain || null }}
|
rust-toolchain: ${{ matrix.platform.toolchain || null }}
|
||||||
before-script-linux: |
|
before-script-linux: |
|
||||||
scripts/install-cargo-extensions.sh
|
scripts/install-cargo-extensions.sh
|
||||||
|
# Install the s390x cross target on x86_64
|
||||||
|
rustup target add ${{ matrix.platform.target }}
|
||||||
|
apt-get update && apt-get install -y gcc-s390x-linux-gnu binutils-s390x-linux-gnu
|
||||||
env:
|
env:
|
||||||
CARGO: ${{ github.workspace }}/scripts/cargo.sh
|
CARGO: ${{ github.workspace }}/scripts/cargo.sh
|
||||||
|
|
||||||
- uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1
|
- uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1
|
||||||
name: "Test wheel"
|
name: "Test wheel"
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -53,6 +53,17 @@ On Fedora-based distributions, you can install a C compiler with:
|
|||||||
sudo dnf install gcc
|
sudo dnf install gcc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
On Windows, [NASM](https://www.nasm.us/) is required for building the TLS backend (`aws-lc-sys`). If
|
||||||
|
it is not present, a prebuilt blob provided by `aws-lc-sys` will be used instead. WinGet can be used
|
||||||
|
to install NASM:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
winget install NASM.NASM
|
||||||
|
```
|
||||||
|
|
||||||
|
After installation, add `C:\Program Files\NASM` to your `PATH`. While the prebuilt blob will not be
|
||||||
|
used when NASM is found, you can guarantee this behavior by setting `AWS_LC_SYS_PREBUILT_NASM=0`.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
For running tests, we recommend [nextest](https://nexte.st/).
|
For running tests, we recommend [nextest](https://nexte.st/).
|
||||||
|
|||||||
Generated
+246
-57
@@ -36,9 +36,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ambient-id"
|
name = "ambient-id"
|
||||||
version = "0.0.10"
|
version = "0.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e61320f0a8ca54a235b0e49307b16dcade6eecd441b1f8a8c7ca9204056cb17c"
|
checksum = "c1daa54020e05aa0b163ee10434fff35a0f18d28a1cafa142bd1290e1abe630e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"astral-reqwest-middleware",
|
"astral-reqwest-middleware",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@@ -114,7 +114,7 @@ version = "1.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -125,7 +125,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"once_cell_polyfill",
|
"once_cell_polyfill",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -256,9 +256,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "astral-reqwest-middleware"
|
name = "astral-reqwest-middleware"
|
||||||
version = "0.4.2"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "638d02e24aeb92f9537897cd1ff82e2bc98fd9ac9575a503e27bb07cdf64d4d7"
|
checksum = "98e1c6be25cfbf1bb4fea1a9da51bc05d3259a9062df4e53f54e5607895e33c9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -271,9 +271,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "astral-reqwest-retry"
|
name = "astral-reqwest-retry"
|
||||||
version = "0.8.0"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ab210f6cdf8fd3254d47e5ee27ce60ed34a428ff71b4ae9477b1c84b49498c"
|
checksum = "48c76a42c052d7a95249b90b83d44e8f1bbde7c8e08dbed50d49c58321815da3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"astral-reqwest-middleware",
|
"astral-reqwest-middleware",
|
||||||
@@ -326,18 +326,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "astral_async_http_range_reader"
|
name = "astral_async_http_range_reader"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ddaca0fbbf0d91103cca7c7611790c65f6eff1d456f7fe6bf565d436dc9b8f3"
|
checksum = "7ab9a05148c7b3e85c17806fef4b2889c44f1a60575c5da9e9be80ce13227185"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"astral-reqwest-middleware",
|
"astral-reqwest-middleware",
|
||||||
"bisection",
|
"bisection",
|
||||||
"futures",
|
"futures",
|
||||||
"http-content-range",
|
"http-content-range",
|
||||||
"itertools 0.13.0",
|
"itertools 0.14.0",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@@ -436,10 +436,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axoasset"
|
name = "aws-lc-rs"
|
||||||
version = "1.5.0"
|
version = "1.16.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "448205969ebf1eec16088881b9d309e90e52ceed1bfa92e7efbfb00f3b10982a"
|
checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-sys",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aws-lc-sys"
|
||||||
|
version = "0.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cmake",
|
||||||
|
"dunce",
|
||||||
|
"fs_extra",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axoasset"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1be1b9c2739b635e04c7bbcde9e89dd5e874b9e86e28f1b41c44eb830635d83e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"camino",
|
"camino",
|
||||||
"image",
|
"image",
|
||||||
@@ -478,9 +500,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axoupdater"
|
name = "axoupdater"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc482a1926df098f4e3806b834f3fe73a1ab54b24ab0ac481f72de479af5e982"
|
checksum = "0ab66f118bab79524a27139b7341cdf1c4f839c6274ef89a6d8fb4365cb218cf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axoasset",
|
"axoasset",
|
||||||
"axoprocess",
|
"axoprocess",
|
||||||
@@ -736,6 +758,12 @@ dependencies = [
|
|||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cesu8"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -866,6 +894,15 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmake"
|
||||||
|
version = "0.1.57"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codspeed"
|
name = "codspeed"
|
||||||
version = "4.4.1"
|
version = "4.4.1"
|
||||||
@@ -945,7 +982,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "combine"
|
||||||
|
version = "4.6.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1313,7 +1360,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
"option-ext",
|
||||||
"redox_users",
|
"redox_users",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1492,7 +1539,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1668,6 +1715,12 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs_extra"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.32"
|
version = "0.3.32"
|
||||||
@@ -2079,12 +2132,10 @@ dependencies = [
|
|||||||
"hyper",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-native-certs",
|
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"webpki-roots",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2357,7 +2408,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2391,15 +2442,6 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@@ -2427,7 +2469,7 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2456,6 +2498,50 @@ dependencies = [
|
|||||||
"jiff-tzdb",
|
"jiff-tzdb",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni"
|
||||||
|
version = "0.21.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
||||||
|
dependencies = [
|
||||||
|
"cesu8",
|
||||||
|
"cfg-if",
|
||||||
|
"combine",
|
||||||
|
"jni-sys 0.3.1",
|
||||||
|
"log",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"walkdir",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-sys"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
|
||||||
|
dependencies = [
|
||||||
|
"jni-sys 0.4.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-sys"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
|
||||||
|
dependencies = [
|
||||||
|
"jni-sys-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-sys-macros"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.34"
|
version = "0.1.34"
|
||||||
@@ -2498,7 +2584,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8cfc352a66ba903c23239ef51e809508b6fc2b0f90e3476ac7a9ff47e863ae95"
|
checksum = "8cfc352a66ba903c23239ef51e809508b6fc2b0f90e3476ac7a9ff47e863ae95"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2769,7 +2855,7 @@ version = "0.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08"
|
checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2857,7 +2943,7 @@ version = "0.50.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3553,6 +3639,7 @@ version = "0.11.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
|
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
"bytes",
|
"bytes",
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"lru-slab",
|
"lru-slab",
|
||||||
@@ -3579,7 +3666,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3806,9 +3893,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqsign"
|
name = "reqsign"
|
||||||
version = "0.18.1"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea386ba750000b6e59f760a08bdcca9461809b95e6f8f209ce5724056802824f"
|
checksum = "af8fa9a6948938319944b0f5399b1de4324767ac35c8bdbf991b3e71197b1087"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"reqsign-aws-v4",
|
"reqsign-aws-v4",
|
||||||
"reqsign-command-execute-tokio",
|
"reqsign-command-execute-tokio",
|
||||||
@@ -3907,9 +3994,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqsign-http-send-reqwest"
|
name = "reqsign-http-send-reqwest"
|
||||||
version = "2.0.1"
|
version = "3.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46186bce769674f9200ad01af6f2ca42de3e819ddc002fff1edae135bfb6cd9c"
|
checksum = "c76a1aafda3c789146d5df362fb2e760051c38e0957f863c0bec8713d3a35833"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3924,11 +4011,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.22"
|
version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
|
checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-compression",
|
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@@ -3948,8 +4034,8 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn",
|
"quinn",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-native-certs",
|
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
"rustls-platform-verifier",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
@@ -3965,7 +4051,6 @@ dependencies = [
|
|||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-streams",
|
"wasm-streams",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"webpki-roots",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4148,7 +4233,7 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4157,8 +4242,8 @@ version = "0.23.37"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
|
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ring",
|
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"rustls-webpki",
|
"rustls-webpki",
|
||||||
"subtle",
|
"subtle",
|
||||||
@@ -4187,12 +4272,40 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation 0.10.1",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"jni",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"rustls-platform-verifier-android",
|
||||||
|
"rustls-webpki",
|
||||||
|
"security-framework",
|
||||||
|
"security-framework-sys",
|
||||||
|
"webpki-root-certs",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier-android"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.103.9"
|
version = "0.103.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
|
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"untrusted",
|
"untrusted",
|
||||||
@@ -4656,7 +4769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4870,6 +4983,7 @@ version = "0.3.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96374855068f47402c3121c6eed88d29cb1de8f3ab27090e273e420bdabcf050"
|
checksum = "96374855068f47402c3121c6eed88d29cb1de8f3ab27090e273e420bdabcf050"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4880,10 +4994,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.4.1",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5299,13 +5413,18 @@ version = "0.6.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
|
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-compression",
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
"iri-string",
|
"iri-string",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@@ -5495,7 +5614,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"memoffset",
|
"memoffset",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6131,8 +6250,11 @@ dependencies = [
|
|||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"temp-env",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -6161,6 +6283,7 @@ dependencies = [
|
|||||||
"uv-torch",
|
"uv-torch",
|
||||||
"uv-version",
|
"uv-version",
|
||||||
"uv-warnings",
|
"uv-warnings",
|
||||||
|
"webpki-root-certs",
|
||||||
"wiremock",
|
"wiremock",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -7544,9 +7667,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-streams"
|
name = "wasm-streams"
|
||||||
version = "0.4.2"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
|
checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -7602,10 +7725,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-root-certs"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed"
|
checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
@@ -7651,7 +7774,7 @@ version = "0.1.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7773,6 +7896,15 @@ dependencies = [
|
|||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.45.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -7809,6 +7941,21 @@ dependencies = [
|
|||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.42.2",
|
||||||
|
"windows_aarch64_msvc 0.42.2",
|
||||||
|
"windows_i686_gnu 0.42.2",
|
||||||
|
"windows_i686_msvc 0.42.2",
|
||||||
|
"windows_x86_64_gnu 0.42.2",
|
||||||
|
"windows_x86_64_gnullvm 0.42.2",
|
||||||
|
"windows_x86_64_msvc 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -7860,6 +8007,12 @@ dependencies = [
|
|||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -7872,6 +8025,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -7884,6 +8043,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -7908,6 +8073,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -7920,6 +8091,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -7932,6 +8109,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -7944,6 +8127,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|||||||
+17
-12
@@ -88,8 +88,8 @@ uv-warnings = { version = "0.0.32", path = "crates/uv-warnings" }
|
|||||||
uv-windows = { version = "0.0.32", path = "crates/uv-windows" }
|
uv-windows = { version = "0.0.32", path = "crates/uv-windows" }
|
||||||
uv-workspace = { version = "0.0.32", path = "crates/uv-workspace" }
|
uv-workspace = { version = "0.0.32", path = "crates/uv-workspace" }
|
||||||
|
|
||||||
ambient-id = { version = "0.0.10", default-features = false, features = [
|
ambient-id = { version = "0.0.11", default-features = false, features = [
|
||||||
"astral-reqwest-middleware",
|
"reqwest-middleware",
|
||||||
] }
|
] }
|
||||||
anstream = { version = "1.0.0" }
|
anstream = { version = "1.0.0" }
|
||||||
anyhow = { version = "1.0.89" }
|
anyhow = { version = "1.0.89" }
|
||||||
@@ -104,7 +104,7 @@ async-compression = { version = "0.4.12", features = [
|
|||||||
"zstd",
|
"zstd",
|
||||||
] }
|
] }
|
||||||
async-trait = { version = "0.1.82" }
|
async-trait = { version = "0.1.82" }
|
||||||
async_http_range_reader = { version = "0.9.1", package = "astral_async_http_range_reader" }
|
async_http_range_reader = { version = "0.10.0", package = "astral_async_http_range_reader" }
|
||||||
async_zip = { version = "0.0.17", package = "astral_async_zip", features = [
|
async_zip = { version = "0.0.17", package = "astral_async_zip", features = [
|
||||||
"bzip2",
|
"bzip2",
|
||||||
"deflate",
|
"deflate",
|
||||||
@@ -113,7 +113,7 @@ async_zip = { version = "0.0.17", package = "astral_async_zip", features = [
|
|||||||
"xz",
|
"xz",
|
||||||
"zstd",
|
"zstd",
|
||||||
] }
|
] }
|
||||||
axoupdater = { version = "0.9.0", default-features = false }
|
axoupdater = { version = "0.10.0", default-features = false }
|
||||||
backon = { version = "1.3.0" }
|
backon = { version = "1.3.0" }
|
||||||
base64 = { version = "0.22.1" }
|
base64 = { version = "0.22.1" }
|
||||||
bitflags = { version = "2.6.0" }
|
bitflags = { version = "2.6.0" }
|
||||||
@@ -196,35 +196,39 @@ regex-automata = { version = "0.4.8", default-features = false, features = [
|
|||||||
"std",
|
"std",
|
||||||
"syntax",
|
"syntax",
|
||||||
] }
|
] }
|
||||||
reqsign = { version = "0.18.1", features = [
|
reqsign = { version = "0.19.0", features = [
|
||||||
"aws",
|
"aws",
|
||||||
"google",
|
"google",
|
||||||
"default-context",
|
"default-context",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
reqwest = { version = "0.12.22", default-features = false, features = [
|
reqwest = { version = "0.13.1", default-features = false, features = [
|
||||||
"json",
|
"json",
|
||||||
"gzip",
|
"gzip",
|
||||||
"deflate",
|
"deflate",
|
||||||
"zstd",
|
"zstd",
|
||||||
"stream",
|
"stream",
|
||||||
"system-proxy",
|
"system-proxy",
|
||||||
"rustls-tls",
|
"rustls",
|
||||||
"rustls-tls-native-roots",
|
|
||||||
"socks",
|
"socks",
|
||||||
"multipart",
|
"multipart",
|
||||||
"http2",
|
"http2",
|
||||||
"blocking",
|
"blocking",
|
||||||
|
"query",
|
||||||
|
"form",
|
||||||
] }
|
] }
|
||||||
reqwest-middleware = { version = "0.4.2", package = "astral-reqwest-middleware", features = [
|
reqwest-middleware = { version = "0.5.1", package = "astral-reqwest-middleware", features = [
|
||||||
"multipart",
|
"multipart",
|
||||||
|
"query",
|
||||||
] }
|
] }
|
||||||
reqwest-retry = { version = "0.8.0", package = "astral-reqwest-retry", features = [
|
reqwest-retry = { version = "0.9.1", package = "astral-reqwest-retry", features = [
|
||||||
"tracing",
|
"tracing",
|
||||||
] }
|
] }
|
||||||
rkyv = { version = "0.8.14", features = ["bytecheck"] }
|
rkyv = { version = "0.8.14", features = ["bytecheck"] }
|
||||||
rmp-serde = { version = "1.3.0" }
|
rmp-serde = { version = "1.3.0" }
|
||||||
rust-netrc = { version = "0.1.2" }
|
rust-netrc = { version = "0.1.2" }
|
||||||
rustc-hash = { version = "2.0.0" }
|
rustc-hash = { version = "2.0.0" }
|
||||||
|
rustls-native-certs = { version = "0.8.3" }
|
||||||
|
rustls-pki-types = { version = "1.14.0" }
|
||||||
rustix = { version = "1.0.0", default-features = false, features = [
|
rustix = { version = "1.0.0", default-features = false, features = [
|
||||||
"fs",
|
"fs",
|
||||||
"std",
|
"std",
|
||||||
@@ -274,6 +278,7 @@ url = { version = "2.5.2", features = ["serde"] }
|
|||||||
uuid = { version = "1.16.0" }
|
uuid = { version = "1.16.0" }
|
||||||
version-ranges = { version = "0.1.3", package = "astral-version-ranges" }
|
version-ranges = { version = "0.1.3", package = "astral-version-ranges" }
|
||||||
walkdir = { version = "2.5.0" }
|
walkdir = { version = "2.5.0" }
|
||||||
|
webpki-root-certs = { version = "1" }
|
||||||
which = { version = "8.0.0", features = ["regex"] }
|
which = { version = "8.0.0", features = ["regex"] }
|
||||||
windows = { version = "0.61.0", features = [
|
windows = { version = "0.61.0", features = [
|
||||||
"std",
|
"std",
|
||||||
@@ -325,9 +330,9 @@ rcgen = { version = "0.14.5", features = [
|
|||||||
"pem",
|
"pem",
|
||||||
"ring",
|
"ring",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
rustls = { version = "0.23.29", default-features = false }
|
rustls = { version = "0.23.36", default-features = false }
|
||||||
similar = { version = "2.6.0" }
|
similar = { version = "2.6.0" }
|
||||||
temp-env = { version = "0.3.6" }
|
temp-env = { version = "0.3.6", features = ["async_closure"] }
|
||||||
test-case = { version = "3.3.1" }
|
test-case = { version = "3.3.1" }
|
||||||
test-log = { version = "0.2.16", features = [
|
test-log = { version = "0.2.16", features = [
|
||||||
"trace",
|
"trace",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ doc-valid-idents = [
|
|||||||
"UV_LOCKED",
|
"UV_LOCKED",
|
||||||
"UV_MANAGED_PYTHON",
|
"UV_MANAGED_PYTHON",
|
||||||
"UV_NATIVE_TLS",
|
"UV_NATIVE_TLS",
|
||||||
|
"UV_SYSTEM_CERTS",
|
||||||
"UV_NO_DEV",
|
"UV_NO_DEV",
|
||||||
"UV_NO_EDITABLE",
|
"UV_NO_EDITABLE",
|
||||||
"UV_NO_ENV_FILE",
|
"UV_NO_ENV_FILE",
|
||||||
|
|||||||
@@ -34,8 +34,7 @@ tracing = { workspace = true }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = { workspace = true }
|
insta = { workspace = true }
|
||||||
reqwest = { workspace = true, default-features = false, features = [
|
reqwest = { workspace = true, default-features = false, features = [
|
||||||
"rustls-tls",
|
"rustls",
|
||||||
"rustls-tls-native-roots",
|
|
||||||
] }
|
] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
|||||||
@@ -236,20 +236,32 @@ pub struct GlobalArgs {
|
|||||||
)]
|
)]
|
||||||
pub color: Option<ColorChoice>,
|
pub color: Option<ColorChoice>,
|
||||||
|
|
||||||
/// Whether to load TLS certificates from the platform's native store [env: UV_NATIVE_TLS=]
|
/// Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
/// UV_NATIVE_TLS=]
|
||||||
///
|
///
|
||||||
/// By default, uv loads certificates from the bundled `webpki-roots` crate. The
|
/// By default, uv uses bundled Mozilla root certificates. When enabled, this flag loads
|
||||||
/// `webpki-roots` are a reliable set of trust roots from Mozilla, and including them in uv
|
/// certificates from the platform's native certificate store instead.
|
||||||
/// improves portability and performance (especially on macOS).
|
///
|
||||||
|
/// This is equivalent to `--system-certs`.
|
||||||
|
#[arg(global = true, long, value_parser = clap::builder::BoolishValueParser::new(), overrides_with_all = ["no_native_tls", "system_certs", "no_system_certs"], hide = true)]
|
||||||
|
pub native_tls: bool,
|
||||||
|
|
||||||
|
#[arg(global = true, long, overrides_with_all = ["native_tls", "system_certs", "no_system_certs"], hide = true)]
|
||||||
|
pub no_native_tls: bool,
|
||||||
|
|
||||||
|
/// Whether to load TLS certificates from the platform's native certificate store [env: UV_SYSTEM_CERTS=]
|
||||||
|
///
|
||||||
|
/// By default, uv uses bundled Mozilla root certificates, which improves portability and
|
||||||
|
/// performance (especially on macOS).
|
||||||
///
|
///
|
||||||
/// However, in some cases, you may want to use the platform's native certificate store,
|
/// However, in some cases, you may want to use the platform's native certificate store,
|
||||||
/// especially if you're relying on a corporate trust root (e.g., for a mandatory proxy) that's
|
/// especially if you're relying on a corporate trust root (e.g., for a mandatory proxy) that's
|
||||||
/// included in your system's certificate store.
|
/// included in your system's certificate store.
|
||||||
#[arg(global = true, long, value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_native_tls"))]
|
#[arg(global = true, long, value_parser = clap::builder::BoolishValueParser::new(), overrides_with_all = ["no_system_certs", "native_tls", "no_native_tls"])]
|
||||||
pub native_tls: bool,
|
pub system_certs: bool,
|
||||||
|
|
||||||
#[arg(global = true, long, overrides_with("native_tls"), hide = true)]
|
#[arg(global = true, long, overrides_with_all = ["system_certs", "native_tls", "no_native_tls"], hide = true)]
|
||||||
pub no_native_tls: bool,
|
pub no_system_certs: bool,
|
||||||
|
|
||||||
/// Disable network access [env: UV_OFFLINE=]
|
/// Disable network access [env: UV_OFFLINE=]
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ repository = { workspace = true }
|
|||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
license = { workspace = true }
|
license = { workspace = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
test-pypi = []
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
@@ -57,6 +60,8 @@ reqwest-retry = { workspace = true }
|
|||||||
rkyv = { workspace = true }
|
rkyv = { workspace = true }
|
||||||
rmp-serde = { workspace = true }
|
rmp-serde = { workspace = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
|
rustls-native-certs = { workspace = true }
|
||||||
|
rustls-pki-types = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
uv-platform = { workspace = true }
|
uv-platform = { workspace = true }
|
||||||
@@ -65,6 +70,7 @@ tokio = { workspace = true }
|
|||||||
tokio-util = { workspace = true }
|
tokio-util = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
webpki-root-certs = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
@@ -75,6 +81,7 @@ insta = { workspace = true }
|
|||||||
rcgen = { workspace = true }
|
rcgen = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
rustls = { workspace = true }
|
rustls = { workspace = true }
|
||||||
|
temp-env = { workspace = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tokio-rustls = { workspace = true }
|
tokio-rustls = { workspace = true }
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ use std::error::Error;
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, SystemTime, SystemTimeError};
|
use std::time::{Duration, SystemTime, SystemTimeError};
|
||||||
use std::{env, io, iter};
|
use std::{env, io, iter};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
|
||||||
use http::{
|
use http::{
|
||||||
HeaderMap, HeaderName, HeaderValue, Method, StatusCode,
|
HeaderMap, HeaderName, HeaderValue, Method, StatusCode,
|
||||||
header::{
|
header::{
|
||||||
@@ -16,7 +16,9 @@ use http::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use reqwest::{Client, ClientBuilder, IntoUrl, NoProxy, Proxy, Request, Response, multipart};
|
use reqwest::{
|
||||||
|
Certificate, Client, ClientBuilder, IntoUrl, NoProxy, Proxy, Request, Response, multipart,
|
||||||
|
};
|
||||||
use reqwest_middleware::{ClientWithMiddleware, Middleware};
|
use reqwest_middleware::{ClientWithMiddleware, Middleware};
|
||||||
use reqwest_retry::policies::ExponentialBackoff;
|
use reqwest_retry::policies::ExponentialBackoff;
|
||||||
use reqwest_retry::{
|
use reqwest_retry::{
|
||||||
@@ -33,7 +35,7 @@ use url::Url;
|
|||||||
use uv_auth::{AuthMiddleware, Credentials, CredentialsCache, Indexes, PyxTokenStore};
|
use uv_auth::{AuthMiddleware, Credentials, CredentialsCache, Indexes, PyxTokenStore};
|
||||||
use uv_configuration::ProxyUrlKind;
|
use uv_configuration::ProxyUrlKind;
|
||||||
use uv_configuration::{KeyringProviderType, ProxyUrl, TrustedHost};
|
use uv_configuration::{KeyringProviderType, ProxyUrl, TrustedHost};
|
||||||
use uv_fs::Simplified;
|
|
||||||
use uv_pep508::MarkerEnvironment;
|
use uv_pep508::MarkerEnvironment;
|
||||||
use uv_platform_tags::Platform;
|
use uv_platform_tags::Platform;
|
||||||
use uv_preview::Preview;
|
use uv_preview::Preview;
|
||||||
@@ -45,7 +47,7 @@ use uv_warnings::warn_user_once;
|
|||||||
|
|
||||||
use crate::linehaul::LineHaul;
|
use crate::linehaul::LineHaul;
|
||||||
use crate::middleware::OfflineMiddleware;
|
use crate::middleware::OfflineMiddleware;
|
||||||
use crate::tls::read_identity;
|
use crate::tls::{Certificates, read_identity};
|
||||||
use crate::{Connectivity, WrappedReqwestError};
|
use crate::{Connectivity, WrappedReqwestError};
|
||||||
|
|
||||||
pub const DEFAULT_RETRIES: u32 = 3;
|
pub const DEFAULT_RETRIES: u32 = 3;
|
||||||
@@ -88,8 +90,7 @@ pub struct BaseClientBuilder<'a> {
|
|||||||
keyring: KeyringProviderType,
|
keyring: KeyringProviderType,
|
||||||
preview: Preview,
|
preview: Preview,
|
||||||
allow_insecure_host: Vec<TrustedHost>,
|
allow_insecure_host: Vec<TrustedHost>,
|
||||||
native_tls: bool,
|
system_certs: bool,
|
||||||
built_in_root_certs: bool,
|
|
||||||
retries: u32,
|
retries: u32,
|
||||||
pub connectivity: Connectivity,
|
pub connectivity: Connectivity,
|
||||||
markers: Option<&'a MarkerEnvironment>,
|
markers: Option<&'a MarkerEnvironment>,
|
||||||
@@ -161,8 +162,7 @@ impl Default for BaseClientBuilder<'_> {
|
|||||||
keyring: KeyringProviderType::default(),
|
keyring: KeyringProviderType::default(),
|
||||||
preview: Preview::default(),
|
preview: Preview::default(),
|
||||||
allow_insecure_host: vec![],
|
allow_insecure_host: vec![],
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
built_in_root_certs: false,
|
|
||||||
connectivity: Connectivity::Online,
|
connectivity: Connectivity::Online,
|
||||||
retries: DEFAULT_RETRIES,
|
retries: DEFAULT_RETRIES,
|
||||||
markers: None,
|
markers: None,
|
||||||
@@ -190,7 +190,7 @@ impl Default for BaseClientBuilder<'_> {
|
|||||||
impl<'a> BaseClientBuilder<'a> {
|
impl<'a> BaseClientBuilder<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
connectivity: Connectivity,
|
connectivity: Connectivity,
|
||||||
native_tls: bool,
|
system_certs: bool,
|
||||||
allow_insecure_host: Vec<TrustedHost>,
|
allow_insecure_host: Vec<TrustedHost>,
|
||||||
preview: Preview,
|
preview: Preview,
|
||||||
read_timeout: Duration,
|
read_timeout: Duration,
|
||||||
@@ -200,7 +200,7 @@ impl<'a> BaseClientBuilder<'a> {
|
|||||||
Self {
|
Self {
|
||||||
preview,
|
preview,
|
||||||
allow_insecure_host,
|
allow_insecure_host,
|
||||||
native_tls,
|
system_certs,
|
||||||
retries,
|
retries,
|
||||||
connectivity,
|
connectivity,
|
||||||
read_timeout,
|
read_timeout,
|
||||||
@@ -251,14 +251,13 @@ impl<'a> BaseClientBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn native_tls(mut self, native_tls: bool) -> Self {
|
pub fn system_certs(&self) -> bool {
|
||||||
self.native_tls = native_tls;
|
self.system_certs
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn built_in_root_certs(mut self, built_in_root_certs: bool) -> Self {
|
pub fn with_system_certs(mut self, system_certs: bool) -> Self {
|
||||||
self.built_in_root_certs = built_in_root_certs;
|
self.system_certs = system_certs;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,10 +371,6 @@ impl<'a> BaseClientBuilder<'a> {
|
|||||||
self.credentials_cache.store_credentials(url, credentials);
|
self.credentials_cache.store_credentials(url, credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_native_tls(&self) -> bool {
|
|
||||||
self.native_tls
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_offline(&self) -> bool {
|
pub fn is_offline(&self) -> bool {
|
||||||
matches!(self.connectivity, Connectivity::Offline)
|
matches!(self.connectivity, Connectivity::Offline)
|
||||||
}
|
}
|
||||||
@@ -479,93 +474,15 @@ impl<'a> BaseClientBuilder<'a> {
|
|||||||
let _ = write!(user_agent_string, " {output}");
|
let _ = write!(user_agent_string, " {output}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks for the presence of `SSL_CERT_FILE`.
|
// Load custom CA certificates from `SSL_CERT_FILE` and `SSL_CERT_DIR`.
|
||||||
// Certificate loading support is delegated to `rustls-native-certs`.
|
let custom_certs = Certificates::from_env().map(|certs| certs.to_reqwest_certs());
|
||||||
// See https://github.com/rustls/rustls-native-certs/blob/813790a297ad4399efe70a8e5264ca1b420acbec/src/lib.rs#L118-L125
|
|
||||||
let ssl_cert_file_exists = env::var_os(EnvVars::SSL_CERT_FILE).is_some_and(|path| {
|
|
||||||
let path = Path::new(&path);
|
|
||||||
match path.metadata() {
|
|
||||||
Ok(metadata) if metadata.is_file() => true,
|
|
||||||
Ok(_) => {
|
|
||||||
warn_user_once!(
|
|
||||||
"Ignoring invalid `SSL_CERT_FILE`. Path is not a file: {}.",
|
|
||||||
path.simplified_display().cyan()
|
|
||||||
);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
|
||||||
warn_user_once!(
|
|
||||||
"Ignoring invalid `SSL_CERT_FILE`. Path does not exist: {}.",
|
|
||||||
path.simplified_display().cyan()
|
|
||||||
);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn_user_once!(
|
|
||||||
"Ignoring invalid `SSL_CERT_FILE`. Path is not accessible: {} ({err}).",
|
|
||||||
path.simplified_display().cyan()
|
|
||||||
);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Checks for the presence of `SSL_CERT_DIR`.
|
|
||||||
// Certificate loading support is delegated to `rustls-native-certs`.
|
|
||||||
// See https://github.com/rustls/rustls-native-certs/blob/813790a297ad4399efe70a8e5264ca1b420acbec/src/lib.rs#L118-L125
|
|
||||||
let ssl_cert_dir_exists = env::var_os(EnvVars::SSL_CERT_DIR)
|
|
||||||
.filter(|v| !v.is_empty())
|
|
||||||
.is_some_and(|dirs| {
|
|
||||||
// Parse `SSL_CERT_DIR`, with support for multiple entries using
|
|
||||||
// a platform-specific delimiter (`:` on Unix, `;` on Windows)
|
|
||||||
let (existing, missing): (Vec<_>, Vec<_>) =
|
|
||||||
env::split_paths(&dirs).partition(|p| p.exists());
|
|
||||||
|
|
||||||
if existing.is_empty() {
|
|
||||||
let end_note = if missing.len() == 1 {
|
|
||||||
"The directory does not exist."
|
|
||||||
} else {
|
|
||||||
"The entries do not exist."
|
|
||||||
};
|
|
||||||
warn_user_once!(
|
|
||||||
"Ignoring invalid `SSL_CERT_DIR`. {end_note}: {}.",
|
|
||||||
missing
|
|
||||||
.iter()
|
|
||||||
.map(Simplified::simplified_display)
|
|
||||||
.join(", ")
|
|
||||||
.cyan()
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warn on any missing entries
|
|
||||||
if !missing.is_empty() {
|
|
||||||
let end_note = if missing.len() == 1 {
|
|
||||||
"The following directory does not exist:"
|
|
||||||
} else {
|
|
||||||
"The following entries do not exist:"
|
|
||||||
};
|
|
||||||
warn_user_once!(
|
|
||||||
"Invalid entries in `SSL_CERT_DIR`. {end_note}: {}.",
|
|
||||||
missing
|
|
||||||
.iter()
|
|
||||||
.map(Simplified::simplified_display)
|
|
||||||
.join(", ")
|
|
||||||
.cyan()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proceed while ignoring missing entries
|
|
||||||
true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a secure client that validates certificates.
|
// Create a secure client that validates certificates.
|
||||||
let raw_client = self.create_client(
|
let raw_client = self.create_client(
|
||||||
&user_agent_string,
|
&user_agent_string,
|
||||||
read_timeout,
|
read_timeout,
|
||||||
connect_timeout,
|
connect_timeout,
|
||||||
ssl_cert_file_exists,
|
custom_certs.clone(),
|
||||||
ssl_cert_dir_exists,
|
|
||||||
Security::Secure,
|
Security::Secure,
|
||||||
self.redirect_policy,
|
self.redirect_policy,
|
||||||
);
|
);
|
||||||
@@ -575,8 +492,7 @@ impl<'a> BaseClientBuilder<'a> {
|
|||||||
&user_agent_string,
|
&user_agent_string,
|
||||||
read_timeout,
|
read_timeout,
|
||||||
connect_timeout,
|
connect_timeout,
|
||||||
ssl_cert_file_exists,
|
custom_certs,
|
||||||
ssl_cert_dir_exists,
|
|
||||||
Security::Insecure,
|
Security::Insecure,
|
||||||
self.redirect_policy,
|
self.redirect_policy,
|
||||||
);
|
);
|
||||||
@@ -589,8 +505,7 @@ impl<'a> BaseClientBuilder<'a> {
|
|||||||
user_agent: &str,
|
user_agent: &str,
|
||||||
read_timeout: Duration,
|
read_timeout: Duration,
|
||||||
connect_timeout: Duration,
|
connect_timeout: Duration,
|
||||||
ssl_cert_file_exists: bool,
|
custom_certs: Option<Vec<Certificate>>,
|
||||||
ssl_cert_dir_exists: bool,
|
|
||||||
security: Security,
|
security: Security,
|
||||||
redirect_policy: RedirectPolicy,
|
redirect_policy: RedirectPolicy,
|
||||||
) -> Client {
|
) -> Client {
|
||||||
@@ -601,7 +516,6 @@ impl<'a> BaseClientBuilder<'a> {
|
|||||||
.pool_max_idle_per_host(20)
|
.pool_max_idle_per_host(20)
|
||||||
.read_timeout(read_timeout)
|
.read_timeout(read_timeout)
|
||||||
.connect_timeout(connect_timeout)
|
.connect_timeout(connect_timeout)
|
||||||
.tls_built_in_root_certs(self.built_in_root_certs)
|
|
||||||
.redirect(redirect_policy.reqwest_policy());
|
.redirect(redirect_policy.reqwest_policy());
|
||||||
|
|
||||||
// If necessary, accept invalid certificates.
|
// If necessary, accept invalid certificates.
|
||||||
@@ -610,10 +524,18 @@ impl<'a> BaseClientBuilder<'a> {
|
|||||||
Security::Insecure => client_builder.danger_accept_invalid_certs(true),
|
Security::Insecure => client_builder.danger_accept_invalid_certs(true),
|
||||||
};
|
};
|
||||||
|
|
||||||
let client_builder = if self.native_tls || ssl_cert_file_exists || ssl_cert_dir_exists {
|
let client_builder = client_builder.tls_backend_rustls();
|
||||||
client_builder.tls_built_in_native_certs(true)
|
|
||||||
|
// Configure the certificate source.
|
||||||
|
//
|
||||||
|
// `SSL_CERT_FILE` and `SSL_CERT_DIR` override the default certificate source when they
|
||||||
|
// contain valid certificates.
|
||||||
|
let client_builder = if let Some(custom_certs) = custom_certs {
|
||||||
|
client_builder.tls_certs_only(custom_certs)
|
||||||
|
} else if self.system_certs {
|
||||||
|
client_builder
|
||||||
} else {
|
} else {
|
||||||
client_builder.tls_built_in_webpki_certs(true)
|
client_builder.tls_certs_only(Certificates::webpki_roots().to_reqwest_certs())
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configure mTLS.
|
// Configure mTLS.
|
||||||
|
|||||||
@@ -99,14 +99,6 @@ impl<'a> RegistryClientBuilder<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn built_in_root_certs(mut self, built_in_root_certs: bool) -> Self {
|
|
||||||
self.base_client_builder = self
|
|
||||||
.base_client_builder
|
|
||||||
.built_in_root_certs(built_in_root_certs);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn cache(mut self, cache: Cache) -> Self {
|
pub fn cache(mut self, cache: Cache) -> Self {
|
||||||
self.cache = cache;
|
self.cache = cache;
|
||||||
@@ -1147,7 +1139,18 @@ impl RegistryClient {
|
|||||||
if let Some(authorization) = req.headers().get("authorization") {
|
if let Some(authorization) = req.headers().get("authorization") {
|
||||||
headers.append("authorization", authorization.clone());
|
headers.append("authorization", authorization.clone());
|
||||||
}
|
}
|
||||||
|
// These range requests need the bytes from the wheel archive itself.
|
||||||
|
// After `reqwest` moved decompression to tower-http[1], this path could receive
|
||||||
|
// transparently decompressed responses. That breaks the byte offsets used by
|
||||||
|
// `AsyncHttpRangeReader` and results in us incorrectly trying to double-decompress gzip streams[2].
|
||||||
|
// We request with `Accept: identity` so that the range reader always sees the compressed wheel bytes.
|
||||||
|
//
|
||||||
|
// [1]: https://github.com/seanmonstar/reqwest/pull/2840
|
||||||
|
// [2]: https://github.com/astral-sh/async_http_range_reader/pull/3#discussion_r2700194798
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::ACCEPT_ENCODING,
|
||||||
|
reqwest::header::HeaderValue::from_static("identity"),
|
||||||
|
);
|
||||||
// This response callback is special, we actually make a number of subsequent requests to
|
// This response callback is special, we actually make a number of subsequent requests to
|
||||||
// fetch the file from the remote zip.
|
// fetch the file from the remote zip.
|
||||||
let read_metadata_range_request = |response: Response| {
|
let read_metadata_range_request = |response: Response| {
|
||||||
|
|||||||
+324
-5
@@ -1,17 +1,253 @@
|
|||||||
use reqwest::Identity;
|
use std::env;
|
||||||
use std::ffi::OsStr;
|
use std::io::{self, Read};
|
||||||
use std::io::Read;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use reqwest::{Certificate, Identity};
|
||||||
|
use rustls_native_certs::{CertificateResult, load_certs_from_paths};
|
||||||
|
use rustls_pki_types::CertificateDer;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use uv_fs::Simplified;
|
||||||
|
use uv_static::EnvVars;
|
||||||
|
use uv_warnings::warn_user_once;
|
||||||
|
|
||||||
|
/// A collection of TLS certificates in DER form.
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub(crate) struct Certificates(Vec<CertificateDer<'static>>);
|
||||||
|
|
||||||
|
impl Certificates {
|
||||||
|
/// Load the bundled Mozilla root certificates.
|
||||||
|
///
|
||||||
|
/// We use `webpki-root-certs` (which gives us [`CertificateDer`] values) rather than the more
|
||||||
|
/// space-efficient `webpki-roots` (pre-parsed [`TrustAnchor`] values) because reqwest's
|
||||||
|
/// [`ClientBuilder::tls_certs_only`] accepts [`Certificate`] values built from DER bytes. Using
|
||||||
|
/// `webpki-roots` would require constructing a [`rustls::ClientConfig`] manually and passing it
|
||||||
|
/// via the semver-unstable [`ClientBuilder::tls_backend_preconfigured`], which also means
|
||||||
|
/// taking ownership of ALPN, SNI, certificate verification, and mTLS configuration that reqwest
|
||||||
|
/// otherwise handles for us.
|
||||||
|
pub(crate) fn webpki_roots() -> Self {
|
||||||
|
// Each [`CertificateDer`] in [`webpki_root_certs::TLS_SERVER_ROOT_CERTS`] borrows from static
|
||||||
|
// data, so cloning into the [`Vec`] only copies the fat pointer, not the certificate bytes.
|
||||||
|
Self(webpki_root_certs::TLS_SERVER_ROOT_CERTS.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load custom CA certificates from `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables.
|
||||||
|
///
|
||||||
|
/// Returns `None` if neither variable is set, if the referenced files or directories are
|
||||||
|
/// missing or inaccessible, or if no valid certificates are found (with a warning in each
|
||||||
|
/// case). Delegates path loading to [`rustls_native_certs::load_certs_from_paths`].
|
||||||
|
pub(crate) fn from_env() -> Option<Self> {
|
||||||
|
let mut certs = Self::default();
|
||||||
|
let mut has_source = false;
|
||||||
|
|
||||||
|
if let Some(ssl_cert_file) = env::var_os(EnvVars::SSL_CERT_FILE)
|
||||||
|
&& let Some(file_certs) = Self::from_ssl_cert_file(&ssl_cert_file)
|
||||||
|
{
|
||||||
|
has_source = true;
|
||||||
|
certs.merge(file_certs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ssl_cert_dir) = env::var_os(EnvVars::SSL_CERT_DIR)
|
||||||
|
&& let Some(dir_certs) = Self::from_ssl_cert_dir(&ssl_cert_dir)
|
||||||
|
{
|
||||||
|
has_source = true;
|
||||||
|
certs.merge(dir_certs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_source { Some(certs) } else { None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load certificates from the value of `SSL_CERT_FILE`.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the value is empty, the path does not refer to an accessible file,
|
||||||
|
/// or the file contains no valid certificates.
|
||||||
|
fn from_ssl_cert_file(ssl_cert_file: &std::ffi::OsStr) -> Option<Self> {
|
||||||
|
if ssl_cert_file.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = PathBuf::from(ssl_cert_file);
|
||||||
|
match file.metadata() {
|
||||||
|
Ok(metadata) if metadata.is_file() => {
|
||||||
|
let result = Self::from_paths(Some(&file), None);
|
||||||
|
for err in &result.errors {
|
||||||
|
warn_user_once!(
|
||||||
|
"Failed to load `SSL_CERT_FILE` ({}): {err}",
|
||||||
|
file.simplified_display().cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let certs = Self::from(result);
|
||||||
|
if certs.0.is_empty() {
|
||||||
|
warn_user_once!(
|
||||||
|
"Ignoring `SSL_CERT_FILE`. No certificates found in: {}.",
|
||||||
|
file.simplified_display().cyan()
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(certs)
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
warn_user_once!(
|
||||||
|
"Ignoring invalid `SSL_CERT_FILE`. Path is not a file: {}.",
|
||||||
|
file.simplified_display().cyan()
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Err(err) if err.kind() == io::ErrorKind::NotFound => {
|
||||||
|
warn_user_once!(
|
||||||
|
"Ignoring invalid `SSL_CERT_FILE`. Path does not exist: {}.",
|
||||||
|
file.simplified_display().cyan()
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn_user_once!(
|
||||||
|
"Ignoring invalid `SSL_CERT_FILE`. Path is not accessible: {} ({err}).",
|
||||||
|
file.simplified_display().cyan()
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load certificates from the value of `SSL_CERT_DIR`.
|
||||||
|
///
|
||||||
|
/// The value may include multiple entries, separated by a platform-specific delimiter (`:` on
|
||||||
|
/// Unix, `;` on Windows).
|
||||||
|
///
|
||||||
|
/// Returns `None` if the value is empty, no listed directories exist, or no valid
|
||||||
|
/// certificates are found.
|
||||||
|
fn from_ssl_cert_dir(ssl_cert_dir: &std::ffi::OsStr) -> Option<Self> {
|
||||||
|
if ssl_cert_dir.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (existing, missing): (Vec<_>, Vec<_>) =
|
||||||
|
env::split_paths(ssl_cert_dir).partition(|path| path.exists());
|
||||||
|
|
||||||
|
if existing.is_empty() {
|
||||||
|
let end_note = if missing.len() == 1 {
|
||||||
|
"The directory does not exist."
|
||||||
|
} else {
|
||||||
|
"The entries do not exist."
|
||||||
|
};
|
||||||
|
warn_user_once!(
|
||||||
|
"Ignoring invalid `SSL_CERT_DIR`. {end_note}: {}.",
|
||||||
|
missing
|
||||||
|
.iter()
|
||||||
|
.map(Simplified::simplified_display)
|
||||||
|
.join(", ")
|
||||||
|
.cyan()
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !missing.is_empty() {
|
||||||
|
let end_note = if missing.len() == 1 {
|
||||||
|
"The following directory does not exist:"
|
||||||
|
} else {
|
||||||
|
"The following entries do not exist:"
|
||||||
|
};
|
||||||
|
warn_user_once!(
|
||||||
|
"Invalid entries in `SSL_CERT_DIR`. {end_note}: {}.",
|
||||||
|
missing
|
||||||
|
.iter()
|
||||||
|
.map(Simplified::simplified_display)
|
||||||
|
.join(", ")
|
||||||
|
.cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut certs = Self::default();
|
||||||
|
for dir in &existing {
|
||||||
|
let result = Self::from_paths(None, Some(dir));
|
||||||
|
for err in &result.errors {
|
||||||
|
warn_user_once!(
|
||||||
|
"Failed to load `SSL_CERT_DIR` ({}): {err}",
|
||||||
|
dir.simplified_display().cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
certs.merge(Self::from(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
if certs.0.is_empty() {
|
||||||
|
warn_user_once!(
|
||||||
|
"Ignoring `SSL_CERT_DIR`. No certificates found in: {}.",
|
||||||
|
existing
|
||||||
|
.iter()
|
||||||
|
.map(Simplified::simplified_display)
|
||||||
|
.join(", ")
|
||||||
|
.cyan()
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(certs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load certificates from explicit file and directory paths.
|
||||||
|
fn from_paths(file: Option<&Path>, dir: Option<&Path>) -> CertificateResult {
|
||||||
|
load_certs_from_paths(file, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove duplicate certificates, sorting by DER bytes.
|
||||||
|
fn dedup(&mut self) {
|
||||||
|
self.0
|
||||||
|
.sort_unstable_by(|left, right| left.as_ref().cmp(right.as_ref()));
|
||||||
|
self.0.dedup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Merge another set of certificates into this one.
|
||||||
|
///
|
||||||
|
/// After merging, duplicates are removed.
|
||||||
|
fn merge(&mut self, other: Self) {
|
||||||
|
self.0.extend(other.0);
|
||||||
|
self.dedup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert certificates to reqwest [`Certificate`] objects.
|
||||||
|
pub(crate) fn to_reqwest_certs(&self) -> Vec<Certificate> {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
// `Certificate::from_der` returns a `Result` for backend compatibility, but these
|
||||||
|
// certificates come from `rustls-native-certs` and are already validated DER certs.
|
||||||
|
// In our rustls-based client configuration this conversion is expected to succeed.
|
||||||
|
.filter_map(|cert| match Certificate::from_der(cert) {
|
||||||
|
Ok(certificate) => Some(certificate),
|
||||||
|
Err(err) => {
|
||||||
|
debug!("Failed to convert DER certificate to reqwest certificate: {err}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over raw DER certificates.
|
||||||
|
#[cfg(test)]
|
||||||
|
fn iter(&self) -> impl Iterator<Item = &CertificateDer<'static>> {
|
||||||
|
self.0.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CertificateResult> for Certificates {
|
||||||
|
fn from(result: CertificateResult) -> Self {
|
||||||
|
Self(result.certs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub(crate) enum CertificateError {
|
pub(crate) enum CertificateError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] io::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Reqwest(reqwest::Error),
|
Reqwest(reqwest::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the `Identity` from the provided file.
|
/// Return the `Identity` from the provided file.
|
||||||
pub(crate) fn read_identity(ssl_client_cert: &OsStr) -> Result<Identity, CertificateError> {
|
pub(crate) fn read_identity(
|
||||||
|
ssl_client_cert: &std::ffi::OsStr,
|
||||||
|
) -> Result<Identity, CertificateError> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
fs_err::File::open(ssl_client_cert)?.read_to_end(&mut buf)?;
|
fs_err::File::open(ssl_client_cert)?.read_to_end(&mut buf)?;
|
||||||
Identity::from_pem(&buf).map_err(|tls_err| {
|
Identity::from_pem(&buf).map_err(|tls_err| {
|
||||||
@@ -19,3 +255,86 @@ pub(crate) fn read_identity(ssl_client_cert: &OsStr) -> Result<Identity, Certifi
|
|||||||
CertificateError::Reqwest(tls_err)
|
CertificateError::Reqwest(tls_err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::ffi::OsString;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn generate_cert_pem() -> String {
|
||||||
|
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()]).unwrap();
|
||||||
|
cert.cert.pem()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_ssl_cert_file_nonexistent_returns_none() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let missing_file = dir.path().join("missing.pem");
|
||||||
|
|
||||||
|
let certs = Certificates::from_ssl_cert_file(missing_file.as_os_str());
|
||||||
|
assert!(certs.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_ssl_cert_file_empty_value_returns_none() {
|
||||||
|
let certs = Certificates::from_ssl_cert_file(OsString::new().as_os_str());
|
||||||
|
assert!(certs.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_ssl_cert_file_no_valid_certs_returns_none() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let cert_path = dir.path().join("empty.pem");
|
||||||
|
fs_err::write(&cert_path, "not a certificate").unwrap();
|
||||||
|
|
||||||
|
let certs = Certificates::from_ssl_cert_file(cert_path.as_os_str());
|
||||||
|
assert!(certs.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_ssl_cert_dir_empty_value_returns_none() {
|
||||||
|
let certs = Certificates::from_ssl_cert_dir(OsString::new().as_os_str());
|
||||||
|
assert!(certs.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_ssl_cert_dir_nonexistent_returns_none() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let missing_dir = dir.path().join("missing-dir");
|
||||||
|
let cert_dirs = std::env::join_paths([&missing_dir]).unwrap();
|
||||||
|
|
||||||
|
let certs = Certificates::from_ssl_cert_dir(cert_dirs.as_os_str());
|
||||||
|
assert!(certs.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_ssl_cert_dir_empty_existing_returns_none() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let cert_dirs = std::env::join_paths([dir.path()]).unwrap();
|
||||||
|
|
||||||
|
let certs = Certificates::from_ssl_cert_dir(cert_dirs.as_os_str());
|
||||||
|
assert!(certs.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_merge_deduplicates() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let cert_path = dir.path().join("cert.pem");
|
||||||
|
fs_err::write(&cert_path, generate_cert_pem()).unwrap();
|
||||||
|
|
||||||
|
let first = Certificates::from(Certificates::from_paths(Some(&cert_path), None));
|
||||||
|
let second = Certificates::from(Certificates::from_paths(Some(&cert_path), None));
|
||||||
|
|
||||||
|
let mut merged = first;
|
||||||
|
merged.merge(second);
|
||||||
|
|
||||||
|
assert_eq!(merged.iter().count(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_webpki_roots_not_empty() {
|
||||||
|
let certs = Certificates::webpki_roots();
|
||||||
|
assert!(certs.iter().count() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,41 +46,6 @@ pub(crate) fn test_cert_dir() -> PathBuf {
|
|||||||
.join("certs")
|
.join("certs")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a self-signed server certificate for `uv-test-server`, `localhost` and `127.0.0.1`.
|
|
||||||
/// This certificate is standalone and not issued by a self-signed Root CA.
|
|
||||||
///
|
|
||||||
/// Use sparingly as generation of certs is a slow operation.
|
|
||||||
pub(crate) fn generate_self_signed_certs() -> Result<SelfSigned> {
|
|
||||||
let mut params = CertificateParams::default();
|
|
||||||
params.is_ca = IsCa::NoCa;
|
|
||||||
params.not_before = date_time_ymd(1975, 1, 1);
|
|
||||||
params.not_after = date_time_ymd(4096, 1, 1);
|
|
||||||
params.key_usages.push(KeyUsagePurpose::DigitalSignature);
|
|
||||||
params.key_usages.push(KeyUsagePurpose::KeyEncipherment);
|
|
||||||
params
|
|
||||||
.extended_key_usages
|
|
||||||
.push(ExtendedKeyUsagePurpose::ServerAuth);
|
|
||||||
params
|
|
||||||
.distinguished_name
|
|
||||||
.push(DnType::OrganizationName, "Astral Software Inc.");
|
|
||||||
params
|
|
||||||
.distinguished_name
|
|
||||||
.push(DnType::CommonName, "uv-test-server");
|
|
||||||
params
|
|
||||||
.subject_alt_names
|
|
||||||
.push(SanType::DnsName("uv-test-server".try_into()?));
|
|
||||||
params
|
|
||||||
.subject_alt_names
|
|
||||||
.push(SanType::DnsName("localhost".try_into()?));
|
|
||||||
params
|
|
||||||
.subject_alt_names
|
|
||||||
.push(SanType::IpAddress("127.0.0.1".parse()?));
|
|
||||||
let private = KeyPair::generate()?;
|
|
||||||
let public = params.self_signed(&private)?;
|
|
||||||
|
|
||||||
Ok(SelfSigned { public, private })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a self-signed root CA, server certificate, and client certificate.
|
/// Generates a self-signed root CA, server certificate, and client certificate.
|
||||||
/// There are no intermediate certs generated as part of this function.
|
/// There are no intermediate certs generated as part of this function.
|
||||||
/// The server certificate is for `uv-test-server`, `localhost` and `127.0.0.1` issued by this CA.
|
/// The server certificate is for `uv-test-server`, `localhost` and `127.0.0.1` issued by this CA.
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use rustls::AlertDescription;
|
use temp_env::async_with_vars;
|
||||||
|
use tempfile::{NamedTempFile, TempDir};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
@@ -11,335 +15,655 @@ use uv_redacted::DisplaySafeUrl;
|
|||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
use crate::http_util::{
|
use crate::http_util::{
|
||||||
generate_self_signed_certs, generate_self_signed_certs_with_ca,
|
SelfSigned, generate_self_signed_certs_with_ca, start_https_mtls_user_agent_server,
|
||||||
start_https_mtls_user_agent_server, start_https_user_agent_server, test_cert_dir,
|
start_https_user_agent_server, test_cert_dir,
|
||||||
};
|
};
|
||||||
|
|
||||||
// SAFETY: This test is meant to run with single thread configuration
|
/// A self-signed CA together with a server certificate and a client certificate
|
||||||
#[tokio::test]
|
/// it has issued. Every [`TestCertificate`] is an independent trust domain.
|
||||||
#[allow(unsafe_code)]
|
struct TestCertificate {
|
||||||
async fn ssl_env_vars() -> Result<()> {
|
_temp_dir: TempDir,
|
||||||
// Ensure our environment is not polluted with anything that may affect `rustls-native-certs`
|
/// The CA certificate (root of trust).
|
||||||
unsafe {
|
ca: SelfSigned,
|
||||||
std::env::remove_var(EnvVars::UV_NATIVE_TLS);
|
/// A server certificate signed by [`ca`](Self::ca).
|
||||||
std::env::remove_var(EnvVars::SSL_CERT_FILE);
|
server: SelfSigned,
|
||||||
std::env::remove_var(EnvVars::SSL_CERT_DIR);
|
/// Path to the CA public cert PEM — the file you put in `SSL_CERT_FILE` to
|
||||||
std::env::remove_var(EnvVars::SSL_CLIENT_CERT);
|
/// trust this certificate family.
|
||||||
|
trust_path: PathBuf,
|
||||||
|
/// Path to the combined client cert + key PEM — the file you put in
|
||||||
|
/// `SSL_CLIENT_CERT` for mTLS.
|
||||||
|
client_cert_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestCertificate {
|
||||||
|
/// Generate a fresh CA, server cert, and client cert, persisting the
|
||||||
|
/// relevant PEM files to a temporary directory.
|
||||||
|
fn new() -> Result<Self> {
|
||||||
|
let cert_dir = test_cert_dir();
|
||||||
|
fs_err::create_dir_all(&cert_dir)?;
|
||||||
|
let temp_dir = TempDir::new_in(cert_dir)?;
|
||||||
|
|
||||||
|
let (ca, server, client) = generate_self_signed_certs_with_ca()?;
|
||||||
|
|
||||||
|
let trust_path = temp_dir.path().join("ca.pem");
|
||||||
|
fs_err::write(&trust_path, ca.public.pem())?;
|
||||||
|
|
||||||
|
let client_cert_path = temp_dir.path().join("client.pem");
|
||||||
|
fs_err::write(
|
||||||
|
&client_cert_path,
|
||||||
|
format!(
|
||||||
|
"{}\n{}",
|
||||||
|
client.public.pem(),
|
||||||
|
client.private.serialize_pem()
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
_temp_dir: temp_dir,
|
||||||
|
ca,
|
||||||
|
server,
|
||||||
|
trust_path,
|
||||||
|
client_cert_path,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create temporary cert dirs
|
/// Write a CA + server PEM bundle to a [`NamedTempFile`].
|
||||||
let cert_dir = test_cert_dir();
|
fn write_bundle_pem(&self) -> NamedTempFile {
|
||||||
fs_err::create_dir_all(&cert_dir).expect("Failed to create test cert bucket");
|
let mut file = NamedTempFile::new().unwrap();
|
||||||
let cert_dir =
|
write!(
|
||||||
tempfile::TempDir::new_in(cert_dir).expect("Failed to create test cert directory");
|
file,
|
||||||
let does_not_exist_cert_dir = cert_dir.path().join("does_not_exist");
|
|
||||||
|
|
||||||
// Generate self-signed standalone cert
|
|
||||||
let standalone_server_cert = generate_self_signed_certs()?;
|
|
||||||
let standalone_public_pem_path = cert_dir.path().join("standalone_public.pem");
|
|
||||||
let standalone_private_pem_path = cert_dir.path().join("standalone_private.pem");
|
|
||||||
|
|
||||||
// Generate self-signed CA, server, and client certs
|
|
||||||
let (ca_cert, server_cert, client_cert) = generate_self_signed_certs_with_ca()?;
|
|
||||||
let ca_public_pem_path = cert_dir.path().join("ca_public.pem");
|
|
||||||
let ca_private_pem_path = cert_dir.path().join("ca_private.pem");
|
|
||||||
let server_public_pem_path = cert_dir.path().join("server_public.pem");
|
|
||||||
let server_private_pem_path = cert_dir.path().join("server_private.pem");
|
|
||||||
let client_combined_pem_path = cert_dir.path().join("client_combined.pem");
|
|
||||||
|
|
||||||
// Persist the certs in PKCS8 format as the env vars expect a path on disk
|
|
||||||
fs_err::write(
|
|
||||||
standalone_public_pem_path.as_path(),
|
|
||||||
standalone_server_cert.public.pem(),
|
|
||||||
)?;
|
|
||||||
fs_err::write(
|
|
||||||
standalone_private_pem_path.as_path(),
|
|
||||||
standalone_server_cert.private.serialize_pem(),
|
|
||||||
)?;
|
|
||||||
fs_err::write(ca_public_pem_path.as_path(), ca_cert.public.pem())?;
|
|
||||||
fs_err::write(
|
|
||||||
ca_private_pem_path.as_path(),
|
|
||||||
ca_cert.private.serialize_pem(),
|
|
||||||
)?;
|
|
||||||
fs_err::write(server_public_pem_path.as_path(), server_cert.public.pem())?;
|
|
||||||
fs_err::write(
|
|
||||||
server_private_pem_path.as_path(),
|
|
||||||
server_cert.private.serialize_pem(),
|
|
||||||
)?;
|
|
||||||
fs_err::write(
|
|
||||||
client_combined_pem_path.as_path(),
|
|
||||||
// SSL_CLIENT_CERT expects a "combined" cert with the public and private key.
|
|
||||||
format!(
|
|
||||||
"{}\n{}",
|
"{}\n{}",
|
||||||
client_cert.public.pem(),
|
self.ca.public.pem(),
|
||||||
client_cert.private.serialize_pem()
|
self.server.public.pem()
|
||||||
),
|
)
|
||||||
)?;
|
.unwrap();
|
||||||
|
file
|
||||||
// ** Set SSL_CERT_FILE to non-existent location
|
|
||||||
// ** Then verify our request fails to establish a connection
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
std::env::set_var(EnvVars::SSL_CERT_FILE, does_not_exist_cert_dir.as_os_str());
|
|
||||||
}
|
|
||||||
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
|
||||||
let cache = Cache::temp()?.init().await?;
|
|
||||||
let client =
|
|
||||||
RegistryClientBuilder::new(BaseClientBuilder::default().no_retry_delay(true), cache)
|
|
||||||
.build();
|
|
||||||
let res = client
|
|
||||||
.cached_client()
|
|
||||||
.uncached()
|
|
||||||
.for_host(&url)
|
|
||||||
.get(Url::from(url))
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
unsafe {
|
|
||||||
std::env::remove_var(EnvVars::SSL_CERT_FILE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the client error
|
/// Write the CA public PEM into a fresh temporary directory, returning it.
|
||||||
let Some(reqwest_middleware::Error::Middleware(middleware_error)) = res.err() else {
|
fn ca_pem_dir(&self) -> TempDir {
|
||||||
panic!("expected middleware error");
|
self.ca_pem_dir_as("ca.pem")
|
||||||
};
|
}
|
||||||
let reqwest_error = middleware_error
|
|
||||||
.chain()
|
/// Write the CA public PEM with a custom filename into a fresh temporary
|
||||||
.find_map(|err| {
|
/// directory, returning it.
|
||||||
err.downcast_ref::<reqwest_middleware::Error>().map(|err| {
|
fn ca_pem_dir_as(&self, filename: &str) -> TempDir {
|
||||||
if let reqwest_middleware::Error::Reqwest(inner) = err {
|
let dir = TempDir::new().unwrap();
|
||||||
inner
|
fs_err::write(dir.path().join(filename), self.ca.public.pem()).unwrap();
|
||||||
} else {
|
dir
|
||||||
panic!("expected reqwest error")
|
}
|
||||||
}
|
|
||||||
})
|
/// Write a CA + server PEM bundle into a fresh temporary directory,
|
||||||
|
/// returning it.
|
||||||
|
fn bundle_pem_dir(&self) -> TempDir {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
fs_err::write(
|
||||||
|
dir.path().join("bundle.pem"),
|
||||||
|
format!("{}\n{}", self.ca.public.pem(), self.server.public.pem()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Client-side configuration builder. Collects environment variable overrides
|
||||||
|
/// and provides terminal assertion methods that start a server, send a request,
|
||||||
|
/// and verify the outcome.
|
||||||
|
struct TestClient {
|
||||||
|
overrides: Vec<(&'static str, String)>,
|
||||||
|
system_certs: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`TestClient`] with no environment overrides.
|
||||||
|
fn client() -> TestClient {
|
||||||
|
TestClient {
|
||||||
|
overrides: Vec::new(),
|
||||||
|
system_certs: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestClient {
|
||||||
|
/// Enable or disable system certificate loading.
|
||||||
|
fn system_certs(mut self, enabled: bool) -> Self {
|
||||||
|
self.system_certs = enabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `SSL_CERT_FILE` to `path`.
|
||||||
|
fn ssl_cert_file(self, path: &Path) -> Self {
|
||||||
|
self.with_env(EnvVars::SSL_CERT_FILE, path.to_str().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `SSL_CERT_DIR` to a single directory.
|
||||||
|
fn ssl_cert_dir(self, path: &Path) -> Self {
|
||||||
|
self.with_env(EnvVars::SSL_CERT_DIR, path.to_str().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `SSL_CERT_DIR` to multiple directories joined with the
|
||||||
|
/// platform-specific path separator.
|
||||||
|
fn ssl_cert_dirs(self, paths: &[&Path]) -> Self {
|
||||||
|
let joined = std::env::join_paths(paths).unwrap();
|
||||||
|
self.with_env(EnvVars::SSL_CERT_DIR, joined.to_str().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `SSL_CLIENT_CERT` to `path`.
|
||||||
|
fn ssl_client_cert(self, path: &Path) -> Self {
|
||||||
|
self.with_env(EnvVars::SSL_CLIENT_CERT, path.to_str().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set an arbitrary environment variable.
|
||||||
|
fn with_env(mut self, key: &'static str, value: &str) -> Self {
|
||||||
|
self.overrides.push((key, value.to_string()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that an HTTPS connection to `cert`'s server succeeds.
|
||||||
|
async fn expect_https_connect_succeeds(&self, cert: &TestCertificate) {
|
||||||
|
self.run_https(cert, |response, server_task| async move {
|
||||||
|
assert!(
|
||||||
|
response.is_ok(),
|
||||||
|
"expected successful response, got: {:?}",
|
||||||
|
response.err()
|
||||||
|
);
|
||||||
|
server_task.await.unwrap().unwrap();
|
||||||
})
|
})
|
||||||
.expect("expected reqwest error");
|
|
||||||
assert!(reqwest_error.is_connect());
|
|
||||||
|
|
||||||
// Validate the server error
|
|
||||||
let server_res = server_task.await?;
|
|
||||||
let expected_err = if let Err(anyhow_err) = server_res
|
|
||||||
&& let Some(io_err) = anyhow_err.downcast_ref::<std::io::Error>()
|
|
||||||
&& let Some(wrapped_err) = io_err.get_ref()
|
|
||||||
&& let Some(tls_err) = wrapped_err.downcast_ref::<rustls::Error>()
|
|
||||||
&& matches!(
|
|
||||||
tls_err,
|
|
||||||
rustls::Error::AlertReceived(AlertDescription::UnknownCA)
|
|
||||||
) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
assert!(expected_err);
|
|
||||||
|
|
||||||
// ** Set SSL_CERT_FILE to our public certificate
|
|
||||||
// ** Then verify our request successfully establishes a connection
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
std::env::set_var(
|
|
||||||
EnvVars::SSL_CERT_FILE,
|
|
||||||
standalone_public_pem_path.as_os_str(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
|
||||||
let cache = Cache::temp()?.init().await?;
|
|
||||||
let client =
|
|
||||||
RegistryClientBuilder::new(BaseClientBuilder::default().no_retry_delay(true), cache)
|
|
||||||
.build();
|
|
||||||
let res = client
|
|
||||||
.cached_client()
|
|
||||||
.uncached()
|
|
||||||
.for_host(&url)
|
|
||||||
.get(Url::from(url))
|
|
||||||
.send()
|
|
||||||
.await;
|
.await;
|
||||||
assert!(res.is_ok());
|
|
||||||
let _ = server_task.await?; // wait for server shutdown
|
|
||||||
unsafe {
|
|
||||||
std::env::remove_var(EnvVars::SSL_CERT_FILE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ** Set SSL_CERT_DIR to our cert dir as well as some other dir that does not exist
|
/// Assert that an HTTPS connection to `cert`'s server fails with a TLS
|
||||||
// ** Then verify our request still successfully establishes a connection
|
/// error on the client side.
|
||||||
|
async fn expect_https_connect_fails(&self, cert: &TestCertificate) {
|
||||||
unsafe {
|
self.run_https(cert, |response, server_task| async move {
|
||||||
std::env::set_var(
|
assert_connection_error(&response);
|
||||||
EnvVars::SSL_CERT_DIR,
|
// Server may or may not have errored — just ensure no panic.
|
||||||
std::env::join_paths(vec![
|
let _ = server_task.await;
|
||||||
cert_dir.path().as_os_str(),
|
|
||||||
does_not_exist_cert_dir.as_os_str(),
|
|
||||||
])?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
|
||||||
let cache = Cache::temp()?.init().await?;
|
|
||||||
let client =
|
|
||||||
RegistryClientBuilder::new(BaseClientBuilder::default().no_retry_delay(true), cache)
|
|
||||||
.build();
|
|
||||||
let res = client
|
|
||||||
.cached_client()
|
|
||||||
.uncached()
|
|
||||||
.for_host(&url)
|
|
||||||
.get(Url::from(url))
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
assert!(res.is_ok());
|
|
||||||
let _ = server_task.await?; // wait for server shutdown
|
|
||||||
unsafe {
|
|
||||||
std::env::remove_var(EnvVars::SSL_CERT_DIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ** Set SSL_CERT_DIR to only the dir that does not exist
|
|
||||||
// ** Then verify our request fails to establish a connection
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
std::env::set_var(EnvVars::SSL_CERT_DIR, does_not_exist_cert_dir.as_os_str());
|
|
||||||
}
|
|
||||||
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
|
||||||
let cache = Cache::temp()?.init().await?;
|
|
||||||
let client =
|
|
||||||
RegistryClientBuilder::new(BaseClientBuilder::default().no_retry_delay(true), cache)
|
|
||||||
.build();
|
|
||||||
let res = client
|
|
||||||
.cached_client()
|
|
||||||
.uncached()
|
|
||||||
.for_host(&url)
|
|
||||||
.get(Url::from(url))
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
unsafe {
|
|
||||||
std::env::remove_var(EnvVars::SSL_CERT_DIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the client error
|
|
||||||
let Some(reqwest_middleware::Error::Middleware(middleware_error)) = res.err() else {
|
|
||||||
panic!("expected middleware error");
|
|
||||||
};
|
|
||||||
let reqwest_error = middleware_error
|
|
||||||
.chain()
|
|
||||||
.find_map(|err| {
|
|
||||||
err.downcast_ref::<reqwest_middleware::Error>().map(|err| {
|
|
||||||
if let reqwest_middleware::Error::Reqwest(inner) = err {
|
|
||||||
inner
|
|
||||||
} else {
|
|
||||||
panic!("expected reqwest error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.expect("expected reqwest error");
|
|
||||||
assert!(reqwest_error.is_connect());
|
|
||||||
|
|
||||||
// Validate the server error
|
|
||||||
let server_res = server_task.await?;
|
|
||||||
let expected_err = if let Err(anyhow_err) = server_res
|
|
||||||
&& let Some(io_err) = anyhow_err.downcast_ref::<std::io::Error>()
|
|
||||||
&& let Some(wrapped_err) = io_err.get_ref()
|
|
||||||
&& let Some(tls_err) = wrapped_err.downcast_ref::<rustls::Error>()
|
|
||||||
&& matches!(
|
|
||||||
tls_err,
|
|
||||||
rustls::Error::AlertReceived(AlertDescription::UnknownCA)
|
|
||||||
) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
assert!(expected_err);
|
|
||||||
|
|
||||||
// *** mTLS Tests
|
|
||||||
|
|
||||||
// ** Set SSL_CERT_FILE to our CA and SSL_CLIENT_CERT to our client cert
|
|
||||||
// ** Then verify our request still successfully establishes a connection
|
|
||||||
|
|
||||||
// We need to set SSL_CERT_FILE or SSL_CERT_DIR to our CA as we need to tell
|
|
||||||
// our HTTP client that we trust certificates issued by our self-signed CA.
|
|
||||||
// This inherently also tests that our server cert is also validated as part
|
|
||||||
// of the certificate path validation algorithm.
|
|
||||||
unsafe {
|
|
||||||
std::env::set_var(EnvVars::SSL_CERT_FILE, ca_public_pem_path.as_os_str());
|
|
||||||
std::env::set_var(
|
|
||||||
EnvVars::SSL_CLIENT_CERT,
|
|
||||||
client_combined_pem_path.as_os_str(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let (server_task, addr) = start_https_mtls_user_agent_server(&ca_cert, &server_cert).await?;
|
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
|
||||||
let cache = Cache::temp()?.init().await?;
|
|
||||||
let client =
|
|
||||||
RegistryClientBuilder::new(BaseClientBuilder::default().no_retry_delay(true), cache)
|
|
||||||
.build();
|
|
||||||
let res = client
|
|
||||||
.cached_client()
|
|
||||||
.uncached()
|
|
||||||
.for_host(&url)
|
|
||||||
.get(Url::from(url))
|
|
||||||
.send()
|
|
||||||
.await;
|
.await;
|
||||||
assert!(res.is_ok());
|
|
||||||
let _ = server_task.await?; // wait for server shutdown
|
|
||||||
unsafe {
|
|
||||||
std::env::remove_var(EnvVars::SSL_CERT_FILE);
|
|
||||||
std::env::remove_var(EnvVars::SSL_CLIENT_CERT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ** Set SSL_CERT_FILE to our CA and unset SSL_CLIENT_CERT
|
/// Assert that an mTLS connection to `cert`'s server succeeds.
|
||||||
// ** Then verify our request fails to establish a connection
|
async fn expect_mtls_connect_succeeds(&self, cert: &TestCertificate) {
|
||||||
|
self.run_mtls(cert, |response, server_task| async move {
|
||||||
unsafe {
|
assert!(
|
||||||
std::env::set_var(EnvVars::SSL_CERT_FILE, ca_public_pem_path.as_os_str());
|
response.is_ok(),
|
||||||
}
|
"expected successful response, got: {:?}",
|
||||||
let (server_task, addr) = start_https_mtls_user_agent_server(&ca_cert, &server_cert).await?;
|
response.err()
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
);
|
||||||
let cache = Cache::temp()?.init().await?;
|
server_task.await.unwrap().unwrap();
|
||||||
let client =
|
|
||||||
RegistryClientBuilder::new(BaseClientBuilder::default().no_retry_delay(true), cache)
|
|
||||||
.build();
|
|
||||||
let res = client
|
|
||||||
.cached_client()
|
|
||||||
.uncached()
|
|
||||||
.for_host(&url)
|
|
||||||
.get(Url::from(url))
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
unsafe {
|
|
||||||
std::env::remove_var(EnvVars::SSL_CERT_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the client error
|
|
||||||
let Some(reqwest_middleware::Error::Middleware(middleware_error)) = res.err() else {
|
|
||||||
panic!("expected middleware error");
|
|
||||||
};
|
|
||||||
let reqwest_error = middleware_error
|
|
||||||
.chain()
|
|
||||||
.find_map(|err| {
|
|
||||||
err.downcast_ref::<reqwest_middleware::Error>().map(|err| {
|
|
||||||
if let reqwest_middleware::Error::Reqwest(inner) = err {
|
|
||||||
inner
|
|
||||||
} else {
|
|
||||||
panic!("expected reqwest error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.expect("expected reqwest error");
|
.await;
|
||||||
assert!(reqwest_error.is_connect());
|
}
|
||||||
|
|
||||||
// Validate the server error
|
/// Assert that an mTLS connection to `cert`'s server fails and the server
|
||||||
let server_res = server_task.await?;
|
/// reports a specific TLS error.
|
||||||
let expected_err = if let Err(anyhow_err) = server_res
|
async fn expect_mtls_connect_fails_with_server_tls_error<F>(
|
||||||
&& let Some(io_err) = anyhow_err.downcast_ref::<std::io::Error>()
|
&self,
|
||||||
&& let Some(wrapped_err) = io_err.get_ref()
|
cert: &TestCertificate,
|
||||||
&& let Some(tls_err) = wrapped_err.downcast_ref::<rustls::Error>()
|
assert_tls_error: F,
|
||||||
&& matches!(tls_err, rustls::Error::NoCertificatesPresented)
|
) where
|
||||||
|
F: FnOnce(&rustls::Error),
|
||||||
{
|
{
|
||||||
true
|
self.run_mtls(cert, |response, server_task| async move {
|
||||||
} else {
|
assert_connection_error(&response);
|
||||||
false
|
|
||||||
};
|
|
||||||
assert!(expected_err);
|
|
||||||
|
|
||||||
// Fin.
|
let server_res = server_task.await.expect("server task panicked");
|
||||||
|
let Err(anyhow_err) = server_res else {
|
||||||
|
panic!("expected server error, got Ok");
|
||||||
|
};
|
||||||
|
let Some(io_err) = anyhow_err.downcast_ref::<std::io::Error>() else {
|
||||||
|
panic!("expected io::Error, got: {anyhow_err}");
|
||||||
|
};
|
||||||
|
let Some(wrapped_err) = io_err.get_ref() else {
|
||||||
|
panic!("expected wrapped error in io::Error, got: {io_err}");
|
||||||
|
};
|
||||||
|
let Some(tls_err) = wrapped_err.downcast_ref::<rustls::Error>() else {
|
||||||
|
panic!("expected rustls::Error, got: {wrapped_err}");
|
||||||
|
};
|
||||||
|
assert_tls_error(tls_err);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that an mTLS connection to `cert`'s server fails because no
|
||||||
|
/// valid client certificate was presented.
|
||||||
|
async fn expect_mtls_connect_fails(&self, cert: &TestCertificate) {
|
||||||
|
self.expect_mtls_connect_fails_with_server_tls_error(cert, |tls_err| {
|
||||||
|
assert!(
|
||||||
|
matches!(tls_err, rustls::Error::NoCertificatesPresented),
|
||||||
|
"expected NoCertificatesPresented, got: {tls_err}"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the full environment variable list: clear all SSL-related
|
||||||
|
/// variables, then apply the accumulated overrides.
|
||||||
|
fn ssl_vars(&self) -> Vec<(&'static str, Option<&str>)> {
|
||||||
|
let mut vars: Vec<(&'static str, Option<&str>)> = vec![
|
||||||
|
(EnvVars::UV_NATIVE_TLS, None),
|
||||||
|
(EnvVars::UV_SYSTEM_CERTS, None),
|
||||||
|
(EnvVars::SSL_CERT_FILE, None),
|
||||||
|
(EnvVars::SSL_CERT_DIR, None),
|
||||||
|
(EnvVars::SSL_CLIENT_CERT, None),
|
||||||
|
];
|
||||||
|
vars.extend(self.overrides.iter().map(|(k, v)| (*k, Some(v.as_str()))));
|
||||||
|
vars
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that an HTTPS connection to a public host succeeds.
|
||||||
|
#[cfg(feature = "test-pypi")]
|
||||||
|
async fn expect_https_connect_succeeds_for_host(&self, host: &str) {
|
||||||
|
let url = DisplaySafeUrl::from_str(&format!("https://{host}/")).unwrap();
|
||||||
|
let vars = self.ssl_vars();
|
||||||
|
let system_certs = self.system_certs;
|
||||||
|
async_with_vars(vars, async {
|
||||||
|
let response = send_request_to(&url, system_certs).await;
|
||||||
|
assert!(
|
||||||
|
response.is_ok(),
|
||||||
|
"expected successful response to {host}, got: {:?}",
|
||||||
|
response.err()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that an HTTPS connection to a public host fails with a TLS
|
||||||
|
/// error on the client side.
|
||||||
|
#[cfg(feature = "test-pypi")]
|
||||||
|
async fn expect_https_connect_fails_for_host(&self, host: &str) {
|
||||||
|
let url = DisplaySafeUrl::from_str(&format!("https://{host}/")).unwrap();
|
||||||
|
let vars = self.ssl_vars();
|
||||||
|
let system_certs = self.system_certs;
|
||||||
|
async_with_vars(vars, async {
|
||||||
|
let response = send_request_to(&url, system_certs).await;
|
||||||
|
assert_connection_error(&response);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an HTTPS server, send a request inside `async_with_vars`, and
|
||||||
|
/// hand the response + server task to `check`.
|
||||||
|
async fn run_https<F, Fut>(&self, cert: &TestCertificate, check: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(
|
||||||
|
Result<reqwest::Response, reqwest_middleware::Error>,
|
||||||
|
tokio::task::JoinHandle<Result<()>>,
|
||||||
|
) -> Fut,
|
||||||
|
Fut: std::future::Future<Output = ()>,
|
||||||
|
{
|
||||||
|
let vars = self.ssl_vars();
|
||||||
|
let system_certs = self.system_certs;
|
||||||
|
async_with_vars(vars, async {
|
||||||
|
let (server_task, addr) = start_https_user_agent_server(&cert.server).await.unwrap();
|
||||||
|
let response = send_request(addr, system_certs).await;
|
||||||
|
check(response, server_task).await;
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an mTLS server, send a request inside `async_with_vars`, and
|
||||||
|
/// hand the response + server task to `check`.
|
||||||
|
async fn run_mtls<F, Fut>(&self, cert: &TestCertificate, check: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(
|
||||||
|
Result<reqwest::Response, reqwest_middleware::Error>,
|
||||||
|
tokio::task::JoinHandle<Result<()>>,
|
||||||
|
) -> Fut,
|
||||||
|
Fut: std::future::Future<Output = ()>,
|
||||||
|
{
|
||||||
|
let vars = self.ssl_vars();
|
||||||
|
let system_certs = self.system_certs;
|
||||||
|
async_with_vars(vars, async {
|
||||||
|
let (server_task, addr) = start_https_mtls_user_agent_server(&cert.ca, &cert.server)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let response = send_request(addr, system_certs).await;
|
||||||
|
check(response, server_task).await;
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a GET request to the given server address using a fresh registry client.
|
||||||
|
async fn send_request(
|
||||||
|
addr: SocketAddr,
|
||||||
|
system_certs: bool,
|
||||||
|
) -> Result<reqwest::Response, reqwest_middleware::Error> {
|
||||||
|
let url = DisplaySafeUrl::from_str(&format!("https://{addr}")).unwrap();
|
||||||
|
send_request_to(&url, system_certs).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a GET request to an arbitrary URL using a fresh registry client.
|
||||||
|
async fn send_request_to(
|
||||||
|
url: &DisplaySafeUrl,
|
||||||
|
system_certs: bool,
|
||||||
|
) -> Result<reqwest::Response, reqwest_middleware::Error> {
|
||||||
|
let cache = Cache::temp().unwrap().init().await.unwrap();
|
||||||
|
let base = BaseClientBuilder::default()
|
||||||
|
.no_retry_delay(true)
|
||||||
|
.with_system_certs(system_certs);
|
||||||
|
let client = RegistryClientBuilder::new(base, cache).build();
|
||||||
|
client
|
||||||
|
.cached_client()
|
||||||
|
.uncached()
|
||||||
|
.for_host(url)
|
||||||
|
.get(Url::from(url.clone()))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that a request result is a TLS connection error.
|
||||||
|
fn assert_connection_error(res: &Result<reqwest::Response, reqwest_middleware::Error>) {
|
||||||
|
let Some(reqwest_middleware::Error::Middleware(middleware_error)) = res.as_ref().err() else {
|
||||||
|
panic!("expected middleware error, got: {res:?}");
|
||||||
|
};
|
||||||
|
let reqwest_error = middleware_error
|
||||||
|
.chain()
|
||||||
|
.find_map(|err| {
|
||||||
|
err.downcast_ref::<reqwest_middleware::Error>().map(|err| {
|
||||||
|
if let reqwest_middleware::Error::Reqwest(inner) = err {
|
||||||
|
inner
|
||||||
|
} else {
|
||||||
|
panic!("expected reqwest error, got: {err}")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.expect("expected reqwest error");
|
||||||
|
assert!(reqwest_error.is_connect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A self-signed server certificate is rejected when no custom certs are
|
||||||
|
/// configured — the bundled webpki roots don't include our test CA.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_no_custom_certs_rejects_self_signed() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
client().expect_https_connect_fails(&cert).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trusting cert A does not let you connect to a server presenting cert B.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_file_wrong_cert_rejected() -> Result<()> {
|
||||||
|
let cert_a = TestCertificate::new()?;
|
||||||
|
let cert_b = TestCertificate::new()?;
|
||||||
|
client()
|
||||||
|
.ssl_cert_file(&cert_a.trust_path)
|
||||||
|
.expect_https_connect_fails(&cert_b)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A nonexistent `SSL_CERT_FILE` is ignored; the client falls back to webpki
|
||||||
|
/// roots which don't include our test CA.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_file_nonexistent_falls_back() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
let dir = TempDir::new()?;
|
||||||
|
let missing = dir.path().join("missing.pem");
|
||||||
|
client()
|
||||||
|
.ssl_cert_file(&missing)
|
||||||
|
.expect_https_connect_fails(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A nonexistent `SSL_CERT_DIR` is ignored; the client falls back to webpki
|
||||||
|
/// roots which don't include our test CA.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_dir_nonexistent_falls_back() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
let dir = TempDir::new()?;
|
||||||
|
let missing = dir.path().join("missing-certs");
|
||||||
|
client()
|
||||||
|
.ssl_cert_dir(&missing)
|
||||||
|
.expect_https_connect_fails(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A valid `SSL_CERT_FILE` pointing to the server's CA cert is trusted.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_file_valid() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
client()
|
||||||
|
.ssl_cert_file(&cert.trust_path)
|
||||||
|
.expect_https_connect_succeeds(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A PEM bundle containing multiple certificates in `SSL_CERT_FILE` is loaded.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_file_bundle() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
let bundle = cert.write_bundle_pem();
|
||||||
|
client()
|
||||||
|
.ssl_cert_file(bundle.path())
|
||||||
|
.expect_https_connect_succeeds(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Certificates from both `SSL_CERT_FILE` and `SSL_CERT_DIR` are trusted.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_file_and_dir_combined() -> Result<()> {
|
||||||
|
let cert_a = TestCertificate::new()?;
|
||||||
|
let cert_b = TestCertificate::new()?;
|
||||||
|
|
||||||
|
let dir = cert_b.ca_pem_dir();
|
||||||
|
let c = client()
|
||||||
|
.ssl_cert_file(&cert_a.trust_path)
|
||||||
|
.ssl_cert_dir(dir.path());
|
||||||
|
c.expect_https_connect_succeeds(&cert_a).await;
|
||||||
|
c.expect_https_connect_succeeds(&cert_b).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PEM bundles inside `SSL_CERT_DIR` are loaded correctly.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_dir_bundle_files() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
let dir = cert.bundle_pem_dir();
|
||||||
|
client()
|
||||||
|
.ssl_cert_dir(dir.path())
|
||||||
|
.expect_https_connect_succeeds(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// OpenSSL hash-based filenames in `SSL_CERT_DIR` are loaded correctly.
|
||||||
|
///
|
||||||
|
/// The filename `5d30f3c5.3` is not the actual OpenSSL hash of the CA cert —
|
||||||
|
/// it's an arbitrary name matching the `[hex].[digit]` pattern to verify that
|
||||||
|
/// such files are loaded from the directory.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_dir_hash_named_files() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
let dir = cert.ca_pem_dir_as("5d30f3c5.3");
|
||||||
|
client()
|
||||||
|
.ssl_cert_dir(dir.path())
|
||||||
|
.expect_https_connect_succeeds(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `SSL_CERT_DIR` supports multiple platform-separated directories. Certs are
|
||||||
|
/// split across two directories; each only has one cert, but both are trusted.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_dir_multiple_directories() -> Result<()> {
|
||||||
|
let cert_a = TestCertificate::new()?;
|
||||||
|
let cert_b = TestCertificate::new()?;
|
||||||
|
|
||||||
|
let dir_a = cert_a.ca_pem_dir();
|
||||||
|
let dir_b = cert_b.ca_pem_dir();
|
||||||
|
let c = client().ssl_cert_dirs(&[dir_a.path(), dir_b.path()]);
|
||||||
|
c.expect_https_connect_succeeds(&cert_a).await;
|
||||||
|
c.expect_https_connect_succeeds(&cert_b).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Missing entries in `SSL_CERT_DIR` do not prevent valid directories from
|
||||||
|
/// being loaded.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ssl_cert_dir_multiple_directories_with_missing_entry() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
|
||||||
|
let dir = cert.ca_pem_dir();
|
||||||
|
let scratch = TempDir::new()?;
|
||||||
|
let missing = scratch.path().join("missing-certs");
|
||||||
|
|
||||||
|
client()
|
||||||
|
.ssl_cert_dirs(&[&missing, dir.path()])
|
||||||
|
.expect_https_connect_succeeds(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `SSL_CLIENT_CERT` with invalid content is ignored and the mTLS server
|
||||||
|
/// rejects the connection.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_mtls_with_invalid_client_cert() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
|
||||||
|
let mut invalid = NamedTempFile::new()?;
|
||||||
|
write!(invalid, "not a valid certificate or key")?;
|
||||||
|
|
||||||
|
client()
|
||||||
|
.ssl_cert_file(&cert.trust_path)
|
||||||
|
.ssl_client_cert(invalid.path())
|
||||||
|
.expect_mtls_connect_fails(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// mTLS succeeds when `SSL_CLIENT_CERT` contains a valid client certificate
|
||||||
|
/// and key.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_mtls_with_client_cert() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
client()
|
||||||
|
.ssl_cert_file(&cert.trust_path)
|
||||||
|
.ssl_client_cert(&cert.client_cert_path)
|
||||||
|
.expect_mtls_connect_succeeds(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// mTLS rejects a syntactically valid client certificate from the wrong trust
|
||||||
|
/// domain.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_mtls_with_wrong_client_cert() -> Result<()> {
|
||||||
|
let server_cert = TestCertificate::new()?;
|
||||||
|
let other_cert = TestCertificate::new()?;
|
||||||
|
client()
|
||||||
|
.ssl_cert_file(&server_cert.trust_path)
|
||||||
|
.ssl_client_cert(&other_cert.client_cert_path)
|
||||||
|
.expect_mtls_connect_fails_with_server_tls_error(&server_cert, |tls_err| {
|
||||||
|
assert!(
|
||||||
|
matches!(
|
||||||
|
tls_err,
|
||||||
|
rustls::Error::InvalidCertificate(
|
||||||
|
rustls::CertificateError::BadSignature
|
||||||
|
| rustls::CertificateError::UnknownIssuer
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"expected InvalidCertificate(BadSignature | UnknownIssuer), got: {tls_err}"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// mTLS rejects connections when no client certificate is presented.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_mtls_without_client_cert() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
client()
|
||||||
|
.ssl_cert_file(&cert.trust_path)
|
||||||
|
.expect_mtls_connect_fails(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When `system_certs` is enabled, `SSL_CERT_FILE` still overrides the
|
||||||
|
/// certificate source — a valid cert connects successfully.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_system_certs_with_ssl_cert_file_valid() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
client()
|
||||||
|
.system_certs(true)
|
||||||
|
.ssl_cert_file(&cert.trust_path)
|
||||||
|
.expect_https_connect_succeeds(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When `system_certs` is enabled, `SSL_CERT_DIR` still overrides the
|
||||||
|
/// certificate source.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_system_certs_with_ssl_cert_dir_valid() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
let dir = cert.ca_pem_dir();
|
||||||
|
client()
|
||||||
|
.system_certs(true)
|
||||||
|
.ssl_cert_dir(dir.path())
|
||||||
|
.expect_https_connect_succeeds(&cert)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Webpki roots include the CA for pypi.org, so a connection succeeds without
|
||||||
|
/// any custom configuration.
|
||||||
|
#[cfg(feature = "test-pypi")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_webpki_roots_trusts_pypi() -> Result<()> {
|
||||||
|
client()
|
||||||
|
.expect_https_connect_succeeds_for_host("pypi.org")
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// System certificate roots include the CA for pypi.org, so a connection
|
||||||
|
/// succeeds when `system_certs` is enabled.
|
||||||
|
#[cfg(feature = "test-pypi")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_system_certs_trusts_pypi() -> Result<()> {
|
||||||
|
client()
|
||||||
|
.system_certs(true)
|
||||||
|
.expect_https_connect_succeeds_for_host("pypi.org")
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When `system_certs` is enabled and `SSL_CERT_FILE` is set to a self-signed
|
||||||
|
/// CA, a public host (whose CA is in the system store but not in the override
|
||||||
|
/// file) is rejected — proving that `SSL_CERT_FILE` replaces rather than
|
||||||
|
/// supplements the system roots.
|
||||||
|
#[cfg(feature = "test-pypi")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_system_certs_with_ssl_cert_file_replaces_system_roots() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
client()
|
||||||
|
.system_certs(true)
|
||||||
|
.ssl_cert_file(&cert.trust_path)
|
||||||
|
.expect_https_connect_fails_for_host("pypi.org")
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When `system_certs` is enabled and `SSL_CERT_DIR` points to a directory
|
||||||
|
/// with only a self-signed CA, a public host (whose CA is in the system store
|
||||||
|
/// but not in the override directory) is rejected — proving that `SSL_CERT_DIR`
|
||||||
|
/// replaces rather than supplements the system roots.
|
||||||
|
#[cfg(feature = "test-pypi")]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_system_certs_with_ssl_cert_dir_replaces_system_roots() -> Result<()> {
|
||||||
|
let cert = TestCertificate::new()?;
|
||||||
|
let dir = cert.ca_pem_dir();
|
||||||
|
client()
|
||||||
|
.system_certs(true)
|
||||||
|
.ssl_cert_dir(dir.path())
|
||||||
|
.expect_https_connect_fails_for_host("pypi.org")
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub use required_version::*;
|
|||||||
pub use sources::*;
|
pub use sources::*;
|
||||||
pub use target_triple::*;
|
pub use target_triple::*;
|
||||||
pub use threading::*;
|
pub use threading::*;
|
||||||
|
|
||||||
pub use trusted_host::*;
|
pub use trusted_host::*;
|
||||||
pub use trusted_publishing::*;
|
pub use trusted_publishing::*;
|
||||||
pub use vcs::*;
|
pub use vcs::*;
|
||||||
@@ -46,6 +47,7 @@ mod required_version;
|
|||||||
mod sources;
|
mod sources;
|
||||||
mod target_triple;
|
mod target_triple;
|
||||||
mod threading;
|
mod threading;
|
||||||
|
|
||||||
mod trusted_host;
|
mod trusted_host;
|
||||||
mod trusted_publishing;
|
mod trusted_publishing;
|
||||||
mod vcs;
|
mod vcs;
|
||||||
|
|||||||
@@ -300,11 +300,13 @@ fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> {
|
|||||||
/// Validate that an [`Options`] contains no fields that `uv.toml` would mask
|
/// Validate that an [`Options`] contains no fields that `uv.toml` would mask
|
||||||
///
|
///
|
||||||
/// This is essentially the inverse of [`validate_uv_toml`].
|
/// This is essentially the inverse of [`validate_uv_toml`].
|
||||||
|
#[allow(deprecated)]
|
||||||
fn warn_uv_toml_masked_fields(options: &Options) {
|
fn warn_uv_toml_masked_fields(options: &Options) {
|
||||||
let Options {
|
let Options {
|
||||||
globals:
|
globals:
|
||||||
GlobalOptions {
|
GlobalOptions {
|
||||||
required_version,
|
required_version,
|
||||||
|
system_certs,
|
||||||
native_tls,
|
native_tls,
|
||||||
offline,
|
offline,
|
||||||
no_cache,
|
no_cache,
|
||||||
@@ -392,6 +394,9 @@ fn warn_uv_toml_masked_fields(options: &Options) {
|
|||||||
if required_version.is_some() {
|
if required_version.is_some() {
|
||||||
masked_fields.push("required-version");
|
masked_fields.push("required-version");
|
||||||
}
|
}
|
||||||
|
if system_certs.is_some() {
|
||||||
|
masked_fields.push("system-certs");
|
||||||
|
}
|
||||||
if native_tls.is_some() {
|
if native_tls.is_some() {
|
||||||
masked_fields.push("native-tls");
|
masked_fields.push("native-tls");
|
||||||
}
|
}
|
||||||
@@ -652,6 +657,7 @@ pub struct EnvironmentOptions {
|
|||||||
pub managed_python: EnvFlag,
|
pub managed_python: EnvFlag,
|
||||||
pub no_managed_python: EnvFlag,
|
pub no_managed_python: EnvFlag,
|
||||||
pub native_tls: EnvFlag,
|
pub native_tls: EnvFlag,
|
||||||
|
pub system_certs: EnvFlag,
|
||||||
pub preview: EnvFlag,
|
pub preview: EnvFlag,
|
||||||
pub isolated: EnvFlag,
|
pub isolated: EnvFlag,
|
||||||
pub no_progress: EnvFlag,
|
pub no_progress: EnvFlag,
|
||||||
@@ -746,6 +752,7 @@ impl EnvironmentOptions {
|
|||||||
managed_python: EnvFlag::new(EnvVars::UV_MANAGED_PYTHON)?,
|
managed_python: EnvFlag::new(EnvVars::UV_MANAGED_PYTHON)?,
|
||||||
no_managed_python: EnvFlag::new(EnvVars::UV_NO_MANAGED_PYTHON)?,
|
no_managed_python: EnvFlag::new(EnvVars::UV_NO_MANAGED_PYTHON)?,
|
||||||
native_tls: EnvFlag::new(EnvVars::UV_NATIVE_TLS)?,
|
native_tls: EnvFlag::new(EnvVars::UV_NATIVE_TLS)?,
|
||||||
|
system_certs: EnvFlag::new(EnvVars::UV_SYSTEM_CERTS)?,
|
||||||
preview: EnvFlag::new(EnvVars::UV_PREVIEW)?,
|
preview: EnvFlag::new(EnvVars::UV_PREVIEW)?,
|
||||||
isolated: EnvFlag::new(EnvVars::UV_ISOLATED)?,
|
isolated: EnvFlag::new(EnvVars::UV_ISOLATED)?,
|
||||||
no_progress: EnvFlag::new(EnvVars::UV_NO_PROGRESS)?,
|
no_progress: EnvFlag::new(EnvVars::UV_NO_PROGRESS)?,
|
||||||
|
|||||||
@@ -236,13 +236,24 @@ pub struct GlobalOptions {
|
|||||||
pub required_version: Option<RequiredVersion>,
|
pub required_version: Option<RequiredVersion>,
|
||||||
/// Whether to load TLS certificates from the platform's native certificate store.
|
/// Whether to load TLS certificates from the platform's native certificate store.
|
||||||
///
|
///
|
||||||
/// By default, uv loads certificates from the bundled `webpki-roots` crate. The
|
/// By default, uv uses bundled Mozilla root certificates. When enabled, this loads
|
||||||
/// `webpki-roots` are a reliable set of trust roots from Mozilla, and including them in uv
|
/// certificates from the platform's native certificate store instead.
|
||||||
/// improves portability and performance (especially on macOS).
|
#[option(
|
||||||
|
default = "false",
|
||||||
|
value_type = "bool",
|
||||||
|
uv_toml_only = true,
|
||||||
|
example = r#"
|
||||||
|
system-certs = true
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub system_certs: Option<bool>,
|
||||||
|
/// Whether to load TLS certificates from the platform's native certificate store.
|
||||||
///
|
///
|
||||||
/// However, in some cases, you may want to use the platform's native certificate store,
|
/// By default, uv uses bundled Mozilla root certificates. When enabled, this loads
|
||||||
/// especially if you're relying on a corporate trust root (e.g., for a mandatory proxy) that's
|
/// certificates from the platform's native certificate store instead.
|
||||||
/// included in your system's certificate store.
|
///
|
||||||
|
/// (Deprecated: use `system-certs` instead.)
|
||||||
|
#[deprecated(note = "use `system-certs` instead")]
|
||||||
#[option(
|
#[option(
|
||||||
default = "false",
|
default = "false",
|
||||||
value_type = "bool",
|
value_type = "bool",
|
||||||
@@ -2187,6 +2198,7 @@ pub struct OptionsWire {
|
|||||||
// #[serde(flatten)]
|
// #[serde(flatten)]
|
||||||
// globals: GlobalOptions
|
// globals: GlobalOptions
|
||||||
required_version: Option<RequiredVersion>,
|
required_version: Option<RequiredVersion>,
|
||||||
|
system_certs: Option<bool>,
|
||||||
native_tls: Option<bool>,
|
native_tls: Option<bool>,
|
||||||
offline: Option<bool>,
|
offline: Option<bool>,
|
||||||
no_cache: Option<bool>,
|
no_cache: Option<bool>,
|
||||||
@@ -2283,9 +2295,11 @@ pub struct OptionsWire {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<OptionsWire> for Options {
|
impl From<OptionsWire> for Options {
|
||||||
|
#[allow(deprecated)]
|
||||||
fn from(value: OptionsWire) -> Self {
|
fn from(value: OptionsWire) -> Self {
|
||||||
let OptionsWire {
|
let OptionsWire {
|
||||||
required_version,
|
required_version,
|
||||||
|
system_certs,
|
||||||
native_tls,
|
native_tls,
|
||||||
offline,
|
offline,
|
||||||
no_cache,
|
no_cache,
|
||||||
@@ -2362,6 +2376,7 @@ impl From<OptionsWire> for Options {
|
|||||||
Self {
|
Self {
|
||||||
globals: GlobalOptions {
|
globals: GlobalOptions {
|
||||||
required_version,
|
required_version,
|
||||||
|
system_certs,
|
||||||
native_tls,
|
native_tls,
|
||||||
offline,
|
offline,
|
||||||
no_cache,
|
no_cache,
|
||||||
|
|||||||
@@ -108,10 +108,17 @@ impl EnvVars {
|
|||||||
pub const UV_BREAK_SYSTEM_PACKAGES: &'static str = "UV_BREAK_SYSTEM_PACKAGES";
|
pub const UV_BREAK_SYSTEM_PACKAGES: &'static str = "UV_BREAK_SYSTEM_PACKAGES";
|
||||||
|
|
||||||
/// Equivalent to the `--native-tls` command-line argument. If set to `true`, uv will
|
/// Equivalent to the `--native-tls` command-line argument. If set to `true`, uv will
|
||||||
/// use the system's trust store instead of the bundled `webpki-roots` crate.
|
/// load TLS certificates from the platform's native certificate store instead of the
|
||||||
|
/// bundled Mozilla root certificates.
|
||||||
#[attr_added_in("0.1.19")]
|
#[attr_added_in("0.1.19")]
|
||||||
pub const UV_NATIVE_TLS: &'static str = "UV_NATIVE_TLS";
|
pub const UV_NATIVE_TLS: &'static str = "UV_NATIVE_TLS";
|
||||||
|
|
||||||
|
/// Equivalent to the `--system-certs` command-line argument. If set to `true`, uv will
|
||||||
|
/// load TLS certificates from the platform's native certificate store instead of the
|
||||||
|
/// bundled Mozilla root certificates.
|
||||||
|
#[attr_added_in("next release")]
|
||||||
|
pub const UV_SYSTEM_CERTS: &'static str = "UV_SYSTEM_CERTS";
|
||||||
|
|
||||||
/// Equivalent to the `--index-strategy` command-line argument.
|
/// Equivalent to the `--index-strategy` command-line argument.
|
||||||
///
|
///
|
||||||
/// For example, if set to `unsafe-best-match`, uv will consider versions of a given package
|
/// For example, if set to `unsafe-best-match`, uv will consider versions of a given package
|
||||||
@@ -647,17 +654,31 @@ impl EnvVars {
|
|||||||
#[attr_added_in("0.2.16")]
|
#[attr_added_in("0.2.16")]
|
||||||
pub const XDG_BIN_HOME: &'static str = "XDG_BIN_HOME";
|
pub const XDG_BIN_HOME: &'static str = "XDG_BIN_HOME";
|
||||||
|
|
||||||
/// Custom certificate bundle file path for SSL connections.
|
/// Path to a CA certificate bundle file for TLS connections.
|
||||||
///
|
///
|
||||||
/// Takes precedence over `UV_NATIVE_TLS` when set.
|
/// Requires a PEM-encoded certificate file (e.g., `certs.pem`, `ca-bundle.crt`). DER-encoded
|
||||||
|
/// files are not supported.
|
||||||
|
///
|
||||||
|
/// When set, this overrides the default certificate source (bundled Mozilla roots or system
|
||||||
|
/// certificates). Only the certificates in this file will be trusted.
|
||||||
#[attr_added_in("0.1.14")]
|
#[attr_added_in("0.1.14")]
|
||||||
pub const SSL_CERT_FILE: &'static str = "SSL_CERT_FILE";
|
pub const SSL_CERT_FILE: &'static str = "SSL_CERT_FILE";
|
||||||
|
|
||||||
/// Custom path for certificate bundles for SSL connections.
|
/// Path to a directory containing PEM-encoded CA certificate files for TLS connections.
|
||||||
/// Multiple entries are supported separated using a platform-specific
|
|
||||||
/// delimiter (`:` on Unix, `;` on Windows).
|
|
||||||
///
|
///
|
||||||
/// Takes precedence over `UV_NATIVE_TLS` when set.
|
/// Multiple entries are supported, separated using a platform-specific delimiter (`:` on Unix,
|
||||||
|
/// `;` on Windows).
|
||||||
|
///
|
||||||
|
/// Certificates are usually stored with `.pem`, `.crt`, or `.cer` extensions, but uv will
|
||||||
|
/// attempt to read a certificate from any regular file in the provided `SSL_CERT_DIR`.
|
||||||
|
///
|
||||||
|
/// Files that cannot be parsed as PEM certificates are ignored. uv resolves symlinks and
|
||||||
|
/// ignores dangling symlinks.
|
||||||
|
///
|
||||||
|
/// Only PEM-encoded files are supported, i.e., DER-encoded files are not supported.
|
||||||
|
///
|
||||||
|
/// When set, this overrides the default certificate source (bundled Mozilla roots or system
|
||||||
|
/// certificates). Only the certificates in this directory will be trusted.
|
||||||
#[attr_added_in("0.9.10")]
|
#[attr_added_in("0.9.10")]
|
||||||
pub const SSL_CERT_DIR: &'static str = "SSL_CERT_DIR";
|
pub const SSL_CERT_DIR: &'static str = "SSL_CERT_DIR";
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ pub const INSTA_FILTERS: &[(&str, &str)] = &[
|
|||||||
),
|
),
|
||||||
// Trim end-of-line whitespaces, to allow removing them on save.
|
// Trim end-of-line whitespaces, to allow removing them on save.
|
||||||
(r"([^\s])[ \t]+(\r?\n)", "$1$2"),
|
(r"([^\s])[ \t]+(\r?\n)", "$1$2"),
|
||||||
|
// Filter SSL certificate loading debug messages (environment-dependent)
|
||||||
|
(r"DEBUG Loaded \d+ certificate\(s\) from [^\n]+\n", ""),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Create a context for tests which simplifies shared behavior across tests.
|
/// Create a context for tests which simplifies shared behavior across tests.
|
||||||
@@ -1954,6 +1956,7 @@ impl TestContext {
|
|||||||
EnvVars::SSL_CERT_DIR,
|
EnvVars::SSL_CERT_DIR,
|
||||||
EnvVars::SSL_CERT_FILE,
|
EnvVars::SSL_CERT_FILE,
|
||||||
EnvVars::UV_NATIVE_TLS,
|
EnvVars::UV_NATIVE_TLS,
|
||||||
|
EnvVars::UV_SYSTEM_CERTS,
|
||||||
];
|
];
|
||||||
|
|
||||||
for env_var in EnvVars::all_names()
|
for env_var in EnvVars::all_names()
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ test-git = ["uv-test?/git"]
|
|||||||
# Introduces a testing dependency on Git LFS.
|
# Introduces a testing dependency on Git LFS.
|
||||||
test-git-lfs = ["test-git"]
|
test-git-lfs = ["test-git"]
|
||||||
# Introduces a testing dependency on PyPI.
|
# Introduces a testing dependency on PyPI.
|
||||||
test-pypi = []
|
test-pypi = ["uv-client/test-pypi"]
|
||||||
# Introduces a testing dependency on R2.
|
# Introduces a testing dependency on R2.
|
||||||
test-r2 = []
|
test-r2 = []
|
||||||
# Introduces a testing dependency on a local Python installation.
|
# Introduces a testing dependency on a local Python installation.
|
||||||
|
|||||||
@@ -34,18 +34,18 @@ static SUGGESTIONS: LazyLock<FxHashMap<PackageName, PackageName>> = LazyLock::ne
|
|||||||
pub(crate) struct OperationDiagnostic {
|
pub(crate) struct OperationDiagnostic {
|
||||||
/// The hint to display to the user upon resolution failure.
|
/// The hint to display to the user upon resolution failure.
|
||||||
pub(crate) hint: Option<String>,
|
pub(crate) hint: Option<String>,
|
||||||
/// Whether native TLS is enabled.
|
/// Whether system certificates are being used.
|
||||||
pub(crate) native_tls: bool,
|
pub(crate) system_certs: bool,
|
||||||
/// The context to display to the user upon resolution failure.
|
/// The context to display to the user upon resolution failure.
|
||||||
pub(crate) context: Option<&'static str>,
|
pub(crate) context: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OperationDiagnostic {
|
impl OperationDiagnostic {
|
||||||
/// Create an [`OperationDiagnostic`] with the given native TLS setting.
|
/// Create an [`OperationDiagnostic`] with the given system certificates setting.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn native_tls(native_tls: bool) -> Self {
|
pub(crate) fn with_system_certs(system_certs: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
native_tls,
|
system_certs,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,9 +131,9 @@ impl OperationDiagnostic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pip::operations::Error::Resolve(uv_resolver::ResolveError::Client(err))
|
pip::operations::Error::Resolve(uv_resolver::ResolveError::Client(err))
|
||||||
if !self.native_tls && err.is_ssl() =>
|
if !self.system_certs && err.is_ssl() =>
|
||||||
{
|
{
|
||||||
native_tls_hint(err);
|
system_certs_hint(err);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
pip::operations::Error::OutdatedEnvironment => {
|
pip::operations::Error::OutdatedEnvironment => {
|
||||||
@@ -335,7 +335,7 @@ pub(crate) fn no_solution_hint(err: Box<uv_resolver::NoSolutionError>, help: Str
|
|||||||
/// Render a [`uv_resolver::NoSolutionError`] with a help message.
|
/// Render a [`uv_resolver::NoSolutionError`] with a help message.
|
||||||
// https://github.com/rust-lang/rust/issues/147648
|
// https://github.com/rust-lang/rust/issues/147648
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
pub(crate) fn native_tls_hint(err: uv_client::Error) {
|
pub(crate) fn system_certs_hint(err: uv_client::Error) {
|
||||||
#[derive(Debug, miette::Diagnostic)]
|
#[derive(Debug, miette::Diagnostic)]
|
||||||
#[diagnostic()]
|
#[diagnostic()]
|
||||||
struct Error {
|
struct Error {
|
||||||
@@ -363,7 +363,7 @@ pub(crate) fn native_tls_hint(err: uv_client::Error) {
|
|||||||
err,
|
err,
|
||||||
help: format!(
|
help: format!(
|
||||||
"Consider enabling use of system TLS certificates with the `{}` command-line flag",
|
"Consider enabling use of system TLS certificates with the `{}` command-line flag",
|
||||||
"--native-tls".green()
|
"--system-certs".green()
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
anstream::eprint!("{report:?}");
|
anstream::eprint!("{report:?}");
|
||||||
|
|||||||
@@ -597,9 +597,11 @@ pub(crate) async fn pip_compile(
|
|||||||
{
|
{
|
||||||
Ok(resolution) => resolution,
|
Ok(resolution) => resolution,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -607,8 +607,8 @@ pub(crate) async fn pip_install(
|
|||||||
{
|
{
|
||||||
Ok(graph) => Resolution::from(graph),
|
Ok(graph) => Resolution::from(graph),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
@@ -675,9 +675,11 @@ pub(crate) async fn pip_install(
|
|||||||
{
|
{
|
||||||
Ok(..) => {}
|
Ok(..) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -498,8 +498,8 @@ pub(crate) async fn pip_sync(
|
|||||||
{
|
{
|
||||||
Ok(resolution) => Resolution::from(resolution),
|
Ok(resolution) => Resolution::from(resolution),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
@@ -566,9 +566,11 @@ pub(crate) async fn pip_sync(
|
|||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -764,7 +764,7 @@ pub(crate) async fn add(
|
|||||||
let _ = snapshot.revert();
|
let _ = snapshot.revert();
|
||||||
}
|
}
|
||||||
match err {
|
match err {
|
||||||
ProjectError::Operation(err) => diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls()).with_hint(format!("If you want to add the package regardless of the failed resolution, provide the `{}` flag to skip locking and syncing.", "--frozen".green()))
|
ProjectError::Operation(err) => diagnostics::OperationDiagnostic::with_system_certs(client_builder.system_certs()).with_hint(format!("If you want to add the package regardless of the failed resolution, provide the `{}` flag to skip locking and syncing.", "--frozen".green()))
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into())),
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into())),
|
||||||
err => Err(err.into()),
|
err => Err(err.into()),
|
||||||
|
|||||||
@@ -167,9 +167,11 @@ pub(crate) async fn audit(
|
|||||||
{
|
{
|
||||||
Ok(result) => result.into_lock(),
|
Ok(result) => result.into_lock(),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -221,9 +221,11 @@ pub(crate) async fn export(
|
|||||||
{
|
{
|
||||||
Ok(result) => result.into_lock(),
|
Ok(result) => result.into_lock(),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ pub(crate) async fn lock(
|
|||||||
Ok(ExitStatus::Failure)
|
Ok(ExitStatus::Failure)
|
||||||
}
|
}
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
diagnostics::OperationDiagnostic::with_system_certs(client_builder.system_certs())
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()))
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,9 +316,11 @@ pub(crate) async fn remove(
|
|||||||
{
|
{
|
||||||
Ok(result) => result.into_lock(),
|
Ok(result) => result.into_lock(),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
@@ -373,9 +375,11 @@ pub(crate) async fn remove(
|
|||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,8 +294,8 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
|||||||
{
|
{
|
||||||
Ok(result) => result.into_lock(),
|
Ok(result) => result.into_lock(),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.with_context("script")
|
.with_context("script")
|
||||||
.report(err)
|
.report(err)
|
||||||
@@ -341,8 +341,8 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
|||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.with_context("script")
|
.with_context("script")
|
||||||
.report(err)
|
.report(err)
|
||||||
@@ -458,8 +458,8 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
|||||||
{
|
{
|
||||||
Ok(update) => Some(update.into_environment().into_interpreter()),
|
Ok(update) => Some(update.into_environment().into_interpreter()),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.with_context("script")
|
.with_context("script")
|
||||||
.report(err)
|
.report(err)
|
||||||
@@ -794,8 +794,8 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
|||||||
{
|
{
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
@@ -882,8 +882,8 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
|||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
@@ -1037,8 +1037,8 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
|||||||
let environment = match result {
|
let environment = match result {
|
||||||
Ok(resolution) => resolution,
|
Ok(resolution) => resolution,
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.with_context("`--with`")
|
.with_context("`--with`")
|
||||||
.report(err)
|
.report(err)
|
||||||
|
|||||||
@@ -305,8 +305,8 @@ pub(crate) async fn sync(
|
|||||||
}
|
}
|
||||||
// TODO(zanieb): We should respect `--output-format json` for the error case
|
// TODO(zanieb): We should respect `--output-format json` for the error case
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
@@ -354,9 +354,11 @@ pub(crate) async fn sync(
|
|||||||
{
|
{
|
||||||
Ok(result) => Outcome::Success(result),
|
Ok(result) => Outcome::Success(result),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(ProjectError::LockMismatch(prev, cur, lock_source)) => {
|
Err(ProjectError::LockMismatch(prev, cur, lock_source)) => {
|
||||||
if dry_run.enabled() {
|
if dry_run.enabled() {
|
||||||
@@ -430,9 +432,11 @@ pub(crate) async fn sync(
|
|||||||
{
|
{
|
||||||
Ok(changelog) => changelog,
|
Ok(changelog) => changelog,
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -158,9 +158,11 @@ pub(crate) async fn tree(
|
|||||||
{
|
{
|
||||||
Ok(result) => result.into_lock(),
|
Ok(result) => result.into_lock(),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -509,9 +509,11 @@ async fn print_frozen_version(
|
|||||||
{
|
{
|
||||||
Ok(result) => result.into_lock(),
|
Ok(result) => result.into_lock(),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
@@ -650,9 +652,11 @@ async fn lock_and_sync(
|
|||||||
{
|
{
|
||||||
Ok(result) => result.into_lock(),
|
Ok(result) => result.into_lock(),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
@@ -709,9 +713,11 @@ async fn lock_and_sync(
|
|||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
.report(err)
|
client_builder.system_certs(),
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
)
|
||||||
|
.report(err)
|
||||||
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use axoupdater::{AxoUpdater, AxoupdateError, UpdateRequest};
|
|||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use uv_client::{BaseClientBuilder, WrappedReqwestError};
|
use uv_client::BaseClientBuilder;
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
|
|
||||||
use crate::commands::ExitStatus;
|
use crate::commands::ExitStatus;
|
||||||
@@ -210,7 +210,7 @@ pub(crate) async fn self_update(
|
|||||||
)?;
|
)?;
|
||||||
Ok(ExitStatus::Error)
|
Ok(ExitStatus::Error)
|
||||||
} else {
|
} else {
|
||||||
Err(WrappedReqwestError::from(err).into())
|
Err(err.into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(err.into())
|
Err(err.into())
|
||||||
|
|||||||
@@ -594,8 +594,8 @@ pub(crate) async fn install(
|
|||||||
{
|
{
|
||||||
Ok(update) => update.into_environment(),
|
Ok(update) => update.into_environment(),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
@@ -658,8 +658,8 @@ pub(crate) async fn install(
|
|||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.flatten() else {
|
.flatten() else {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
@@ -690,8 +690,8 @@ pub(crate) async fn install(
|
|||||||
{
|
{
|
||||||
Ok(resolution) => (resolution, interpreter),
|
Ok(resolution) => (resolution, interpreter),
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
@@ -735,8 +735,8 @@ pub(crate) async fn install(
|
|||||||
}) {
|
}) {
|
||||||
Ok(environment) => environment,
|
Ok(environment) => environment,
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
|
|||||||
@@ -319,8 +319,8 @@ pub(crate) async fn run(
|
|||||||
// If the user ran `uvx run ...`, the `run` is likely a mistake. Show a dedicated hint.
|
// If the user ran `uvx run ...`, the `run` is likely a mistake. Show a dedicated hint.
|
||||||
if from.is_none() && invocation_source == ToolRunCommand::Uvx && target == "run" {
|
if from.is_none() && invocation_source == ToolRunCommand::Uvx && target == "run" {
|
||||||
let rest = args.iter().map(|s| s.to_string_lossy()).join(" ");
|
let rest = args.iter().map(|s| s.to_string_lossy()).join(" ");
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::with_system_certs(
|
||||||
client_builder.is_native_tls(),
|
client_builder.system_certs(),
|
||||||
)
|
)
|
||||||
.with_hint(format!(
|
.with_hint(format!(
|
||||||
"`{}` invokes the `{}` package. Did you mean `{}`?",
|
"`{}` invokes the `{}` package. Did you mean `{}`?",
|
||||||
@@ -334,7 +334,7 @@ pub(crate) async fn run(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let diagnostic =
|
let diagnostic =
|
||||||
diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls());
|
diagnostics::OperationDiagnostic::with_system_certs(client_builder.system_certs());
|
||||||
let diagnostic = if let Some(verbose_flag) = find_verbose_flag(args) {
|
let diagnostic = if let Some(verbose_flag) = find_verbose_flag(args) {
|
||||||
diagnostic.with_hint(format!(
|
diagnostic.with_hint(format!(
|
||||||
"You provided `{}` to `{}`. Did you mean to provide it to `{}`? e.g., `{}`",
|
"You provided `{}` to `{}`. Did you mean to provide it to `{}`? e.g., `{}`",
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||||||
);
|
);
|
||||||
let client_builder = BaseClientBuilder::new(
|
let client_builder = BaseClientBuilder::new(
|
||||||
settings.network_settings.connectivity,
|
settings.network_settings.connectivity,
|
||||||
settings.network_settings.native_tls,
|
settings.network_settings.system_certs,
|
||||||
settings.network_settings.allow_insecure_host,
|
settings.network_settings.allow_insecure_host,
|
||||||
settings.preview,
|
settings.preview,
|
||||||
settings.network_settings.read_timeout,
|
settings.network_settings.read_timeout,
|
||||||
@@ -556,7 +556,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||||||
// Configure the global network settings.
|
// Configure the global network settings.
|
||||||
let client_builder = BaseClientBuilder::new(
|
let client_builder = BaseClientBuilder::new(
|
||||||
globals.network_settings.connectivity,
|
globals.network_settings.connectivity,
|
||||||
globals.network_settings.native_tls,
|
globals.network_settings.system_certs,
|
||||||
globals.network_settings.allow_insecure_host.clone(),
|
globals.network_settings.allow_insecure_host.clone(),
|
||||||
globals.preview,
|
globals.preview,
|
||||||
globals.network_settings.read_timeout,
|
globals.network_settings.read_timeout,
|
||||||
|
|||||||
+30
-14
@@ -241,7 +241,7 @@ pub(crate) fn resolve_preview(
|
|||||||
pub(crate) struct NetworkSettings {
|
pub(crate) struct NetworkSettings {
|
||||||
pub(crate) connectivity: Connectivity,
|
pub(crate) connectivity: Connectivity,
|
||||||
pub(crate) offline: Flag,
|
pub(crate) offline: Flag,
|
||||||
pub(crate) native_tls: bool,
|
pub(crate) system_certs: bool,
|
||||||
pub(crate) http_proxy: Option<ProxyUrl>,
|
pub(crate) http_proxy: Option<ProxyUrl>,
|
||||||
pub(crate) https_proxy: Option<ProxyUrl>,
|
pub(crate) https_proxy: Option<ProxyUrl>,
|
||||||
pub(crate) no_proxy: Option<Vec<String>>,
|
pub(crate) no_proxy: Option<Vec<String>>,
|
||||||
@@ -252,6 +252,7 @@ pub(crate) struct NetworkSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkSettings {
|
impl NetworkSettings {
|
||||||
|
#[allow(deprecated)]
|
||||||
pub(crate) fn resolve(
|
pub(crate) fn resolve(
|
||||||
args: &GlobalArgs,
|
args: &GlobalArgs,
|
||||||
workspace: Option<&FilesystemOptions>,
|
workspace: Option<&FilesystemOptions>,
|
||||||
@@ -284,18 +285,33 @@ impl NetworkSettings {
|
|||||||
} else {
|
} else {
|
||||||
Connectivity::Online
|
Connectivity::Online
|
||||||
};
|
};
|
||||||
let native_tls = match flag(args.native_tls, args.no_native_tls, "native-tls") {
|
|
||||||
Some(value) => value,
|
// Resolve whether to use system certificates.
|
||||||
None => {
|
//
|
||||||
if environment.native_tls.value == Some(true) {
|
// `--native-tls` is a legacy alias for `--system-certs` — it enables system certificates
|
||||||
true
|
// but does NOT change the TLS backend. Any explicit CLI setting should take precedence
|
||||||
} else {
|
// over environment variables and workspace configuration, regardless of which spelling is
|
||||||
workspace
|
// used.
|
||||||
.and_then(|workspace| workspace.globals.native_tls)
|
let system_certs =
|
||||||
.unwrap_or(false)
|
if let Some(value) = flag(args.system_certs, args.no_system_certs, "system-certs") {
|
||||||
}
|
value
|
||||||
}
|
} else if let Some(value) = flag(args.native_tls, args.no_native_tls, "native-tls") {
|
||||||
};
|
value
|
||||||
|
} else if let Some(true) = environment.system_certs.value {
|
||||||
|
true
|
||||||
|
} else if let Some(true) = environment.native_tls.value {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
workspace
|
||||||
|
.and_then(|workspace| {
|
||||||
|
workspace
|
||||||
|
.globals
|
||||||
|
.system_certs
|
||||||
|
.or(workspace.globals.native_tls)
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
};
|
||||||
|
|
||||||
let allow_insecure_host = args
|
let allow_insecure_host = args
|
||||||
.allow_insecure_host
|
.allow_insecure_host
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -320,7 +336,7 @@ impl NetworkSettings {
|
|||||||
Self {
|
Self {
|
||||||
connectivity,
|
connectivity,
|
||||||
offline,
|
offline,
|
||||||
native_tls,
|
system_certs,
|
||||||
http_proxy,
|
http_proxy,
|
||||||
https_proxy,
|
https_proxy,
|
||||||
no_proxy,
|
no_proxy,
|
||||||
|
|||||||
+31
-28
@@ -57,8 +57,9 @@ fn help() {
|
|||||||
Use verbose output
|
Use verbose output
|
||||||
--color <COLOR_CHOICE>
|
--color <COLOR_CHOICE>
|
||||||
Control the use of color in output [possible values: auto, always, never]
|
Control the use of color in output [possible values: auto, always, never]
|
||||||
--native-tls
|
--system-certs
|
||||||
Whether to load TLS certificates from the platform's native store [env: UV_NATIVE_TLS=]
|
Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
UV_SYSTEM_CERTS=]
|
||||||
--offline
|
--offline
|
||||||
Disable network access [env: UV_OFFLINE=]
|
Disable network access [env: UV_OFFLINE=]
|
||||||
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
||||||
@@ -138,8 +139,9 @@ fn help_flag() {
|
|||||||
Use verbose output
|
Use verbose output
|
||||||
--color <COLOR_CHOICE>
|
--color <COLOR_CHOICE>
|
||||||
Control the use of color in output [possible values: auto, always, never]
|
Control the use of color in output [possible values: auto, always, never]
|
||||||
--native-tls
|
--system-certs
|
||||||
Whether to load TLS certificates from the platform's native store [env: UV_NATIVE_TLS=]
|
Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
UV_SYSTEM_CERTS=]
|
||||||
--offline
|
--offline
|
||||||
Disable network access [env: UV_OFFLINE=]
|
Disable network access [env: UV_OFFLINE=]
|
||||||
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
||||||
@@ -218,8 +220,9 @@ fn help_short_flag() {
|
|||||||
Use verbose output
|
Use verbose output
|
||||||
--color <COLOR_CHOICE>
|
--color <COLOR_CHOICE>
|
||||||
Control the use of color in output [possible values: auto, always, never]
|
Control the use of color in output [possible values: auto, always, never]
|
||||||
--native-tls
|
--system-certs
|
||||||
Whether to load TLS certificates from the platform's native store [env: UV_NATIVE_TLS=]
|
Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
UV_SYSTEM_CERTS=]
|
||||||
--offline
|
--offline
|
||||||
Disable network access [env: UV_OFFLINE=]
|
Disable network access [env: UV_OFFLINE=]
|
||||||
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
||||||
@@ -368,19 +371,17 @@ fn help_subcommand() {
|
|||||||
- always: Enables colored output regardless of the detected environment
|
- always: Enables colored output regardless of the detected environment
|
||||||
- never: Disables colored output
|
- never: Disables colored output
|
||||||
|
|
||||||
--native-tls
|
--system-certs
|
||||||
Whether to load TLS certificates from the platform's native store.
|
Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
UV_SYSTEM_CERTS=]
|
||||||
|
|
||||||
By default, uv loads certificates from the bundled `webpki-roots` crate. The
|
By default, uv uses bundled Mozilla root certificates, which improves portability and
|
||||||
`webpki-roots` are a reliable set of trust roots from Mozilla, and including them in uv
|
performance (especially on macOS).
|
||||||
improves portability and performance (especially on macOS).
|
|
||||||
|
|
||||||
However, in some cases, you may want to use the platform's native certificate store,
|
However, in some cases, you may want to use the platform's native certificate store,
|
||||||
especially if you're relying on a corporate trust root (e.g., for a mandatory proxy)
|
especially if you're relying on a corporate trust root (e.g., for a mandatory proxy)
|
||||||
that's included in your system's certificate store.
|
that's included in your system's certificate store.
|
||||||
|
|
||||||
[env: UV_NATIVE_TLS=]
|
|
||||||
|
|
||||||
--offline
|
--offline
|
||||||
Disable network access.
|
Disable network access.
|
||||||
|
|
||||||
@@ -654,19 +655,17 @@ fn help_subsubcommand() {
|
|||||||
- always: Enables colored output regardless of the detected environment
|
- always: Enables colored output regardless of the detected environment
|
||||||
- never: Disables colored output
|
- never: Disables colored output
|
||||||
|
|
||||||
--native-tls
|
--system-certs
|
||||||
Whether to load TLS certificates from the platform's native store.
|
Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
UV_SYSTEM_CERTS=]
|
||||||
|
|
||||||
By default, uv loads certificates from the bundled `webpki-roots` crate. The
|
By default, uv uses bundled Mozilla root certificates, which improves portability and
|
||||||
`webpki-roots` are a reliable set of trust roots from Mozilla, and including them in uv
|
performance (especially on macOS).
|
||||||
improves portability and performance (especially on macOS).
|
|
||||||
|
|
||||||
However, in some cases, you may want to use the platform's native certificate store,
|
However, in some cases, you may want to use the platform's native certificate store,
|
||||||
especially if you're relying on a corporate trust root (e.g., for a mandatory proxy)
|
especially if you're relying on a corporate trust root (e.g., for a mandatory proxy)
|
||||||
that's included in your system's certificate store.
|
that's included in your system's certificate store.
|
||||||
|
|
||||||
[env: UV_NATIVE_TLS=]
|
|
||||||
|
|
||||||
--offline
|
--offline
|
||||||
Disable network access.
|
Disable network access.
|
||||||
|
|
||||||
@@ -783,8 +782,9 @@ fn help_flag_subcommand() {
|
|||||||
Use verbose output
|
Use verbose output
|
||||||
--color <COLOR_CHOICE>
|
--color <COLOR_CHOICE>
|
||||||
Control the use of color in output [possible values: auto, always, never]
|
Control the use of color in output [possible values: auto, always, never]
|
||||||
--native-tls
|
--system-certs
|
||||||
Whether to load TLS certificates from the platform's native store [env: UV_NATIVE_TLS=]
|
Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
UV_SYSTEM_CERTS=]
|
||||||
--offline
|
--offline
|
||||||
Disable network access [env: UV_OFFLINE=]
|
Disable network access [env: UV_OFFLINE=]
|
||||||
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
||||||
@@ -866,8 +866,9 @@ fn help_flag_subsubcommand() {
|
|||||||
Use verbose output
|
Use verbose output
|
||||||
--color <COLOR_CHOICE>
|
--color <COLOR_CHOICE>
|
||||||
Control the use of color in output [possible values: auto, always, never]
|
Control the use of color in output [possible values: auto, always, never]
|
||||||
--native-tls
|
--system-certs
|
||||||
Whether to load TLS certificates from the platform's native store [env: UV_NATIVE_TLS=]
|
Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
UV_SYSTEM_CERTS=]
|
||||||
--offline
|
--offline
|
||||||
Disable network access [env: UV_OFFLINE=]
|
Disable network access [env: UV_OFFLINE=]
|
||||||
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
||||||
@@ -1030,8 +1031,9 @@ fn help_with_global_option() {
|
|||||||
Use verbose output
|
Use verbose output
|
||||||
--color <COLOR_CHOICE>
|
--color <COLOR_CHOICE>
|
||||||
Control the use of color in output [possible values: auto, always, never]
|
Control the use of color in output [possible values: auto, always, never]
|
||||||
--native-tls
|
--system-certs
|
||||||
Whether to load TLS certificates from the platform's native store [env: UV_NATIVE_TLS=]
|
Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
UV_SYSTEM_CERTS=]
|
||||||
--offline
|
--offline
|
||||||
Disable network access [env: UV_OFFLINE=]
|
Disable network access [env: UV_OFFLINE=]
|
||||||
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
||||||
@@ -1153,8 +1155,9 @@ fn help_with_no_pager() {
|
|||||||
Use verbose output
|
Use verbose output
|
||||||
--color <COLOR_CHOICE>
|
--color <COLOR_CHOICE>
|
||||||
Control the use of color in output [possible values: auto, always, never]
|
Control the use of color in output [possible values: auto, always, never]
|
||||||
--native-tls
|
--system-certs
|
||||||
Whether to load TLS certificates from the platform's native store [env: UV_NATIVE_TLS=]
|
Whether to load TLS certificates from the platform's native certificate store [env:
|
||||||
|
UV_SYSTEM_CERTS=]
|
||||||
--offline
|
--offline
|
||||||
Disable network access [env: UV_OFFLINE=]
|
Disable network access [env: UV_OFFLINE=]
|
||||||
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
--allow-insecure-host <ALLOW_INSECURE_HOST>
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ fn invalid_pyproject_toml_option_unknown_field() -> Result<()> {
|
|||||||
|
|
|
|
||||||
2 | unknown = "field"
|
2 | unknown = "field"
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
unknown field `unknown`, expected one of `required-version`, `native-tls`, [...]
|
unknown field `unknown`, expected one of `required-version`, `system-certs`, `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `http-proxy`, `https-proxy`, `no-proxy`, `allow-insecure-host`, `resolution`, `prerelease`, `fork-strategy`, `dependency-metadata`, `config-settings`, `config-settings-package`, `no-build-isolation`, `no-build-isolation-package`, `extra-build-dependencies`, `extra-build-variables`, `exclude-newer`, `exclude-newer-package`, `link-mode`, `compile-bytecode`, `no-sources`, `no-sources-package`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `torch-backend`, `python-install-mirror`, `pypy-install-mirror`, `python-downloads-json-url`, `publish-url`, `trusted-publishing`, `check-url`, `add-bounds`, `pip`, `cache-keys`, `override-dependencies`, `exclude-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dependency-groups`, `dev-dependencies`, `build-backend`
|
||||||
|
|
||||||
Resolved in [TIME]
|
Resolved in [TIME]
|
||||||
Checked in [TIME]
|
Checked in [TIME]
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -272,7 +272,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -484,7 +484,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -728,7 +728,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -941,7 +941,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -1128,7 +1128,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -1364,7 +1364,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -1608,7 +1608,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -1910,7 +1910,7 @@ fn resolve_find_links() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -2143,7 +2143,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -2335,7 +2335,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -2577,7 +2577,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -2842,7 +2842,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -3024,7 +3024,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -3206,7 +3206,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -3390,7 +3390,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -3593,7 +3593,7 @@ fn resolve_tool() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -3791,7 +3791,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -4007,7 +4007,7 @@ fn resolve_both() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -4264,7 +4264,7 @@ fn resolve_both_special_fields() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -4600,7 +4600,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -4821,7 +4821,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
|
|||||||
|
|
|
|
||||||
1 | [project]
|
1 | [project]
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
unknown field `project`, expected one of `required-version`, `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `http-proxy`, `https-proxy`, `no-proxy`, `allow-insecure-host`, `resolution`, `prerelease`, `fork-strategy`, `dependency-metadata`, `config-settings`, `config-settings-package`, `no-build-isolation`, `no-build-isolation-package`, `extra-build-dependencies`, `extra-build-variables`, `exclude-newer`, `exclude-newer-package`, `link-mode`, `compile-bytecode`, `no-sources`, `no-sources-package`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `torch-backend`, `python-install-mirror`, `pypy-install-mirror`, `python-downloads-json-url`, `publish-url`, `trusted-publishing`, `check-url`, `add-bounds`, `pip`, `cache-keys`, `override-dependencies`, `exclude-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dependency-groups`, `dev-dependencies`, `build-backend`
|
unknown field `project`, expected one of `required-version`, `system-certs`, `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `http-proxy`, `https-proxy`, `no-proxy`, `allow-insecure-host`, `resolution`, `prerelease`, `fork-strategy`, `dependency-metadata`, `config-settings`, `config-settings-package`, `no-build-isolation`, `no-build-isolation-package`, `extra-build-dependencies`, `extra-build-variables`, `exclude-newer`, `exclude-newer-package`, `link-mode`, `compile-bytecode`, `no-sources`, `no-sources-package`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `torch-backend`, `python-install-mirror`, `pypy-install-mirror`, `python-downloads-json-url`, `publish-url`, `trusted-publishing`, `check-url`, `add-bounds`, `pip`, `cache-keys`, `override-dependencies`, `exclude-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dependency-groups`, `dev-dependencies`, `build-backend`
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4909,7 +4909,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -5094,7 +5094,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -5287,7 +5287,7 @@ fn allow_insecure_host() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -5494,7 +5494,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -5740,7 +5740,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -5992,7 +5992,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -6239,7 +6239,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -6493,7 +6493,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -6740,7 +6740,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -7000,7 +7000,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -7175,7 +7175,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -7348,7 +7348,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -7523,7 +7523,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -7696,7 +7696,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -7870,7 +7870,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -8059,7 +8059,7 @@ fn preview_features() {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -8209,7 +8209,7 @@ fn preview_features() {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -8330,7 +8330,7 @@ fn preview_features() {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -8480,7 +8480,7 @@ fn preview_features() {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -8604,7 +8604,7 @@ fn preview_features() {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -8730,7 +8730,7 @@ fn preview_features() {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -8840,6 +8840,526 @@ fn preview_features() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(
|
||||||
|
windows,
|
||||||
|
ignore = "Configuration tests are not yet supported on Windows"
|
||||||
|
)]
|
||||||
|
fn system_certs_cli_aliases_override_env() {
|
||||||
|
let context = uv_test::test_context!("3.12");
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), add_shared_args(context.version())
|
||||||
|
.arg("--show-settings")
|
||||||
|
.arg("--no-native-tls")
|
||||||
|
.env(EnvVars::UV_SYSTEM_CERTS, "1"), @r#"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
GlobalSettings {
|
||||||
|
required_version: None,
|
||||||
|
quiet: 0,
|
||||||
|
verbose: 0,
|
||||||
|
color: Auto,
|
||||||
|
network_settings: NetworkSettings {
|
||||||
|
connectivity: Online,
|
||||||
|
offline: Disabled,
|
||||||
|
system_certs: false,
|
||||||
|
http_proxy: None,
|
||||||
|
https_proxy: None,
|
||||||
|
no_proxy: None,
|
||||||
|
allow_insecure_host: [],
|
||||||
|
read_timeout: [TIME],
|
||||||
|
connect_timeout: [TIME],
|
||||||
|
retries: 3,
|
||||||
|
},
|
||||||
|
concurrency: Concurrency {
|
||||||
|
downloads: 50,
|
||||||
|
builds: 16,
|
||||||
|
installs: 8,
|
||||||
|
},
|
||||||
|
show_settings: true,
|
||||||
|
preview: Preview {
|
||||||
|
flags: [],
|
||||||
|
},
|
||||||
|
python_preference: Managed,
|
||||||
|
python_downloads: Automatic,
|
||||||
|
no_progress: false,
|
||||||
|
installer_metadata: true,
|
||||||
|
}
|
||||||
|
CacheSettings {
|
||||||
|
no_cache: false,
|
||||||
|
cache_dir: Some(
|
||||||
|
"[CACHE_DIR]/",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
VersionSettings {
|
||||||
|
value: None,
|
||||||
|
bump: [],
|
||||||
|
short: false,
|
||||||
|
output_format: Text,
|
||||||
|
dry_run: false,
|
||||||
|
lock_check: Disabled,
|
||||||
|
frozen: None,
|
||||||
|
active: None,
|
||||||
|
no_sync: false,
|
||||||
|
package: None,
|
||||||
|
python: None,
|
||||||
|
install_mirrors: PythonInstallMirrors {
|
||||||
|
python_install_mirror: None,
|
||||||
|
pypy_install_mirror: None,
|
||||||
|
python_downloads_json_url: None,
|
||||||
|
},
|
||||||
|
refresh: None(
|
||||||
|
Timestamp(
|
||||||
|
SystemTime {
|
||||||
|
tv_sec: [TIME],
|
||||||
|
tv_nsec: [TIME],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
settings: ResolverInstallerSettings {
|
||||||
|
resolver: ResolverSettings {
|
||||||
|
build_options: BuildOptions {
|
||||||
|
no_binary: None,
|
||||||
|
no_build: None,
|
||||||
|
},
|
||||||
|
config_setting: ConfigSettings(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
config_settings_package: PackageConfigSettings(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
dependency_metadata: DependencyMetadata(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
exclude_newer: ExcludeNewer {
|
||||||
|
global: None,
|
||||||
|
package: ExcludeNewerPackage(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
fork_strategy: RequiresPython,
|
||||||
|
index_locations: IndexLocations {
|
||||||
|
indexes: [],
|
||||||
|
flat_index: [],
|
||||||
|
no_index: false,
|
||||||
|
},
|
||||||
|
index_strategy: FirstIndex,
|
||||||
|
keyring_provider: Disabled,
|
||||||
|
link_mode: Clone,
|
||||||
|
build_isolation: Isolate,
|
||||||
|
extra_build_dependencies: ExtraBuildDependencies(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
extra_build_variables: ExtraBuildVariables(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
prerelease: IfNecessaryOrExplicit,
|
||||||
|
resolution: Highest,
|
||||||
|
sources: None,
|
||||||
|
torch_backend: None,
|
||||||
|
upgrade: Upgrade {
|
||||||
|
strategy: None,
|
||||||
|
constraints: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
compile_bytecode: false,
|
||||||
|
reinstall: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), add_shared_args(context.version())
|
||||||
|
.arg("--show-settings")
|
||||||
|
.arg("--no-system-certs")
|
||||||
|
.env(EnvVars::UV_NATIVE_TLS, "1"), @r#"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
GlobalSettings {
|
||||||
|
required_version: None,
|
||||||
|
quiet: 0,
|
||||||
|
verbose: 0,
|
||||||
|
color: Auto,
|
||||||
|
network_settings: NetworkSettings {
|
||||||
|
connectivity: Online,
|
||||||
|
offline: Disabled,
|
||||||
|
system_certs: false,
|
||||||
|
http_proxy: None,
|
||||||
|
https_proxy: None,
|
||||||
|
no_proxy: None,
|
||||||
|
allow_insecure_host: [],
|
||||||
|
read_timeout: [TIME],
|
||||||
|
connect_timeout: [TIME],
|
||||||
|
retries: 3,
|
||||||
|
},
|
||||||
|
concurrency: Concurrency {
|
||||||
|
downloads: 50,
|
||||||
|
builds: 16,
|
||||||
|
installs: 8,
|
||||||
|
},
|
||||||
|
show_settings: true,
|
||||||
|
preview: Preview {
|
||||||
|
flags: [],
|
||||||
|
},
|
||||||
|
python_preference: Managed,
|
||||||
|
python_downloads: Automatic,
|
||||||
|
no_progress: false,
|
||||||
|
installer_metadata: true,
|
||||||
|
}
|
||||||
|
CacheSettings {
|
||||||
|
no_cache: false,
|
||||||
|
cache_dir: Some(
|
||||||
|
"[CACHE_DIR]/",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
VersionSettings {
|
||||||
|
value: None,
|
||||||
|
bump: [],
|
||||||
|
short: false,
|
||||||
|
output_format: Text,
|
||||||
|
dry_run: false,
|
||||||
|
lock_check: Disabled,
|
||||||
|
frozen: None,
|
||||||
|
active: None,
|
||||||
|
no_sync: false,
|
||||||
|
package: None,
|
||||||
|
python: None,
|
||||||
|
install_mirrors: PythonInstallMirrors {
|
||||||
|
python_install_mirror: None,
|
||||||
|
pypy_install_mirror: None,
|
||||||
|
python_downloads_json_url: None,
|
||||||
|
},
|
||||||
|
refresh: None(
|
||||||
|
Timestamp(
|
||||||
|
SystemTime {
|
||||||
|
tv_sec: [TIME],
|
||||||
|
tv_nsec: [TIME],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
settings: ResolverInstallerSettings {
|
||||||
|
resolver: ResolverSettings {
|
||||||
|
build_options: BuildOptions {
|
||||||
|
no_binary: None,
|
||||||
|
no_build: None,
|
||||||
|
},
|
||||||
|
config_setting: ConfigSettings(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
config_settings_package: PackageConfigSettings(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
dependency_metadata: DependencyMetadata(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
exclude_newer: ExcludeNewer {
|
||||||
|
global: None,
|
||||||
|
package: ExcludeNewerPackage(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
fork_strategy: RequiresPython,
|
||||||
|
index_locations: IndexLocations {
|
||||||
|
indexes: [],
|
||||||
|
flat_index: [],
|
||||||
|
no_index: false,
|
||||||
|
},
|
||||||
|
index_strategy: FirstIndex,
|
||||||
|
keyring_provider: Disabled,
|
||||||
|
link_mode: Clone,
|
||||||
|
build_isolation: Isolate,
|
||||||
|
extra_build_dependencies: ExtraBuildDependencies(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
extra_build_variables: ExtraBuildVariables(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
prerelease: IfNecessaryOrExplicit,
|
||||||
|
resolution: Highest,
|
||||||
|
sources: None,
|
||||||
|
torch_backend: None,
|
||||||
|
upgrade: Upgrade {
|
||||||
|
strategy: None,
|
||||||
|
constraints: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
compile_bytecode: false,
|
||||||
|
reinstall: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(
|
||||||
|
windows,
|
||||||
|
ignore = "Configuration tests are not yet supported on Windows"
|
||||||
|
)]
|
||||||
|
fn system_certs_config_aliases() -> anyhow::Result<()> {
|
||||||
|
let context = uv_test::test_context!("3.12");
|
||||||
|
|
||||||
|
let config = context.temp_dir.child("uv.toml");
|
||||||
|
config.write_str("system-certs = true\n")?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), add_shared_args(context.version())
|
||||||
|
.arg("--show-settings"), @r#"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
GlobalSettings {
|
||||||
|
required_version: None,
|
||||||
|
quiet: 0,
|
||||||
|
verbose: 0,
|
||||||
|
color: Auto,
|
||||||
|
network_settings: NetworkSettings {
|
||||||
|
connectivity: Online,
|
||||||
|
offline: Disabled,
|
||||||
|
system_certs: true,
|
||||||
|
http_proxy: None,
|
||||||
|
https_proxy: None,
|
||||||
|
no_proxy: None,
|
||||||
|
allow_insecure_host: [],
|
||||||
|
read_timeout: [TIME],
|
||||||
|
connect_timeout: [TIME],
|
||||||
|
retries: 3,
|
||||||
|
},
|
||||||
|
concurrency: Concurrency {
|
||||||
|
downloads: 50,
|
||||||
|
builds: 16,
|
||||||
|
installs: 8,
|
||||||
|
},
|
||||||
|
show_settings: true,
|
||||||
|
preview: Preview {
|
||||||
|
flags: [],
|
||||||
|
},
|
||||||
|
python_preference: Managed,
|
||||||
|
python_downloads: Automatic,
|
||||||
|
no_progress: false,
|
||||||
|
installer_metadata: true,
|
||||||
|
}
|
||||||
|
CacheSettings {
|
||||||
|
no_cache: false,
|
||||||
|
cache_dir: Some(
|
||||||
|
"[CACHE_DIR]/",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
VersionSettings {
|
||||||
|
value: None,
|
||||||
|
bump: [],
|
||||||
|
short: false,
|
||||||
|
output_format: Text,
|
||||||
|
dry_run: false,
|
||||||
|
lock_check: Disabled,
|
||||||
|
frozen: None,
|
||||||
|
active: None,
|
||||||
|
no_sync: false,
|
||||||
|
package: None,
|
||||||
|
python: None,
|
||||||
|
install_mirrors: PythonInstallMirrors {
|
||||||
|
python_install_mirror: None,
|
||||||
|
pypy_install_mirror: None,
|
||||||
|
python_downloads_json_url: None,
|
||||||
|
},
|
||||||
|
refresh: None(
|
||||||
|
Timestamp(
|
||||||
|
SystemTime {
|
||||||
|
tv_sec: [TIME],
|
||||||
|
tv_nsec: [TIME],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
settings: ResolverInstallerSettings {
|
||||||
|
resolver: ResolverSettings {
|
||||||
|
build_options: BuildOptions {
|
||||||
|
no_binary: None,
|
||||||
|
no_build: None,
|
||||||
|
},
|
||||||
|
config_setting: ConfigSettings(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
config_settings_package: PackageConfigSettings(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
dependency_metadata: DependencyMetadata(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
exclude_newer: ExcludeNewer {
|
||||||
|
global: None,
|
||||||
|
package: ExcludeNewerPackage(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
fork_strategy: RequiresPython,
|
||||||
|
index_locations: IndexLocations {
|
||||||
|
indexes: [],
|
||||||
|
flat_index: [],
|
||||||
|
no_index: false,
|
||||||
|
},
|
||||||
|
index_strategy: FirstIndex,
|
||||||
|
keyring_provider: Disabled,
|
||||||
|
link_mode: Clone,
|
||||||
|
build_isolation: Isolate,
|
||||||
|
extra_build_dependencies: ExtraBuildDependencies(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
extra_build_variables: ExtraBuildVariables(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
prerelease: IfNecessaryOrExplicit,
|
||||||
|
resolution: Highest,
|
||||||
|
sources: None,
|
||||||
|
torch_backend: None,
|
||||||
|
upgrade: Upgrade {
|
||||||
|
strategy: None,
|
||||||
|
constraints: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
compile_bytecode: false,
|
||||||
|
reinstall: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
config.write_str(indoc::indoc! {r"
|
||||||
|
system-certs = false
|
||||||
|
native-tls = true
|
||||||
|
"})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), add_shared_args(context.version())
|
||||||
|
.arg("--show-settings"), @r#"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
GlobalSettings {
|
||||||
|
required_version: None,
|
||||||
|
quiet: 0,
|
||||||
|
verbose: 0,
|
||||||
|
color: Auto,
|
||||||
|
network_settings: NetworkSettings {
|
||||||
|
connectivity: Online,
|
||||||
|
offline: Disabled,
|
||||||
|
system_certs: false,
|
||||||
|
http_proxy: None,
|
||||||
|
https_proxy: None,
|
||||||
|
no_proxy: None,
|
||||||
|
allow_insecure_host: [],
|
||||||
|
read_timeout: [TIME],
|
||||||
|
connect_timeout: [TIME],
|
||||||
|
retries: 3,
|
||||||
|
},
|
||||||
|
concurrency: Concurrency {
|
||||||
|
downloads: 50,
|
||||||
|
builds: 16,
|
||||||
|
installs: 8,
|
||||||
|
},
|
||||||
|
show_settings: true,
|
||||||
|
preview: Preview {
|
||||||
|
flags: [],
|
||||||
|
},
|
||||||
|
python_preference: Managed,
|
||||||
|
python_downloads: Automatic,
|
||||||
|
no_progress: false,
|
||||||
|
installer_metadata: true,
|
||||||
|
}
|
||||||
|
CacheSettings {
|
||||||
|
no_cache: false,
|
||||||
|
cache_dir: Some(
|
||||||
|
"[CACHE_DIR]/",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
VersionSettings {
|
||||||
|
value: None,
|
||||||
|
bump: [],
|
||||||
|
short: false,
|
||||||
|
output_format: Text,
|
||||||
|
dry_run: false,
|
||||||
|
lock_check: Disabled,
|
||||||
|
frozen: None,
|
||||||
|
active: None,
|
||||||
|
no_sync: false,
|
||||||
|
package: None,
|
||||||
|
python: None,
|
||||||
|
install_mirrors: PythonInstallMirrors {
|
||||||
|
python_install_mirror: None,
|
||||||
|
pypy_install_mirror: None,
|
||||||
|
python_downloads_json_url: None,
|
||||||
|
},
|
||||||
|
refresh: None(
|
||||||
|
Timestamp(
|
||||||
|
SystemTime {
|
||||||
|
tv_sec: [TIME],
|
||||||
|
tv_nsec: [TIME],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
settings: ResolverInstallerSettings {
|
||||||
|
resolver: ResolverSettings {
|
||||||
|
build_options: BuildOptions {
|
||||||
|
no_binary: None,
|
||||||
|
no_build: None,
|
||||||
|
},
|
||||||
|
config_setting: ConfigSettings(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
config_settings_package: PackageConfigSettings(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
dependency_metadata: DependencyMetadata(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
exclude_newer: ExcludeNewer {
|
||||||
|
global: None,
|
||||||
|
package: ExcludeNewerPackage(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
fork_strategy: RequiresPython,
|
||||||
|
index_locations: IndexLocations {
|
||||||
|
indexes: [],
|
||||||
|
flat_index: [],
|
||||||
|
no_index: false,
|
||||||
|
},
|
||||||
|
index_strategy: FirstIndex,
|
||||||
|
keyring_provider: Disabled,
|
||||||
|
link_mode: Clone,
|
||||||
|
build_isolation: Isolate,
|
||||||
|
extra_build_dependencies: ExtraBuildDependencies(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
extra_build_variables: ExtraBuildVariables(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
prerelease: IfNecessaryOrExplicit,
|
||||||
|
resolution: Highest,
|
||||||
|
sources: None,
|
||||||
|
torch_backend: None,
|
||||||
|
upgrade: Upgrade {
|
||||||
|
strategy: None,
|
||||||
|
constraints: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
compile_bytecode: false,
|
||||||
|
reinstall: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Track the interactions between `upgrade` and `upgrade-package` across the `uv pip` CLI and a
|
/// Track the interactions between `upgrade` and `upgrade-package` across the `uv pip` CLI and a
|
||||||
/// configuration file.
|
/// configuration file.
|
||||||
#[test]
|
#[test]
|
||||||
@@ -8872,7 +9392,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -9061,7 +9581,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -9244,7 +9764,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -9425,7 +9945,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -9606,7 +10126,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -9788,7 +10308,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -9995,7 +10515,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -10127,7 +10647,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -10253,7 +10773,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -10377,7 +10897,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -10497,7 +11017,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -10618,7 +11138,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -10763,7 +11283,7 @@ fn build_isolation_override() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
@@ -10941,7 +11461,7 @@ fn build_isolation_override() -> anyhow::Result<()> {
|
|||||||
network_settings: NetworkSettings {
|
network_settings: NetworkSettings {
|
||||||
connectivity: Online,
|
connectivity: Online,
|
||||||
offline: Disabled,
|
offline: Disabled,
|
||||||
native_tls: false,
|
system_certs: false,
|
||||||
http_proxy: None,
|
http_proxy: None,
|
||||||
https_proxy: None,
|
https_proxy: None,
|
||||||
no_proxy: None,
|
no_proxy: None,
|
||||||
|
|||||||
@@ -1,32 +1,70 @@
|
|||||||
# TLS certificates
|
# TLS certificates
|
||||||
|
|
||||||
By default, uv loads certificates from the bundled `webpki-roots` crate. The `webpki-roots` are a
|
uv uses TLS to securely communicate with package indexes and other HTTPS servers. TLS certificates
|
||||||
reliable set of trust roots from Mozilla, and including them in uv improves portability and
|
are used to verify the identity of these servers, ensuring that connections are not intercepted.
|
||||||
performance (especially on macOS, where reading the system trust store incurs a significant delay).
|
|
||||||
|
## TLS backend
|
||||||
|
|
||||||
|
uv uses [`rustls`](https://github.com/rustls/rustls), a memory-safe TLS implementation written in
|
||||||
|
Rust, with [`aws-lc-rs`](https://github.com/aws/aws-lc-rs) as the cryptography provider.
|
||||||
|
|
||||||
|
uv supports the following X.509 certificate signature algorithms:
|
||||||
|
|
||||||
|
- ECDSA (P-256, P-384, P-521) with SHA-256, SHA-384, or SHA-512
|
||||||
|
- Ed25519
|
||||||
|
- RSA PKCS#1 v1.5 (2048–8192 bit) with SHA-256, SHA-384, or SHA-512
|
||||||
|
- RSA-PSS (2048–8192 bit) with SHA-256, SHA-384, or SHA-512
|
||||||
|
|
||||||
## System certificates
|
## System certificates
|
||||||
|
|
||||||
In some cases, you may want to use the platform's native certificate store, especially if you're
|
By default, uv uses bundled Mozilla root certificates for TLS verification. In some cases, you may
|
||||||
relying on a corporate trust root (e.g., for a mandatory proxy) that's included in your system's
|
want to use the platform's native certificate store instead — for example, if you're relying on a
|
||||||
certificate store. To instruct uv to use the system's trust store, run uv with the `--native-tls`
|
corporate trust root (e.g., for a mandatory proxy) that's included in your system's certificate
|
||||||
command-line flag, or set the `UV_NATIVE_TLS` environment variable to `true`.
|
store.
|
||||||
|
|
||||||
|
To use system certificates, pass the [`--system-certs`](../../reference/cli.md#uv) flag, set the
|
||||||
|
[`UV_SYSTEM_CERTS`](../../reference/environment.md#uv_system_certs) environment variable to `true`,
|
||||||
|
or set [`system-certs = true`](../../reference/settings.md#system-certs) in `uv.toml`.
|
||||||
|
|
||||||
|
When using system certificates, certificate verification is performed by
|
||||||
|
[`rustls-platform-verifier`](https://github.com/rustls/rustls-platform-verifier), which delegates to
|
||||||
|
the operating system's certificate verifier.
|
||||||
|
|
||||||
## Custom certificates
|
## Custom certificates
|
||||||
|
|
||||||
If a direct path to the certificate is required (e.g., in CI), set the `SSL_CERT_FILE` environment
|
To use custom CA certificates, set the
|
||||||
variable to the path of the certificate bundle, to instruct uv to use that file instead of the
|
[`SSL_CERT_FILE`](../../reference/environment.md#ssl_cert_file) environment variable to the path of
|
||||||
system's trust store.
|
a PEM-encoded certificate bundle (e.g., `certs.pem`, `ca-bundle.crt`), or set
|
||||||
|
[`SSL_CERT_DIR`](../../reference/environment.md#ssl_cert_dir) to one or more directories containing
|
||||||
|
PEM-encoded certificate files. Multiple entries are supported, separated using a platform-specific
|
||||||
|
delimiter (`:` on Unix, `;` on Windows).
|
||||||
|
|
||||||
If client certificate authentication (mTLS) is desired, set the `SSL_CLIENT_CERT` environment
|
Certificates are usually stored with `.pem`, `.crt`, or `.cer` extensions, but uv will attempt to
|
||||||
variable to the path of the PEM formatted file containing the certificate followed by the private
|
read a certificate from any regular file in the provided `SSL_CERT_DIR`.
|
||||||
key.
|
|
||||||
|
Files that cannot be parsed as PEM certificates are ignored. uv resolves symlinks and ignores
|
||||||
|
dangling symlinks.
|
||||||
|
|
||||||
|
DER-encoded files are not supported.
|
||||||
|
|
||||||
|
When set, these environment variables **override** the default certificate source entirely — only
|
||||||
|
the provided certificates will be trusted.
|
||||||
|
|
||||||
|
`SSL_CERT_FILE` can point to a single certificate or a bundle containing multiple certificates.
|
||||||
|
`SSL_CERT_DIR` can include multiple directory entries; uv will load all valid certificates from each
|
||||||
|
directory.
|
||||||
|
|
||||||
|
If client certificate authentication (mTLS) is desired, set the
|
||||||
|
[`SSL_CLIENT_CERT`](../../reference/environment.md#ssl_client_cert) environment variable to the path
|
||||||
|
of a PEM formatted file containing the certificate followed by the private key.
|
||||||
|
|
||||||
## Insecure hosts
|
## Insecure hosts
|
||||||
|
|
||||||
If you're using a setup in which you want to trust a self-signed certificate or otherwise disable
|
If you're using a setup in which you want to trust a self-signed certificate or otherwise disable
|
||||||
certificate verification, you can instruct uv to allow insecure connections to dedicated hosts via
|
certificate verification, you can instruct uv to allow insecure connections to dedicated hosts via
|
||||||
the `allow-insecure-host` configuration option. For example, adding the following to
|
the [`allow-insecure-host`](../../reference/settings.md#allow-insecure-host) configuration option.
|
||||||
`pyproject.toml` will allow insecure connections to `example.com`:
|
For example, adding the following to `pyproject.toml` will allow insecure connections to
|
||||||
|
`example.com`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[tool.uv]
|
[tool.uv]
|
||||||
|
|||||||
Generated
+8
-3
@@ -322,8 +322,9 @@
|
|||||||
"type": ["boolean", "null"]
|
"type": ["boolean", "null"]
|
||||||
},
|
},
|
||||||
"native-tls": {
|
"native-tls": {
|
||||||
"description": "Whether to load TLS certificates from the platform's native certificate store.\n\nBy default, uv loads certificates from the bundled `webpki-roots` crate. The\n`webpki-roots` are a reliable set of trust roots from Mozilla, and including them in uv\nimproves portability and performance (especially on macOS).\n\nHowever, in some cases, you may want to use the platform's native certificate store,\nespecially if you're relying on a corporate trust root (e.g., for a mandatory proxy) that's\nincluded in your system's certificate store.",
|
"description": "Whether to load TLS certificates from the platform's native certificate store.\n\nBy default, uv uses bundled Mozilla root certificates. When enabled, this loads\ncertificates from the platform's native certificate store instead.\n\n(Deprecated: use `system-certs` instead.)",
|
||||||
"type": ["boolean", "null"]
|
"type": ["boolean", "null"],
|
||||||
|
"deprecated": true
|
||||||
},
|
},
|
||||||
"no-binary": {
|
"no-binary": {
|
||||||
"description": "Don't install pre-built wheels.\n\nThe given packages will be built and installed from source. The resolver will still use\npre-built wheels to extract package metadata, if available.",
|
"description": "Don't install pre-built wheels.\n\nThe given packages will be built and installed from source. The resolver will still use\npre-built wheels to extract package metadata, if available.",
|
||||||
@@ -520,6 +521,10 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"system-certs": {
|
||||||
|
"description": "Whether to load TLS certificates from the platform's native certificate store.\n\nBy default, uv uses bundled Mozilla root certificates. When enabled, this loads\ncertificates from the platform's native certificate store instead.",
|
||||||
|
"type": ["boolean", "null"]
|
||||||
|
},
|
||||||
"torch-backend": {
|
"torch-backend": {
|
||||||
"description": "The backend to use when fetching packages in the PyTorch ecosystem.\n\nWhen set, uv will ignore the configured index URLs for packages in the PyTorch ecosystem,\nand will instead use the defined backend.\n\nFor example, when set to `cpu`, uv will use the CPU-only PyTorch index; when set to `cu126`,\nuv will use the PyTorch index for CUDA 12.6.\n\nThe `auto` mode will attempt to detect the appropriate PyTorch index based on the currently\ninstalled CUDA drivers.\n\nThis setting is only respected by `uv pip` commands.\n\nThis option is in preview and may change in any future release.",
|
"description": "The backend to use when fetching packages in the PyTorch ecosystem.\n\nWhen set, uv will ignore the configured index URLs for packages in the PyTorch ecosystem,\nand will instead use the defined backend.\n\nFor example, when set to `cpu`, uv will use the CPU-only PyTorch index; when set to `cu126`,\nuv will use the PyTorch index for CUDA 12.6.\n\nThe `auto` mode will attempt to detect the appropriate PyTorch index based on the currently\ninstalled CUDA drivers.\n\nThis setting is only respected by `uv pip` commands.\n\nThis option is in preview and may change in any future release.",
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
@@ -1051,7 +1056,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"LinkMode": {
|
"LinkMode": {
|
||||||
"description": "The method to use when linking.\n\nDefaults to [`Clone`](LinkMode::Clone) on macOS and Linux (which support copy-on-write on\nAPFS and btrfs/xfs/bcachefs respectively), and [`Hardlink`](LinkMode::Hardlink) on other\nplatforms.",
|
"description": "The method to use when linking.\n\nDefaults to [`LinkMode::Clone`] on macOS and Linux (which support copy-on-write on\nAPFS and btrfs/xfs/bcachefs respectively), and [`LinkMode::Hardlink`] on other\nplatforms.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"description": "Clone (i.e., copy-on-write) packages from the source into the destination.",
|
"description": "Clone (i.e., copy-on-write) packages from the source into the destination.",
|
||||||
|
|||||||
Reference in New Issue
Block a user