mirror of https://github.com/astral-sh/uv
Compare commits
124 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
6fa8204efe | |
|
|
6578e0521b | |
|
|
0a83bf7dd5 | |
|
|
e603761862 | |
|
|
4f6f56b070 | |
|
|
66f7093ad2 | |
|
|
60df92f9aa | |
|
|
0cee76417f | |
|
|
af348c2a88 | |
|
|
b58f543e5e | |
|
|
13e7ad62cb | |
|
|
94c97b6434 | |
|
|
af95677b9b | |
|
|
a5d50a20d2 | |
|
|
a768a9d111 | |
|
|
d20948bec2 | |
|
|
a2d64aa224 | |
|
|
c43315f4eb | |
|
|
e77ee15204 | |
|
|
ed37f3b432 | |
|
|
3e80b10272 | |
|
|
7ad441a0bd | |
|
|
5a55bbe883 | |
|
|
6ad80c5150 | |
|
|
38ae414682 | |
|
|
6de869cc88 | |
|
|
59d73fdddf | |
|
|
4c1571fb76 | |
|
|
ebdffaf728 | |
|
|
3bb7f67c71 | |
|
|
caac4814df | |
|
|
a550743bed | |
|
|
94f1f02d85 | |
|
|
36806f8e66 | |
|
|
2b5d65e61d | |
|
|
81c99dd438 | |
|
|
b931c6687c | |
|
|
eca36eed08 | |
|
|
69910b4aab | |
|
|
8d2c2e8cdf | |
|
|
b6686fbce3 | |
|
|
4af2d2b922 | |
|
|
2502577c9d | |
|
|
d0a6f5d13f | |
|
|
7b6b02a7d1 | |
|
|
0dd71f4382 | |
|
|
38ce3b2919 | |
|
|
a70ee58ae1 | |
|
|
9774f8f1d4 | |
|
|
77df5887e4 | |
|
|
4e1469b151 | |
|
|
5a6f2ea319 | |
|
|
28a8194a67 | |
|
|
a63e5b62e3 | |
|
|
ed19672f1f | |
|
|
9635258867 | |
|
|
c269619b1b | |
|
|
eaa1882c51 | |
|
|
8390b311f8 | |
|
|
b73281d222 | |
|
|
9f58280eb8 | |
|
|
f6ad3dcd57 | |
|
|
fb5de2228c | |
|
|
0c5391a7c7 | |
|
|
ee6e3be815 | |
|
|
d3cd94ecaf | |
|
|
62bf92132b | |
|
|
2748dce860 | |
|
|
2abe56a357 | |
|
|
2f553bfc51 | |
|
|
539b7368cd | |
|
|
99660a8574 | |
|
|
20ab80ad8f | |
|
|
1d8252599a | |
|
|
05fa19c440 | |
|
|
e00cc8c35f | |
|
|
932d7b8fce | |
|
|
49b70e7225 | |
|
|
b1078fe595 | |
|
|
f01366bae8 | |
|
|
ed63be5dab | |
|
|
d2db06983a | |
|
|
7954b34989 | |
|
|
5eafae3327 | |
|
|
89c411f0ae | |
|
|
18a36528ea | |
|
|
eb65f9ff74 | |
|
|
e7af5838bb | |
|
|
87adf14fdf | |
|
|
9fc07c8773 | |
|
|
d2162e27e6 | |
|
|
99c40f74c5 | |
|
|
e38cab64ce | |
|
|
e4d193a5f8 | |
|
|
fee7f9d093 | |
|
|
5947fb0c83 | |
|
|
54f9932362 | |
|
|
c8996d24a1 | |
|
|
2cdbf9e547 | |
|
|
3347e196bb | |
|
|
23b8fc9d18 | |
|
|
082be90177 | |
|
|
fbf925ee63 | |
|
|
efa47adefb | |
|
|
05814f9cd5 | |
|
|
6b00d6522c | |
|
|
5773b12fa9 | |
|
|
825ab78790 | |
|
|
d0931e0ca9 | |
|
|
6d8866a4f3 | |
|
|
8a73958c4a | |
|
|
2c3b907dc0 | |
|
|
0b70eba917 | |
|
|
0ae54dbd8a | |
|
|
c29304aaca | |
|
|
5f3d46c241 | |
|
|
5498e4d6f6 | |
|
|
e2bda1173e | |
|
|
0db41803cd | |
|
|
c67a0fdd7b | |
|
|
f02b459d04 | |
|
|
eaa4651df0 | |
|
|
76d769d7a0 | |
|
|
fa6afd5a71 |
|
|
@ -0,0 +1,81 @@
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.12"
|
||||||
|
# dependencies = []
|
||||||
|
# ///
|
||||||
|
|
||||||
|
"""Post-edit hook to auto-format files after Claude edits."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def format_rust(file_path: str, cwd: str) -> None:
|
||||||
|
"""Format Rust files with cargo fmt."""
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
["cargo", "fmt", "--", file_path],
|
||||||
|
cwd=cwd,
|
||||||
|
capture_output=True,
|
||||||
|
)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def format_python(file_path: str, cwd: str) -> None:
|
||||||
|
"""Format Python files with ruff."""
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
["uvx", "ruff", "format", file_path],
|
||||||
|
cwd=cwd,
|
||||||
|
capture_output=True,
|
||||||
|
)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def format_prettier(file_path: str, cwd: str, prose_wrap: bool = False) -> None:
|
||||||
|
"""Format files with prettier."""
|
||||||
|
args = ["npx", "prettier", "--write"]
|
||||||
|
if prose_wrap:
|
||||||
|
args.extend(["--prose-wrap", "always"])
|
||||||
|
args.append(file_path)
|
||||||
|
try:
|
||||||
|
subprocess.run(args, cwd=cwd, capture_output=True)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
import os
|
||||||
|
|
||||||
|
input_data = json.load(sys.stdin)
|
||||||
|
|
||||||
|
tool_name = input_data.get("tool_name")
|
||||||
|
tool_input = input_data.get("tool_input", {})
|
||||||
|
file_path = tool_input.get("file_path")
|
||||||
|
|
||||||
|
# Only process Write, Edit, and MultiEdit tools
|
||||||
|
if tool_name not in ("Write", "Edit", "MultiEdit"):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not file_path:
|
||||||
|
return
|
||||||
|
|
||||||
|
cwd = os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
|
||||||
|
path = Path(file_path)
|
||||||
|
ext = path.suffix
|
||||||
|
|
||||||
|
if ext == ".rs":
|
||||||
|
format_rust(file_path, cwd)
|
||||||
|
elif ext in (".py", ".pyi"):
|
||||||
|
format_python(file_path, cwd)
|
||||||
|
elif ext in (".json5", ".yaml", ".yml"):
|
||||||
|
format_prettier(file_path, cwd)
|
||||||
|
elif ext == ".md":
|
||||||
|
format_prettier(file_path, cwd, prose_wrap=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"PostToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Edit|Write|MultiEdit",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "uv run .claude/hooks/post-edit-format.py"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
dependencyDashboard: true,
|
dependencyDashboard: true,
|
||||||
suppressNotifications: ["prEditedNotification"],
|
suppressNotifications: ["prEditedNotification"],
|
||||||
extends: [
|
extends: [
|
||||||
"config:recommended",
|
"github>astral-sh/renovate-config",
|
||||||
// For tool versions defined in GitHub Actions:
|
// For tool versions defined in GitHub Actions:
|
||||||
"customManagers:githubActionsVersions",
|
"customManagers:githubActionsVersions",
|
||||||
],
|
],
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
schedule: ["* 0-3 * * 1"],
|
schedule: ["* 0-3 * * 1"],
|
||||||
semanticCommits: "disabled",
|
semanticCommits: "disabled",
|
||||||
separateMajorMinor: false,
|
separateMajorMinor: false,
|
||||||
prHourlyLimit: 10,
|
|
||||||
enabledManagers: ["github-actions", "pre-commit", "cargo", "custom.regex"],
|
enabledManagers: ["github-actions", "pre-commit", "cargo", "custom.regex"],
|
||||||
cargo: {
|
cargo: {
|
||||||
// See https://docs.renovatebot.com/configuration-options/#rangestrategy
|
// See https://docs.renovatebot.com/configuration-options/#rangestrategy
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ jobs:
|
||||||
|
|
||||||
macos-x86_64:
|
macos-x86_64:
|
||||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||||
runs-on: macos-14
|
runs-on: depot-macos-14
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
|
|
@ -157,7 +157,7 @@ jobs:
|
||||||
|
|
||||||
macos-aarch64:
|
macos-aarch64:
|
||||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||||
runs-on: macos-14
|
runs-on: depot-macos-14
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
|
|
@ -417,7 +417,7 @@ jobs:
|
||||||
|
|
||||||
linux-arm:
|
linux-arm:
|
||||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: depot-ubuntu-22.04-8
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -956,7 +956,7 @@ jobs:
|
||||||
|
|
||||||
musllinux-cross:
|
musllinux-cross:
|
||||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: depot-ubuntu-22.04-8
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
platform:
|
||||||
|
|
|
||||||
|
|
@ -184,13 +184,13 @@ jobs:
|
||||||
- buildpack-deps:trixie,trixie,debian
|
- buildpack-deps:trixie,trixie,debian
|
||||||
- debian:bookworm-slim,bookworm-slim
|
- debian:bookworm-slim,bookworm-slim
|
||||||
- buildpack-deps:bookworm,bookworm
|
- buildpack-deps:bookworm,bookworm
|
||||||
- python:3.14-alpine,python3.14-alpine
|
- python:3.14-alpine3.23,python3.14-alpine3.23,python3.14-alpine
|
||||||
- python:3.13-alpine,python3.13-alpine
|
- python:3.13-alpine3.23,python3.13-alpine3.23,python3.13-alpine
|
||||||
- python:3.12-alpine,python3.12-alpine
|
- python:3.12-alpine3.23,python3.12-alpine3.23,python3.12-alpine
|
||||||
- python:3.11-alpine,python3.11-alpine
|
- python:3.11-alpine3.23,python3.11-alpine3.23,python3.11-alpine
|
||||||
- python:3.10-alpine,python3.10-alpine
|
- python:3.10-alpine3.23,python3.10-alpine3.23,python3.10-alpine
|
||||||
- python:3.9-alpine,python3.9-alpine
|
- python:3.9-alpine3.22,python3.9-alpine3.22,python3.9-alpine
|
||||||
- python:3.8-alpine,python3.8-alpine
|
- python:3.8-alpine3.20,python3.8-alpine3.20,python3.8-alpine
|
||||||
- python:3.14-trixie,python3.14-trixie
|
- python:3.14-trixie,python3.14-trixie
|
||||||
- python:3.13-trixie,python3.13-trixie
|
- python:3.13-trixie,python3.13-trixie
|
||||||
- python:3.12-trixie,python3.12-trixie
|
- python:3.12-trixie,python3.12-trixie
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
# Flag that is raised when any code is changed
|
# Flag that is raised when any code is changed
|
||||||
code: ${{ steps.changed.outputs.code_any_changed }}
|
code: ${{ steps.changed.outputs.code_any_changed }}
|
||||||
|
# Flag that is raised when uv.schema.json is changed (e.g., in a release PR)
|
||||||
|
schema: ${{ steps.changed.outputs.schema_changed }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
|
|
@ -40,10 +42,16 @@ jobs:
|
||||||
CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD)
|
CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD)
|
||||||
|
|
||||||
CODE_CHANGED=false
|
CODE_CHANGED=false
|
||||||
|
SCHEMA_CHANGED=false
|
||||||
|
|
||||||
while IFS= read -r file; do
|
while IFS= read -r file; do
|
||||||
# Generated markdown and JSON files are checked during test runs.
|
# Check if the schema file changed (e.g., in a release PR)
|
||||||
if [[ "${file}" =~ ^docs/ && ! "${file}" =~ ^docs/reference/(cli|settings).md && ! "${file}" =~ ^docs/reference/environment.md ]]; then
|
if [[ "${file}" == "uv.schema.json" ]]; then
|
||||||
|
echo "Detected schema change: ${file}"
|
||||||
|
SCHEMA_CHANGED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${file}" =~ ^docs/ ]]; then
|
||||||
echo "Skipping ${file} (matches docs/ pattern)"
|
echo "Skipping ${file} (matches docs/ pattern)"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
@ -70,6 +78,7 @@ jobs:
|
||||||
|
|
||||||
done <<< "${CHANGED_FILES}"
|
done <<< "${CHANGED_FILES}"
|
||||||
echo "code_any_changed=${CODE_CHANGED}" >> "${GITHUB_OUTPUT}"
|
echo "code_any_changed=${CODE_CHANGED}" >> "${GITHUB_OUTPUT}"
|
||||||
|
echo "schema_changed=${SCHEMA_CHANGED}" >> "${GITHUB_OUTPUT}"
|
||||||
lint:
|
lint:
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
name: "lint"
|
name: "lint"
|
||||||
|
|
@ -89,7 +98,7 @@ jobs:
|
||||||
- name: "Install uv"
|
- name: "Install uv"
|
||||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
||||||
with:
|
with:
|
||||||
version: "0.9.11"
|
version: "0.9.13"
|
||||||
|
|
||||||
- name: "rustfmt"
|
- name: "rustfmt"
|
||||||
run: cargo fmt --all --check
|
run: cargo fmt --all --check
|
||||||
|
|
@ -135,7 +144,7 @@ jobs:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Check uv_build dependencies"
|
- name: "Check uv_build dependencies"
|
||||||
|
|
@ -167,7 +176,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
workspaces: ${{ env.UV_WORKSPACE }}
|
workspaces: ${{ env.UV_WORKSPACE }}
|
||||||
|
|
||||||
|
|
@ -188,7 +197,7 @@ jobs:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "cargo publish dry-run"
|
- name: "cargo publish dry-run"
|
||||||
|
|
@ -204,11 +213,16 @@ jobs:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Generate all"
|
- name: "Generate all"
|
||||||
run: cargo dev generate-all --mode check
|
run: cargo dev generate-all --mode dry-run
|
||||||
|
- name: "Check sysconfig mappings"
|
||||||
|
run: cargo dev generate-sysconfig-metadata --mode check
|
||||||
|
- name: "Check JSON schema"
|
||||||
|
if: ${{ needs.determine_changes.outputs.schema == 'true' }}
|
||||||
|
run: cargo dev generate-json-schema --mode check
|
||||||
|
|
||||||
cargo-shear:
|
cargo-shear:
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
@ -219,7 +233,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: "Install cargo shear"
|
- name: "Install cargo shear"
|
||||||
uses: taiki-e/install-action@a416ddeedbd372e614cc1386e8b642692f66865e # v2.57.1
|
uses: taiki-e/install-action@d850aa816998e5cf15f67a78c7b933f2a5033f8a # v2.63.3
|
||||||
with:
|
with:
|
||||||
tool: cargo-shear
|
tool: cargo-shear
|
||||||
- run: cargo shear
|
- run: cargo shear
|
||||||
|
|
@ -241,14 +255,14 @@ jobs:
|
||||||
|
|
||||||
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
|
|
||||||
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
||||||
with:
|
with:
|
||||||
version: "0.9.11"
|
version: "0.9.13"
|
||||||
|
|
||||||
- name: "Install required Python versions"
|
- name: "Install required Python versions"
|
||||||
run: uv python install
|
run: uv python install
|
||||||
|
|
@ -275,12 +289,13 @@ jobs:
|
||||||
UV_HTTP_RETRIES: 5
|
UV_HTTP_RETRIES: 5
|
||||||
run: |
|
run: |
|
||||||
cargo nextest run \
|
cargo nextest run \
|
||||||
|
--cargo-profile fast-build \
|
||||||
--features python-patch,native-auth,secret-service \
|
--features python-patch,native-auth,secret-service \
|
||||||
--workspace \
|
--workspace \
|
||||||
--status-level skip --failure-output immediate-final --no-fail-fast -j 20 --final-status-level slow
|
--status-level skip --failure-output immediate-final --no-fail-fast -j 20 --final-status-level slow
|
||||||
|
|
||||||
cargo-test-macos:
|
cargo-test-macos:
|
||||||
timeout-minutes: 15
|
timeout-minutes: 20
|
||||||
needs: determine_changes
|
needs: determine_changes
|
||||||
# Only run macOS tests on main without opt-in
|
# Only run macOS tests on main without opt-in
|
||||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'test:macos') || github.ref == 'refs/heads/main' }}
|
if: ${{ contains(github.event.pull_request.labels.*.name, 'test:macos') || github.ref == 'refs/heads/main' }}
|
||||||
|
|
@ -293,14 +308,14 @@ jobs:
|
||||||
|
|
||||||
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
|
|
||||||
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
||||||
with:
|
with:
|
||||||
version: "0.9.11"
|
version: "0.9.13"
|
||||||
|
|
||||||
- name: "Install required Python versions"
|
- name: "Install required Python versions"
|
||||||
run: uv python install
|
run: uv python install
|
||||||
|
|
@ -316,8 +331,9 @@ jobs:
|
||||||
UV_HTTP_RETRIES: 5
|
UV_HTTP_RETRIES: 5
|
||||||
run: |
|
run: |
|
||||||
cargo nextest run \
|
cargo nextest run \
|
||||||
|
--cargo-profile fast-build \
|
||||||
--no-default-features \
|
--no-default-features \
|
||||||
--features python,python-managed,pypi,git,performance,crates-io,native-auth,apple-native \
|
--features python,python-managed,pypi,git,git-lfs,performance,crates-io,native-auth,apple-native \
|
||||||
--workspace \
|
--workspace \
|
||||||
--status-level skip --failure-output immediate-final --no-fail-fast -j 12 --final-status-level slow
|
--status-level skip --failure-output immediate-final --no-fail-fast -j 12 --final-status-level slow
|
||||||
|
|
||||||
|
|
@ -342,12 +358,12 @@ jobs:
|
||||||
|
|
||||||
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
||||||
with:
|
with:
|
||||||
version: "0.9.11"
|
version: "0.9.13"
|
||||||
|
|
||||||
- name: "Install required Python versions"
|
- name: "Install required Python versions"
|
||||||
run: uv python install
|
run: uv python install
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
workspaces: ${{ env.UV_WORKSPACE }}
|
workspaces: ${{ env.UV_WORKSPACE }}
|
||||||
|
|
||||||
|
|
@ -371,6 +387,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cargo nextest run \
|
cargo nextest run \
|
||||||
|
--cargo-profile fast-build \
|
||||||
--no-default-features \
|
--no-default-features \
|
||||||
--features python,pypi,python-managed,native-auth,windows-native \
|
--features python,pypi,python-managed,native-auth,windows-native \
|
||||||
--workspace \
|
--workspace \
|
||||||
|
|
@ -400,7 +417,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
workspaces: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
|
workspaces: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
|
||||||
|
|
||||||
|
|
@ -460,7 +477,7 @@ jobs:
|
||||||
- name: Copy Git Repo to Dev Drive
|
- name: Copy Git Repo to Dev Drive
|
||||||
run: |
|
run: |
|
||||||
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
workspaces: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
|
workspaces: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
|
|
@ -478,8 +495,8 @@ jobs:
|
||||||
working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
|
working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline
|
||||||
run: |
|
run: |
|
||||||
cargo build --target ${{ matrix.target-arch }}-pc-windows-msvc
|
cargo build --target ${{ matrix.target-arch }}-pc-windows-msvc
|
||||||
cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-console.exe trampolines/uv-trampoline-${{ matrix.target-arch }}-console.exe
|
cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-console.exe ../uv-trampoline-builder/trampolines/uv-trampoline-${{ matrix.target-arch }}-console.exe
|
||||||
cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-gui.exe trampolines/uv-trampoline-${{ matrix.target-arch }}-gui.exe
|
cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-gui.exe ../uv-trampoline-builder/trampolines/uv-trampoline-${{ matrix.target-arch }}-gui.exe
|
||||||
- name: "Test new binaries"
|
- name: "Test new binaries"
|
||||||
working-directory: ${{ env.UV_WORKSPACE }}
|
working-directory: ${{ env.UV_WORKSPACE }}
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -507,9 +524,17 @@ jobs:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
||||||
with:
|
with:
|
||||||
version: "0.9.11"
|
version: "0.9.13"
|
||||||
|
|
||||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
|
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||||
|
with:
|
||||||
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
- name: "Generate reference documentation"
|
||||||
|
run: |
|
||||||
|
cargo dev generate-options-reference
|
||||||
|
cargo dev generate-cli-reference
|
||||||
|
cargo dev generate-env-vars-reference
|
||||||
- name: "Add SSH key"
|
- name: "Add SSH key"
|
||||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||||
|
|
@ -536,18 +561,18 @@ jobs:
|
||||||
|
|
||||||
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
run: cargo build
|
run: cargo build --profile no-debug
|
||||||
|
|
||||||
- name: "Upload binary"
|
- name: "Upload binary"
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: uv-linux-libc-${{ github.sha }}
|
name: uv-linux-libc-${{ github.sha }}
|
||||||
path: |
|
path: |
|
||||||
./target/debug/uv
|
./target/no-debug/uv
|
||||||
./target/debug/uvx
|
./target/no-debug/uvx
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-binary-linux-aarch64:
|
build-binary-linux-aarch64:
|
||||||
|
|
@ -563,18 +588,18 @@ jobs:
|
||||||
|
|
||||||
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
run: cargo build
|
run: cargo build --profile no-debug
|
||||||
|
|
||||||
- name: "Upload binary"
|
- name: "Upload binary"
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: uv-linux-aarch64-${{ github.sha }}
|
name: uv-linux-aarch64-${{ github.sha }}
|
||||||
path: |
|
path: |
|
||||||
./target/debug/uv
|
./target/no-debug/uv
|
||||||
./target/debug/uvx
|
./target/no-debug/uvx
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-binary-linux-musl:
|
build-binary-linux-musl:
|
||||||
|
|
@ -595,18 +620,18 @@ jobs:
|
||||||
sudo apt-get install musl-tools
|
sudo apt-get install musl-tools
|
||||||
rustup target add x86_64-unknown-linux-musl
|
rustup target add x86_64-unknown-linux-musl
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
run: cargo build --target x86_64-unknown-linux-musl --bin uv --bin uvx
|
run: cargo build --profile no-debug --target x86_64-unknown-linux-musl --bin uv --bin uvx
|
||||||
|
|
||||||
- name: "Upload binary"
|
- name: "Upload binary"
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: uv-linux-musl-${{ github.sha }}
|
name: uv-linux-musl-${{ github.sha }}
|
||||||
path: |
|
path: |
|
||||||
./target/x86_64-unknown-linux-musl/debug/uv
|
./target/x86_64-unknown-linux-musl/no-debug/uv
|
||||||
./target/x86_64-unknown-linux-musl/debug/uvx
|
./target/x86_64-unknown-linux-musl/no-debug/uvx
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-binary-macos-aarch64:
|
build-binary-macos-aarch64:
|
||||||
|
|
@ -622,17 +647,17 @@ jobs:
|
||||||
|
|
||||||
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
run: cargo build --bin uv --bin uvx
|
run: cargo build --profile no-debug --bin uv --bin uvx
|
||||||
|
|
||||||
- name: "Upload binary"
|
- name: "Upload binary"
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: uv-macos-aarch64-${{ github.sha }}
|
name: uv-macos-aarch64-${{ github.sha }}
|
||||||
path: |
|
path: |
|
||||||
./target/debug/uv
|
./target/no-debug/uv
|
||||||
./target/debug/uvx
|
./target/no-debug/uvx
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-binary-macos-x86_64:
|
build-binary-macos-x86_64:
|
||||||
|
|
@ -648,17 +673,17 @@ jobs:
|
||||||
|
|
||||||
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
- uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
run: cargo build --bin uv --bin uvx
|
run: cargo build --profile no-debug --bin uv --bin uvx
|
||||||
|
|
||||||
- name: "Upload binary"
|
- name: "Upload binary"
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: uv-macos-x86_64-${{ github.sha }}
|
name: uv-macos-x86_64-${{ github.sha }}
|
||||||
path: |
|
path: |
|
||||||
./target/debug/uv
|
./target/no-debug/uv
|
||||||
./target/debug/uvx
|
./target/no-debug/uvx
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-binary-windows-x86_64:
|
build-binary-windows-x86_64:
|
||||||
|
|
@ -680,21 +705,21 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
workspaces: ${{ env.UV_WORKSPACE }}
|
workspaces: ${{ env.UV_WORKSPACE }}
|
||||||
|
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
working-directory: ${{ env.UV_WORKSPACE }}
|
working-directory: ${{ env.UV_WORKSPACE }}
|
||||||
run: cargo build --bin uv --bin uvx
|
run: cargo build --profile no-debug --bin uv --bin uvx
|
||||||
|
|
||||||
- name: "Upload binary"
|
- name: "Upload binary"
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: uv-windows-x86_64-${{ github.sha }}
|
name: uv-windows-x86_64-${{ github.sha }}
|
||||||
path: |
|
path: |
|
||||||
${{ env.UV_WORKSPACE }}/target/debug/uv.exe
|
${{ env.UV_WORKSPACE }}/target/no-debug/uv.exe
|
||||||
${{ env.UV_WORKSPACE }}/target/debug/uvx.exe
|
${{ env.UV_WORKSPACE }}/target/no-debug/uvx.exe
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-binary-windows-aarch64:
|
build-binary-windows-aarch64:
|
||||||
|
|
@ -717,7 +742,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
workspaces: ${{ env.UV_WORKSPACE }}
|
workspaces: ${{ env.UV_WORKSPACE }}
|
||||||
|
|
||||||
|
|
@ -726,15 +751,15 @@ jobs:
|
||||||
|
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
working-directory: ${{ env.UV_WORKSPACE }}
|
working-directory: ${{ env.UV_WORKSPACE }}
|
||||||
run: cargo build --target aarch64-pc-windows-msvc
|
run: cargo build --profile no-debug --target aarch64-pc-windows-msvc
|
||||||
|
|
||||||
- name: "Upload binary"
|
- name: "Upload binary"
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: uv-windows-aarch64-${{ github.sha }}
|
name: uv-windows-aarch64-${{ github.sha }}
|
||||||
path: |
|
path: |
|
||||||
${{ env.UV_WORKSPACE }}/target/aarch64-pc-windows-msvc/debug/uv.exe
|
${{ env.UV_WORKSPACE }}/target/aarch64-pc-windows-msvc/no-debug/uv.exe
|
||||||
${{ env.UV_WORKSPACE }}/target/aarch64-pc-windows-msvc/debug/uvx.exe
|
${{ env.UV_WORKSPACE }}/target/aarch64-pc-windows-msvc/no-debug/uvx.exe
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-binary-msrv:
|
build-binary-msrv:
|
||||||
|
|
@ -758,11 +783,11 @@ jobs:
|
||||||
MSRV: ${{ steps.msrv.outputs.value }}
|
MSRV: ${{ steps.msrv.outputs.value }}
|
||||||
- name: "Install mold"
|
- name: "Install mold"
|
||||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
- run: cargo +${MSRV} build
|
- run: cargo +${MSRV} build --profile no-debug
|
||||||
env:
|
env:
|
||||||
MSRV: ${{ steps.msrv.outputs.value }}
|
MSRV: ${{ steps.msrv.outputs.value }}
|
||||||
- run: ./target/debug/uv --version
|
- run: ./target/no-debug/uv --version
|
||||||
|
|
||||||
build-binary-freebsd:
|
build-binary-freebsd:
|
||||||
needs: determine_changes
|
needs: determine_changes
|
||||||
|
|
@ -775,7 +800,7 @@ jobs:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
- name: "Cross build"
|
- name: "Cross build"
|
||||||
run: |
|
run: |
|
||||||
# Install cross from `freebsd-firecracker`
|
# Install cross from `freebsd-firecracker`
|
||||||
|
|
@ -783,7 +808,7 @@ jobs:
|
||||||
chmod +x cross
|
chmod +x cross
|
||||||
mv cross /usr/local/bin/cross
|
mv cross /usr/local/bin/cross
|
||||||
|
|
||||||
cross build --target x86_64-unknown-freebsd
|
cross build --target x86_64-unknown-freebsd --profile no-debug
|
||||||
|
|
||||||
- name: Test in Firecracker VM
|
- name: Test in Firecracker VM
|
||||||
uses: acj/freebsd-firecracker-action@a5a3fc1709c5b5368141a5699f10259aca3cd965 # v0.6.0
|
uses: acj/freebsd-firecracker-action@a5a3fc1709c5b5368141a5699f10259aca3cd965 # v0.6.0
|
||||||
|
|
@ -797,8 +822,8 @@ jobs:
|
||||||
cat <<EOF > $include_path
|
cat <<EOF > $include_path
|
||||||
target
|
target
|
||||||
target/x86_64-unknown-freebsd
|
target/x86_64-unknown-freebsd
|
||||||
target/x86_64-unknown-freebsd/debug
|
target/x86_64-unknown-freebsd/no-debug
|
||||||
target/x86_64-unknown-freebsd/debug/uv
|
target/x86_64-unknown-freebsd/no-debug/uv
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
rsync -r -e "ssh" \
|
rsync -r -e "ssh" \
|
||||||
|
|
@ -808,7 +833,7 @@ jobs:
|
||||||
--exclude "*" \
|
--exclude "*" \
|
||||||
. firecracker:
|
. firecracker:
|
||||||
run-in-vm: |
|
run-in-vm: |
|
||||||
mv target/x86_64-unknown-freebsd/debug/uv uv
|
mv target/x86_64-unknown-freebsd/no-debug/uv uv
|
||||||
chmod +x uv
|
chmod +x uv
|
||||||
./uv --version
|
./uv --version
|
||||||
|
|
||||||
|
|
@ -1848,7 +1873,7 @@ jobs:
|
||||||
run: chmod +x ./uv
|
run: chmod +x ./uv
|
||||||
|
|
||||||
- name: "Configure AWS credentials"
|
- name: "Configure AWS credentials"
|
||||||
uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 # v5.0.0
|
uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1
|
||||||
with:
|
with:
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
|
@ -2042,22 +2067,22 @@ jobs:
|
||||||
|
|
||||||
# Test the main path (`build_wheel`) through pip
|
# Test the main path (`build_wheel`) through pip
|
||||||
./uv venv -v --seed
|
./uv venv -v --seed
|
||||||
./uv run --no-project python -m pip install -v scripts/packages/built-by-uv --find-links crates/uv-build/dist --no-index --no-deps
|
./uv run --no-project python -m pip install -v test/packages/built-by-uv --find-links crates/uv-build/dist --no-index --no-deps
|
||||||
./uv run --no-project python -c "from built_by_uv import greet; print(greet())"
|
./uv run --no-project python -c "from built_by_uv import greet; print(greet())"
|
||||||
|
|
||||||
# Test both `build_wheel` and `build_sdist` through uv
|
# Test both `build_wheel` and `build_sdist` through uv
|
||||||
./uv venv -c -v
|
./uv venv -c -v
|
||||||
./uv build -v --force-pep517 scripts/packages/built-by-uv --find-links crates/uv-build/dist --offline
|
./uv build -v --force-pep517 test/packages/built-by-uv --find-links crates/uv-build/dist --offline
|
||||||
./uv pip install -v scripts/packages/built-by-uv/dist/*.tar.gz --find-links crates/uv-build/dist --offline --no-deps
|
./uv pip install -v test/packages/built-by-uv/dist/*.tar.gz --find-links crates/uv-build/dist --offline --no-deps
|
||||||
./uv run --no-project python -c "from built_by_uv import greet; print(greet())"
|
./uv run --no-project python -c "from built_by_uv import greet; print(greet())"
|
||||||
|
|
||||||
# Test both `build_wheel` and `build_sdist` through the official `build`
|
# Test both `build_wheel` and `build_sdist` through the official `build`
|
||||||
rm -rf scripts/packages/built-by-uv/dist/
|
rm -rf test/packages/built-by-uv/dist/
|
||||||
./uv venv -c -v
|
./uv venv -c -v
|
||||||
./uv pip install build
|
./uv pip install build
|
||||||
# Add the uv binary to PATH for `build` to find
|
# Add the uv binary to PATH for `build` to find
|
||||||
PATH="$(pwd):$PATH" UV_OFFLINE=1 UV_FIND_LINKS=crates/uv-build/dist ./uv run --no-project python -m build -v --installer uv scripts/packages/built-by-uv
|
PATH="$(pwd):$PATH" UV_OFFLINE=1 UV_FIND_LINKS=crates/uv-build/dist ./uv run --no-project python -m build -v --installer uv test/packages/built-by-uv
|
||||||
./uv pip install -v scripts/packages/built-by-uv/dist/*.tar.gz --find-links crates/uv-build/dist --offline --no-deps
|
./uv pip install -v test/packages/built-by-uv/dist/*.tar.gz --find-links crates/uv-build/dist --offline --no-deps
|
||||||
./uv run --no-project python -c "from built_by_uv import greet; print(greet())"
|
./uv run --no-project python -c "from built_by_uv import greet; print(greet())"
|
||||||
|
|
||||||
cache-test-ubuntu:
|
cache-test-ubuntu:
|
||||||
|
|
@ -2906,7 +2931,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
|
|
@ -2921,8 +2946,8 @@ jobs:
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libsasl2-dev libldap2-dev libkrb5-dev
|
sudo apt-get install -y libsasl2-dev libldap2-dev libkrb5-dev
|
||||||
cargo run --bin uv -- venv --cache-dir .cache
|
cargo run --bin uv -- venv --cache-dir .cache
|
||||||
cargo run --bin uv -- pip compile scripts/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
|
cargo run --bin uv -- pip compile test/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
|
||||||
cargo run --bin uv -- pip compile scripts/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
|
cargo run --bin uv -- pip compile test/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
|
||||||
|
|
||||||
- name: "Build benchmarks"
|
- name: "Build benchmarks"
|
||||||
run: cargo codspeed build --profile profiling -p uv-bench
|
run: cargo codspeed build --profile profiling -p uv-bench
|
||||||
|
|
@ -2946,7 +2971,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
|
|
@ -2961,8 +2986,8 @@ jobs:
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libsasl2-dev libldap2-dev libkrb5-dev
|
sudo apt-get install -y libsasl2-dev libldap2-dev libkrb5-dev
|
||||||
cargo run --bin uv -- venv --cache-dir .cache
|
cargo run --bin uv -- venv --cache-dir .cache
|
||||||
cargo run --bin uv -- pip compile scripts/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
|
cargo run --bin uv -- pip compile test/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
|
||||||
cargo run --bin uv -- pip compile scripts/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
|
cargo run --bin uv -- pip compile test/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache
|
||||||
|
|
||||||
- name: "Build benchmarks"
|
- name: "Build benchmarks"
|
||||||
run: cargo codspeed build --profile profiling -p uv-bench
|
run: cargo codspeed build --profile profiling -p uv-bench
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ jobs:
|
||||||
# - uses: rust-lang/crates-io-auth-action@v1
|
# - uses: rust-lang/crates-io-auth-action@v1
|
||||||
# id: auth
|
# id: auth
|
||||||
- name: Publish workspace crates
|
- name: Publish workspace crates
|
||||||
run: cargo publish --workspace
|
# Note `--no-verify` is safe because we do a publish dry-run elsewhere in CI
|
||||||
|
run: cargo publish --workspace --no-verify
|
||||||
env:
|
env:
|
||||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_TOKEN }}
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_TOKEN }}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,14 @@ jobs:
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.12
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||||
|
|
||||||
|
- name: "Generate reference documentation"
|
||||||
|
run: |
|
||||||
|
cargo dev generate-options-reference
|
||||||
|
cargo dev generate-cli-reference
|
||||||
|
cargo dev generate-env-vars-reference
|
||||||
|
|
||||||
- name: "Set docs display name"
|
- name: "Set docs display name"
|
||||||
run: |
|
run: |
|
||||||
version="${VERSION}"
|
version="${VERSION}"
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,7 @@ jobs:
|
||||||
environment:
|
environment:
|
||||||
name: release
|
name: release
|
||||||
permissions:
|
permissions:
|
||||||
# For PyPI's trusted publishing.
|
id-token: write # For PyPI's trusted publishing
|
||||||
id-token: write
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Install uv"
|
- name: "Install uv"
|
||||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
||||||
|
|
@ -37,8 +36,7 @@ jobs:
|
||||||
environment:
|
environment:
|
||||||
name: release
|
name: release
|
||||||
permissions:
|
permissions:
|
||||||
# For PyPI's trusted publishing.
|
id-token: write # For PyPI's trusted publishing
|
||||||
id-token: write
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Install uv"
|
- name: "Install uv"
|
||||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,7 @@ jobs:
|
||||||
needs:
|
needs:
|
||||||
- plan
|
- plan
|
||||||
- host
|
- host
|
||||||
|
- custom-publish-pypi # DIRTY: see #16989
|
||||||
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
|
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
|
||||||
uses: ./.github/workflows/publish-crates.yml
|
uses: ./.github/workflows/publish-crates.yml
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,11 @@ profile.json.gz
|
||||||
# MkDocs
|
# MkDocs
|
||||||
/site
|
/site
|
||||||
|
|
||||||
|
# Generated reference docs (use `cargo dev generate-all` to regenerate)
|
||||||
|
/docs/reference/cli.md
|
||||||
|
/docs/reference/environment.md
|
||||||
|
/docs/reference/settings.md
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,5 @@ PREVIEW-CHANGELOG.md
|
||||||
docs/reference/cli.md
|
docs/reference/cli.md
|
||||||
docs/reference/settings.md
|
docs/reference/settings.md
|
||||||
docs/reference/environment.md
|
docs/reference/environment.md
|
||||||
ecosystem/home-assistant-core/LICENSE.md
|
test/ecosystem/home-assistant-core/LICENSE.md
|
||||||
docs/guides/integration/gitlab.md
|
docs/guides/integration/gitlab.md
|
||||||
|
|
|
||||||
164
CHANGELOG.md
164
CHANGELOG.md
|
|
@ -3,6 +3,158 @@
|
||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
|
|
||||||
|
## 0.9.18
|
||||||
|
|
||||||
|
Released on 2025-12-16.
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
- Add value hints to command line arguments to improve shell completion accuracy ([#17080](https://github.com/astral-sh/uv/pull/17080))
|
||||||
|
- Improve error handling in `uv publish` ([#17096](https://github.com/astral-sh/uv/pull/17096))
|
||||||
|
- Improve rendering of multiline error messages ([#17132](https://github.com/astral-sh/uv/pull/17132))
|
||||||
|
- Support redirects in `uv publish` ([#17130](https://github.com/astral-sh/uv/pull/17130))
|
||||||
|
- Include Docker images with the alpine version, e.g., `python3.x-alpine3.23` ([#17100](https://github.com/astral-sh/uv/pull/17100))
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
- Accept `--torch-backend` in `[tool.uv]` ([#17116](https://github.com/astral-sh/uv/pull/17116))
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
- Speed up `uv cache size` ([#17015](https://github.com/astral-sh/uv/pull/17015))
|
||||||
|
- Initialize S3 signer once ([#17092](https://github.com/astral-sh/uv/pull/17092))
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Avoid panics due to reads on failed requests ([#17098](https://github.com/astral-sh/uv/pull/17098))
|
||||||
|
- Enforce latest-version in `@latest` requests ([#17114](https://github.com/astral-sh/uv/pull/17114))
|
||||||
|
- Explicitly set `EntryType` for file entries in tar ([#17043](https://github.com/astral-sh/uv/pull/17043))
|
||||||
|
- Ignore `pyproject.toml` index username in lockfile comparison ([#16995](https://github.com/astral-sh/uv/pull/16995))
|
||||||
|
- Relax error when using `uv add` with `UV_GIT_LFS` set ([#17127](https://github.com/astral-sh/uv/pull/17127))
|
||||||
|
- Support file locks on ExFAT on macOS ([#17115](https://github.com/astral-sh/uv/pull/17115))
|
||||||
|
- Change schema for `exclude-newer` into optional string ([#17121](https://github.com/astral-sh/uv/pull/17121))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Drop arm musl caveat from Docker documentation ([#17111](https://github.com/astral-sh/uv/pull/17111))
|
||||||
|
- Fix version reference in resolver example ([#17085](https://github.com/astral-sh/uv/pull/17085))
|
||||||
|
- Better documentation for `exclude-newer*` ([#17079](https://github.com/astral-sh/uv/pull/17079))
|
||||||
|
|
||||||
|
## 0.9.17
|
||||||
|
|
||||||
|
Released on 2025-12-09.
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
- Add `torch-tensorrt` and `torchao` to the PyTorch list ([#17053](https://github.com/astral-sh/uv/pull/17053))
|
||||||
|
- Add hint for misplaced `--verbose` in `uv tool run` ([#17020](https://github.com/astral-sh/uv/pull/17020))
|
||||||
|
- Add support for relative durations in `exclude-newer` (a.k.a., dependency cooldowns) ([#16814](https://github.com/astral-sh/uv/pull/16814))
|
||||||
|
- Add support for relocatable nushell activation script ([#17036](https://github.com/astral-sh/uv/pull/17036))
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Respect dropped (but explicit) indexes in dependency groups ([#17012](https://github.com/astral-sh/uv/pull/17012))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Improve `source-exclude` reference docs ([#16832](https://github.com/astral-sh/uv/pull/16832))
|
||||||
|
- Recommend `UV_NO_DEV` in Docker installs ([#17030](https://github.com/astral-sh/uv/pull/17030))
|
||||||
|
- Update `UV_VERSION` in docs for GitLab CI/CD ([#17040](https://github.com/astral-sh/uv/pull/17040))
|
||||||
|
|
||||||
|
## 0.9.16
|
||||||
|
|
||||||
|
Released on 2025-12-06.
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
- Add CPython 3.14.2
|
||||||
|
- Add CPython 3.13.11
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
- Add a 5m default timeout to acquiring file locks to fail faster on deadlock ([#16342](https://github.com/astral-sh/uv/pull/16342))
|
||||||
|
- Add a stub `debug` subcommand to `uv pip` announcing its intentional absence ([#16966](https://github.com/astral-sh/uv/pull/16966))
|
||||||
|
- Add bounds in `uv add --script` ([#16954](https://github.com/astral-sh/uv/pull/16954))
|
||||||
|
- Add brew specific message for `uv self update` ([#16838](https://github.com/astral-sh/uv/pull/16838))
|
||||||
|
- Error when built wheel is for the wrong platform ([#16074](https://github.com/astral-sh/uv/pull/16074))
|
||||||
|
- Filter wheels from PEP 751 files based on `--no-binary` et al in `uv pip compile` ([#16956](https://github.com/astral-sh/uv/pull/16956))
|
||||||
|
- Support `--target` and `--prefix` in `uv pip list`, `uv pip freeze`, and `uv pip show` ([#16955](https://github.com/astral-sh/uv/pull/16955))
|
||||||
|
- Tweak language for build backend validation errors ([#16720](https://github.com/astral-sh/uv/pull/16720))
|
||||||
|
- Use explicit credentials cache instead of global static ([#16768](https://github.com/astral-sh/uv/pull/16768))
|
||||||
|
- Enable SIMD in HTML parsing ([#17010](https://github.com/astral-sh/uv/pull/17010))
|
||||||
|
|
||||||
|
### Preview features
|
||||||
|
|
||||||
|
- Fix missing preview warning in `uv workspace metadata` ([#16988](https://github.com/astral-sh/uv/pull/16988))
|
||||||
|
- Add a `uv auth helper --protocol bazel` command ([#16886](https://github.com/astral-sh/uv/pull/16886))
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Fix Pyston wheel compatibility tags ([#16972](https://github.com/astral-sh/uv/pull/16972))
|
||||||
|
- Allow redundant entries in `tool.uv.build-backend.module-name` but emit warnings ([#16928](https://github.com/astral-sh/uv/pull/16928))
|
||||||
|
- Fix infinite loop in non-attribute re-treats during HTML parsing ([#17010](https://github.com/astral-sh/uv/pull/17010))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Clarify `--project` flag help text to indicate project discovery ([#16965](https://github.com/astral-sh/uv/pull/16965))
|
||||||
|
- Regenerate the crates.io READMEs on release ([#16992](https://github.com/astral-sh/uv/pull/16992))
|
||||||
|
- Update Docker integration guide to prefer `COPY` over `ADD` for simple cases ([#16883](https://github.com/astral-sh/uv/pull/16883))
|
||||||
|
- Update PyTorch documentation to include information about supporting CUDA 13.0.x ([#16957](https://github.com/astral-sh/uv/pull/16957))
|
||||||
|
- Update the versioning policy ([#16710](https://github.com/astral-sh/uv/pull/16710))
|
||||||
|
- Upgrade PyTorch documentation to latest versions ([#16970](https://github.com/astral-sh/uv/pull/16970))
|
||||||
|
|
||||||
|
## 0.9.15
|
||||||
|
|
||||||
|
Released on 2025-12-02.
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
- Add CPython 3.14.1
|
||||||
|
- Add CPython 3.13.10
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
- Add ROCm 6.4 to `--torch-backend=auto` ([#16919](https://github.com/astral-sh/uv/pull/16919))
|
||||||
|
- Add a Windows manifest to uv binaries ([#16894](https://github.com/astral-sh/uv/pull/16894))
|
||||||
|
- Add LFS toggle to Git sources ([#16143](https://github.com/astral-sh/uv/pull/16143))
|
||||||
|
- Cache source reads during resolution ([#16888](https://github.com/astral-sh/uv/pull/16888))
|
||||||
|
- Allow reading requirements from scripts without an extension ([#16923](https://github.com/astral-sh/uv/pull/16923))
|
||||||
|
- Allow reading requirements from scripts with HTTP(S) paths ([#16891](https://github.com/astral-sh/uv/pull/16891))
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
- Add `UV_HIDE_BUILD_OUTPUT` to omit build logs ([#16885](https://github.com/astral-sh/uv/pull/16885))
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Fix `uv-trampoline-builder` builds from crates.io by moving bundled executables ([#16922](https://github.com/astral-sh/uv/pull/16922))
|
||||||
|
- Respect `NO_COLOR` and always show the command as a header when paging `uv help` output ([#16908](https://github.com/astral-sh/uv/pull/16908))
|
||||||
|
- Use `0o666` permissions for flock files instead of `0o777` ([#16845](https://github.com/astral-sh/uv/pull/16845))
|
||||||
|
- Revert "Bump `astral-tl` to v0.7.10 (#16887)" to narrow down a regression causing hangs in metadata retrieval ([#16938](https://github.com/astral-sh/uv/pull/16938))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Link to the uv version in crates.io member READMEs ([#16939](https://github.com/astral-sh/uv/pull/16939))
|
||||||
|
|
||||||
|
## 0.9.14
|
||||||
|
|
||||||
|
Released on 2025-12-01.
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
- Bump `astral-tl` to v0.7.10 to enable SIMD for HTML parsing ([#16887](https://github.com/astral-sh/uv/pull/16887))
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Allow earlier post releases with exclusive ordering ([#16881](https://github.com/astral-sh/uv/pull/16881))
|
||||||
|
- Prefer updating existing `.zshenv` over creating a new one in `tool update-shell` ([#16866](https://github.com/astral-sh/uv/pull/16866))
|
||||||
|
- Respect `-e` flags in `uv add` ([#16882](https://github.com/astral-sh/uv/pull/16882))
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
- Attach subcommand to User-Agent string ([#16837](https://github.com/astral-sh/uv/pull/16837))
|
||||||
|
- Prefer `UV_WORKING_DIR` over `UV_WORKING_DIRECTORY` for consistency ([#16884](https://github.com/astral-sh/uv/pull/16884))
|
||||||
|
|
||||||
## 0.9.13
|
## 0.9.13
|
||||||
|
|
||||||
Released on 2025-11-26.
|
Released on 2025-11-26.
|
||||||
|
|
@ -351,25 +503,25 @@ There are no breaking changes to [`uv_build`](https://docs.astral.sh/uv/concepts
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|
||||||
- **Python 3.14 is now the default stable version**
|
- **Python 3.14 is now the default stable version**
|
||||||
|
|
||||||
The default Python version has changed from 3.13 to 3.14. This applies to Python version installation when no Python version is requested, e.g., `uv python install`. By default, uv will use the system Python version if present, so this may not cause changes to general use of uv. For example, if Python 3.13 is installed already, then `uv venv` will use that version. If no Python versions are installed on a machine and automatic downloads are enabled, uv will now use 3.14 instead of 3.13, e.g., for `uv venv` or `uvx python`. This change will not affect users who are using a `.python-version` file to pin to a specific Python version.
|
The default Python version has changed from 3.13 to 3.14. This applies to Python version installation when no Python version is requested, e.g., `uv python install`. By default, uv will use the system Python version if present, so this may not cause changes to general use of uv. For example, if Python 3.13 is installed already, then `uv venv` will use that version. If no Python versions are installed on a machine and automatic downloads are enabled, uv will now use 3.14 instead of 3.13, e.g., for `uv venv` or `uvx python`. This change will not affect users who are using a `.python-version` file to pin to a specific Python version.
|
||||||
- **Allow use of free-threaded variants in Python 3.14+ without explicit opt-in** ([#16142](https://github.com/astral-sh/uv/pull/16142))
|
- **Allow use of free-threaded variants in Python 3.14+ without explicit opt-in** ([#16142](https://github.com/astral-sh/uv/pull/16142))
|
||||||
|
|
||||||
Previously, free-threaded variants of Python were considered experimental and required explicit opt-in (i.e., with `3.14t`) for usage. Now uv will allow use of free-threaded Python 3.14+ interpreters without explicit selection. The GIL-enabled build of Python will still be preferred, e.g., when performing an installation with `uv python install 3.14`. However, e.g., if a free-threaded interpreter comes before a GIL-enabled build on the `PATH`, it will be used. This change does not apply to free-threaded Python 3.13 interpreters, which will continue to require opt-in.
|
Previously, free-threaded variants of Python were considered experimental and required explicit opt-in (i.e., with `3.14t`) for usage. Now uv will allow use of free-threaded Python 3.14+ interpreters without explicit selection. The GIL-enabled build of Python will still be preferred, e.g., when performing an installation with `uv python install 3.14`. However, e.g., if a free-threaded interpreter comes before a GIL-enabled build on the `PATH`, it will be used. This change does not apply to free-threaded Python 3.13 interpreters, which will continue to require opt-in.
|
||||||
- **Use Python 3.14 stable Docker images** ([#16150](https://github.com/astral-sh/uv/pull/16150))
|
- **Use Python 3.14 stable Docker images** ([#16150](https://github.com/astral-sh/uv/pull/16150))
|
||||||
|
|
||||||
Previously, the Python 3.14 images had an `-rc` suffix, e.g., `python:3.14-rc-alpine` or
|
Previously, the Python 3.14 images had an `-rc` suffix, e.g., `python:3.14-rc-alpine` or
|
||||||
`python:3.14-rc-trixie`. Now, the `-rc` suffix has been removed to match the stable
|
`python:3.14-rc-trixie`. Now, the `-rc` suffix has been removed to match the stable
|
||||||
[upstream images](https://hub.docker.com/_/python). The `-rc` images tags will no longer be
|
[upstream images](https://hub.docker.com/_/python). The `-rc` images tags will no longer be
|
||||||
updated. This change should not break existing workflows.
|
updated. This change should not break existing workflows.
|
||||||
- **Upgrade Alpine Docker image to Alpine 3.22**
|
- **Upgrade Alpine Docker image to Alpine 3.22**
|
||||||
|
|
||||||
Previously, the `uv:alpine` Docker image was based on Alpine 3.21. Now, this image is based on Alpine 3.22. The previous image can be recovered with `uv:alpine3.21` and will continue to be updated until a future release.
|
Previously, the `uv:alpine` Docker image was based on Alpine 3.21. Now, this image is based on Alpine 3.22. The previous image can be recovered with `uv:alpine3.21` and will continue to be updated until a future release.
|
||||||
- **Upgrade Debian Docker images to Debian 13 "Trixie"**
|
- **Upgrade Debian Docker images to Debian 13 "Trixie"**
|
||||||
|
|
||||||
Previously, the `uv:debian` and `uv:debian-slim` Docker images were based on Debian 12 "Bookworm". Now, these images are based on Debian 13 "Trixie". The previous images can be recovered with `uv:bookworm` and `uv:bookworm-slim` and will continue to be updated until a future release.
|
Previously, the `uv:debian` and `uv:debian-slim` Docker images were based on Debian 12 "Bookworm". Now, these images are based on Debian 13 "Trixie". The previous images can be recovered with `uv:bookworm` and `uv:bookworm-slim` and will continue to be updated until a future release.
|
||||||
- **Fix incorrect output path when a trailing `/` is used in `uv build`** ([#15133](https://github.com/astral-sh/uv/pull/15133))
|
- **Fix incorrect output path when a trailing `/` is used in `uv build`** ([#15133](https://github.com/astral-sh/uv/pull/15133))
|
||||||
|
|
||||||
When using `uv build` in a workspace, the artifacts are intended to be written to a `dist` directory in the workspace root. A bug caused workspace root determination to fail when the input path included a trailing `/` causing the `dist` directory to be placed in the child directory. This bug has been fixed in this release. For example, `uv build child/` is used, the output path will now be in `<workspace root>/dist/` rather than `<workspace root>/child/dist/`.
|
When using `uv build` in a workspace, the artifacts are intended to be written to a `dist` directory in the workspace root. A bug caused workspace root determination to fail when the input path included a trailing `/` causing the `dist` directory to be placed in the child directory. This bug has been fixed in this release. For example, `uv build child/` is used, the output path will now be in `<workspace root>/dist/` rather than `<workspace root>/child/dist/`.
|
||||||
|
|
||||||
### Python
|
### Python
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
- [Our Pledge](#our-pledge)
|
||||||
|
- [Our Standards](#our-standards)
|
||||||
|
- [Enforcement Responsibilities](#enforcement-responsibilities)
|
||||||
|
- [Scope](#scope)
|
||||||
|
- [Enforcement](#enforcement)
|
||||||
|
- [Enforcement Guidelines](#enforcement-guidelines)
|
||||||
|
- [1. Correction](#1-correction)
|
||||||
|
- [2. Warning](#2-warning)
|
||||||
|
- [3. Temporary Ban](#3-temporary-ban)
|
||||||
|
- [4. Permanent Ban](#4-permanent-ban)
|
||||||
|
- [Attribution](#attribution)
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our community a
|
||||||
|
harassment-free experience for everyone, regardless of age, body size, visible or invisible
|
||||||
|
disability, ethnicity, sex characteristics, gender identity and expression, level of experience,
|
||||||
|
education, socio-economic status, nationality, personal appearance, race, religion, or sexual
|
||||||
|
identity and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and
|
||||||
|
healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our community include:
|
||||||
|
|
||||||
|
- Demonstrating empathy and kindness toward other people
|
||||||
|
- Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
- Giving and gracefully accepting constructive feedback
|
||||||
|
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the
|
||||||
|
experience
|
||||||
|
- Focusing on what is best not just for us as individuals, but for the overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
||||||
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
- Public or private harassment
|
||||||
|
- Publishing others' private information, such as a physical or email address, without their
|
||||||
|
explicit permission
|
||||||
|
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior
|
||||||
|
and will take appropriate and fair corrective action in response to any behavior that they deem
|
||||||
|
inappropriate, threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject comments, commits,
|
||||||
|
code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and
|
||||||
|
will communicate reasons for moderation decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when an individual is
|
||||||
|
officially representing the community in public spaces. Examples of representing our community
|
||||||
|
include using an official e-mail address, posting via an official social media account, or acting as
|
||||||
|
an appointed representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community
|
||||||
|
leaders responsible for enforcement at <hey@astral.sh>. All complaints will be reviewed and
|
||||||
|
investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the reporter of any
|
||||||
|
incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining the consequences for
|
||||||
|
any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or
|
||||||
|
unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing clarity around the
|
||||||
|
nature of the violation and an explanation of why the behavior was inappropriate. A public apology
|
||||||
|
may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No interaction with the people
|
||||||
|
involved, including unsolicited interaction with those enforcing the Code of Conduct, for a
|
||||||
|
specified period of time. This includes avoiding interactions in community spaces as well as
|
||||||
|
external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including sustained inappropriate
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public communication with the
|
||||||
|
community for a specified period of time. No public or private interaction with the people involved,
|
||||||
|
including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this
|
||||||
|
period. Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community standards, including
|
||||||
|
sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement
|
||||||
|
of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available
|
||||||
|
[here](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by
|
||||||
|
[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the
|
||||||
|
[FAQ](https://www.contributor-covenant.org/faq). Translations are available
|
||||||
|
[here](https://www.contributor-covenant.org/translations).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
@ -86,6 +86,13 @@ cargo test --package <package> --test <test> -- <test_name> -- --exact
|
||||||
cargo insta review
|
cargo insta review
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Git and Git LFS
|
||||||
|
|
||||||
|
A subset of uv tests require both [Git](https://git-scm.com) and [Git LFS](https://git-lfs.com/) to
|
||||||
|
execute properly.
|
||||||
|
|
||||||
|
These tests can be disabled by turning off either `git` or `git-lfs` uv features.
|
||||||
|
|
||||||
### Local testing
|
### Local testing
|
||||||
|
|
||||||
You can invoke your development version of uv with `cargo run -- <args>`. For example:
|
You can invoke your development version of uv with `cargo run -- <args>`. For example:
|
||||||
|
|
@ -95,6 +102,15 @@ cargo run -- venv
|
||||||
cargo run -- pip install requests
|
cargo run -- pip install requests
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Crate structure
|
||||||
|
|
||||||
|
Rust does not allow circular dependencies between crates. To visualize the crate hierarchy, install
|
||||||
|
[cargo-depgraph](https://github.com/jplatte/cargo-depgraph) and graphviz, then run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo depgraph --dedup-transitive-deps --workspace-only | dot -Tpng > graph.png
|
||||||
|
```
|
||||||
|
|
||||||
## Running inside a Docker container
|
## Running inside a Docker container
|
||||||
|
|
||||||
Source distributions can run arbitrary code on build and can make unwanted modifications to your
|
Source distributions can run arbitrary code on build and can make unwanted modifications to your
|
||||||
|
|
@ -120,7 +136,7 @@ Please refer to Ruff's
|
||||||
it applies to uv, too.
|
it applies to uv, too.
|
||||||
|
|
||||||
We provide diverse sets of requirements for testing and benchmarking the resolver in
|
We provide diverse sets of requirements for testing and benchmarking the resolver in
|
||||||
`scripts/requirements` and for the installer in `scripts/requirements/compiled`.
|
`test/requirements` and for the installer in `test/requirements/compiled`.
|
||||||
|
|
||||||
You can use `scripts/benchmark` to benchmark predefined workloads between uv versions and with other
|
You can use `scripts/benchmark` to benchmark predefined workloads between uv versions and with other
|
||||||
tools, e.g., from the `scripts/benchmark` directory:
|
tools, e.g., from the `scripts/benchmark` directory:
|
||||||
|
|
@ -131,7 +147,7 @@ uv run resolver \
|
||||||
--poetry \
|
--poetry \
|
||||||
--benchmark \
|
--benchmark \
|
||||||
resolve-cold \
|
resolve-cold \
|
||||||
../scripts/requirements/trio.in
|
../test/requirements/trio.in
|
||||||
```
|
```
|
||||||
|
|
||||||
### Analyzing concurrency
|
### Analyzing concurrency
|
||||||
|
|
@ -141,7 +157,7 @@ visualize parallel requests and find any spots where uv is CPU-bound. Example us
|
||||||
`uv-dev` respectively:
|
`uv-dev` respectively:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
RUST_LOG=uv=info TRACING_DURATIONS_FILE=target/traces/jupyter.ndjson cargo run --features tracing-durations-export --profile profiling -- pip compile scripts/requirements/jupyter.in
|
RUST_LOG=uv=info TRACING_DURATIONS_FILE=target/traces/jupyter.ndjson cargo run --features tracing-durations-export --profile profiling -- pip compile test/requirements/jupyter.in
|
||||||
```
|
```
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ambient-id"
|
name = "ambient-id"
|
||||||
version = "0.0.6"
|
version = "0.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36b48a3b1ad866e5034859be45edd1ebba2f097289c8a34b61623c76f10480f3"
|
checksum = "b8cad022ed72ad2176498be1c097bb46e598193e92f3491ea0766980edeee168"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"astral-reqwest-middleware",
|
"astral-reqwest-middleware",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
|
@ -198,9 +198,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "astral-pubgrub"
|
name = "astral-pubgrub"
|
||||||
version = "0.3.2"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf544aa6f110fc4bfdffcc68b1ebeb1b39ce6188e3b9e057d7a5ed4fa865e7be"
|
checksum = "d6cb15b4f5096a3a1b41fdc2736a1c33d87c78f34d3c1ec2b669e766edadd559"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"astral-version-ranges",
|
"astral-version-ranges",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
|
@ -248,9 +248,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "astral-tl"
|
name = "astral-tl"
|
||||||
version = "0.7.9"
|
version = "0.7.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "915b5af1203c9c635c62edcbdaa36ee54b17f84809f7769912d356c35f9a6cd7"
|
checksum = "d90933ffb0f97e2fc2e0de21da9d3f20597b804012d199843a6fe7c2810d28f3"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "astral-tokio-tar"
|
name = "astral-tokio-tar"
|
||||||
|
|
@ -897,7 +900,7 @@ 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]]
|
[[package]]
|
||||||
|
|
@ -1035,6 +1038,15 @@ dependencies = [
|
||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.6"
|
version = "0.8.6"
|
||||||
|
|
@ -1252,6 +1264,16 @@ dependencies = [
|
||||||
"windows-sys 0.61.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diskus"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec07379c016f78e7ddcd953663b9ed17928ff384928d34d824ed7e463bd3d908"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"rayon",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dispatch2"
|
name = "dispatch2"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
|
@ -1314,6 +1336,12 @@ version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embed-manifest"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94cdc65b1cf9e871453ce2f86f5aaec24ff2eaa36a1fa3e02e441dddc3613b99"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
@ -1417,7 +1445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1776,9 +1804,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "goblin"
|
name = "goblin"
|
||||||
version = "0.10.3"
|
version = "0.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51876e3748c4a347fe65b906f2b1ae46a1e55a497b22c94c1f4f2c469ff7673a"
|
checksum = "4db6758c546e6f81f265638c980e5e84dfbda80cfd8e89e02f83454c8e8124bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"plain",
|
"plain",
|
||||||
|
|
@ -2259,7 +2287,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2329,7 +2357,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.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2426,9 +2454,9 @@ checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libmimalloc-sys"
|
name = "libmimalloc-sys"
|
||||||
version = "0.1.43"
|
version = "0.1.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf88cd67e9de251c1781dbe2f641a1a3ad66eaae831b8a2c38fbdc5ddae16d4d"
|
checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
@ -2617,9 +2645,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mimalloc"
|
name = "mimalloc"
|
||||||
version = "0.1.47"
|
version = "0.1.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1791cbe101e95af5764f06f20f6760521f7158f69dbf9d6baf941ee1bf6bc40"
|
checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libmimalloc-sys",
|
"libmimalloc-sys",
|
||||||
]
|
]
|
||||||
|
|
@ -2860,9 +2888,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "open"
|
name = "open"
|
||||||
version = "5.3.2"
|
version = "5.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95"
|
checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"is-wsl",
|
"is-wsl",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
@ -3340,7 +3368,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2 0.5.10",
|
"socket2 0.5.10",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3434,9 +3462,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.10.0"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"rayon-core",
|
"rayon-core",
|
||||||
|
|
@ -3444,9 +3472,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon-core"
|
name = "rayon-core"
|
||||||
version = "1.12.1"
|
version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-deque",
|
"crossbeam-deque",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
|
|
@ -3878,7 +3906,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.4.15",
|
"linux-raw-sys 0.4.15",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3891,14 +3919,14 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.9.4",
|
"linux-raw-sys 0.9.4",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.29"
|
version = "0.23.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1"
|
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ring",
|
"ring",
|
||||||
|
|
@ -3932,9 +3960,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.103.4"
|
version = "0.103.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
|
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
|
@ -4369,9 +4397,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spdx"
|
name = "spdx"
|
||||||
version = "0.12.0"
|
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 = "41cf87c0efffc158b9dde4d6e0567a43e4383adc4c949e687a2039732db2f23a"
|
checksum = "35107b1c818f4e9cb9e6c4444ca560ba03b4ee1288dcecc6d7830c2023a7609e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
@ -4500,9 +4528,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.108"
|
version = "2.0.111"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -4602,7 +4630,7 @@ dependencies = [
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix 1.0.8",
|
"rustix 1.0.8",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5378,7 +5406,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv"
|
name = "uv"
|
||||||
version = "0.9.13"
|
version = "0.9.18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
@ -5393,13 +5421,14 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"console 0.16.1",
|
"console 0.16.1",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
|
"diskus",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"dunce",
|
"dunce",
|
||||||
|
"embed-manifest",
|
||||||
"filetime",
|
"filetime",
|
||||||
"flate2",
|
"flate2",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"futures",
|
"futures",
|
||||||
"h2",
|
|
||||||
"http",
|
"http",
|
||||||
"ignore",
|
"ignore",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
|
@ -5496,7 +5525,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-auth"
|
name = "uv-auth"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arcstr",
|
"arcstr",
|
||||||
|
|
@ -5539,7 +5568,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-bench"
|
name = "uv-bench"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"codspeed-criterion-compat",
|
"codspeed-criterion-compat",
|
||||||
|
|
@ -5566,7 +5595,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-bin-install"
|
name = "uv-bin-install"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"astral-reqwest-middleware",
|
"astral-reqwest-middleware",
|
||||||
"astral-reqwest-retry",
|
"astral-reqwest-retry",
|
||||||
|
|
@ -5590,7 +5619,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-build"
|
name = "uv-build"
|
||||||
version = "0.9.13"
|
version = "0.9.18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
@ -5602,7 +5631,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-build-backend"
|
name = "uv-build-backend"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"astral-version-ranges",
|
"astral-version-ranges",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
|
@ -5618,7 +5647,7 @@ dependencies = [
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"spdx 0.12.0",
|
"spdx 0.13.2",
|
||||||
"tar",
|
"tar",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
|
|
@ -5642,7 +5671,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-build-frontend"
|
name = "uv-build-frontend"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
|
|
@ -5659,6 +5688,7 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml_edit 0.23.7",
|
"toml_edit 0.23.7",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uv-auth",
|
||||||
"uv-cache-key",
|
"uv-cache-key",
|
||||||
"uv-configuration",
|
"uv-configuration",
|
||||||
"uv-distribution",
|
"uv-distribution",
|
||||||
|
|
@ -5679,7 +5709,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-cache"
|
name = "uv-cache"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
|
|
@ -5689,6 +5719,7 @@ dependencies = [
|
||||||
"same-file",
|
"same-file",
|
||||||
"serde",
|
"serde",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"thiserror 2.0.17",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uv-cache-info",
|
"uv-cache-info",
|
||||||
"uv-cache-key",
|
"uv-cache-key",
|
||||||
|
|
@ -5704,7 +5735,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-cache-info"
|
name = "uv-cache-info"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
|
|
@ -5721,7 +5752,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-cache-key"
|
name = "uv-cache-key"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -5733,7 +5764,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-cli"
|
name = "uv-cli"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
@ -5765,7 +5796,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-client"
|
name = "uv-client"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"astral-reqwest-middleware",
|
"astral-reqwest-middleware",
|
||||||
|
|
@ -5828,7 +5859,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-configuration"
|
name = "uv-configuration"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|
@ -5857,14 +5888,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-console"
|
name = "uv-console"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console 0.16.1",
|
"console 0.16.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-dev"
|
name = "uv-dev"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
@ -5913,7 +5944,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-dirs"
|
name = "uv-dirs"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_fs",
|
"assert_fs",
|
||||||
"etcetera",
|
"etcetera",
|
||||||
|
|
@ -5925,7 +5956,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-dispatch"
|
name = "uv-dispatch"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures",
|
"futures",
|
||||||
|
|
@ -5957,7 +5988,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-distribution"
|
name = "uv-distribution"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"astral-reqwest-middleware",
|
"astral-reqwest-middleware",
|
||||||
|
|
@ -5987,6 +6018,7 @@ dependencies = [
|
||||||
"uv-distribution-filename",
|
"uv-distribution-filename",
|
||||||
"uv-distribution-types",
|
"uv-distribution-types",
|
||||||
"uv-extract",
|
"uv-extract",
|
||||||
|
"uv-flags",
|
||||||
"uv-fs",
|
"uv-fs",
|
||||||
"uv-git",
|
"uv-git",
|
||||||
"uv-git-types",
|
"uv-git-types",
|
||||||
|
|
@ -6005,7 +6037,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-distribution-filename"
|
name = "uv-distribution-filename"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"insta",
|
"insta",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -6022,7 +6054,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-distribution-types"
|
name = "uv-distribution-types"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcstr",
|
"arcstr",
|
||||||
"astral-version-ranges",
|
"astral-version-ranges",
|
||||||
|
|
@ -6062,7 +6094,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-extract"
|
name = "uv-extract"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"astral-tokio-tar",
|
"astral-tokio-tar",
|
||||||
"astral_async_zip",
|
"astral_async_zip",
|
||||||
|
|
@ -6092,14 +6124,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-flags"
|
name = "uv-flags"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.4",
|
"bitflags 2.9.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-fs"
|
name = "uv-fs"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backon",
|
"backon",
|
||||||
"dunce",
|
"dunce",
|
||||||
|
|
@ -6114,20 +6146,23 @@ dependencies = [
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uv-static",
|
||||||
"windows 0.59.0",
|
"windows 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-git"
|
name = "uv-git"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"astral-reqwest-middleware",
|
"astral-reqwest-middleware",
|
||||||
"cargo-util",
|
"cargo-util",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
|
"owo-colors",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
@ -6140,23 +6175,25 @@ dependencies = [
|
||||||
"uv-redacted",
|
"uv-redacted",
|
||||||
"uv-static",
|
"uv-static",
|
||||||
"uv-version",
|
"uv-version",
|
||||||
|
"uv-warnings",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-git-types"
|
name = "uv-git-types"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"uv-redacted",
|
"uv-redacted",
|
||||||
|
"uv-static",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-globfilter"
|
name = "uv-globfilter"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
|
|
@ -6173,7 +6210,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-install-wheel"
|
name = "uv-install-wheel"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_fs",
|
"assert_fs",
|
||||||
|
|
@ -6213,7 +6250,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-installer"
|
name = "uv-installer"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-channel",
|
"async-channel",
|
||||||
|
|
@ -6254,7 +6291,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-keyring"
|
name = "uv-keyring"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
|
@ -6271,7 +6308,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-logging"
|
name = "uv-logging"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jiff",
|
"jiff",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
|
|
@ -6281,7 +6318,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-macros"
|
name = "uv-macros"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -6291,7 +6328,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-metadata"
|
name = "uv-metadata"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"astral_async_zip",
|
"astral_async_zip",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
|
|
@ -6308,7 +6345,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-normalize"
|
name = "uv-normalize"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rkyv",
|
"rkyv",
|
||||||
"schemars",
|
"schemars",
|
||||||
|
|
@ -6318,7 +6355,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-once-map"
|
name = "uv-once-map"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"futures",
|
"futures",
|
||||||
|
|
@ -6327,14 +6364,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-options-metadata"
|
name = "uv-options-metadata"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-pep440"
|
name = "uv-pep440"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"astral-version-ranges",
|
"astral-version-ranges",
|
||||||
"indoc",
|
"indoc",
|
||||||
|
|
@ -6348,7 +6385,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-pep508"
|
name = "uv-pep508"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcstr",
|
"arcstr",
|
||||||
"astral-version-ranges",
|
"astral-version-ranges",
|
||||||
|
|
@ -6377,7 +6414,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-performance-memory-allocator"
|
name = "uv-performance-memory-allocator"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"tikv-jemallocator",
|
"tikv-jemallocator",
|
||||||
|
|
@ -6385,7 +6422,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-platform"
|
name = "uv-platform"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"goblin",
|
"goblin",
|
||||||
|
|
@ -6402,7 +6439,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-platform-tags"
|
name = "uv-platform-tags"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"insta",
|
"insta",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -6415,7 +6452,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-preview"
|
name = "uv-preview"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.4",
|
"bitflags 2.9.4",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
|
|
@ -6424,9 +6461,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-publish"
|
name = "uv-publish"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ambient-id",
|
"ambient-id",
|
||||||
|
"anstream",
|
||||||
"astral-reqwest-middleware",
|
"astral-reqwest-middleware",
|
||||||
"astral-reqwest-retry",
|
"astral-reqwest-retry",
|
||||||
"astral-tokio-tar",
|
"astral-tokio-tar",
|
||||||
|
|
@ -6460,11 +6498,12 @@ dependencies = [
|
||||||
"uv-redacted",
|
"uv-redacted",
|
||||||
"uv-static",
|
"uv-static",
|
||||||
"uv-warnings",
|
"uv-warnings",
|
||||||
|
"wiremock",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-pypi-types"
|
name = "uv-pypi-types"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
|
|
@ -6496,7 +6535,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-python"
|
name = "uv-python"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_fs",
|
"assert_fs",
|
||||||
|
|
@ -6558,7 +6597,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-redacted"
|
name = "uv-redacted"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ref-cast",
|
"ref-cast",
|
||||||
"schemars",
|
"schemars",
|
||||||
|
|
@ -6569,7 +6608,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-requirements"
|
name = "uv-requirements"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"configparser",
|
"configparser",
|
||||||
|
|
@ -6604,7 +6643,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-requirements-txt"
|
name = "uv-requirements-txt"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_fs",
|
"assert_fs",
|
||||||
|
|
@ -6637,7 +6676,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-resolver"
|
name = "uv-resolver"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcstr",
|
"arcstr",
|
||||||
"astral-pubgrub",
|
"astral-pubgrub",
|
||||||
|
|
@ -6702,7 +6741,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-scripts"
|
name = "uv-scripts"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"indoc",
|
"indoc",
|
||||||
|
|
@ -6726,7 +6765,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-settings"
|
name = "uv-settings"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
|
|
@ -6761,12 +6800,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-shell"
|
name = "uv-shell"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"nix",
|
"nix",
|
||||||
"same-file",
|
"same-file",
|
||||||
|
"temp-env",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uv-fs",
|
"uv-fs",
|
||||||
|
|
@ -6777,7 +6817,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-small-str"
|
name = "uv-small-str"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcstr",
|
"arcstr",
|
||||||
"rkyv",
|
"rkyv",
|
||||||
|
|
@ -6787,7 +6827,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-state"
|
name = "uv-state"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
|
@ -6796,14 +6836,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-static"
|
name = "uv-static"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"uv-macros",
|
"uv-macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-tool"
|
name = "uv-tool"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
|
|
@ -6832,7 +6872,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-torch"
|
name = "uv-torch"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"either",
|
"either",
|
||||||
|
|
@ -6852,7 +6892,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-trampoline-builder"
|
name = "uv-trampoline-builder"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
|
|
@ -6869,7 +6909,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-types"
|
name = "uv-types"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
|
@ -6891,11 +6931,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-version"
|
name = "uv-version"
|
||||||
version = "0.9.13"
|
version = "0.9.18"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-virtualenv"
|
name = "uv-virtualenv"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console 0.16.1",
|
"console 0.16.1",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
|
|
@ -6917,16 +6957,19 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-warnings"
|
name = "uv-warnings"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
|
"anyhow",
|
||||||
|
"indoc",
|
||||||
|
"insta",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-workspace"
|
name = "uv-workspace"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_fs",
|
"assert_fs",
|
||||||
|
|
@ -7208,7 +7251,7 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
140
Cargo.toml
140
Cargo.toml
|
|
@ -16,66 +16,66 @@ authors = ["uv"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
uv-auth = { version = "0.0.3", path = "crates/uv-auth" }
|
uv-auth = { version = "0.0.8", path = "crates/uv-auth" }
|
||||||
uv-bin-install = { version = "0.0.3", path = "crates/uv-bin-install" }
|
uv-bin-install = { version = "0.0.8", path = "crates/uv-bin-install" }
|
||||||
uv-build-backend = { version = "0.0.3", path = "crates/uv-build-backend" }
|
uv-build-backend = { version = "0.0.8", path = "crates/uv-build-backend" }
|
||||||
uv-build-frontend = { version = "0.0.3", path = "crates/uv-build-frontend" }
|
uv-build-frontend = { version = "0.0.8", path = "crates/uv-build-frontend" }
|
||||||
uv-cache = { version = "0.0.3", path = "crates/uv-cache" }
|
uv-cache = { version = "0.0.8", path = "crates/uv-cache" }
|
||||||
uv-cache-info = { version = "0.0.3", path = "crates/uv-cache-info" }
|
uv-cache-info = { version = "0.0.8", path = "crates/uv-cache-info" }
|
||||||
uv-cache-key = { version = "0.0.3", path = "crates/uv-cache-key" }
|
uv-cache-key = { version = "0.0.8", path = "crates/uv-cache-key" }
|
||||||
uv-cli = { version = "0.0.3", path = "crates/uv-cli" }
|
uv-cli = { version = "0.0.8", path = "crates/uv-cli" }
|
||||||
uv-client = { version = "0.0.3", path = "crates/uv-client" }
|
uv-client = { version = "0.0.8", path = "crates/uv-client" }
|
||||||
uv-configuration = { version = "0.0.3", path = "crates/uv-configuration" }
|
uv-configuration = { version = "0.0.8", path = "crates/uv-configuration" }
|
||||||
uv-console = { version = "0.0.3", path = "crates/uv-console" }
|
uv-console = { version = "0.0.8", path = "crates/uv-console" }
|
||||||
uv-dirs = { version = "0.0.3", path = "crates/uv-dirs" }
|
uv-dirs = { version = "0.0.8", path = "crates/uv-dirs" }
|
||||||
uv-dispatch = { version = "0.0.3", path = "crates/uv-dispatch" }
|
uv-dispatch = { version = "0.0.8", path = "crates/uv-dispatch" }
|
||||||
uv-distribution = { version = "0.0.3", path = "crates/uv-distribution" }
|
uv-distribution = { version = "0.0.8", path = "crates/uv-distribution" }
|
||||||
uv-distribution-filename = { version = "0.0.3", path = "crates/uv-distribution-filename" }
|
uv-distribution-filename = { version = "0.0.8", path = "crates/uv-distribution-filename" }
|
||||||
uv-distribution-types = { version = "0.0.3", path = "crates/uv-distribution-types" }
|
uv-distribution-types = { version = "0.0.8", path = "crates/uv-distribution-types" }
|
||||||
uv-extract = { version = "0.0.3", path = "crates/uv-extract" }
|
uv-extract = { version = "0.0.8", path = "crates/uv-extract" }
|
||||||
uv-flags = { version = "0.0.3", path = "crates/uv-flags" }
|
uv-flags = { version = "0.0.8", path = "crates/uv-flags" }
|
||||||
uv-fs = { version = "0.0.3", path = "crates/uv-fs", features = ["serde", "tokio"] }
|
uv-fs = { version = "0.0.8", path = "crates/uv-fs", features = ["serde", "tokio"] }
|
||||||
uv-git = { version = "0.0.3", path = "crates/uv-git" }
|
uv-git = { version = "0.0.8", path = "crates/uv-git" }
|
||||||
uv-git-types = { version = "0.0.3", path = "crates/uv-git-types" }
|
uv-git-types = { version = "0.0.8", path = "crates/uv-git-types" }
|
||||||
uv-globfilter = { version = "0.0.3", path = "crates/uv-globfilter" }
|
uv-globfilter = { version = "0.0.8", path = "crates/uv-globfilter" }
|
||||||
uv-install-wheel = { version = "0.0.3", path = "crates/uv-install-wheel", default-features = false }
|
uv-install-wheel = { version = "0.0.8", path = "crates/uv-install-wheel", default-features = false }
|
||||||
uv-installer = { version = "0.0.3", path = "crates/uv-installer" }
|
uv-installer = { version = "0.0.8", path = "crates/uv-installer" }
|
||||||
uv-keyring = { version = "0.0.3", path = "crates/uv-keyring" }
|
uv-keyring = { version = "0.0.8", path = "crates/uv-keyring" }
|
||||||
uv-logging = { version = "0.0.3", path = "crates/uv-logging" }
|
uv-logging = { version = "0.0.8", path = "crates/uv-logging" }
|
||||||
uv-macros = { version = "0.0.3", path = "crates/uv-macros" }
|
uv-macros = { version = "0.0.8", path = "crates/uv-macros" }
|
||||||
uv-metadata = { version = "0.0.3", path = "crates/uv-metadata" }
|
uv-metadata = { version = "0.0.8", path = "crates/uv-metadata" }
|
||||||
uv-normalize = { version = "0.0.3", path = "crates/uv-normalize" }
|
uv-normalize = { version = "0.0.8", path = "crates/uv-normalize" }
|
||||||
uv-once-map = { version = "0.0.3", path = "crates/uv-once-map" }
|
uv-once-map = { version = "0.0.8", path = "crates/uv-once-map" }
|
||||||
uv-options-metadata = { version = "0.0.3", path = "crates/uv-options-metadata" }
|
uv-options-metadata = { version = "0.0.8", path = "crates/uv-options-metadata" }
|
||||||
uv-performance-memory-allocator = { version = "0.0.3", path = "crates/uv-performance-memory-allocator" }
|
uv-performance-memory-allocator = { version = "0.0.8", path = "crates/uv-performance-memory-allocator" }
|
||||||
uv-pep440 = { version = "0.0.3", path = "crates/uv-pep440", features = ["tracing", "rkyv", "version-ranges"] }
|
uv-pep440 = { version = "0.0.8", path = "crates/uv-pep440", features = ["tracing", "rkyv", "version-ranges"] }
|
||||||
uv-pep508 = { version = "0.0.3", path = "crates/uv-pep508", features = ["non-pep508-extensions"] }
|
uv-pep508 = { version = "0.0.8", path = "crates/uv-pep508", features = ["non-pep508-extensions"] }
|
||||||
uv-platform = { version = "0.0.3", path = "crates/uv-platform" }
|
uv-platform = { version = "0.0.8", path = "crates/uv-platform" }
|
||||||
uv-platform-tags = { version = "0.0.3", path = "crates/uv-platform-tags" }
|
uv-platform-tags = { version = "0.0.8", path = "crates/uv-platform-tags" }
|
||||||
uv-preview = { version = "0.0.3", path = "crates/uv-preview" }
|
uv-preview = { version = "0.0.8", path = "crates/uv-preview" }
|
||||||
uv-publish = { version = "0.0.3", path = "crates/uv-publish" }
|
uv-publish = { version = "0.0.8", path = "crates/uv-publish" }
|
||||||
uv-pypi-types = { version = "0.0.3", path = "crates/uv-pypi-types" }
|
uv-pypi-types = { version = "0.0.8", path = "crates/uv-pypi-types" }
|
||||||
uv-python = { version = "0.0.3", path = "crates/uv-python" }
|
uv-python = { version = "0.0.8", path = "crates/uv-python" }
|
||||||
uv-redacted = { version = "0.0.3", path = "crates/uv-redacted" }
|
uv-redacted = { version = "0.0.8", path = "crates/uv-redacted" }
|
||||||
uv-requirements = { version = "0.0.3", path = "crates/uv-requirements" }
|
uv-requirements = { version = "0.0.8", path = "crates/uv-requirements" }
|
||||||
uv-requirements-txt = { version = "0.0.3", path = "crates/uv-requirements-txt" }
|
uv-requirements-txt = { version = "0.0.8", path = "crates/uv-requirements-txt" }
|
||||||
uv-resolver = { version = "0.0.3", path = "crates/uv-resolver" }
|
uv-resolver = { version = "0.0.8", path = "crates/uv-resolver" }
|
||||||
uv-scripts = { version = "0.0.3", path = "crates/uv-scripts" }
|
uv-scripts = { version = "0.0.8", path = "crates/uv-scripts" }
|
||||||
uv-settings = { version = "0.0.3", path = "crates/uv-settings" }
|
uv-settings = { version = "0.0.8", path = "crates/uv-settings" }
|
||||||
uv-shell = { version = "0.0.3", path = "crates/uv-shell" }
|
uv-shell = { version = "0.0.8", path = "crates/uv-shell" }
|
||||||
uv-small-str = { version = "0.0.3", path = "crates/uv-small-str" }
|
uv-small-str = { version = "0.0.8", path = "crates/uv-small-str" }
|
||||||
uv-state = { version = "0.0.3", path = "crates/uv-state" }
|
uv-state = { version = "0.0.8", path = "crates/uv-state" }
|
||||||
uv-static = { version = "0.0.3", path = "crates/uv-static" }
|
uv-static = { version = "0.0.8", path = "crates/uv-static" }
|
||||||
uv-tool = { version = "0.0.3", path = "crates/uv-tool" }
|
uv-tool = { version = "0.0.8", path = "crates/uv-tool" }
|
||||||
uv-torch = { version = "0.0.3", path = "crates/uv-torch" }
|
uv-torch = { version = "0.0.8", path = "crates/uv-torch" }
|
||||||
uv-trampoline-builder = { version = "0.0.3", path = "crates/uv-trampoline-builder" }
|
uv-trampoline-builder = { version = "0.0.8", path = "crates/uv-trampoline-builder" }
|
||||||
uv-types = { version = "0.0.3", path = "crates/uv-types" }
|
uv-types = { version = "0.0.8", path = "crates/uv-types" }
|
||||||
uv-version = { version = "0.9.13", path = "crates/uv-version" }
|
uv-version = { version = "0.9.18", path = "crates/uv-version" }
|
||||||
uv-virtualenv = { version = "0.0.3", path = "crates/uv-virtualenv" }
|
uv-virtualenv = { version = "0.0.8", path = "crates/uv-virtualenv" }
|
||||||
uv-warnings = { version = "0.0.3", path = "crates/uv-warnings" }
|
uv-warnings = { version = "0.0.8", path = "crates/uv-warnings" }
|
||||||
uv-workspace = { version = "0.0.3", path = "crates/uv-workspace" }
|
uv-workspace = { version = "0.0.8", path = "crates/uv-workspace" }
|
||||||
|
|
||||||
ambient-id = { version = "0.0.6", default-features = false, features = ["astral-reqwest-middleware"] }
|
ambient-id = { version = "0.0.7", default-features = false, features = ["astral-reqwest-middleware"] }
|
||||||
anstream = { version = "0.6.15" }
|
anstream = { version = "0.6.15" }
|
||||||
anyhow = { version = "1.0.89" }
|
anyhow = { version = "1.0.89" }
|
||||||
arcstr = { version = "1.2.0" }
|
arcstr = { version = "1.2.0" }
|
||||||
|
|
@ -103,10 +103,12 @@ ctrlc = { version = "3.4.5" }
|
||||||
cyclonedx-bom = { version = "0.8.0" }
|
cyclonedx-bom = { version = "0.8.0" }
|
||||||
dashmap = { version = "6.1.0" }
|
dashmap = { version = "6.1.0" }
|
||||||
data-encoding = { version = "2.6.0" }
|
data-encoding = { version = "2.6.0" }
|
||||||
|
diskus = { version = "0.9.0", default-features = false }
|
||||||
dotenvy = { version = "0.15.7" }
|
dotenvy = { version = "0.15.7" }
|
||||||
dunce = { version = "1.0.5" }
|
dunce = { version = "1.0.5" }
|
||||||
either = { version = "1.13.0" }
|
either = { version = "1.13.0" }
|
||||||
encoding_rs_io = { version = "0.1.7" }
|
encoding_rs_io = { version = "0.1.7" }
|
||||||
|
embed-manifest = { version = "1.5.0" }
|
||||||
etcetera = { version = "0.11.0" }
|
etcetera = { version = "0.11.0" }
|
||||||
fastrand = { version = "2.3.0" }
|
fastrand = { version = "2.3.0" }
|
||||||
flate2 = { version = "1.0.33", default-features = false, features = ["zlib-rs"] }
|
flate2 = { version = "1.0.33", default-features = false, features = ["zlib-rs"] }
|
||||||
|
|
@ -141,7 +143,7 @@ percent-encoding = { version = "2.3.1" }
|
||||||
petgraph = { version = "0.8.0" }
|
petgraph = { version = "0.8.0" }
|
||||||
proc-macro2 = { version = "1.0.86" }
|
proc-macro2 = { version = "1.0.86" }
|
||||||
procfs = { version = "0.17.0", default-features = false, features = ["flate2"] }
|
procfs = { version = "0.17.0", default-features = false, features = ["flate2"] }
|
||||||
pubgrub = { version = "0.3.2" , package = "astral-pubgrub" }
|
pubgrub = { version = "0.3.3" , package = "astral-pubgrub" }
|
||||||
quote = { version = "1.0.37" }
|
quote = { version = "1.0.37" }
|
||||||
rayon = { version = "1.10.0" }
|
rayon = { version = "1.10.0" }
|
||||||
ref-cast = { version = "1.0.24" }
|
ref-cast = { version = "1.0.24" }
|
||||||
|
|
@ -168,7 +170,7 @@ serde-untagged = { version = "0.1.6" }
|
||||||
serde_json = { version = "1.0.128" }
|
serde_json = { version = "1.0.128" }
|
||||||
sha2 = { version = "0.10.8" }
|
sha2 = { version = "0.10.8" }
|
||||||
smallvec = { version = "1.13.2" }
|
smallvec = { version = "1.13.2" }
|
||||||
spdx = { version = "0.12.0" }
|
spdx = { version = "0.13.0" }
|
||||||
syn = { version = "2.0.77" }
|
syn = { version = "2.0.77" }
|
||||||
sys-info = { version = "0.9.1" }
|
sys-info = { version = "0.9.1" }
|
||||||
tar = { version = "0.4.43" }
|
tar = { version = "0.4.43" }
|
||||||
|
|
@ -176,8 +178,8 @@ target-lexicon = { version = "0.13.0" }
|
||||||
tempfile = { version = "3.14.0" }
|
tempfile = { version = "3.14.0" }
|
||||||
textwrap = { version = "0.16.1" }
|
textwrap = { version = "0.16.1" }
|
||||||
thiserror = { version = "2.0.0" }
|
thiserror = { version = "2.0.0" }
|
||||||
astral-tl = { version = "0.7.9" }
|
astral-tl = { version = "0.7.11" }
|
||||||
tokio = { version = "1.40.0", features = ["fs", "io-util", "macros", "process", "rt", "signal", "sync"] }
|
tokio = { version = "1.40.0", features = ["fs", "io-util", "macros", "process", "rt", "signal", "sync", "time"] }
|
||||||
tokio-stream = { version = "0.1.16" }
|
tokio-stream = { version = "0.1.16" }
|
||||||
tokio-util = { version = "0.7.12", features = ["compat", "io"] }
|
tokio-util = { version = "0.7.12", features = ["compat", "io"] }
|
||||||
toml = { version = "0.9.2", features = ["fast_hash"] }
|
toml = { version = "0.9.2", features = ["fast_hash"] }
|
||||||
|
|
@ -223,9 +225,6 @@ test-log = { version = "0.2.16", features = ["trace"], default-features = false
|
||||||
tokio-rustls = { version = "0.26.2", default-features = false }
|
tokio-rustls = { version = "0.26.2", default-features = false }
|
||||||
whoami = { version = "1.6.0" }
|
whoami = { version = "1.6.0" }
|
||||||
|
|
||||||
[workspace.metadata.cargo-shear]
|
|
||||||
ignored = ["flate2", "xz2", "h2", "uv-performance-memory-allocator"]
|
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
unsafe_code = "warn"
|
unsafe_code = "warn"
|
||||||
unreachable_pub = "warn"
|
unreachable_pub = "warn"
|
||||||
|
|
@ -311,12 +310,21 @@ strip = false
|
||||||
debug = "full"
|
debug = "full"
|
||||||
lto = false
|
lto = false
|
||||||
|
|
||||||
|
# Profile for fast test execution: Skip debug info generation, and
|
||||||
|
# apply basic optimization, which speed up build and running tests.
|
||||||
[profile.fast-build]
|
[profile.fast-build]
|
||||||
inherits = "dev"
|
inherits = "dev"
|
||||||
opt-level = 1
|
opt-level = 1
|
||||||
debug = 0
|
debug = 0
|
||||||
strip = "debuginfo"
|
strip = "debuginfo"
|
||||||
|
|
||||||
|
# Profile for faster builds: Skip debug info generation, for faster
|
||||||
|
# builds of smaller binaries.
|
||||||
|
[profile.no-debug]
|
||||||
|
inherits = "dev"
|
||||||
|
debug = 0
|
||||||
|
strip = "debuginfo"
|
||||||
|
|
||||||
# Profile to build a minimally sized binary for uv-build
|
# Profile to build a minimally sized binary for uv-build
|
||||||
[profile.minimal-size]
|
[profile.minimal-size]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|
|
||||||
33
README.md
33
README.md
|
|
@ -42,7 +42,7 @@ An extremely fast Python package and project manager, written in Rust.
|
||||||
- 🖥️ Supports macOS, Linux, and Windows.
|
- 🖥️ Supports macOS, Linux, and Windows.
|
||||||
|
|
||||||
uv is backed by [Astral](https://astral.sh), the creators of
|
uv is backed by [Astral](https://astral.sh), the creators of
|
||||||
[Ruff](https://github.com/astral-sh/ruff).
|
[Ruff](https://github.com/astral-sh/ruff) and [ty](https://github.com/astral-sh/ty).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -192,14 +192,12 @@ uv installs Python and allows quickly switching between versions.
|
||||||
Install multiple Python versions:
|
Install multiple Python versions:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ uv python install 3.10 3.11 3.12
|
$ uv python install 3.12 3.13 3.14
|
||||||
Searching for Python versions matching: Python 3.10
|
Installed 3 versions in 972ms
|
||||||
Searching for Python versions matching: Python 3.11
|
+ cpython-3.12.12-macos-aarch64-none (python3.12)
|
||||||
Searching for Python versions matching: Python 3.12
|
+ cpython-3.13.9-macos-aarch64-none (python3.13)
|
||||||
Installed 3 versions in 3.42s
|
+ cpython-3.14.0-macos-aarch64-none (python3.14)
|
||||||
+ cpython-3.10.14-macos-aarch64-none
|
|
||||||
+ cpython-3.11.9-macos-aarch64-none
|
|
||||||
+ cpython-3.12.4-macos-aarch64-none
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Download Python versions as needed:
|
Download Python versions as needed:
|
||||||
|
|
@ -270,14 +268,6 @@ Installed 43 packages in 208ms
|
||||||
|
|
||||||
See the [pip interface documentation](https://docs.astral.sh/uv/pip/index/) to get started.
|
See the [pip interface documentation](https://docs.astral.sh/uv/pip/index/) to get started.
|
||||||
|
|
||||||
## Platform support
|
|
||||||
|
|
||||||
See uv's [platform support](https://docs.astral.sh/uv/reference/platforms/) document.
|
|
||||||
|
|
||||||
## Versioning policy
|
|
||||||
|
|
||||||
See uv's [versioning policy](https://docs.astral.sh/uv/reference/versioning/) document.
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We are passionate about supporting contributors of all levels of experience and would love to see
|
We are passionate about supporting contributors of all levels of experience and would love to see
|
||||||
|
|
@ -294,6 +284,15 @@ It's pronounced as "you - vee" ([`/juː viː/`](https://en.wikipedia.org/wiki/He
|
||||||
|
|
||||||
Just "uv", please. See the [style guide](./STYLE.md#styling-uv) for details.
|
Just "uv", please. See the [style guide](./STYLE.md#styling-uv) for details.
|
||||||
|
|
||||||
|
#### What platforms does uv support?
|
||||||
|
|
||||||
|
See uv's [platform support](https://docs.astral.sh/uv/reference/platforms/) document.
|
||||||
|
|
||||||
|
#### Is uv ready for production?
|
||||||
|
|
||||||
|
Yes, uv is stable and widely used in production. See uv's
|
||||||
|
[versioning policy](https://docs.astral.sh/uv/reference/versioning/) document for details.
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
uv's dependency resolver uses [PubGrub](https://github.com/pubgrub-rs/pubgrub) under the hood. We're
|
uv's dependency resolver uses [PubGrub](https://github.com/pubgrub-rs/pubgrub) under the hood. We're
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
[files]
|
[files]
|
||||||
extend-exclude = [
|
extend-exclude = [
|
||||||
"**/snapshots/",
|
"**/snapshots/",
|
||||||
"ecosystem/**",
|
"test/ecosystem/**",
|
||||||
"scripts/**/*.in",
|
"test/requirements/**/*.in",
|
||||||
"crates/uv-build-frontend/src/pipreqs/mapping",
|
"crates/uv-build-frontend/src/pipreqs/mapping",
|
||||||
]
|
]
|
||||||
ignore-hidden = false
|
ignore-hidden = false
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-auth"
|
name = "uv-auth"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-auth).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ use url::Url;
|
||||||
use uv_once_map::OnceMap;
|
use uv_once_map::OnceMap;
|
||||||
use uv_redacted::DisplaySafeUrl;
|
use uv_redacted::DisplaySafeUrl;
|
||||||
|
|
||||||
use crate::Realm;
|
|
||||||
use crate::credentials::{Authentication, Username};
|
use crate::credentials::{Authentication, Username};
|
||||||
|
use crate::{Credentials, Realm};
|
||||||
|
|
||||||
type FxOnceMap<K, V> = OnceMap<K, V, BuildHasherDefault<FxHasher>>;
|
type FxOnceMap<K, V> = OnceMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||||
|
|
||||||
|
|
@ -33,6 +33,7 @@ impl Display for FetchUrl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)] // All internal types are redacted.
|
||||||
pub struct CredentialsCache {
|
pub struct CredentialsCache {
|
||||||
/// A cache per realm and username
|
/// A cache per realm and username
|
||||||
realms: RwLock<FxHashMap<(Realm, Username), Arc<Authentication>>>,
|
realms: RwLock<FxHashMap<(Realm, Username), Arc<Authentication>>>,
|
||||||
|
|
@ -58,6 +59,27 @@ impl CredentialsCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Populate the global authentication store with credentials on a URL, if there are any.
|
||||||
|
///
|
||||||
|
/// Returns `true` if the store was updated.
|
||||||
|
pub fn store_credentials_from_url(&self, url: &DisplaySafeUrl) -> bool {
|
||||||
|
if let Some(credentials) = Credentials::from_url(url) {
|
||||||
|
trace!("Caching credentials for {url}");
|
||||||
|
self.insert(url, Arc::new(Authentication::from(credentials)));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Populate the global authentication store with credentials on a URL, if there are any.
|
||||||
|
///
|
||||||
|
/// Returns `true` if the store was updated.
|
||||||
|
pub fn store_credentials(&self, url: &DisplaySafeUrl, credentials: Credentials) {
|
||||||
|
trace!("Caching credentials for {url}");
|
||||||
|
self.insert(url, Arc::new(Authentication::from(credentials)));
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the credentials that should be used for a realm and username, if any.
|
/// Return the credentials that should be used for a realm and username, if any.
|
||||||
pub(crate) fn get_realm(
|
pub(crate) fn get_realm(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,5 @@
|
||||||
use std::sync::{Arc, LazyLock};
|
|
||||||
|
|
||||||
use tracing::trace;
|
|
||||||
|
|
||||||
use uv_redacted::DisplaySafeUrl;
|
|
||||||
|
|
||||||
use crate::credentials::Authentication;
|
|
||||||
pub use access_token::AccessToken;
|
pub use access_token::AccessToken;
|
||||||
use cache::CredentialsCache;
|
pub use cache::CredentialsCache;
|
||||||
pub use credentials::{Credentials, Username};
|
pub use credentials::{Credentials, Username};
|
||||||
pub use index::{AuthPolicy, Index, Indexes};
|
pub use index::{AuthPolicy, Index, Indexes};
|
||||||
pub use keyring::KeyringProvider;
|
pub use keyring::KeyringProvider;
|
||||||
|
|
@ -29,32 +22,3 @@ mod pyx;
|
||||||
mod realm;
|
mod realm;
|
||||||
mod service;
|
mod service;
|
||||||
mod store;
|
mod store;
|
||||||
|
|
||||||
// TODO(zanieb): Consider passing a cache explicitly throughout
|
|
||||||
|
|
||||||
/// Global authentication cache for a uv invocation
|
|
||||||
///
|
|
||||||
/// This is used to share credentials across uv clients.
|
|
||||||
pub(crate) static CREDENTIALS_CACHE: LazyLock<CredentialsCache> =
|
|
||||||
LazyLock::new(CredentialsCache::default);
|
|
||||||
|
|
||||||
/// Populate the global authentication store with credentials on a URL, if there are any.
|
|
||||||
///
|
|
||||||
/// Returns `true` if the store was updated.
|
|
||||||
pub fn store_credentials_from_url(url: &DisplaySafeUrl) -> bool {
|
|
||||||
if let Some(credentials) = Credentials::from_url(url) {
|
|
||||||
trace!("Caching credentials for {url}");
|
|
||||||
CREDENTIALS_CACHE.insert(url, Arc::new(Authentication::from(credentials)));
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Populate the global authentication store with credentials on a URL, if there are any.
|
|
||||||
///
|
|
||||||
/// Returns `true` if the store was updated.
|
|
||||||
pub fn store_credentials(url: &DisplaySafeUrl, credentials: Credentials) {
|
|
||||||
trace!("Caching credentials for {url}");
|
|
||||||
CREDENTIALS_CACHE.insert(url, Arc::new(Authentication::from(credentials)));
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,13 @@ use crate::credentials::Authentication;
|
||||||
use crate::providers::{HuggingFaceProvider, S3EndpointProvider};
|
use crate::providers::{HuggingFaceProvider, S3EndpointProvider};
|
||||||
use crate::pyx::{DEFAULT_TOLERANCE_SECS, PyxTokenStore};
|
use crate::pyx::{DEFAULT_TOLERANCE_SECS, PyxTokenStore};
|
||||||
use crate::{
|
use crate::{
|
||||||
AccessToken, CREDENTIALS_CACHE, CredentialsCache, KeyringProvider,
|
AccessToken, CredentialsCache, KeyringProvider,
|
||||||
cache::FetchUrl,
|
cache::FetchUrl,
|
||||||
credentials::{Credentials, Username},
|
credentials::{Credentials, Username},
|
||||||
index::{AuthPolicy, Indexes},
|
index::{AuthPolicy, Indexes},
|
||||||
realm::Realm,
|
realm::Realm,
|
||||||
};
|
};
|
||||||
use crate::{Index, TextCredentialStore, TomlCredentialError};
|
use crate::{Index, TextCredentialStore};
|
||||||
|
|
||||||
/// Cached check for whether we're running in Dependabot.
|
/// Cached check for whether we're running in Dependabot.
|
||||||
static IS_DEPENDABOT: LazyLock<bool> =
|
static IS_DEPENDABOT: LazyLock<bool> =
|
||||||
|
|
@ -65,49 +65,55 @@ impl NetrcMode {
|
||||||
|
|
||||||
/// Strategy for loading text-based credential files.
|
/// Strategy for loading text-based credential files.
|
||||||
enum TextStoreMode {
|
enum TextStoreMode {
|
||||||
Automatic(LazyLock<Option<TextCredentialStore>>),
|
Automatic(tokio::sync::OnceCell<Option<TextCredentialStore>>),
|
||||||
Enabled(TextCredentialStore),
|
Enabled(TextCredentialStore),
|
||||||
Disabled,
|
Disabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextStoreMode {
|
impl Default for TextStoreMode {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// TODO(zanieb): Reconsider this pattern. We're just mirroring the [`NetrcMode`]
|
Self::Automatic(tokio::sync::OnceCell::new())
|
||||||
// implementation for now.
|
|
||||||
Self::Automatic(LazyLock::new(|| {
|
|
||||||
let path = TextCredentialStore::default_file()
|
|
||||||
.inspect_err(|err| {
|
|
||||||
warn!("Failed to determine credentials file path: {}", err);
|
|
||||||
})
|
|
||||||
.ok()?;
|
|
||||||
|
|
||||||
match TextCredentialStore::read(&path) {
|
|
||||||
Ok((store, _lock)) => {
|
|
||||||
debug!("Loaded credential file {}", path.display());
|
|
||||||
Some(store)
|
|
||||||
}
|
|
||||||
Err(TomlCredentialError::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
|
|
||||||
debug!("No credentials file found at {}", path.display());
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(
|
|
||||||
"Failed to load credentials from {}: {}",
|
|
||||||
path.display(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextStoreMode {
|
impl TextStoreMode {
|
||||||
|
async fn load_default_store() -> Option<TextCredentialStore> {
|
||||||
|
let path = TextCredentialStore::default_file()
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to determine credentials file path: {}", err);
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
match TextCredentialStore::read(&path).await {
|
||||||
|
Ok((store, _lock)) => {
|
||||||
|
debug!("Loaded credential file {}", path.display());
|
||||||
|
Some(store)
|
||||||
|
}
|
||||||
|
Err(err)
|
||||||
|
if err
|
||||||
|
.as_io_error()
|
||||||
|
.is_some_and(|err| err.kind() == std::io::ErrorKind::NotFound) =>
|
||||||
|
{
|
||||||
|
debug!("No credentials file found at {}", path.display());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(
|
||||||
|
"Failed to load credentials from {}: {}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the parsed credential store, if enabled.
|
/// Get the parsed credential store, if enabled.
|
||||||
fn get(&self) -> Option<&TextCredentialStore> {
|
async fn get(&self) -> Option<&TextCredentialStore> {
|
||||||
match self {
|
match self {
|
||||||
Self::Automatic(lock) => lock.as_ref(),
|
// TODO(zanieb): Reconsider this pattern. We're just mirroring the [`NetrcMode`]
|
||||||
|
// implementation for now.
|
||||||
|
Self::Automatic(lock) => lock.get_or_init(Self::load_default_store).await.as_ref(),
|
||||||
Self::Enabled(store) => Some(store),
|
Self::Enabled(store) => Some(store),
|
||||||
Self::Disabled => None,
|
Self::Disabled => None,
|
||||||
}
|
}
|
||||||
|
|
@ -123,6 +129,15 @@ enum TokenState {
|
||||||
Initialized(Option<AccessToken>),
|
Initialized(Option<AccessToken>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum S3CredentialState {
|
||||||
|
/// The S3 credential state has not yet been initialized.
|
||||||
|
Uninitialized,
|
||||||
|
/// The S3 credential state has been initialized, with either a signer or `None` if
|
||||||
|
/// no S3 endpoint is configured.
|
||||||
|
Initialized(Option<Arc<Authentication>>),
|
||||||
|
}
|
||||||
|
|
||||||
/// A middleware that adds basic authentication to requests.
|
/// A middleware that adds basic authentication to requests.
|
||||||
///
|
///
|
||||||
/// Uses a cache to propagate credentials from previously seen requests and
|
/// Uses a cache to propagate credentials from previously seen requests and
|
||||||
|
|
@ -131,7 +146,8 @@ pub struct AuthMiddleware {
|
||||||
netrc: NetrcMode,
|
netrc: NetrcMode,
|
||||||
text_store: TextStoreMode,
|
text_store: TextStoreMode,
|
||||||
keyring: Option<KeyringProvider>,
|
keyring: Option<KeyringProvider>,
|
||||||
cache: Option<CredentialsCache>,
|
/// Global authentication cache for a uv invocation to share credentials across uv clients.
|
||||||
|
cache: Arc<CredentialsCache>,
|
||||||
/// Auth policies for specific URLs.
|
/// Auth policies for specific URLs.
|
||||||
indexes: Indexes,
|
indexes: Indexes,
|
||||||
/// Set all endpoints as needing authentication. We never try to send an
|
/// Set all endpoints as needing authentication. We never try to send an
|
||||||
|
|
@ -143,21 +159,31 @@ pub struct AuthMiddleware {
|
||||||
pyx_token_store: Option<PyxTokenStore>,
|
pyx_token_store: Option<PyxTokenStore>,
|
||||||
/// Tokens to use for persistent credentials.
|
/// Tokens to use for persistent credentials.
|
||||||
pyx_token_state: Mutex<TokenState>,
|
pyx_token_state: Mutex<TokenState>,
|
||||||
|
/// Cached S3 credentials to avoid running the credential helper multiple times.
|
||||||
|
s3_credential_state: Mutex<S3CredentialState>,
|
||||||
preview: Preview,
|
preview: Preview,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for AuthMiddleware {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AuthMiddleware {
|
impl AuthMiddleware {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
netrc: NetrcMode::default(),
|
netrc: NetrcMode::default(),
|
||||||
text_store: TextStoreMode::default(),
|
text_store: TextStoreMode::default(),
|
||||||
keyring: None,
|
keyring: None,
|
||||||
cache: None,
|
// TODO(konsti): There shouldn't be a credential cache without that in the initializer.
|
||||||
|
cache: Arc::new(CredentialsCache::default()),
|
||||||
indexes: Indexes::new(),
|
indexes: Indexes::new(),
|
||||||
only_authenticated: false,
|
only_authenticated: false,
|
||||||
base_client: None,
|
base_client: None,
|
||||||
pyx_token_store: None,
|
pyx_token_store: None,
|
||||||
pyx_token_state: Mutex::new(TokenState::Uninitialized),
|
pyx_token_state: Mutex::new(TokenState::Uninitialized),
|
||||||
|
s3_credential_state: Mutex::new(S3CredentialState::Uninitialized),
|
||||||
preview: Preview::default(),
|
preview: Preview::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +231,14 @@ impl AuthMiddleware {
|
||||||
/// Configure the [`CredentialsCache`] to use.
|
/// Configure the [`CredentialsCache`] to use.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_cache(mut self, cache: CredentialsCache) -> Self {
|
pub fn with_cache(mut self, cache: CredentialsCache) -> Self {
|
||||||
self.cache = Some(cache);
|
self.cache = Arc::new(cache);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the [`CredentialsCache`] to use from an existing [`Arc`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_cache_arc(mut self, cache: Arc<CredentialsCache>) -> Self {
|
||||||
|
self.cache = cache;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,17 +271,9 @@ impl AuthMiddleware {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the configured authentication store.
|
/// Global authentication cache for a uv invocation to share credentials across uv clients.
|
||||||
///
|
|
||||||
/// If not set, the global store is used.
|
|
||||||
fn cache(&self) -> &CredentialsCache {
|
fn cache(&self) -> &CredentialsCache {
|
||||||
self.cache.as_ref().unwrap_or(&CREDENTIALS_CACHE)
|
&self.cache
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AuthMiddleware {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -665,13 +690,26 @@ impl AuthMiddleware {
|
||||||
return Some(credentials);
|
return Some(credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(credentials) = S3EndpointProvider::credentials_for(url, self.preview)
|
if S3EndpointProvider::is_s3_endpoint(url, self.preview) {
|
||||||
.map(Authentication::from)
|
let mut s3_state = self.s3_credential_state.lock().await;
|
||||||
.map(Arc::new)
|
|
||||||
{
|
// If the S3 credential state is uninitialized, initialize it.
|
||||||
debug!("Found S3 credentials for {url}");
|
let credentials = match &*s3_state {
|
||||||
self.cache().fetches.done(key, Some(credentials.clone()));
|
S3CredentialState::Uninitialized => {
|
||||||
return Some(credentials);
|
trace!("Initializing S3 credentials for {url}");
|
||||||
|
let signer = S3EndpointProvider::create_signer();
|
||||||
|
let credentials = Arc::new(Authentication::from(signer));
|
||||||
|
*s3_state = S3CredentialState::Initialized(Some(credentials.clone()));
|
||||||
|
Some(credentials)
|
||||||
|
}
|
||||||
|
S3CredentialState::Initialized(credentials) => credentials.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(credentials) = credentials {
|
||||||
|
debug!("Found S3 credentials for {url}");
|
||||||
|
self.cache().fetches.done(key, Some(credentials.clone()));
|
||||||
|
return Some(credentials);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a known URL, authenticate it via the token store.
|
// If this is a known URL, authenticate it via the token store.
|
||||||
|
|
@ -729,9 +767,16 @@ impl AuthMiddleware {
|
||||||
Some(credentials)
|
Some(credentials)
|
||||||
|
|
||||||
// Text credential store support.
|
// Text credential store support.
|
||||||
} else if let Some(credentials) = self.text_store.get().and_then(|text_store| {
|
} else if let Some(credentials) = self.text_store.get().await.and_then(|text_store| {
|
||||||
debug!("Checking text store for credentials for {url}");
|
debug!("Checking text store for credentials for {url}");
|
||||||
text_store.get_credentials(url, credentials.as_ref().and_then(|credentials| credentials.username())).cloned()
|
text_store
|
||||||
|
.get_credentials(
|
||||||
|
url,
|
||||||
|
credentials
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|credentials| credentials.username()),
|
||||||
|
)
|
||||||
|
.cloned()
|
||||||
}) {
|
}) {
|
||||||
debug!("Found credentials in plaintext store for {url}");
|
debug!("Found credentials in plaintext store for {url}");
|
||||||
Some(credentials)
|
Some(credentials)
|
||||||
|
|
@ -747,10 +792,16 @@ impl AuthMiddleware {
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
// N.B. The native store performs an exact look up right now, so we use the root
|
// N.B. The native store performs an exact look up right now, so we use the root
|
||||||
// URL of the index instead of relying on prefix-matching.
|
// URL of the index instead of relying on prefix-matching.
|
||||||
debug!("Checking native store for credentials for index URL {}{}", display_username, index.root_url);
|
debug!(
|
||||||
|
"Checking native store for credentials for index URL {}{}",
|
||||||
|
display_username, index.root_url
|
||||||
|
);
|
||||||
native_store.fetch(&index.root_url, username).await
|
native_store.fetch(&index.root_url, username).await
|
||||||
} else {
|
} else {
|
||||||
debug!("Checking native store for credentials for URL {}{}", display_username, url);
|
debug!(
|
||||||
|
"Checking native store for credentials for URL {}{}",
|
||||||
|
display_username, url
|
||||||
|
);
|
||||||
native_store.fetch(url, username).await
|
native_store.fetch(url, username).await
|
||||||
}
|
}
|
||||||
// TODO(zanieb): We should have a realm fallback here too
|
// TODO(zanieb): We should have a realm fallback here too
|
||||||
|
|
@ -771,10 +822,18 @@ impl AuthMiddleware {
|
||||||
// always authenticate.
|
// always authenticate.
|
||||||
if let Some(username) = credentials.and_then(|credentials| credentials.username()) {
|
if let Some(username) = credentials.and_then(|credentials| credentials.username()) {
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
debug!("Checking keyring for credentials for index URL {}@{}", username, index.url);
|
debug!(
|
||||||
keyring.fetch(DisplaySafeUrl::ref_cast(&index.url), Some(username)).await
|
"Checking keyring for credentials for index URL {}@{}",
|
||||||
|
username, index.url
|
||||||
|
);
|
||||||
|
keyring
|
||||||
|
.fetch(DisplaySafeUrl::ref_cast(&index.url), Some(username))
|
||||||
|
.await
|
||||||
} else {
|
} else {
|
||||||
debug!("Checking keyring for credentials for full URL {}@{}", username, url);
|
debug!(
|
||||||
|
"Checking keyring for credentials for full URL {}@{}",
|
||||||
|
username, url
|
||||||
|
);
|
||||||
keyring.fetch(url, Some(username)).await
|
keyring.fetch(url, Some(username)).await
|
||||||
}
|
}
|
||||||
} else if matches!(auth_policy, AuthPolicy::Always) {
|
} else if matches!(auth_policy, AuthPolicy::Always) {
|
||||||
|
|
@ -783,12 +842,16 @@ impl AuthMiddleware {
|
||||||
"Checking keyring for credentials for index URL {} without username due to `authenticate = always`",
|
"Checking keyring for credentials for index URL {} without username due to `authenticate = always`",
|
||||||
index.url
|
index.url
|
||||||
);
|
);
|
||||||
keyring.fetch(DisplaySafeUrl::ref_cast(&index.url), None).await
|
keyring
|
||||||
|
.fetch(DisplaySafeUrl::ref_cast(&index.url), None)
|
||||||
|
.await
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("Skipping keyring fetch for {url} without username; use `authenticate = always` to force");
|
debug!(
|
||||||
|
"Skipping keyring fetch for {url} without username; use `authenticate = always` to force"
|
||||||
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -798,9 +861,9 @@ impl AuthMiddleware {
|
||||||
Some(credentials)
|
Some(credentials)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
};
|
||||||
.map(Authentication::from)
|
|
||||||
.map(Arc::new);
|
let credentials = credentials.map(Authentication::from).map(Arc::new);
|
||||||
|
|
||||||
// Register the fetch for this key
|
// Register the fetch for this key
|
||||||
self.cache().fetches.done(key, credentials.clone());
|
self.cache().fetches.done(key, credentials.clone());
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,8 @@ static S3_ENDPOINT_REALM: LazyLock<Option<Realm>> = LazyLock::new(|| {
|
||||||
pub(crate) struct S3EndpointProvider;
|
pub(crate) struct S3EndpointProvider;
|
||||||
|
|
||||||
impl S3EndpointProvider {
|
impl S3EndpointProvider {
|
||||||
/// Returns the credentials for the S3 endpoint, if available.
|
/// Returns `true` if the URL matches the configured S3 endpoint.
|
||||||
pub(crate) fn credentials_for(url: &Url, preview: Preview) -> Option<DefaultSigner> {
|
pub(crate) fn is_s3_endpoint(url: &Url, preview: Preview) -> bool {
|
||||||
if let Some(s3_endpoint_realm) = S3_ENDPOINT_REALM.as_ref().map(RealmRef::from) {
|
if let Some(s3_endpoint_realm) = S3_ENDPOINT_REALM.as_ref().map(RealmRef::from) {
|
||||||
if !preview.is_enabled(PreviewFeatures::S3_ENDPOINT) {
|
if !preview.is_enabled(PreviewFeatures::S3_ENDPOINT) {
|
||||||
warn_user_once!(
|
warn_user_once!(
|
||||||
|
|
@ -79,19 +79,26 @@ impl S3EndpointProvider {
|
||||||
// Treat any URL on the same domain or subdomain as available for S3 signing.
|
// Treat any URL on the same domain or subdomain as available for S3 signing.
|
||||||
let realm = RealmRef::from(url);
|
let realm = RealmRef::from(url);
|
||||||
if realm == s3_endpoint_realm || realm.is_subdomain_of(s3_endpoint_realm) {
|
if realm == s3_endpoint_realm || realm.is_subdomain_of(s3_endpoint_realm) {
|
||||||
// TODO(charlie): Can `reqsign` infer the region for us? Profiles, for example,
|
return true;
|
||||||
// often have a region set already.
|
|
||||||
let region = std::env::var(EnvVars::AWS_REGION)
|
|
||||||
.map(Cow::Owned)
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
std::env::var(EnvVars::AWS_DEFAULT_REGION)
|
|
||||||
.map(Cow::Owned)
|
|
||||||
.unwrap_or_else(|_| Cow::Borrowed("us-east-1"))
|
|
||||||
});
|
|
||||||
let signer = reqsign::aws::default_signer("s3", ®ion);
|
|
||||||
return Some(signer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new S3 signer with the configured region.
|
||||||
|
///
|
||||||
|
/// This is potentially expensive as it may invoke credential helpers, so the result
|
||||||
|
/// should be cached.
|
||||||
|
pub(crate) fn create_signer() -> DefaultSigner {
|
||||||
|
// TODO(charlie): Can `reqsign` infer the region for us? Profiles, for example,
|
||||||
|
// often have a region set already.
|
||||||
|
let region = std::env::var(EnvVars::AWS_REGION)
|
||||||
|
.map(Cow::Owned)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
std::env::var(EnvVars::AWS_DEFAULT_REGION)
|
||||||
|
.map(Cow::Owned)
|
||||||
|
.unwrap_or_else(|_| Cow::Borrowed("us-east-1"))
|
||||||
|
});
|
||||||
|
reqsign::aws::default_signer("s3", ®ion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use fs_err as fs;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uv_fs::{LockedFile, with_added_extension};
|
use uv_fs::{LockedFile, LockedFileError, LockedFileMode, with_added_extension};
|
||||||
use uv_preview::{Preview, PreviewFeatures};
|
use uv_preview::{Preview, PreviewFeatures};
|
||||||
use uv_redacted::DisplaySafeUrl;
|
use uv_redacted::DisplaySafeUrl;
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ pub enum AuthBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthBackend {
|
impl AuthBackend {
|
||||||
pub fn from_settings(preview: Preview) -> Result<Self, TomlCredentialError> {
|
pub async fn from_settings(preview: Preview) -> Result<Self, TomlCredentialError> {
|
||||||
// If preview is enabled, we'll use the system-native store
|
// If preview is enabled, we'll use the system-native store
|
||||||
if preview.is_enabled(PreviewFeatures::NATIVE_AUTH) {
|
if preview.is_enabled(PreviewFeatures::NATIVE_AUTH) {
|
||||||
return Ok(Self::System(KeyringProvider::native()));
|
return Ok(Self::System(KeyringProvider::native()));
|
||||||
|
|
@ -36,12 +36,16 @@ impl AuthBackend {
|
||||||
|
|
||||||
// Otherwise, we'll use the plaintext credential store
|
// Otherwise, we'll use the plaintext credential store
|
||||||
let path = TextCredentialStore::default_file()?;
|
let path = TextCredentialStore::default_file()?;
|
||||||
match TextCredentialStore::read(&path) {
|
match TextCredentialStore::read(&path).await {
|
||||||
Ok((store, lock)) => Ok(Self::TextStore(store, lock)),
|
Ok((store, lock)) => Ok(Self::TextStore(store, lock)),
|
||||||
Err(TomlCredentialError::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
|
Err(err)
|
||||||
|
if err
|
||||||
|
.as_io_error()
|
||||||
|
.is_some_and(|err| err.kind() == std::io::ErrorKind::NotFound) =>
|
||||||
|
{
|
||||||
Ok(Self::TextStore(
|
Ok(Self::TextStore(
|
||||||
TextCredentialStore::default(),
|
TextCredentialStore::default(),
|
||||||
TextCredentialStore::lock(&path)?,
|
TextCredentialStore::lock(&path).await?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
|
|
@ -69,6 +73,8 @@ pub enum AuthScheme {
|
||||||
pub enum TomlCredentialError {
|
pub enum TomlCredentialError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
LockedFile(#[from] LockedFileError),
|
||||||
#[error("Failed to parse TOML credential file: {0}")]
|
#[error("Failed to parse TOML credential file: {0}")]
|
||||||
ParseError(#[from] toml::de::Error),
|
ParseError(#[from] toml::de::Error),
|
||||||
#[error("Failed to serialize credentials to TOML")]
|
#[error("Failed to serialize credentials to TOML")]
|
||||||
|
|
@ -83,6 +89,21 @@ pub enum TomlCredentialError {
|
||||||
TokenNotUnicode(#[from] std::string::FromUtf8Error),
|
TokenNotUnicode(#[from] std::string::FromUtf8Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TomlCredentialError {
|
||||||
|
pub fn as_io_error(&self) -> Option<&std::io::Error> {
|
||||||
|
match self {
|
||||||
|
Self::Io(err) => Some(err),
|
||||||
|
Self::LockedFile(err) => err.as_io_error(),
|
||||||
|
Self::ParseError(_)
|
||||||
|
| Self::SerializeError(_)
|
||||||
|
| Self::BasicAuthError(_)
|
||||||
|
| Self::BearerAuthError(_)
|
||||||
|
| Self::CredentialsDirError
|
||||||
|
| Self::TokenNotUnicode(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum BasicAuthError {
|
pub enum BasicAuthError {
|
||||||
#[error("`username` is required with `scheme = basic`")]
|
#[error("`username` is required with `scheme = basic`")]
|
||||||
|
|
@ -233,12 +254,12 @@ impl TextCredentialStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acquire a lock on the credentials file at the given path.
|
/// Acquire a lock on the credentials file at the given path.
|
||||||
pub fn lock(path: &Path) -> Result<LockedFile, TomlCredentialError> {
|
pub async fn lock(path: &Path) -> Result<LockedFile, TomlCredentialError> {
|
||||||
if let Some(parent) = path.parent() {
|
if let Some(parent) = path.parent() {
|
||||||
fs::create_dir_all(parent)?;
|
fs::create_dir_all(parent)?;
|
||||||
}
|
}
|
||||||
let lock = with_added_extension(path, ".lock");
|
let lock = with_added_extension(path, ".lock");
|
||||||
Ok(LockedFile::acquire_blocking(lock, "credentials store")?)
|
Ok(LockedFile::acquire(lock, LockedFileMode::Exclusive, "credentials store").await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read credentials from a file.
|
/// Read credentials from a file.
|
||||||
|
|
@ -269,8 +290,8 @@ impl TextCredentialStore {
|
||||||
/// Returns [`TextCredentialStore`] and a [`LockedFile`] to hold if mutating the store.
|
/// Returns [`TextCredentialStore`] and a [`LockedFile`] to hold if mutating the store.
|
||||||
///
|
///
|
||||||
/// If the store will not be written to following the read, the lock can be dropped.
|
/// If the store will not be written to following the read, the lock can be dropped.
|
||||||
pub fn read<P: AsRef<Path>>(path: P) -> Result<(Self, LockedFile), TomlCredentialError> {
|
pub async fn read<P: AsRef<Path>>(path: P) -> Result<(Self, LockedFile), TomlCredentialError> {
|
||||||
let lock = Self::lock(path.as_ref())?;
|
let lock = Self::lock(path.as_ref()).await?;
|
||||||
let store = Self::from_file(path)?;
|
let store = Self::from_file(path)?;
|
||||||
Ok((store, lock))
|
Ok((store, lock))
|
||||||
}
|
}
|
||||||
|
|
@ -450,8 +471,8 @@ mod tests {
|
||||||
assert!(store.get_credentials(&url, None).is_none());
|
assert!(store.get_credentials(&url, None).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn test_file_operations() {
|
async fn test_file_operations() {
|
||||||
let mut temp_file = NamedTempFile::new().unwrap();
|
let mut temp_file = NamedTempFile::new().unwrap();
|
||||||
writeln!(
|
writeln!(
|
||||||
temp_file,
|
temp_file,
|
||||||
|
|
@ -487,7 +508,7 @@ password = "pass2"
|
||||||
store
|
store
|
||||||
.write(
|
.write(
|
||||||
temp_output.path(),
|
temp_output.path(),
|
||||||
TextCredentialStore::lock(temp_file.path()).unwrap(),
|
TextCredentialStore::lock(temp_file.path()).await.unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-bench"
|
name = "uv-bench"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
publish = false
|
publish = false
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
|
|
@ -22,14 +22,14 @@ name = "uv"
|
||||||
path = "benches/uv.rs"
|
path = "benches/uv.rs"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dev-dependencies]
|
||||||
uv-cache = { workspace = true }
|
uv-cache = { workspace = true }
|
||||||
uv-client = { workspace = true }
|
uv-client = { workspace = true }
|
||||||
uv-configuration = { workspace = true }
|
uv-configuration = { workspace = true }
|
||||||
uv-dispatch = { workspace = true }
|
uv-dispatch = { workspace = true }
|
||||||
uv-distribution = { workspace = true }
|
uv-distribution = { workspace = true }
|
||||||
uv-distribution-types = { workspace = true }
|
uv-distribution-types = { workspace = true }
|
||||||
uv-extract = { workspace = true, optional = true }
|
uv-extract = { workspace = true }
|
||||||
uv-install-wheel = { workspace = true }
|
uv-install-wheel = { workspace = true }
|
||||||
uv-pep440 = { workspace = true }
|
uv-pep440 = { workspace = true }
|
||||||
uv-pep508 = { workspace = true }
|
uv-pep508 = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-bench).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,10 @@ fn setup(manifest: Manifest) -> impl Fn(bool) {
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let cache = Cache::from_path("../../.cache").init().unwrap();
|
let cache = Cache::from_path("../../.cache")
|
||||||
|
.init_no_wait()
|
||||||
|
.expect("No cache contention when running benchmarks")
|
||||||
|
.unwrap();
|
||||||
let interpreter = PythonEnvironment::from_root("../../.venv", &cache)
|
let interpreter = PythonEnvironment::from_root("../../.venv", &cache)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_interpreter();
|
.into_interpreter();
|
||||||
|
|
@ -131,7 +134,7 @@ mod resolver {
|
||||||
);
|
);
|
||||||
|
|
||||||
static TAGS: LazyLock<Tags> = LazyLock::new(|| {
|
static TAGS: LazyLock<Tags> = LazyLock::new(|| {
|
||||||
Tags::from_env(&PLATFORM, (3, 11), "cpython", (3, 11), false, false).unwrap()
|
Tags::from_env(&PLATFORM, (3, 11), "cpython", (3, 11), false, false, false).unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
pub(crate) async fn resolve(
|
pub(crate) async fn resolve(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-bin-install"
|
name = "uv-bin-install"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
@ -23,6 +23,7 @@ uv-extract = { workspace = true }
|
||||||
uv-pep440 = { workspace = true }
|
uv-pep440 = { workspace = true }
|
||||||
uv-platform = { workspace = true }
|
uv-platform = { workspace = true }
|
||||||
uv-redacted = { workspace = true }
|
uv-redacted = { workspace = true }
|
||||||
|
|
||||||
fs-err = { workspace = true, features = ["tokio"] }
|
fs-err = { workspace = true, features = ["tokio"] }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-bin-install).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ use tracing::debug;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uv_distribution_filename::SourceDistExtension;
|
use uv_distribution_filename::SourceDistExtension;
|
||||||
|
|
||||||
use uv_cache::{Cache, CacheBucket, CacheEntry};
|
use uv_cache::{Cache, CacheBucket, CacheEntry, Error as CacheError};
|
||||||
use uv_client::{BaseClient, is_transient_network_error};
|
use uv_client::{BaseClient, is_transient_network_error};
|
||||||
use uv_extract::{Error as ExtractError, stream};
|
use uv_extract::{Error as ExtractError, stream};
|
||||||
use uv_pep440::Version;
|
use uv_pep440::Version;
|
||||||
|
|
@ -135,6 +135,9 @@ pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Cache(#[from] CacheError),
|
||||||
|
|
||||||
#[error("Failed to detect platform")]
|
#[error("Failed to detect platform")]
|
||||||
Platform(#[from] uv_platform::Error),
|
Platform(#[from] uv_platform::Error),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-build-backend"
|
name = "uv-build-backend"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-build-backend).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use itertools::Itertools;
|
||||||
mod metadata;
|
mod metadata;
|
||||||
mod serde_verbatim;
|
mod serde_verbatim;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
|
@ -7,8 +8,10 @@ mod wheel;
|
||||||
pub use metadata::{PyProjectToml, check_direct_build};
|
pub use metadata::{PyProjectToml, check_direct_build};
|
||||||
pub use settings::{BuildBackendSettings, WheelDataIncludes};
|
pub use settings::{BuildBackendSettings, WheelDataIncludes};
|
||||||
pub use source_dist::{build_source_dist, list_source_dist};
|
pub use source_dist::{build_source_dist, list_source_dist};
|
||||||
|
use uv_warnings::warn_user_once;
|
||||||
pub use wheel::{build_editable, build_wheel, list_wheel, metadata};
|
pub use wheel::{build_editable, build_wheel, list_wheel, metadata};
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
@ -29,9 +32,9 @@ use crate::settings::ModuleName;
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] io::Error),
|
Io(#[from] io::Error),
|
||||||
#[error("Invalid pyproject.toml")]
|
#[error("Invalid metadata format in: {}", _0.user_display())]
|
||||||
Toml(#[from] toml::de::Error),
|
Toml(PathBuf, #[source] toml::de::Error),
|
||||||
#[error("Invalid pyproject.toml")]
|
#[error("Invalid project metadata")]
|
||||||
Validation(#[from] ValidationError),
|
Validation(#[from] ValidationError),
|
||||||
#[error("Invalid module name: {0}")]
|
#[error("Invalid module name: {0}")]
|
||||||
InvalidModuleName(String, #[source] IdentifierParseError),
|
InvalidModuleName(String, #[source] IdentifierParseError),
|
||||||
|
|
@ -191,6 +194,60 @@ fn check_metadata_directory(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the list of module names without names which would be included twice
|
||||||
|
///
|
||||||
|
/// In normal cases it should do nothing:
|
||||||
|
///
|
||||||
|
/// * `["aaa"] -> ["aaa"]`
|
||||||
|
/// * `["aaa", "bbb"] -> ["aaa", "bbb"]`
|
||||||
|
///
|
||||||
|
/// Duplicate elements are removed:
|
||||||
|
///
|
||||||
|
/// * `["aaa", "aaa"] -> ["aaa"]`
|
||||||
|
/// * `["bbb", "aaa", "bbb"] -> ["aaa", "bbb"]`
|
||||||
|
///
|
||||||
|
/// Names with more specific paths are removed in favour of more general paths:
|
||||||
|
///
|
||||||
|
/// * `["aaa.foo", "aaa"] -> ["aaa"]`
|
||||||
|
/// * `["bbb", "aaa", "bbb.foo", "ccc.foo", "ccc.foo.bar", "aaa"] -> ["aaa", "bbb.foo", "ccc.foo"]`
|
||||||
|
///
|
||||||
|
/// This does not preserve the order of the elements.
|
||||||
|
fn prune_redundant_modules(mut names: Vec<String>) -> Vec<String> {
|
||||||
|
names.sort();
|
||||||
|
let mut pruned = Vec::with_capacity(names.len());
|
||||||
|
for name in names {
|
||||||
|
if let Some(last) = pruned.last() {
|
||||||
|
if name == *last {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// This is a more specific (narrow) module name than what came before
|
||||||
|
if name
|
||||||
|
.strip_prefix(last)
|
||||||
|
.is_some_and(|suffix| suffix.starts_with('.'))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pruned.push(name);
|
||||||
|
}
|
||||||
|
pruned
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps [`prune_redundant_modules`] with a conditional warning when modules are ignored
|
||||||
|
fn prune_redundant_modules_warn(names: &[String], show_warnings: bool) -> Vec<String> {
|
||||||
|
let pruned = prune_redundant_modules(names.to_vec());
|
||||||
|
if show_warnings && names.len() != pruned.len() {
|
||||||
|
let mut pruned: HashSet<_> = pruned.iter().collect();
|
||||||
|
let ignored: Vec<_> = names.iter().filter(|name| !pruned.remove(name)).collect();
|
||||||
|
let s = if ignored.len() == 1 { "" } else { "s" };
|
||||||
|
warn_user_once!(
|
||||||
|
"Ignoring redundant module name{s} in `tool.uv.build-backend.module-name`: `{}`",
|
||||||
|
ignored.into_iter().join("`, `")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pruned
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the source root and the module path(s) with the `__init__.py[i]` below to it while
|
/// Returns the source root and the module path(s) with the `__init__.py[i]` below to it while
|
||||||
/// checking the project layout and names.
|
/// checking the project layout and names.
|
||||||
///
|
///
|
||||||
|
|
@ -213,6 +270,7 @@ fn find_roots(
|
||||||
relative_module_root: &Path,
|
relative_module_root: &Path,
|
||||||
module_name: Option<&ModuleName>,
|
module_name: Option<&ModuleName>,
|
||||||
namespace: bool,
|
namespace: bool,
|
||||||
|
show_warnings: bool,
|
||||||
) -> Result<(PathBuf, Vec<PathBuf>), Error> {
|
) -> Result<(PathBuf, Vec<PathBuf>), Error> {
|
||||||
let relative_module_root = uv_fs::normalize_path(relative_module_root);
|
let relative_module_root = uv_fs::normalize_path(relative_module_root);
|
||||||
// Check that even if a path contains `..`, we only include files below the module root.
|
// Check that even if a path contains `..`, we only include files below the module root.
|
||||||
|
|
@ -231,8 +289,8 @@ fn find_roots(
|
||||||
ModuleName::Name(name) => {
|
ModuleName::Name(name) => {
|
||||||
vec![name.split('.').collect::<PathBuf>()]
|
vec![name.split('.').collect::<PathBuf>()]
|
||||||
}
|
}
|
||||||
ModuleName::Names(names) => names
|
ModuleName::Names(names) => prune_redundant_modules_warn(names, show_warnings)
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|name| name.split('.').collect::<PathBuf>())
|
.map(|name| name.split('.').collect::<PathBuf>())
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
|
|
@ -250,9 +308,9 @@ fn find_roots(
|
||||||
let modules_relative = if let Some(module_name) = module_name {
|
let modules_relative = if let Some(module_name) = module_name {
|
||||||
match module_name {
|
match module_name {
|
||||||
ModuleName::Name(name) => vec![module_path_from_module_name(&src_root, name)?],
|
ModuleName::Name(name) => vec![module_path_from_module_name(&src_root, name)?],
|
||||||
ModuleName::Names(names) => names
|
ModuleName::Names(names) => prune_redundant_modules_warn(names, show_warnings)
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|name| module_path_from_module_name(&src_root, name))
|
.map(|name| module_path_from_module_name(&src_root, &name))
|
||||||
.collect::<Result<_, _>>()?,
|
.collect::<Result<_, _>>()?,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -420,19 +478,20 @@ mod tests {
|
||||||
fn build(source_root: &Path, dist: &Path) -> Result<BuildResults, Error> {
|
fn build(source_root: &Path, dist: &Path) -> Result<BuildResults, Error> {
|
||||||
// Build a direct wheel, capture all its properties to compare it with the indirect wheel
|
// Build a direct wheel, capture all its properties to compare it with the indirect wheel
|
||||||
// latest and remove it since it has the same filename as the indirect wheel.
|
// latest and remove it since it has the same filename as the indirect wheel.
|
||||||
let (_name, direct_wheel_list_files) = list_wheel(source_root, MOCK_UV_VERSION)?;
|
let (_name, direct_wheel_list_files) = list_wheel(source_root, MOCK_UV_VERSION, false)?;
|
||||||
let direct_wheel_filename = build_wheel(source_root, dist, None, MOCK_UV_VERSION)?;
|
let direct_wheel_filename = build_wheel(source_root, dist, None, MOCK_UV_VERSION, false)?;
|
||||||
let direct_wheel_path = dist.join(direct_wheel_filename.to_string());
|
let direct_wheel_path = dist.join(direct_wheel_filename.to_string());
|
||||||
let direct_wheel_contents = wheel_contents(&direct_wheel_path);
|
let direct_wheel_contents = wheel_contents(&direct_wheel_path);
|
||||||
let direct_wheel_hash = sha2::Sha256::digest(fs_err::read(&direct_wheel_path)?);
|
let direct_wheel_hash = sha2::Sha256::digest(fs_err::read(&direct_wheel_path)?);
|
||||||
fs_err::remove_file(&direct_wheel_path)?;
|
fs_err::remove_file(&direct_wheel_path)?;
|
||||||
|
|
||||||
// Build a source distribution.
|
// Build a source distribution.
|
||||||
let (_name, source_dist_list_files) = list_source_dist(source_root, MOCK_UV_VERSION)?;
|
let (_name, source_dist_list_files) =
|
||||||
|
list_source_dist(source_root, MOCK_UV_VERSION, false)?;
|
||||||
// TODO(konsti): This should run in the unpacked source dist tempdir, but we need to
|
// TODO(konsti): This should run in the unpacked source dist tempdir, but we need to
|
||||||
// normalize the path.
|
// normalize the path.
|
||||||
let (_name, wheel_list_files) = list_wheel(source_root, MOCK_UV_VERSION)?;
|
let (_name, wheel_list_files) = list_wheel(source_root, MOCK_UV_VERSION, false)?;
|
||||||
let source_dist_filename = build_source_dist(source_root, dist, MOCK_UV_VERSION)?;
|
let source_dist_filename = build_source_dist(source_root, dist, MOCK_UV_VERSION, false)?;
|
||||||
let source_dist_path = dist.join(source_dist_filename.to_string());
|
let source_dist_path = dist.join(source_dist_filename.to_string());
|
||||||
let source_dist_contents = sdist_contents(&source_dist_path);
|
let source_dist_contents = sdist_contents(&source_dist_path);
|
||||||
|
|
||||||
|
|
@ -446,7 +505,13 @@ mod tests {
|
||||||
source_dist_filename.name.as_dist_info_name(),
|
source_dist_filename.name.as_dist_info_name(),
|
||||||
source_dist_filename.version
|
source_dist_filename.version
|
||||||
));
|
));
|
||||||
let wheel_filename = build_wheel(&sdist_top_level_directory, dist, None, MOCK_UV_VERSION)?;
|
let wheel_filename = build_wheel(
|
||||||
|
&sdist_top_level_directory,
|
||||||
|
dist,
|
||||||
|
None,
|
||||||
|
MOCK_UV_VERSION,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
let wheel_contents = wheel_contents(&dist.join(wheel_filename.to_string()));
|
let wheel_contents = wheel_contents(&dist.join(wheel_filename.to_string()));
|
||||||
|
|
||||||
// Check that direct and indirect wheels are identical.
|
// Check that direct and indirect wheels are identical.
|
||||||
|
|
@ -534,7 +599,7 @@ mod tests {
|
||||||
/// platform-independent deterministic builds.
|
/// platform-independent deterministic builds.
|
||||||
#[test]
|
#[test]
|
||||||
fn built_by_uv_building() {
|
fn built_by_uv_building() {
|
||||||
let built_by_uv = Path::new("../../scripts/packages/built-by-uv");
|
let built_by_uv = Path::new("../../test/packages/built-by-uv");
|
||||||
let src = TempDir::new().unwrap();
|
let src = TempDir::new().unwrap();
|
||||||
for dir in [
|
for dir in [
|
||||||
"src",
|
"src",
|
||||||
|
|
@ -597,7 +662,7 @@ mod tests {
|
||||||
// Check that the source dist is reproducible across platforms.
|
// Check that the source dist is reproducible across platforms.
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
format!("{:x}", sha2::Sha256::digest(fs_err::read(&source_dist_path).unwrap())),
|
format!("{:x}", sha2::Sha256::digest(fs_err::read(&source_dist_path).unwrap())),
|
||||||
@"871d1f859140721b67cbeaca074e7a2740c88c38028d0509eba87d1285f1da9e"
|
@"bb74bff575b135bb39e5c9bce56349441fb0923bb8857e32a5eaf34ec1843967"
|
||||||
);
|
);
|
||||||
// Check both the files we report and the actual files
|
// Check both the files we report and the actual files
|
||||||
assert_snapshot!(format_file_list(build.source_dist_list_files, src.path()), @r"
|
assert_snapshot!(format_file_list(build.source_dist_list_files, src.path()), @r"
|
||||||
|
|
@ -756,7 +821,7 @@ mod tests {
|
||||||
|
|
||||||
// Build a wheel from a source distribution
|
// Build a wheel from a source distribution
|
||||||
let output_dir = TempDir::new().unwrap();
|
let output_dir = TempDir::new().unwrap();
|
||||||
build_source_dist(src.path(), output_dir.path(), "0.5.15").unwrap();
|
build_source_dist(src.path(), output_dir.path(), "0.5.15", false).unwrap();
|
||||||
let sdist_tree = TempDir::new().unwrap();
|
let sdist_tree = TempDir::new().unwrap();
|
||||||
let source_dist_path = output_dir.path().join("pep_pep639_license-1.0.0.tar.gz");
|
let source_dist_path = output_dir.path().join("pep_pep639_license-1.0.0.tar.gz");
|
||||||
let sdist_reader = BufReader::new(File::open(&source_dist_path).unwrap());
|
let sdist_reader = BufReader::new(File::open(&source_dist_path).unwrap());
|
||||||
|
|
@ -767,6 +832,7 @@ mod tests {
|
||||||
output_dir.path(),
|
output_dir.path(),
|
||||||
None,
|
None,
|
||||||
"0.5.15",
|
"0.5.15",
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let wheel = output_dir
|
let wheel = output_dir
|
||||||
|
|
@ -831,6 +897,7 @@ mod tests {
|
||||||
output_dir.path(),
|
output_dir.path(),
|
||||||
Some(&metadata_dir.path().join(&dist_info_dir)),
|
Some(&metadata_dir.path().join(&dist_info_dir)),
|
||||||
"0.5.15",
|
"0.5.15",
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let wheel = output_dir
|
let wheel = output_dir
|
||||||
|
|
@ -1414,4 +1481,114 @@ mod tests {
|
||||||
simple_namespace_part-1.0.0.dist-info/WHEEL
|
simple_namespace_part-1.0.0.dist-info/WHEEL
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `prune_redundant_modules` should remove modules which are already
|
||||||
|
/// included (either directly or via their parent)
|
||||||
|
#[test]
|
||||||
|
fn test_prune_redundant_modules() {
|
||||||
|
fn check(input: &[&str], expect: &[&str]) {
|
||||||
|
let input = input.iter().map(|s| (*s).to_string()).collect();
|
||||||
|
let expect: Vec<_> = expect.iter().map(|s| (*s).to_string()).collect();
|
||||||
|
assert_eq!(prune_redundant_modules(input), expect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic cases
|
||||||
|
check(&[], &[]);
|
||||||
|
check(&["foo"], &["foo"]);
|
||||||
|
check(&["foo", "bar"], &["bar", "foo"]);
|
||||||
|
|
||||||
|
// Deshadowing
|
||||||
|
check(&["foo", "foo.bar"], &["foo"]);
|
||||||
|
check(&["foo.bar", "foo"], &["foo"]);
|
||||||
|
check(
|
||||||
|
&["foo.bar.a", "foo.bar.b", "foo.bar", "foo", "foo.bar.a.c"],
|
||||||
|
&["foo"],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
&["bar.one", "bar.two", "baz", "bar", "baz.one"],
|
||||||
|
&["bar", "baz"],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Potential false positives
|
||||||
|
check(&["foo", "foobar"], &["foo", "foobar"]);
|
||||||
|
check(
|
||||||
|
&["foo", "foobar", "foo.bar", "foobar.baz"],
|
||||||
|
&["foo", "foobar"],
|
||||||
|
);
|
||||||
|
check(&["foo.bar", "foo.baz"], &["foo.bar", "foo.baz"]);
|
||||||
|
check(&["foo", "foo", "foo.bar", "foo.bar"], &["foo"]);
|
||||||
|
|
||||||
|
// Everything
|
||||||
|
check(
|
||||||
|
&[
|
||||||
|
"foo.inner",
|
||||||
|
"foo.inner.deeper",
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
"bar.sub",
|
||||||
|
"bar.sub.deep",
|
||||||
|
"foobar",
|
||||||
|
"baz.baz.bar",
|
||||||
|
"baz.baz",
|
||||||
|
"qux",
|
||||||
|
],
|
||||||
|
&["bar", "baz.baz", "foo", "foobar", "qux"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A package with duplicate module names.
|
||||||
|
#[test]
|
||||||
|
fn duplicate_module_names() {
|
||||||
|
let src = TempDir::new().unwrap();
|
||||||
|
let pyproject_toml = indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "duplicate"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[tool.uv.build-backend]
|
||||||
|
module-name = ["foo", "foo", "bar.baz", "bar.baz.submodule"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["uv_build>=0.5.15,<0.6.0"]
|
||||||
|
build-backend = "uv_build"
|
||||||
|
"#
|
||||||
|
};
|
||||||
|
fs_err::write(src.path().join("pyproject.toml"), pyproject_toml).unwrap();
|
||||||
|
fs_err::create_dir_all(src.path().join("src").join("foo")).unwrap();
|
||||||
|
File::create(src.path().join("src").join("foo").join("__init__.py")).unwrap();
|
||||||
|
fs_err::create_dir_all(src.path().join("src").join("bar").join("baz")).unwrap();
|
||||||
|
File::create(
|
||||||
|
src.path()
|
||||||
|
.join("src")
|
||||||
|
.join("bar")
|
||||||
|
.join("baz")
|
||||||
|
.join("__init__.py"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let dist = TempDir::new().unwrap();
|
||||||
|
let build = build(src.path(), dist.path()).unwrap();
|
||||||
|
assert_snapshot!(build.source_dist_contents.join("\n"), @r"
|
||||||
|
duplicate-1.0.0/
|
||||||
|
duplicate-1.0.0/PKG-INFO
|
||||||
|
duplicate-1.0.0/pyproject.toml
|
||||||
|
duplicate-1.0.0/src
|
||||||
|
duplicate-1.0.0/src/bar
|
||||||
|
duplicate-1.0.0/src/bar/baz
|
||||||
|
duplicate-1.0.0/src/bar/baz/__init__.py
|
||||||
|
duplicate-1.0.0/src/foo
|
||||||
|
duplicate-1.0.0/src/foo/__init__.py
|
||||||
|
");
|
||||||
|
assert_snapshot!(build.wheel_contents.join("\n"), @r"
|
||||||
|
bar/
|
||||||
|
bar/baz/
|
||||||
|
bar/baz/__init__.py
|
||||||
|
duplicate-1.0.0.dist-info/
|
||||||
|
duplicate-1.0.0.dist-info/METADATA
|
||||||
|
duplicate-1.0.0.dist-info/RECORD
|
||||||
|
duplicate-1.0.0.dist-info/WHEEL
|
||||||
|
foo/
|
||||||
|
foo/__init__.py
|
||||||
|
");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,8 +154,11 @@ impl PyProjectToml {
|
||||||
&self.project.version
|
&self.project.version
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse(contents: &str) -> Result<Self, Error> {
|
pub(crate) fn parse(path: &Path) -> Result<Self, Error> {
|
||||||
Ok(toml::from_str(contents)?)
|
let contents = fs_err::read_to_string(path)?;
|
||||||
|
let pyproject_toml =
|
||||||
|
toml::from_str(&contents).map_err(|err| Error::Toml(path.to_path_buf(), err))?;
|
||||||
|
Ok(pyproject_toml)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn readme(&self) -> Option<&Readme> {
|
pub(crate) fn readme(&self) -> Option<&Readme> {
|
||||||
|
|
@ -949,7 +952,7 @@ mod tests {
|
||||||
requires = ["uv_build>=0.4.15,<0.5.0"]
|
requires = ["uv_build>=0.4.15,<0.5.0"]
|
||||||
build-backend = "uv_build"
|
build-backend = "uv_build"
|
||||||
"#;
|
"#;
|
||||||
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
|
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
|
||||||
let temp_dir = TempDir::new().unwrap();
|
let temp_dir = TempDir::new().unwrap();
|
||||||
|
|
||||||
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();
|
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();
|
||||||
|
|
@ -1034,7 +1037,7 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
};
|
};
|
||||||
|
|
||||||
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
|
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
|
||||||
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();
|
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();
|
||||||
|
|
||||||
assert_snapshot!(metadata.core_metadata_format(), @r###"
|
assert_snapshot!(metadata.core_metadata_format(), @r###"
|
||||||
|
|
@ -1128,7 +1131,7 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
};
|
};
|
||||||
|
|
||||||
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
|
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
|
||||||
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();
|
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();
|
||||||
|
|
||||||
assert_snapshot!(metadata.core_metadata_format(), @r"
|
assert_snapshot!(metadata.core_metadata_format(), @r"
|
||||||
|
|
@ -1220,7 +1223,7 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
};
|
};
|
||||||
|
|
||||||
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
|
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
|
||||||
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();
|
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();
|
||||||
|
|
||||||
assert_snapshot!(metadata.core_metadata_format(), @r###"
|
assert_snapshot!(metadata.core_metadata_format(), @r###"
|
||||||
|
|
@ -1281,7 +1284,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn build_system_valid() {
|
fn build_system_valid() {
|
||||||
let contents = extend_project("");
|
let contents = extend_project("");
|
||||||
let pyproject_toml = PyProjectToml::parse(&contents).unwrap();
|
let pyproject_toml: PyProjectToml = toml::from_str(&contents).unwrap();
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
||||||
@""
|
@""
|
||||||
|
|
@ -1299,7 +1302,7 @@ mod tests {
|
||||||
requires = ["uv_build"]
|
requires = ["uv_build"]
|
||||||
build-backend = "uv_build"
|
build-backend = "uv_build"
|
||||||
"#};
|
"#};
|
||||||
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
|
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
||||||
@r###"`build_system.requires = ["uv_build"]` is missing an upper bound on the `uv_build` version such as `<0.5`. Without bounding the `uv_build` version, the source distribution will break when a future, breaking version of `uv_build` is released."###
|
@r###"`build_system.requires = ["uv_build"]` is missing an upper bound on the `uv_build` version such as `<0.5`. Without bounding the `uv_build` version, the source distribution will break when a future, breaking version of `uv_build` is released."###
|
||||||
|
|
@ -1317,7 +1320,7 @@ mod tests {
|
||||||
requires = ["uv_build>=0.4.15,<0.5.0", "wheel"]
|
requires = ["uv_build>=0.4.15,<0.5.0", "wheel"]
|
||||||
build-backend = "uv_build"
|
build-backend = "uv_build"
|
||||||
"#};
|
"#};
|
||||||
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
|
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
||||||
@"Expected a single uv requirement in `build-system.requires`, found ``"
|
@"Expected a single uv requirement in `build-system.requires`, found ``"
|
||||||
|
|
@ -1335,7 +1338,7 @@ mod tests {
|
||||||
requires = ["setuptools"]
|
requires = ["setuptools"]
|
||||||
build-backend = "uv_build"
|
build-backend = "uv_build"
|
||||||
"#};
|
"#};
|
||||||
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
|
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
||||||
@"Expected a single uv requirement in `build-system.requires`, found ``"
|
@"Expected a single uv requirement in `build-system.requires`, found ``"
|
||||||
|
|
@ -1353,7 +1356,7 @@ mod tests {
|
||||||
requires = ["uv_build>=0.4.15,<0.5.0"]
|
requires = ["uv_build>=0.4.15,<0.5.0"]
|
||||||
build-backend = "setuptools"
|
build-backend = "setuptools"
|
||||||
"#};
|
"#};
|
||||||
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
|
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
|
||||||
@r###"The value for `build_system.build-backend` should be `"uv_build"`, not `"setuptools"`"###
|
@r###"The value for `build_system.build-backend` should be `"uv_build"`, not `"setuptools"`"###
|
||||||
|
|
@ -1364,7 +1367,7 @@ mod tests {
|
||||||
fn minimal() {
|
fn minimal() {
|
||||||
let contents = extend_project("");
|
let contents = extend_project("");
|
||||||
|
|
||||||
let metadata = PyProjectToml::parse(&contents)
|
let metadata = toml::from_str::<PyProjectToml>(&contents)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_metadata(Path::new("/do/not/read"))
|
.to_metadata(Path::new("/do/not/read"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -1383,15 +1386,14 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = PyProjectToml::parse(&contents).unwrap_err();
|
let err = toml::from_str::<PyProjectToml>(&contents).unwrap_err();
|
||||||
assert_snapshot!(format_err(err), @r###"
|
assert_snapshot!(format_err(err), @r#"
|
||||||
Invalid pyproject.toml
|
TOML parse error at line 4, column 10
|
||||||
Caused by: TOML parse error at line 4, column 10
|
|
||||||
|
|
|
|
||||||
4 | readme = { path = "Readme.md" }
|
4 | readme = { path = "Readme.md" }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
data did not match any variant of untagged enum Readme
|
data did not match any variant of untagged enum Readme
|
||||||
"###);
|
"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1401,7 +1403,7 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = PyProjectToml::parse(&contents)
|
let err = toml::from_str::<PyProjectToml>(&contents)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_metadata(Path::new("/do/not/read"))
|
.to_metadata(Path::new("/do/not/read"))
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
|
@ -1423,14 +1425,14 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = PyProjectToml::parse(&contents)
|
let err = toml::from_str::<PyProjectToml>(&contents)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_metadata(Path::new("/do/not/read"))
|
.to_metadata(Path::new("/do/not/read"))
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
assert_snapshot!(format_err(err), @r###"
|
assert_snapshot!(format_err(err), @r"
|
||||||
Invalid pyproject.toml
|
Invalid project metadata
|
||||||
Caused by: `project.description` must be a single line
|
Caused by: `project.description` must be a single line
|
||||||
"###);
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1441,14 +1443,14 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = PyProjectToml::parse(&contents)
|
let err = toml::from_str::<PyProjectToml>(&contents)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_metadata(Path::new("/do/not/read"))
|
.to_metadata(Path::new("/do/not/read"))
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
assert_snapshot!(format_err(err), @r###"
|
assert_snapshot!(format_err(err), @r"
|
||||||
Invalid pyproject.toml
|
Invalid project metadata
|
||||||
Caused by: When `project.license-files` is defined, `project.license` must be an SPDX expression string
|
Caused by: When `project.license-files` is defined, `project.license` must be an SPDX expression string
|
||||||
"###);
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1457,7 +1459,7 @@ mod tests {
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
"#
|
"#
|
||||||
});
|
});
|
||||||
let metadata = PyProjectToml::parse(&contents)
|
let metadata = toml::from_str::<PyProjectToml>(&contents)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_metadata(Path::new("/do/not/read"))
|
.to_metadata(Path::new("/do/not/read"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -1475,13 +1477,13 @@ mod tests {
|
||||||
license = "MIT XOR Apache-2"
|
license = "MIT XOR Apache-2"
|
||||||
"#
|
"#
|
||||||
});
|
});
|
||||||
let err = PyProjectToml::parse(&contents)
|
let err = toml::from_str::<PyProjectToml>(&contents)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_metadata(Path::new("/do/not/read"))
|
.to_metadata(Path::new("/do/not/read"))
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
// TODO(konsti): We mess up the indentation in the error.
|
// TODO(konsti): We mess up the indentation in the error.
|
||||||
assert_snapshot!(format_err(err), @r"
|
assert_snapshot!(format_err(err), @r"
|
||||||
Invalid pyproject.toml
|
Invalid project metadata
|
||||||
Caused by: `project.license` is not a valid SPDX expression: MIT XOR Apache-2
|
Caused by: `project.license` is not a valid SPDX expression: MIT XOR Apache-2
|
||||||
Caused by: MIT XOR Apache-2
|
Caused by: MIT XOR Apache-2
|
||||||
^^^ unknown term
|
^^^ unknown term
|
||||||
|
|
@ -1495,18 +1497,18 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = PyProjectToml::parse(&contents)
|
let err = toml::from_str::<PyProjectToml>(&contents)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_metadata(Path::new("/do/not/read"))
|
.to_metadata(Path::new("/do/not/read"))
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
assert_snapshot!(format_err(err), @r###"
|
assert_snapshot!(format_err(err), @r"
|
||||||
Invalid pyproject.toml
|
Invalid project metadata
|
||||||
Caused by: Dynamic metadata is not supported
|
Caused by: Dynamic metadata is not supported
|
||||||
"###);
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn script_error(contents: &str) -> String {
|
fn script_error(contents: &str) -> String {
|
||||||
let err = PyProjectToml::parse(contents)
|
let err = toml::from_str::<PyProjectToml>(contents)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_entry_points()
|
.to_entry_points()
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,9 @@ pub struct BuildBackendSettings {
|
||||||
pub default_excludes: bool,
|
pub default_excludes: bool,
|
||||||
|
|
||||||
/// Glob expressions which files and directories to exclude from the source distribution.
|
/// Glob expressions which files and directories to exclude from the source distribution.
|
||||||
|
///
|
||||||
|
/// These exclusions are also applied to wheels to ensure that a wheel built from a source tree
|
||||||
|
/// is consistent with a wheel built from a source distribution.
|
||||||
#[option(
|
#[option(
|
||||||
default = r#"[]"#,
|
default = r#"[]"#,
|
||||||
value_type = "list[str]",
|
value_type = "list[str]",
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ pub fn build_source_dist(
|
||||||
source_tree: &Path,
|
source_tree: &Path,
|
||||||
source_dist_directory: &Path,
|
source_dist_directory: &Path,
|
||||||
uv_version: &str,
|
uv_version: &str,
|
||||||
|
show_warnings: bool,
|
||||||
) -> Result<SourceDistFilename, Error> {
|
) -> Result<SourceDistFilename, Error> {
|
||||||
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
|
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
|
||||||
let pyproject_toml = PyProjectToml::parse(&contents)?;
|
|
||||||
let filename = SourceDistFilename {
|
let filename = SourceDistFilename {
|
||||||
name: pyproject_toml.name().clone(),
|
name: pyproject_toml.name().clone(),
|
||||||
version: pyproject_toml.version().clone(),
|
version: pyproject_toml.version().clone(),
|
||||||
|
|
@ -34,7 +34,7 @@ pub fn build_source_dist(
|
||||||
};
|
};
|
||||||
let source_dist_path = source_dist_directory.join(filename.to_string());
|
let source_dist_path = source_dist_directory.join(filename.to_string());
|
||||||
let writer = TarGzWriter::new(&source_dist_path)?;
|
let writer = TarGzWriter::new(&source_dist_path)?;
|
||||||
write_source_dist(source_tree, writer, uv_version)?;
|
write_source_dist(source_tree, writer, uv_version, show_warnings)?;
|
||||||
Ok(filename)
|
Ok(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,9 +42,9 @@ pub fn build_source_dist(
|
||||||
pub fn list_source_dist(
|
pub fn list_source_dist(
|
||||||
source_tree: &Path,
|
source_tree: &Path,
|
||||||
uv_version: &str,
|
uv_version: &str,
|
||||||
|
show_warnings: bool,
|
||||||
) -> Result<(SourceDistFilename, FileList), Error> {
|
) -> Result<(SourceDistFilename, FileList), Error> {
|
||||||
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
|
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
|
||||||
let pyproject_toml = PyProjectToml::parse(&contents)?;
|
|
||||||
let filename = SourceDistFilename {
|
let filename = SourceDistFilename {
|
||||||
name: pyproject_toml.name().clone(),
|
name: pyproject_toml.name().clone(),
|
||||||
version: pyproject_toml.version().clone(),
|
version: pyproject_toml.version().clone(),
|
||||||
|
|
@ -52,7 +52,7 @@ pub fn list_source_dist(
|
||||||
};
|
};
|
||||||
let mut files = FileList::new();
|
let mut files = FileList::new();
|
||||||
let writer = ListWriter::new(&mut files);
|
let writer = ListWriter::new(&mut files);
|
||||||
write_source_dist(source_tree, writer, uv_version)?;
|
write_source_dist(source_tree, writer, uv_version, show_warnings)?;
|
||||||
Ok((filename, files))
|
Ok((filename, files))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,6 +61,7 @@ fn source_dist_matcher(
|
||||||
source_tree: &Path,
|
source_tree: &Path,
|
||||||
pyproject_toml: &PyProjectToml,
|
pyproject_toml: &PyProjectToml,
|
||||||
settings: BuildBackendSettings,
|
settings: BuildBackendSettings,
|
||||||
|
show_warnings: bool,
|
||||||
) -> Result<(GlobDirFilter, GlobSet), Error> {
|
) -> Result<(GlobDirFilter, GlobSet), Error> {
|
||||||
// File and directories to include in the source directory
|
// File and directories to include in the source directory
|
||||||
let mut include_globs = Vec::new();
|
let mut include_globs = Vec::new();
|
||||||
|
|
@ -75,6 +76,7 @@ fn source_dist_matcher(
|
||||||
&settings.module_root,
|
&settings.module_root,
|
||||||
settings.module_name.as_ref(),
|
settings.module_name.as_ref(),
|
||||||
settings.namespace,
|
settings.namespace,
|
||||||
|
show_warnings,
|
||||||
)?;
|
)?;
|
||||||
for module_relative in modules_relative {
|
for module_relative in modules_relative {
|
||||||
// The wheel must not include any files included by the source distribution (at least until we
|
// The wheel must not include any files included by the source distribution (at least until we
|
||||||
|
|
@ -182,9 +184,9 @@ fn write_source_dist(
|
||||||
source_tree: &Path,
|
source_tree: &Path,
|
||||||
mut writer: impl DirectoryWriter,
|
mut writer: impl DirectoryWriter,
|
||||||
uv_version: &str,
|
uv_version: &str,
|
||||||
|
show_warnings: bool,
|
||||||
) -> Result<SourceDistFilename, Error> {
|
) -> Result<SourceDistFilename, Error> {
|
||||||
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
|
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
|
||||||
let pyproject_toml = PyProjectToml::parse(&contents)?;
|
|
||||||
for warning in pyproject_toml.check_build_system(uv_version) {
|
for warning in pyproject_toml.check_build_system(uv_version) {
|
||||||
warn_user_once!("{warning}");
|
warn_user_once!("{warning}");
|
||||||
}
|
}
|
||||||
|
|
@ -218,7 +220,7 @@ fn write_source_dist(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (include_matcher, exclude_matcher) =
|
let (include_matcher, exclude_matcher) =
|
||||||
source_dist_matcher(source_tree, &pyproject_toml, settings)?;
|
source_dist_matcher(source_tree, &pyproject_toml, settings, show_warnings)?;
|
||||||
|
|
||||||
let mut files_visited = 0;
|
let mut files_visited = 0;
|
||||||
for entry in WalkDir::new(source_tree)
|
for entry in WalkDir::new(source_tree)
|
||||||
|
|
@ -297,6 +299,10 @@ impl TarGzWriter {
|
||||||
impl DirectoryWriter for TarGzWriter {
|
impl DirectoryWriter for TarGzWriter {
|
||||||
fn write_bytes(&mut self, path: &str, bytes: &[u8]) -> Result<(), Error> {
|
fn write_bytes(&mut self, path: &str, bytes: &[u8]) -> Result<(), Error> {
|
||||||
let mut header = Header::new_gnu();
|
let mut header = Header::new_gnu();
|
||||||
|
// Work around bug in Python's std tar module
|
||||||
|
// https://github.com/python/cpython/issues/141707
|
||||||
|
// https://github.com/astral-sh/uv/pull/17043#issuecomment-3636841022
|
||||||
|
header.set_entry_type(EntryType::Regular);
|
||||||
header.set_size(bytes.len() as u64);
|
header.set_size(bytes.len() as u64);
|
||||||
// Reasonable default to avoid 0o000 permissions, the user's umask will be applied on
|
// Reasonable default to avoid 0o000 permissions, the user's umask will be applied on
|
||||||
// unpacking.
|
// unpacking.
|
||||||
|
|
@ -310,6 +316,10 @@ impl DirectoryWriter for TarGzWriter {
|
||||||
fn write_file(&mut self, path: &str, file: &Path) -> Result<(), Error> {
|
fn write_file(&mut self, path: &str, file: &Path) -> Result<(), Error> {
|
||||||
let metadata = fs_err::metadata(file)?;
|
let metadata = fs_err::metadata(file)?;
|
||||||
let mut header = Header::new_gnu();
|
let mut header = Header::new_gnu();
|
||||||
|
// Work around bug in Python's std tar module
|
||||||
|
// https://github.com/python/cpython/issues/141707
|
||||||
|
// https://github.com/astral-sh/uv/pull/17043#issuecomment-3636841022
|
||||||
|
header.set_entry_type(EntryType::Regular);
|
||||||
// Preserve the executable bit, especially for scripts
|
// Preserve the executable bit, especially for scripts
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let executable_bit = {
|
let executable_bit = {
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@ pub fn build_wheel(
|
||||||
wheel_dir: &Path,
|
wheel_dir: &Path,
|
||||||
metadata_directory: Option<&Path>,
|
metadata_directory: Option<&Path>,
|
||||||
uv_version: &str,
|
uv_version: &str,
|
||||||
|
show_warnings: bool,
|
||||||
) -> Result<WheelFilename, Error> {
|
) -> Result<WheelFilename, Error> {
|
||||||
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
|
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
|
||||||
let pyproject_toml = PyProjectToml::parse(&contents)?;
|
|
||||||
for warning in pyproject_toml.check_build_system(uv_version) {
|
for warning in pyproject_toml.check_build_system(uv_version) {
|
||||||
warn_user_once!("{warning}");
|
warn_user_once!("{warning}");
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +58,7 @@ pub fn build_wheel(
|
||||||
&filename,
|
&filename,
|
||||||
uv_version,
|
uv_version,
|
||||||
wheel_writer,
|
wheel_writer,
|
||||||
|
show_warnings,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(filename)
|
Ok(filename)
|
||||||
|
|
@ -67,9 +68,9 @@ pub fn build_wheel(
|
||||||
pub fn list_wheel(
|
pub fn list_wheel(
|
||||||
source_tree: &Path,
|
source_tree: &Path,
|
||||||
uv_version: &str,
|
uv_version: &str,
|
||||||
|
show_warnings: bool,
|
||||||
) -> Result<(WheelFilename, FileList), Error> {
|
) -> Result<(WheelFilename, FileList), Error> {
|
||||||
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
|
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
|
||||||
let pyproject_toml = PyProjectToml::parse(&contents)?;
|
|
||||||
for warning in pyproject_toml.check_build_system(uv_version) {
|
for warning in pyproject_toml.check_build_system(uv_version) {
|
||||||
warn_user_once!("{warning}");
|
warn_user_once!("{warning}");
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +88,14 @@ pub fn list_wheel(
|
||||||
|
|
||||||
let mut files = FileList::new();
|
let mut files = FileList::new();
|
||||||
let writer = ListWriter::new(&mut files);
|
let writer = ListWriter::new(&mut files);
|
||||||
write_wheel(source_tree, &pyproject_toml, &filename, uv_version, writer)?;
|
write_wheel(
|
||||||
|
source_tree,
|
||||||
|
&pyproject_toml,
|
||||||
|
&filename,
|
||||||
|
uv_version,
|
||||||
|
writer,
|
||||||
|
show_warnings,
|
||||||
|
)?;
|
||||||
Ok((filename, files))
|
Ok((filename, files))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,6 +105,7 @@ fn write_wheel(
|
||||||
filename: &WheelFilename,
|
filename: &WheelFilename,
|
||||||
uv_version: &str,
|
uv_version: &str,
|
||||||
mut wheel_writer: impl DirectoryWriter,
|
mut wheel_writer: impl DirectoryWriter,
|
||||||
|
show_warnings: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let settings = pyproject_toml
|
let settings = pyproject_toml
|
||||||
.settings()
|
.settings()
|
||||||
|
|
@ -132,6 +141,7 @@ fn write_wheel(
|
||||||
&settings.module_root,
|
&settings.module_root,
|
||||||
settings.module_name.as_ref(),
|
settings.module_name.as_ref(),
|
||||||
settings.namespace,
|
settings.namespace,
|
||||||
|
show_warnings,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut files_visited = 0;
|
let mut files_visited = 0;
|
||||||
|
|
@ -259,9 +269,9 @@ pub fn build_editable(
|
||||||
wheel_dir: &Path,
|
wheel_dir: &Path,
|
||||||
metadata_directory: Option<&Path>,
|
metadata_directory: Option<&Path>,
|
||||||
uv_version: &str,
|
uv_version: &str,
|
||||||
|
show_warnings: bool,
|
||||||
) -> Result<WheelFilename, Error> {
|
) -> Result<WheelFilename, Error> {
|
||||||
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
|
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
|
||||||
let pyproject_toml = PyProjectToml::parse(&contents)?;
|
|
||||||
for warning in pyproject_toml.check_build_system(uv_version) {
|
for warning in pyproject_toml.check_build_system(uv_version) {
|
||||||
warn_user_once!("{warning}");
|
warn_user_once!("{warning}");
|
||||||
}
|
}
|
||||||
|
|
@ -295,6 +305,7 @@ pub fn build_editable(
|
||||||
&settings.module_root,
|
&settings.module_root,
|
||||||
settings.module_name.as_ref(),
|
settings.module_name.as_ref(),
|
||||||
settings.namespace,
|
settings.namespace,
|
||||||
|
show_warnings,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
wheel_writer.write_bytes(
|
wheel_writer.write_bytes(
|
||||||
|
|
@ -321,8 +332,7 @@ pub fn metadata(
|
||||||
metadata_directory: &Path,
|
metadata_directory: &Path,
|
||||||
uv_version: &str,
|
uv_version: &str,
|
||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
|
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
|
||||||
let pyproject_toml = PyProjectToml::parse(&contents)?;
|
|
||||||
for warning in pyproject_toml.check_build_system(uv_version) {
|
for warning in pyproject_toml.check_build_system(uv_version) {
|
||||||
warn_user_once!("{warning}");
|
warn_user_once!("{warning}");
|
||||||
}
|
}
|
||||||
|
|
@ -830,7 +840,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_prepare_metadata() {
|
fn test_prepare_metadata() {
|
||||||
let metadata_dir = TempDir::new().unwrap();
|
let metadata_dir = TempDir::new().unwrap();
|
||||||
let built_by_uv = Path::new("../../scripts/packages/built-by-uv");
|
let built_by_uv = Path::new("../../test/packages/built-by-uv");
|
||||||
metadata(built_by_uv, metadata_dir.path(), "1.0.0+test").unwrap();
|
metadata(built_by_uv, metadata_dir.path(), "1.0.0+test").unwrap();
|
||||||
|
|
||||||
let mut files: Vec<_> = WalkDir::new(metadata_dir.path())
|
let mut files: Vec<_> = WalkDir::new(metadata_dir.path())
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-build-frontend"
|
name = "uv-build-frontend"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
@ -16,6 +16,7 @@ doctest = false
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
uv-auth = { workspace = true }
|
||||||
uv-cache-key = { workspace = true }
|
uv-cache-key = { workspace = true }
|
||||||
uv-configuration = { workspace = true }
|
uv-configuration = { workspace = true }
|
||||||
uv-distribution = { workspace = true }
|
uv-distribution = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-build-frontend).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use tokio::io::AsyncBufReadExt;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tokio::sync::{Mutex, Semaphore};
|
use tokio::sync::{Mutex, Semaphore};
|
||||||
use tracing::{Instrument, debug, info_span, instrument, warn};
|
use tracing::{Instrument, debug, info_span, instrument, warn};
|
||||||
|
use uv_auth::CredentialsCache;
|
||||||
use uv_cache_key::cache_digest;
|
use uv_cache_key::cache_digest;
|
||||||
use uv_configuration::{BuildKind, BuildOutput, SourceStrategy};
|
use uv_configuration::{BuildKind, BuildOutput, SourceStrategy};
|
||||||
use uv_distribution::BuildRequires;
|
use uv_distribution::BuildRequires;
|
||||||
|
|
@ -36,7 +36,7 @@ use uv_distribution_types::{
|
||||||
ConfigSettings, ExtraBuildRequirement, ExtraBuildRequires, IndexLocations, Requirement,
|
ConfigSettings, ExtraBuildRequirement, ExtraBuildRequires, IndexLocations, Requirement,
|
||||||
Resolution,
|
Resolution,
|
||||||
};
|
};
|
||||||
use uv_fs::LockedFile;
|
use uv_fs::{LockedFile, LockedFileMode};
|
||||||
use uv_fs::{PythonExt, Simplified};
|
use uv_fs::{PythonExt, Simplified};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_pep440::Version;
|
use uv_pep440::Version;
|
||||||
|
|
@ -292,6 +292,7 @@ impl SourceBuild {
|
||||||
mut environment_variables: FxHashMap<OsString, OsString>,
|
mut environment_variables: FxHashMap<OsString, OsString>,
|
||||||
level: BuildOutput,
|
level: BuildOutput,
|
||||||
concurrent_builds: usize,
|
concurrent_builds: usize,
|
||||||
|
credentials_cache: &CredentialsCache,
|
||||||
preview: Preview,
|
preview: Preview,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let temp_dir = build_context.cache().venv_dir()?;
|
let temp_dir = build_context.cache().venv_dir()?;
|
||||||
|
|
@ -310,6 +311,7 @@ impl SourceBuild {
|
||||||
locations,
|
locations,
|
||||||
source_strategy,
|
source_strategy,
|
||||||
workspace_cache,
|
workspace_cache,
|
||||||
|
credentials_cache,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| *err)?;
|
.map_err(|err| *err)?;
|
||||||
|
|
@ -452,6 +454,7 @@ impl SourceBuild {
|
||||||
&environment_variables,
|
&environment_variables,
|
||||||
&modified_path,
|
&modified_path,
|
||||||
&temp_dir,
|
&temp_dir,
|
||||||
|
credentials_cache,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
@ -490,12 +493,16 @@ impl SourceBuild {
|
||||||
"uv-setuptools-{}.lock",
|
"uv-setuptools-{}.lock",
|
||||||
cache_digest(&canonical_source_path)
|
cache_digest(&canonical_source_path)
|
||||||
));
|
));
|
||||||
source_tree_lock = LockedFile::acquire(lock_path, self.source_tree.to_string_lossy())
|
source_tree_lock = LockedFile::acquire(
|
||||||
.await
|
lock_path,
|
||||||
.inspect_err(|err| {
|
LockedFileMode::Exclusive,
|
||||||
warn!("Failed to acquire build lock: {err}");
|
self.source_tree.to_string_lossy(),
|
||||||
})
|
)
|
||||||
.ok();
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire build lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
Ok(source_tree_lock)
|
Ok(source_tree_lock)
|
||||||
}
|
}
|
||||||
|
|
@ -556,6 +563,7 @@ impl SourceBuild {
|
||||||
locations: &IndexLocations,
|
locations: &IndexLocations,
|
||||||
source_strategy: SourceStrategy,
|
source_strategy: SourceStrategy,
|
||||||
workspace_cache: &WorkspaceCache,
|
workspace_cache: &WorkspaceCache,
|
||||||
|
credentials_cache: &CredentialsCache,
|
||||||
) -> Result<(Pep517Backend, Option<Project>), Box<Error>> {
|
) -> Result<(Pep517Backend, Option<Project>), Box<Error>> {
|
||||||
match fs::read_to_string(source_tree.join("pyproject.toml")) {
|
match fs::read_to_string(source_tree.join("pyproject.toml")) {
|
||||||
Ok(toml) => {
|
Ok(toml) => {
|
||||||
|
|
@ -584,6 +592,7 @@ impl SourceBuild {
|
||||||
locations,
|
locations,
|
||||||
source_strategy,
|
source_strategy,
|
||||||
workspace_cache,
|
workspace_cache,
|
||||||
|
credentials_cache,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(Error::Lowering)?;
|
.map_err(Error::Lowering)?;
|
||||||
|
|
@ -956,6 +965,7 @@ async fn create_pep517_build_environment(
|
||||||
environment_variables: &FxHashMap<OsString, OsString>,
|
environment_variables: &FxHashMap<OsString, OsString>,
|
||||||
modified_path: &OsString,
|
modified_path: &OsString,
|
||||||
temp_dir: &TempDir,
|
temp_dir: &TempDir,
|
||||||
|
credentials_cache: &CredentialsCache,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Write the hook output to a file so that we can read it back reliably.
|
// Write the hook output to a file so that we can read it back reliably.
|
||||||
let outfile = temp_dir
|
let outfile = temp_dir
|
||||||
|
|
@ -1050,6 +1060,7 @@ async fn create_pep517_build_environment(
|
||||||
locations,
|
locations,
|
||||||
source_strategy,
|
source_strategy,
|
||||||
workspace_cache,
|
workspace_cache,
|
||||||
|
credentials_cache,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(Error::Lowering)?;
|
.map_err(Error::Lowering)?;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-build"
|
name = "uv-build"
|
||||||
version = "0.9.13"
|
version = "0.9.18"
|
||||||
description = "A Python build backend"
|
description = "A Python build backend"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "uv-build"
|
name = "uv-build"
|
||||||
version = "0.9.13"
|
version = "0.9.18"
|
||||||
description = "The uv build backend"
|
description = "The uv build backend"
|
||||||
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
|
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ fn main() -> Result<()> {
|
||||||
&env::current_dir()?,
|
&env::current_dir()?,
|
||||||
&sdist_directory,
|
&sdist_directory,
|
||||||
uv_version::version(),
|
uv_version::version(),
|
||||||
|
false,
|
||||||
)?;
|
)?;
|
||||||
// Tell the build frontend about the name of the artifact we built
|
// Tell the build frontend about the name of the artifact we built
|
||||||
writeln!(&mut std::io::stdout(), "{filename}").context("stdout is closed")?;
|
writeln!(&mut std::io::stdout(), "{filename}").context("stdout is closed")?;
|
||||||
|
|
@ -56,6 +57,7 @@ fn main() -> Result<()> {
|
||||||
&wheel_directory,
|
&wheel_directory,
|
||||||
metadata_directory.as_deref(),
|
metadata_directory.as_deref(),
|
||||||
uv_version::version(),
|
uv_version::version(),
|
||||||
|
false,
|
||||||
)?;
|
)?;
|
||||||
// Tell the build frontend about the name of the artifact we built
|
// Tell the build frontend about the name of the artifact we built
|
||||||
writeln!(&mut std::io::stdout(), "{filename}").context("stdout is closed")?;
|
writeln!(&mut std::io::stdout(), "{filename}").context("stdout is closed")?;
|
||||||
|
|
@ -68,6 +70,7 @@ fn main() -> Result<()> {
|
||||||
&wheel_directory,
|
&wheel_directory,
|
||||||
metadata_directory.as_deref(),
|
metadata_directory.as_deref(),
|
||||||
uv_version::version(),
|
uv_version::version(),
|
||||||
|
false,
|
||||||
)?;
|
)?;
|
||||||
// Tell the build frontend about the name of the artifact we built
|
// Tell the build frontend about the name of the artifact we built
|
||||||
writeln!(&mut std::io::stdout(), "{filename}").context("stdout is closed")?;
|
writeln!(&mut std::io::stdout(), "{filename}").context("stdout is closed")?;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-cache-info"
|
name = "uv-cache-info"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-cache-info).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-cache-key"
|
name = "uv-cache-key"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-cache-key).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -139,8 +139,18 @@ impl std::fmt::Display for CanonicalUrl {
|
||||||
/// `https://github.com/pypa/package.git#subdirectory=pkg_b` would map to different
|
/// `https://github.com/pypa/package.git#subdirectory=pkg_b` would map to different
|
||||||
/// [`CanonicalUrl`] values, but the same [`RepositoryUrl`], since they map to the same
|
/// [`CanonicalUrl`] values, but the same [`RepositoryUrl`], since they map to the same
|
||||||
/// resource.
|
/// resource.
|
||||||
|
///
|
||||||
|
/// The additional information it holds should only be used to discriminate between
|
||||||
|
/// sources that hold the exact same commit in their canonical representation,
|
||||||
|
/// but may differ in the contents such as when Git LFS is enabled.
|
||||||
|
///
|
||||||
|
/// A different cache key will be computed when Git LFS is enabled.
|
||||||
|
/// When Git LFS is `false` or `None`, the cache key remains unchanged.
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
pub struct RepositoryUrl(DisplaySafeUrl);
|
pub struct RepositoryUrl {
|
||||||
|
repo_url: DisplaySafeUrl,
|
||||||
|
with_lfs: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
impl RepositoryUrl {
|
impl RepositoryUrl {
|
||||||
pub fn new(url: &DisplaySafeUrl) -> Self {
|
pub fn new(url: &DisplaySafeUrl) -> Self {
|
||||||
|
|
@ -161,19 +171,31 @@ impl RepositoryUrl {
|
||||||
url.set_fragment(None);
|
url.set_fragment(None);
|
||||||
url.set_query(None);
|
url.set_query(None);
|
||||||
|
|
||||||
Self(url)
|
Self {
|
||||||
|
repo_url: url,
|
||||||
|
with_lfs: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(url: &str) -> Result<Self, DisplaySafeUrlError> {
|
pub fn parse(url: &str) -> Result<Self, DisplaySafeUrlError> {
|
||||||
Ok(Self::new(&DisplaySafeUrl::parse(url)?))
|
Ok(Self::new(&DisplaySafeUrl::parse(url)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_lfs(mut self, lfs: Option<bool>) -> Self {
|
||||||
|
self.with_lfs = lfs;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CacheKey for RepositoryUrl {
|
impl CacheKey for RepositoryUrl {
|
||||||
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
// `as_str` gives the serialisation of a url (which has a spec) and so insulates against
|
// `as_str` gives the serialisation of a url (which has a spec) and so insulates against
|
||||||
// possible changes in how the URL crate does hashing.
|
// possible changes in how the URL crate does hashing.
|
||||||
self.0.as_str().cache_key(state);
|
self.repo_url.as_str().cache_key(state);
|
||||||
|
if let Some(true) = self.with_lfs {
|
||||||
|
1u8.cache_key(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,7 +203,10 @@ impl Hash for RepositoryUrl {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
// `as_str` gives the serialisation of a url (which has a spec) and so insulates against
|
// `as_str` gives the serialisation of a url (which has a spec) and so insulates against
|
||||||
// possible changes in how the URL crate does hashing.
|
// possible changes in how the URL crate does hashing.
|
||||||
self.0.as_str().hash(state);
|
self.repo_url.as_str().hash(state);
|
||||||
|
if let Some(true) = self.with_lfs {
|
||||||
|
1u8.hash(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,13 +214,13 @@ impl Deref for RepositoryUrl {
|
||||||
type Target = Url;
|
type Target = Url;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.repo_url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for RepositoryUrl {
|
impl std::fmt::Display for RepositoryUrl {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
std::fmt::Display::fmt(&self.0, f)
|
std::fmt::Display::fmt(&self.repo_url, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,6 +308,14 @@ mod tests {
|
||||||
)?,
|
)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Two URLs should _not_ be considered equal if they differ in Git LFS enablement.
|
||||||
|
assert_ne!(
|
||||||
|
CanonicalUrl::parse(
|
||||||
|
"git+https://github.com/pypa/sample-namespace-packages.git#lfs=true"
|
||||||
|
)?,
|
||||||
|
CanonicalUrl::parse("git+https://github.com/pypa/sample-namespace-packages.git")?,
|
||||||
|
);
|
||||||
|
|
||||||
// Two URLs should _not_ be considered equal if they request different commit tags.
|
// Two URLs should _not_ be considered equal if they request different commit tags.
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
CanonicalUrl::parse(
|
CanonicalUrl::parse(
|
||||||
|
|
@ -378,6 +411,76 @@ mod tests {
|
||||||
)?,
|
)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Two URLs should be considered equal if they map to the same repository, even if they
|
||||||
|
// differ in Git LFS enablement.
|
||||||
|
assert_eq!(
|
||||||
|
RepositoryUrl::parse(
|
||||||
|
"git+https://github.com/pypa/sample-namespace-packages.git#lfs=true"
|
||||||
|
)?,
|
||||||
|
RepositoryUrl::parse("git+https://github.com/pypa/sample-namespace-packages.git")?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn repository_url_with_lfs() -> Result<(), DisplaySafeUrlError> {
|
||||||
|
let mut hasher = CacheKeyHasher::new();
|
||||||
|
RepositoryUrl::parse("https://example.com/pypa/sample-namespace-packages.git@2.0.0")?
|
||||||
|
.cache_key(&mut hasher);
|
||||||
|
let repo_url_basic = hasher.finish();
|
||||||
|
|
||||||
|
let mut hasher = CacheKeyHasher::new();
|
||||||
|
RepositoryUrl::parse(
|
||||||
|
"https://user:foo@example.com/pypa/sample-namespace-packages.git@2.0.0#foo=bar",
|
||||||
|
)?
|
||||||
|
.cache_key(&mut hasher);
|
||||||
|
let repo_url_with_fragments = hasher.finish();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
repo_url_basic, repo_url_with_fragments,
|
||||||
|
"repository urls should have the exact cache keys as fragments are removed",
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut hasher = CacheKeyHasher::new();
|
||||||
|
RepositoryUrl::parse(
|
||||||
|
"https://user:foo@example.com/pypa/sample-namespace-packages.git@2.0.0#foo=bar",
|
||||||
|
)?
|
||||||
|
.with_lfs(None)
|
||||||
|
.cache_key(&mut hasher);
|
||||||
|
let git_url_with_fragments = hasher.finish();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
repo_url_with_fragments, git_url_with_fragments,
|
||||||
|
"both structs should have the exact cache keys as fragments are still removed",
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut hasher = CacheKeyHasher::new();
|
||||||
|
RepositoryUrl::parse(
|
||||||
|
"https://user:foo@example.com/pypa/sample-namespace-packages.git@2.0.0#foo=bar",
|
||||||
|
)?
|
||||||
|
.with_lfs(Some(false))
|
||||||
|
.cache_key(&mut hasher);
|
||||||
|
let git_url_with_fragments_and_lfs_false = hasher.finish();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
git_url_with_fragments, git_url_with_fragments_and_lfs_false,
|
||||||
|
"both structs should have the exact cache keys as lfs false should not influence them",
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut hasher = CacheKeyHasher::new();
|
||||||
|
RepositoryUrl::parse(
|
||||||
|
"https://user:foo@example.com/pypa/sample-namespace-packages.git@2.0.0#foo=bar",
|
||||||
|
)?
|
||||||
|
.with_lfs(Some(true))
|
||||||
|
.cache_key(&mut hasher);
|
||||||
|
let git_url_with_fragments_and_lfs_true = hasher.finish();
|
||||||
|
|
||||||
|
assert_ne!(
|
||||||
|
git_url_with_fragments, git_url_with_fragments_and_lfs_true,
|
||||||
|
"both structs should have different cache keys as one has Git LFS enabled",
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-cache"
|
name = "uv-cache"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
@ -34,5 +34,6 @@ rustc-hash = { workspace = true }
|
||||||
same-file = { workspace = true }
|
same-file = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
walkdir = { workspace = true }
|
walkdir = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-cache).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
use crate::Cache;
|
use crate::Cache;
|
||||||
use clap::Parser;
|
use clap::{Parser, ValueHint};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
|
@ -27,7 +27,7 @@ pub struct CacheArgs {
|
||||||
/// `%LOCALAPPDATA%\uv\cache` on Windows.
|
/// `%LOCALAPPDATA%\uv\cache` on Windows.
|
||||||
///
|
///
|
||||||
/// To view the location of the cache directory, run `uv cache dir`.
|
/// To view the location of the cache directory, run `uv cache dir`.
|
||||||
#[arg(global = true, long, env = EnvVars::UV_CACHE_DIR)]
|
#[arg(global = true, long, env = EnvVars::UV_CACHE_DIR, value_hint = ValueHint::DirPath)]
|
||||||
pub cache_dir: Option<PathBuf>,
|
pub cache_dir: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use rustc_hash::FxHashMap;
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
use uv_cache_info::Timestamp;
|
use uv_cache_info::Timestamp;
|
||||||
use uv_fs::{LockedFile, Simplified, cachedir, directories};
|
use uv_fs::{LockedFile, LockedFileError, LockedFileMode, Simplified, cachedir, directories};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_pypi_types::ResolutionMetadata;
|
use uv_pypi_types::ResolutionMetadata;
|
||||||
|
|
||||||
|
|
@ -35,6 +35,17 @@ mod wheel;
|
||||||
/// Must be kept in-sync with the version in [`CacheBucket::to_str`].
|
/// Must be kept in-sync with the version in [`CacheBucket::to_str`].
|
||||||
pub const ARCHIVE_VERSION: u8 = 0;
|
pub const ARCHIVE_VERSION: u8 = 0;
|
||||||
|
|
||||||
|
/// Error locking a cache entry or shard
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
#[error("Could not make the path absolute")]
|
||||||
|
Absolute(#[source] io::Error),
|
||||||
|
#[error("Could not acquire lock")]
|
||||||
|
Acquire(#[from] LockedFileError),
|
||||||
|
}
|
||||||
|
|
||||||
/// A [`CacheEntry`] which may or may not exist yet.
|
/// A [`CacheEntry`] which may or may not exist yet.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CacheEntry(PathBuf);
|
pub struct CacheEntry(PathBuf);
|
||||||
|
|
@ -80,9 +91,14 @@ impl CacheEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acquire the [`CacheEntry`] as an exclusive lock.
|
/// Acquire the [`CacheEntry`] as an exclusive lock.
|
||||||
pub async fn lock(&self) -> Result<LockedFile, io::Error> {
|
pub async fn lock(&self) -> Result<LockedFile, Error> {
|
||||||
fs_err::create_dir_all(self.dir())?;
|
fs_err::create_dir_all(self.dir())?;
|
||||||
LockedFile::acquire(self.path(), self.path().display()).await
|
Ok(LockedFile::acquire(
|
||||||
|
self.path(),
|
||||||
|
LockedFileMode::Exclusive,
|
||||||
|
self.path().display(),
|
||||||
|
)
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,9 +125,14 @@ impl CacheShard {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acquire the cache entry as an exclusive lock.
|
/// Acquire the cache entry as an exclusive lock.
|
||||||
pub async fn lock(&self) -> Result<LockedFile, io::Error> {
|
pub async fn lock(&self) -> Result<LockedFile, Error> {
|
||||||
fs_err::create_dir_all(self.as_ref())?;
|
fs_err::create_dir_all(self.as_ref())?;
|
||||||
LockedFile::acquire(self.join(".lock"), self.display()).await
|
Ok(LockedFile::acquire(
|
||||||
|
self.join(".lock"),
|
||||||
|
LockedFileMode::Exclusive,
|
||||||
|
self.display(),
|
||||||
|
)
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`CacheShard`] as a [`PathBuf`].
|
/// Return the [`CacheShard`] as a [`PathBuf`].
|
||||||
|
|
@ -182,7 +203,7 @@ impl Cache {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acquire a lock that allows removing entries from the cache.
|
/// Acquire a lock that allows removing entries from the cache.
|
||||||
pub fn with_exclusive_lock(self) -> Result<Self, io::Error> {
|
pub async fn with_exclusive_lock(self) -> Result<Self, LockedFileError> {
|
||||||
let Self {
|
let Self {
|
||||||
root,
|
root,
|
||||||
refresh,
|
refresh,
|
||||||
|
|
@ -198,8 +219,12 @@ impl Cache {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let lock_file =
|
let lock_file = LockedFile::acquire(
|
||||||
LockedFile::acquire_blocking(root.join(".lock"), root.simplified_display())?;
|
root.join(".lock"),
|
||||||
|
LockedFileMode::Exclusive,
|
||||||
|
root.simplified_display(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
root,
|
root,
|
||||||
|
|
@ -220,7 +245,11 @@ impl Cache {
|
||||||
lock_file,
|
lock_file,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
match LockedFile::acquire_no_wait(root.join(".lock"), root.simplified_display()) {
|
match LockedFile::acquire_no_wait(
|
||||||
|
root.join(".lock"),
|
||||||
|
LockedFileMode::Exclusive,
|
||||||
|
root.simplified_display(),
|
||||||
|
) {
|
||||||
Some(lock_file) => Ok(Self {
|
Some(lock_file) => Ok(Self {
|
||||||
root,
|
root,
|
||||||
refresh,
|
refresh,
|
||||||
|
|
@ -372,10 +401,8 @@ impl Cache {
|
||||||
self.temp_dir.is_some()
|
self.temp_dir.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the [`Cache`].
|
/// Populate the cache scaffold.
|
||||||
pub fn init(self) -> Result<Self, io::Error> {
|
fn create_base_files(root: &PathBuf) -> io::Result<()> {
|
||||||
let root = &self.root;
|
|
||||||
|
|
||||||
// Create the cache directory, if it doesn't exist.
|
// Create the cache directory, if it doesn't exist.
|
||||||
fs_err::create_dir_all(root)?;
|
fs_err::create_dir_all(root)?;
|
||||||
|
|
||||||
|
|
@ -421,29 +448,66 @@ impl Cache {
|
||||||
.join(".git"),
|
.join(".git"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the [`Cache`].
|
||||||
|
pub async fn init(self) -> Result<Self, Error> {
|
||||||
|
let root = &self.root;
|
||||||
|
|
||||||
|
Self::create_base_files(root)?;
|
||||||
|
|
||||||
// Block cache removal operations from interfering.
|
// Block cache removal operations from interfering.
|
||||||
let lock_file = match LockedFile::acquire_shared_blocking(
|
let lock_file = match LockedFile::acquire(
|
||||||
root.join(".lock"),
|
root.join(".lock"),
|
||||||
|
LockedFileMode::Shared,
|
||||||
root.simplified_display(),
|
root.simplified_display(),
|
||||||
) {
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(lock_file) => Some(Arc::new(lock_file)),
|
Ok(lock_file) => Some(Arc::new(lock_file)),
|
||||||
Err(err) if err.kind() == io::ErrorKind::Unsupported => {
|
Err(err)
|
||||||
|
if err
|
||||||
|
.as_io_error()
|
||||||
|
.is_some_and(|err| err.kind() == io::ErrorKind::Unsupported) =>
|
||||||
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Shared locking is not supported by the current platform or filesystem, \
|
"Shared locking is not supported by the current platform or filesystem, \
|
||||||
reduced parallel process safety with `uv cache clean` and `uv cache prune`."
|
reduced parallel process safety with `uv cache clean` and `uv cache prune`."
|
||||||
);
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
root: std::path::absolute(root)?,
|
root: std::path::absolute(root).map_err(Error::Absolute)?,
|
||||||
lock_file,
|
lock_file,
|
||||||
..self
|
..self
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize the [`Cache`], assuming that there are no other uv processes running.
|
||||||
|
pub fn init_no_wait(self) -> Result<Option<Self>, Error> {
|
||||||
|
let root = &self.root;
|
||||||
|
|
||||||
|
Self::create_base_files(root)?;
|
||||||
|
|
||||||
|
// Block cache removal operations from interfering.
|
||||||
|
let Some(lock_file) = LockedFile::acquire_no_wait(
|
||||||
|
root.join(".lock"),
|
||||||
|
LockedFileMode::Shared,
|
||||||
|
root.simplified_display(),
|
||||||
|
) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
Ok(Some(Self {
|
||||||
|
root: std::path::absolute(root).map_err(Error::Absolute)?,
|
||||||
|
lock_file: Some(Arc::new(lock_file)),
|
||||||
|
..self
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
/// Clear the cache, removing all entries.
|
/// Clear the cache, removing all entries.
|
||||||
pub fn clear(self, reporter: Box<dyn CleanReporter>) -> Result<Removal, io::Error> {
|
pub fn clear(self, reporter: Box<dyn CleanReporter>) -> Result<Removal, io::Error> {
|
||||||
// Remove everything but `.lock`, Windows does not allow removal of a locked file
|
// Remove everything but `.lock`, Windows does not allow removal of a locked file
|
||||||
|
|
@ -478,7 +542,7 @@ impl Cache {
|
||||||
/// Remove a package from the cache.
|
/// Remove a package from the cache.
|
||||||
///
|
///
|
||||||
/// Returns the number of entries removed from the cache.
|
/// Returns the number of entries removed from the cache.
|
||||||
pub fn remove(&self, name: &PackageName) -> Result<Removal, io::Error> {
|
pub fn remove(&self, name: &PackageName) -> io::Result<Removal> {
|
||||||
// Collect the set of referenced archives.
|
// Collect the set of referenced archives.
|
||||||
let references = self.find_archive_references()?;
|
let references = self.find_archive_references()?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ pub enum WheelCache<'a> {
|
||||||
Path(&'a DisplaySafeUrl),
|
Path(&'a DisplaySafeUrl),
|
||||||
/// An editable dependency, which we key by URL.
|
/// An editable dependency, which we key by URL.
|
||||||
Editable(&'a DisplaySafeUrl),
|
Editable(&'a DisplaySafeUrl),
|
||||||
/// A Git dependency, which we key by URL and SHA.
|
/// A Git dependency, which we key by URL (including LFS state), SHA.
|
||||||
///
|
///
|
||||||
/// Note that this variant only exists for source distributions; wheels can't be delivered
|
/// Note that this variant only exists for source distributions; wheels can't be delivered
|
||||||
/// through Git.
|
/// through Git.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-cli"
|
name = "uv-cli"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-cli).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -366,6 +366,7 @@ pub fn resolver_options(
|
||||||
exclude_newer_package.unwrap_or_default(),
|
exclude_newer_package.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
link_mode,
|
link_mode,
|
||||||
|
torch_backend: None,
|
||||||
no_build: flag(no_build, build, "build"),
|
no_build: flag(no_build, build, "build"),
|
||||||
no_build_package: Some(no_build_package),
|
no_build_package: Some(no_build_package),
|
||||||
no_binary: flag(no_binary, binary, "binary"),
|
no_binary: flag(no_binary, binary, "binary"),
|
||||||
|
|
@ -495,5 +496,6 @@ pub fn resolver_installer_options(
|
||||||
Some(no_binary_package)
|
Some(no_binary_package)
|
||||||
},
|
},
|
||||||
no_sources: if no_sources { Some(true) } else { None },
|
no_sources: if no_sources { Some(true) } else { None },
|
||||||
|
torch_backend: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-client"
|
name = "uv-client"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-client).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use tracing::{debug, trace};
|
||||||
use url::ParseError;
|
use url::ParseError;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use uv_auth::{AuthMiddleware, Credentials, Indexes, PyxTokenStore};
|
use uv_auth::{AuthMiddleware, Credentials, CredentialsCache, Indexes, PyxTokenStore};
|
||||||
use uv_configuration::{KeyringProviderType, TrustedHost};
|
use uv_configuration::{KeyringProviderType, TrustedHost};
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_pep508::MarkerEnvironment;
|
use uv_pep508::MarkerEnvironment;
|
||||||
|
|
@ -50,7 +50,7 @@ pub const DEFAULT_RETRIES: u32 = 3;
|
||||||
/// Maximum number of redirects to follow before giving up.
|
/// Maximum number of redirects to follow before giving up.
|
||||||
///
|
///
|
||||||
/// This is the default used by [`reqwest`].
|
/// This is the default used by [`reqwest`].
|
||||||
const DEFAULT_MAX_REDIRECTS: u32 = 10;
|
pub const DEFAULT_MAX_REDIRECTS: u32 = 10;
|
||||||
|
|
||||||
/// Selectively skip parts or the entire auth middleware.
|
/// Selectively skip parts or the entire auth middleware.
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
|
@ -78,6 +78,8 @@ pub struct BaseClientBuilder<'a> {
|
||||||
markers: Option<&'a MarkerEnvironment>,
|
markers: Option<&'a MarkerEnvironment>,
|
||||||
platform: Option<&'a Platform>,
|
platform: Option<&'a Platform>,
|
||||||
auth_integration: AuthIntegration,
|
auth_integration: AuthIntegration,
|
||||||
|
/// Global authentication cache for a uv invocation to share credentials across uv clients.
|
||||||
|
credentials_cache: Arc<CredentialsCache>,
|
||||||
indexes: Indexes,
|
indexes: Indexes,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
extra_middleware: Option<ExtraMiddleware>,
|
extra_middleware: Option<ExtraMiddleware>,
|
||||||
|
|
@ -89,6 +91,8 @@ pub struct BaseClientBuilder<'a> {
|
||||||
cross_origin_credential_policy: CrossOriginCredentialsPolicy,
|
cross_origin_credential_policy: CrossOriginCredentialsPolicy,
|
||||||
/// Optional custom reqwest client to use instead of creating a new one.
|
/// Optional custom reqwest client to use instead of creating a new one.
|
||||||
custom_client: Option<Client>,
|
custom_client: Option<Client>,
|
||||||
|
/// uv subcommand in which this client is being used
|
||||||
|
subcommand: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The policy for handling HTTP redirects.
|
/// The policy for handling HTTP redirects.
|
||||||
|
|
@ -100,6 +104,8 @@ pub enum RedirectPolicy {
|
||||||
BypassMiddleware,
|
BypassMiddleware,
|
||||||
/// Handle redirects manually, re-triggering our custom middleware for each request.
|
/// Handle redirects manually, re-triggering our custom middleware for each request.
|
||||||
RetriggerMiddleware,
|
RetriggerMiddleware,
|
||||||
|
/// No redirect for non-cloneable (e.g., streaming) requests with custom redirect logic.
|
||||||
|
NoRedirect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RedirectPolicy {
|
impl RedirectPolicy {
|
||||||
|
|
@ -107,6 +113,7 @@ impl RedirectPolicy {
|
||||||
match self {
|
match self {
|
||||||
Self::BypassMiddleware => reqwest::redirect::Policy::default(),
|
Self::BypassMiddleware => reqwest::redirect::Policy::default(),
|
||||||
Self::RetriggerMiddleware => reqwest::redirect::Policy::none(),
|
Self::RetriggerMiddleware => reqwest::redirect::Policy::none(),
|
||||||
|
Self::NoRedirect => reqwest::redirect::Policy::none(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +143,7 @@ impl Default for BaseClientBuilder<'_> {
|
||||||
markers: None,
|
markers: None,
|
||||||
platform: None,
|
platform: None,
|
||||||
auth_integration: AuthIntegration::default(),
|
auth_integration: AuthIntegration::default(),
|
||||||
|
credentials_cache: Arc::new(CredentialsCache::default()),
|
||||||
indexes: Indexes::new(),
|
indexes: Indexes::new(),
|
||||||
timeout: Duration::from_secs(30),
|
timeout: Duration::from_secs(30),
|
||||||
extra_middleware: None,
|
extra_middleware: None,
|
||||||
|
|
@ -143,11 +151,12 @@ impl Default for BaseClientBuilder<'_> {
|
||||||
redirect_policy: RedirectPolicy::default(),
|
redirect_policy: RedirectPolicy::default(),
|
||||||
cross_origin_credential_policy: CrossOriginCredentialsPolicy::Secure,
|
cross_origin_credential_policy: CrossOriginCredentialsPolicy::Secure,
|
||||||
custom_client: None,
|
custom_client: None,
|
||||||
|
subcommand: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseClientBuilder<'_> {
|
impl<'a> BaseClientBuilder<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
connectivity: Connectivity,
|
connectivity: Connectivity,
|
||||||
native_tls: bool,
|
native_tls: bool,
|
||||||
|
|
@ -166,9 +175,7 @@ impl BaseClientBuilder<'_> {
|
||||||
..Self::default()
|
..Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BaseClientBuilder<'a> {
|
|
||||||
/// Use a custom reqwest client instead of creating a new one.
|
/// Use a custom reqwest client instead of creating a new one.
|
||||||
///
|
///
|
||||||
/// This allows you to provide your own reqwest client with custom configuration.
|
/// This allows you to provide your own reqwest client with custom configuration.
|
||||||
|
|
@ -276,6 +283,26 @@ impl<'a> BaseClientBuilder<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn subcommand(mut self, subcommand: Vec<String>) -> Self {
|
||||||
|
self.subcommand = Some(subcommand);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn credentials_cache(&self) -> &CredentialsCache {
|
||||||
|
&self.credentials_cache
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`CredentialsCache::store_credentials_from_url`].
|
||||||
|
pub fn store_credentials_from_url(&self, url: &DisplaySafeUrl) -> bool {
|
||||||
|
self.credentials_cache.store_credentials_from_url(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`CredentialsCache::store_credentials`].
|
||||||
|
pub fn store_credentials(&self, url: &DisplaySafeUrl, credentials: Credentials) {
|
||||||
|
self.credentials_cache.store_credentials(url, credentials);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_native_tls(&self) -> bool {
|
pub fn is_native_tls(&self) -> bool {
|
||||||
self.native_tls
|
self.native_tls
|
||||||
}
|
}
|
||||||
|
|
@ -324,6 +351,7 @@ impl<'a> BaseClientBuilder<'a> {
|
||||||
dangerous_client,
|
dangerous_client,
|
||||||
raw_dangerous_client,
|
raw_dangerous_client,
|
||||||
timeout,
|
timeout,
|
||||||
|
credentials_cache: self.credentials_cache.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,6 +378,7 @@ impl<'a> BaseClientBuilder<'a> {
|
||||||
raw_client: existing.raw_client.clone(),
|
raw_client: existing.raw_client.clone(),
|
||||||
raw_dangerous_client: existing.raw_dangerous_client.clone(),
|
raw_dangerous_client: existing.raw_dangerous_client.clone(),
|
||||||
timeout: existing.timeout,
|
timeout: existing.timeout,
|
||||||
|
credentials_cache: existing.credentials_cache.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -358,7 +387,7 @@ impl<'a> BaseClientBuilder<'a> {
|
||||||
let mut user_agent_string = format!("uv/{}", version());
|
let mut user_agent_string = format!("uv/{}", version());
|
||||||
|
|
||||||
// Add linehaul metadata.
|
// Add linehaul metadata.
|
||||||
let linehaul = LineHaul::new(self.markers, self.platform);
|
let linehaul = LineHaul::new(self.markers, self.platform, self.subcommand.clone());
|
||||||
if let Ok(output) = serde_json::to_string(&linehaul) {
|
if let Ok(output) = serde_json::to_string(&linehaul) {
|
||||||
let _ = write!(user_agent_string, " {output}");
|
let _ = write!(user_agent_string, " {output}");
|
||||||
}
|
}
|
||||||
|
|
@ -554,6 +583,7 @@ impl<'a> BaseClientBuilder<'a> {
|
||||||
match self.auth_integration {
|
match self.auth_integration {
|
||||||
AuthIntegration::Default => {
|
AuthIntegration::Default => {
|
||||||
let mut auth_middleware = AuthMiddleware::new()
|
let mut auth_middleware = AuthMiddleware::new()
|
||||||
|
.with_cache_arc(self.credentials_cache.clone())
|
||||||
.with_base_client(base_client)
|
.with_base_client(base_client)
|
||||||
.with_indexes(self.indexes.clone())
|
.with_indexes(self.indexes.clone())
|
||||||
.with_keyring(self.keyring.to_provider())
|
.with_keyring(self.keyring.to_provider())
|
||||||
|
|
@ -565,6 +595,7 @@ impl<'a> BaseClientBuilder<'a> {
|
||||||
}
|
}
|
||||||
AuthIntegration::OnlyAuthenticated => {
|
AuthIntegration::OnlyAuthenticated => {
|
||||||
let mut auth_middleware = AuthMiddleware::new()
|
let mut auth_middleware = AuthMiddleware::new()
|
||||||
|
.with_cache_arc(self.credentials_cache.clone())
|
||||||
.with_base_client(base_client)
|
.with_base_client(base_client)
|
||||||
.with_indexes(self.indexes.clone())
|
.with_indexes(self.indexes.clone())
|
||||||
.with_keyring(self.keyring.to_provider())
|
.with_keyring(self.keyring.to_provider())
|
||||||
|
|
@ -608,6 +639,8 @@ pub struct BaseClient {
|
||||||
allow_insecure_host: Vec<TrustedHost>,
|
allow_insecure_host: Vec<TrustedHost>,
|
||||||
/// The number of retries to attempt on transient errors.
|
/// The number of retries to attempt on transient errors.
|
||||||
retries: u32,
|
retries: u32,
|
||||||
|
/// Global authentication cache for a uv invocation to share credentials across uv clients.
|
||||||
|
credentials_cache: Arc<CredentialsCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -659,6 +692,10 @@ impl BaseClient {
|
||||||
}
|
}
|
||||||
builder.build_with_max_retries(self.retries)
|
builder.build_with_max_retries(self.retries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn credentials_cache(&self) -> &CredentialsCache {
|
||||||
|
&self.credentials_cache
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around [`ClientWithMiddleware`] that manages redirects.
|
/// Wrapper around [`ClientWithMiddleware`] that manages redirects.
|
||||||
|
|
@ -695,6 +732,7 @@ impl RedirectClientWithMiddleware {
|
||||||
match self.redirect_policy {
|
match self.redirect_policy {
|
||||||
RedirectPolicy::BypassMiddleware => self.client.execute(req).await,
|
RedirectPolicy::BypassMiddleware => self.client.execute(req).await,
|
||||||
RedirectPolicy::RetriggerMiddleware => self.execute_with_redirect_handling(req).await,
|
RedirectPolicy::RetriggerMiddleware => self.execute_with_redirect_handling(req).await,
|
||||||
|
RedirectPolicy::NoRedirect => self.client.execute(req).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use std::fmt::{Display, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use uv_cache::Error as CacheError;
|
||||||
use uv_distribution_filename::{WheelFilename, WheelFilenameError};
|
use uv_distribution_filename::{WheelFilename, WheelFilenameError};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_redacted::DisplaySafeUrl;
|
use uv_redacted::DisplaySafeUrl;
|
||||||
|
|
@ -337,6 +338,9 @@ pub enum ErrorKind {
|
||||||
#[error("Failed to write to the client cache")]
|
#[error("Failed to write to the client cache")]
|
||||||
CacheWrite(#[source] std::io::Error),
|
CacheWrite(#[source] std::io::Error),
|
||||||
|
|
||||||
|
#[error("Failed to acquire lock on the client cache")]
|
||||||
|
CacheLock(#[source] CacheError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
pub use base_client::{
|
pub use base_client::{
|
||||||
AuthIntegration, BaseClient, BaseClientBuilder, DEFAULT_RETRIES, ExtraMiddleware,
|
AuthIntegration, BaseClient, BaseClientBuilder, DEFAULT_MAX_REDIRECTS, DEFAULT_RETRIES,
|
||||||
RedirectClientWithMiddleware, RequestBuilder, RetryParsingError, UvRetryableStrategy,
|
ExtraMiddleware, RedirectClientWithMiddleware, RedirectPolicy, RequestBuilder,
|
||||||
is_transient_network_error,
|
RetryParsingError, UvRetryableStrategy, is_transient_network_error,
|
||||||
};
|
};
|
||||||
pub use cached_client::{CacheControl, CachedClient, CachedClientError, DataWithCachePolicy};
|
pub use cached_client::{CacheControl, CachedClient, CachedClientError, DataWithCachePolicy};
|
||||||
pub use error::{Error, ErrorKind, WrappedReqwestError};
|
pub use error::{Error, ErrorKind, WrappedReqwestError};
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ use uv_version::version;
|
||||||
pub struct Installer {
|
pub struct Installer {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub version: Option<String>,
|
pub version: Option<String>,
|
||||||
|
pub subcommand: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
|
@ -63,7 +64,11 @@ pub struct LineHaul {
|
||||||
impl LineHaul {
|
impl LineHaul {
|
||||||
/// Initializes Linehaul information based on PEP 508 markers.
|
/// Initializes Linehaul information based on PEP 508 markers.
|
||||||
#[instrument(name = "linehaul", skip_all)]
|
#[instrument(name = "linehaul", skip_all)]
|
||||||
pub fn new(markers: Option<&MarkerEnvironment>, platform: Option<&Platform>) -> Self {
|
pub fn new(
|
||||||
|
markers: Option<&MarkerEnvironment>,
|
||||||
|
platform: Option<&Platform>,
|
||||||
|
subcommand: Option<Vec<String>>,
|
||||||
|
) -> Self {
|
||||||
// https://github.com/pypa/pip/blob/24.0/src/pip/_internal/network/session.py#L87
|
// https://github.com/pypa/pip/blob/24.0/src/pip/_internal/network/session.py#L87
|
||||||
let looks_like_ci = [
|
let looks_like_ci = [
|
||||||
EnvVars::BUILD_BUILDID,
|
EnvVars::BUILD_BUILDID,
|
||||||
|
|
@ -123,6 +128,7 @@ impl LineHaul {
|
||||||
installer: Option::from(Installer {
|
installer: Option::from(Installer {
|
||||||
name: Some("uv".to_string()),
|
name: Some("uv".to_string()),
|
||||||
version: Some(version().to_string()),
|
version: Some(version().to_string()),
|
||||||
|
subcommand,
|
||||||
}),
|
}),
|
||||||
python: markers.map(|markers| markers.python_full_version().version.to_string()),
|
python: markers.map(|markers| markers.python_full_version().version.to_string()),
|
||||||
implementation: Option::from(Implementation {
|
implementation: Option::from(Implementation {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use tokio::sync::{Mutex, Semaphore};
|
||||||
use tracing::{Instrument, debug, info_span, instrument, trace, warn};
|
use tracing::{Instrument, debug, info_span, instrument, trace, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use uv_auth::{Indexes, PyxTokenStore};
|
use uv_auth::{CredentialsCache, Indexes, PyxTokenStore};
|
||||||
use uv_cache::{Cache, CacheBucket, CacheEntry, WheelCache};
|
use uv_cache::{Cache, CacheBucket, CacheEntry, WheelCache};
|
||||||
use uv_configuration::IndexStrategy;
|
use uv_configuration::IndexStrategy;
|
||||||
use uv_configuration::KeyringProviderType;
|
use uv_configuration::KeyringProviderType;
|
||||||
|
|
@ -148,8 +148,30 @@ impl<'a> RegistryClientBuilder<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> RegistryClient {
|
/// Add all authenticated sources to the cache.
|
||||||
self.index_locations.cache_index_credentials();
|
pub fn cache_index_credentials(&mut self) {
|
||||||
|
for index in self.index_locations.known_indexes() {
|
||||||
|
if let Some(credentials) = index.credentials() {
|
||||||
|
trace!(
|
||||||
|
"Read credentials for index {}",
|
||||||
|
index
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.unwrap_or_else(|| index.url.to_string())
|
||||||
|
);
|
||||||
|
if let Some(root_url) = index.root_url() {
|
||||||
|
self.base_client_builder
|
||||||
|
.store_credentials(&root_url, credentials.clone());
|
||||||
|
}
|
||||||
|
self.base_client_builder
|
||||||
|
.store_credentials(index.raw_url(), credentials);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(mut self) -> RegistryClient {
|
||||||
|
self.cache_index_credentials();
|
||||||
let index_urls = self.index_locations.index_urls();
|
let index_urls = self.index_locations.index_urls();
|
||||||
|
|
||||||
// Build a base client
|
// Build a base client
|
||||||
|
|
@ -180,8 +202,8 @@ impl<'a> RegistryClientBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Share the underlying client between two different middleware configurations.
|
/// Share the underlying client between two different middleware configurations.
|
||||||
pub fn wrap_existing(self, existing: &BaseClient) -> RegistryClient {
|
pub fn wrap_existing(mut self, existing: &BaseClient) -> RegistryClient {
|
||||||
self.index_locations.cache_index_credentials();
|
self.cache_index_credentials();
|
||||||
let index_urls = self.index_locations.index_urls();
|
let index_urls = self.index_locations.index_urls();
|
||||||
|
|
||||||
// Wrap in any relevant middleware and handle connectivity.
|
// Wrap in any relevant middleware and handle connectivity.
|
||||||
|
|
@ -269,6 +291,10 @@ impl RegistryClient {
|
||||||
self.timeout
|
self.timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn credentials_cache(&self) -> &CredentialsCache {
|
||||||
|
self.client.uncached().credentials_cache()
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the appropriate index URLs for the given [`PackageName`].
|
/// Return the appropriate index URLs for the given [`PackageName`].
|
||||||
fn index_urls_for(
|
fn index_urls_for(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -513,7 +539,7 @@ impl RegistryClient {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _lock = {
|
let _lock = {
|
||||||
let lock_entry = cache_entry.with_file(format!("{package_name}.lock"));
|
let lock_entry = cache_entry.with_file(format!("{package_name}.lock"));
|
||||||
lock_entry.lock().await.map_err(ErrorKind::CacheWrite)?
|
lock_entry.lock().await.map_err(ErrorKind::CacheLock)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = if matches!(index, IndexUrl::Path(_)) {
|
let result = if matches!(index, IndexUrl::Path(_)) {
|
||||||
|
|
@ -1005,7 +1031,7 @@ impl RegistryClient {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _lock = {
|
let _lock = {
|
||||||
let lock_entry = cache_entry.with_file(format!("{}.lock", filename.stem()));
|
let lock_entry = cache_entry.with_file(format!("{}.lock", filename.stem()));
|
||||||
lock_entry.lock().await.map_err(ErrorKind::CacheWrite)?
|
lock_entry.lock().await.map_err(ErrorKind::CacheLock)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let response_callback = async |response: Response| {
|
let response_callback = async |response: Response| {
|
||||||
|
|
@ -1089,7 +1115,7 @@ impl RegistryClient {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _lock = {
|
let _lock = {
|
||||||
let lock_entry = cache_entry.with_file(format!("{}.lock", filename.stem()));
|
let lock_entry = cache_entry.with_file(format!("{}.lock", filename.stem()));
|
||||||
lock_entry.lock().await.map_err(ErrorKind::CacheWrite)?
|
lock_entry.lock().await.map_err(ErrorKind::CacheLock)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Attempt to fetch via a range request.
|
// Attempt to fetch via a range request.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use uv_redacted::DisplaySafeUrl;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn remote_metadata_with_and_without_cache() -> Result<()> {
|
async fn remote_metadata_with_and_without_cache() -> Result<()> {
|
||||||
let cache = Cache::temp()?.init()?;
|
let cache = Cache::temp()?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
||||||
|
|
||||||
// The first run is without cache (the tempdir is empty), the second has the cache from the
|
// The first run is without cache (the tempdir is empty), the second has the cache from the
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ async fn ssl_env_vars() -> Result<()> {
|
||||||
}
|
}
|
||||||
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
||||||
let cache = Cache::temp()?.init()?;
|
let cache = Cache::temp()?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
||||||
let res = client
|
let res = client
|
||||||
.cached_client()
|
.cached_client()
|
||||||
|
|
@ -142,7 +142,7 @@ async fn ssl_env_vars() -> Result<()> {
|
||||||
}
|
}
|
||||||
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
||||||
let cache = Cache::temp()?.init()?;
|
let cache = Cache::temp()?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
||||||
let res = client
|
let res = client
|
||||||
.cached_client()
|
.cached_client()
|
||||||
|
|
@ -171,7 +171,7 @@ async fn ssl_env_vars() -> Result<()> {
|
||||||
}
|
}
|
||||||
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
||||||
let cache = Cache::temp()?.init()?;
|
let cache = Cache::temp()?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
||||||
let res = client
|
let res = client
|
||||||
.cached_client()
|
.cached_client()
|
||||||
|
|
@ -194,7 +194,7 @@ async fn ssl_env_vars() -> Result<()> {
|
||||||
}
|
}
|
||||||
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
||||||
let cache = Cache::temp()?.init()?;
|
let cache = Cache::temp()?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
||||||
let res = client
|
let res = client
|
||||||
.cached_client()
|
.cached_client()
|
||||||
|
|
@ -259,7 +259,7 @@ async fn ssl_env_vars() -> Result<()> {
|
||||||
}
|
}
|
||||||
let (server_task, addr) = start_https_mtls_user_agent_server(&ca_cert, &server_cert).await?;
|
let (server_task, addr) = start_https_mtls_user_agent_server(&ca_cert, &server_cert).await?;
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
||||||
let cache = Cache::temp()?.init()?;
|
let cache = Cache::temp()?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
||||||
let res = client
|
let res = client
|
||||||
.cached_client()
|
.cached_client()
|
||||||
|
|
@ -283,7 +283,7 @@ async fn ssl_env_vars() -> Result<()> {
|
||||||
}
|
}
|
||||||
let (server_task, addr) = start_https_mtls_user_agent_server(&ca_cert, &server_cert).await?;
|
let (server_task, addr) = start_https_mtls_user_agent_server(&ca_cert, &server_cert).await?;
|
||||||
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
|
||||||
let cache = Cache::temp()?.init()?;
|
let cache = Cache::temp()?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
||||||
let res = client
|
let res = client
|
||||||
.cached_client()
|
.cached_client()
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ async fn test_user_agent_has_version() -> Result<()> {
|
||||||
let (server_task, addr) = start_http_user_agent_server().await?;
|
let (server_task, addr) = start_http_user_agent_server().await?;
|
||||||
|
|
||||||
// Initialize uv-client
|
// Initialize uv-client
|
||||||
let cache = Cache::temp()?.init()?;
|
let cache = Cache::temp()?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
|
||||||
|
|
||||||
// Send request to our dummy server
|
// Send request to our dummy server
|
||||||
|
|
@ -57,7 +57,70 @@ async fn test_user_agent_has_version() -> Result<()> {
|
||||||
assert_json_snapshot!(&linehaul.installer, @r#"
|
assert_json_snapshot!(&linehaul.installer, @r#"
|
||||||
{
|
{
|
||||||
"name": "uv",
|
"name": "uv",
|
||||||
"version": "[VERSION]"
|
"version": "[VERSION]",
|
||||||
|
"subcommand": null
|
||||||
|
}
|
||||||
|
"#);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for the server task to complete, to be a good citizen.
|
||||||
|
let _ = server_task.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_user_agent_has_subcommand() -> Result<()> {
|
||||||
|
// Initialize dummy http server
|
||||||
|
let (server_task, addr) = start_http_user_agent_server().await?;
|
||||||
|
|
||||||
|
// Initialize uv-client
|
||||||
|
let cache = Cache::temp()?.init().await?;
|
||||||
|
let client = RegistryClientBuilder::new(
|
||||||
|
BaseClientBuilder::default().subcommand(vec!["foo".to_owned(), "bar".to_owned()]),
|
||||||
|
cache,
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Send request to our dummy server
|
||||||
|
let url = DisplaySafeUrl::from_str(&format!("http://{addr}"))?;
|
||||||
|
let res = client
|
||||||
|
.cached_client()
|
||||||
|
.uncached()
|
||||||
|
.for_host(&url)
|
||||||
|
.get(Url::from(url))
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Check the HTTP status
|
||||||
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
|
// Check User Agent
|
||||||
|
let body = res.text().await?;
|
||||||
|
|
||||||
|
let (uv_version, uv_linehaul) = body
|
||||||
|
.split_once(' ')
|
||||||
|
.expect("Failed to split User-Agent header");
|
||||||
|
|
||||||
|
// Deserializing Linehaul
|
||||||
|
let linehaul: LineHaul = serde_json::from_str(uv_linehaul)?;
|
||||||
|
|
||||||
|
// Assert linehaul user agent
|
||||||
|
let filters = vec![(version(), "[VERSION]")];
|
||||||
|
with_settings!({
|
||||||
|
filters => filters
|
||||||
|
}, {
|
||||||
|
// Assert uv version
|
||||||
|
assert_snapshot!(uv_version, @"uv/[VERSION]");
|
||||||
|
// Assert linehaul json
|
||||||
|
assert_json_snapshot!(&linehaul.installer, @r#"
|
||||||
|
{
|
||||||
|
"name": "uv",
|
||||||
|
"version": "[VERSION]",
|
||||||
|
"subcommand": [
|
||||||
|
"foo",
|
||||||
|
"bar"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
"#);
|
"#);
|
||||||
});
|
});
|
||||||
|
|
@ -89,7 +152,7 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Initialize uv-client
|
// Initialize uv-client
|
||||||
let cache = Cache::temp()?.init()?;
|
let cache = Cache::temp()?.init().await?;
|
||||||
let mut builder =
|
let mut builder =
|
||||||
RegistryClientBuilder::new(BaseClientBuilder::default(), cache).markers(&markers);
|
RegistryClientBuilder::new(BaseClientBuilder::default(), cache).markers(&markers);
|
||||||
|
|
||||||
|
|
@ -152,11 +215,12 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
|
||||||
assert_json_snapshot!(&linehaul, {
|
assert_json_snapshot!(&linehaul, {
|
||||||
".distro" => "[distro]",
|
".distro" => "[distro]",
|
||||||
".ci" => "[ci]"
|
".ci" => "[ci]"
|
||||||
}, @r###"
|
}, @r#"
|
||||||
{
|
{
|
||||||
"installer": {
|
"installer": {
|
||||||
"name": "uv",
|
"name": "uv",
|
||||||
"version": "[VERSION]"
|
"version": "[VERSION]",
|
||||||
|
"subcommand": null
|
||||||
},
|
},
|
||||||
"python": "3.12.2",
|
"python": "3.12.2",
|
||||||
"implementation": {
|
"implementation": {
|
||||||
|
|
@ -174,7 +238,7 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
|
||||||
"rustc_version": null,
|
"rustc_version": null,
|
||||||
"ci": "[ci]"
|
"ci": "[ci]"
|
||||||
}
|
}
|
||||||
"###);
|
"#);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assert distro
|
// Assert distro
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-configuration"
|
name = "uv-configuration"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-configuration).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -94,3 +94,32 @@ wheels/
|
||||||
# Virtual environments
|
# Virtual environments
|
||||||
.venv
|
.venv
|
||||||
";
|
";
|
||||||
|
|
||||||
|
/// Setting for Git LFS (Large File Storage) support.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub enum GitLfsSetting {
|
||||||
|
/// Git LFS is disabled (default).
|
||||||
|
#[default]
|
||||||
|
Disabled,
|
||||||
|
/// Git LFS is enabled. Tracks whether it came from an environment variable.
|
||||||
|
Enabled { from_env: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GitLfsSetting {
|
||||||
|
pub fn new(from_arg: Option<bool>, from_env: Option<bool>) -> Self {
|
||||||
|
match (from_arg, from_env) {
|
||||||
|
(Some(true), _) => Self::Enabled { from_env: false },
|
||||||
|
(_, Some(true)) => Self::Enabled { from_env: true },
|
||||||
|
_ => Self::Disabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<GitLfsSetting> for Option<bool> {
|
||||||
|
fn from(setting: GitLfsSetting) -> Self {
|
||||||
|
match setting {
|
||||||
|
GitLfsSetting::Enabled { .. } => Some(true),
|
||||||
|
GitLfsSetting::Disabled => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-console"
|
name = "uv-console"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-console).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-dev"
|
name = "uv-dev"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|
@ -79,4 +79,4 @@ performance-memory-allocator = ["dep:uv-performance-memory-allocator"]
|
||||||
render = ["poloto", "resvg", "tagu"]
|
render = ["poloto", "resvg", "tagu"]
|
||||||
|
|
||||||
[package.metadata.cargo-shear]
|
[package.metadata.cargo-shear]
|
||||||
ignored = ["flate2", "uv-extract", "uv-performance-memory-allocator"]
|
ignored = ["uv-performance-memory-allocator"]
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-dev).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ pub(crate) struct CompileArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn compile(args: CompileArgs) -> anyhow::Result<()> {
|
pub(crate) async fn compile(args: CompileArgs) -> anyhow::Result<()> {
|
||||||
let cache = Cache::try_from(args.cache_args)?.init()?;
|
let cache = Cache::try_from(args.cache_args)?.init().await?;
|
||||||
|
|
||||||
let interpreter = if let Some(python) = args.python {
|
let interpreter = if let Some(python) = args.python {
|
||||||
python
|
python
|
||||||
|
|
|
||||||
|
|
@ -342,31 +342,3 @@ fn emit_possible_options(opt: &clap::Arg, output: &mut String) {
|
||||||
output.push_str(&markdown::to_html(&value));
|
output.push_str(&markdown::to_html(&value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
use uv_static::EnvVars;
|
|
||||||
|
|
||||||
use crate::generate_all::Mode;
|
|
||||||
|
|
||||||
use super::{Args, main};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_generate_cli_reference() -> Result<()> {
|
|
||||||
// Skip this test in CI to avoid redundancy with the dedicated CI job
|
|
||||||
if env::var_os(EnvVars::CI).is_some() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mode = if env::var(EnvVars::UV_UPDATE_SCHEMA).as_deref() == Ok("1") {
|
|
||||||
Mode::Write
|
|
||||||
} else {
|
|
||||||
Mode::Check
|
|
||||||
};
|
|
||||||
main(&Args { mode })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -106,31 +106,3 @@ fn render(var: &str, doc: &str, added_in: Option<&str>) -> String {
|
||||||
format!("### `{var}`\n\n{doc}\n\n")
|
format!("### `{var}`\n\n{doc}\n\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
use uv_static::EnvVars;
|
|
||||||
|
|
||||||
use crate::generate_all::Mode;
|
|
||||||
|
|
||||||
use super::{Args, main};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_generate_env_vars_reference() -> Result<()> {
|
|
||||||
// Skip this test in CI to avoid redundancy with the dedicated CI job
|
|
||||||
if env::var_os(EnvVars::CI).is_some() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mode = if env::var(EnvVars::UV_UPDATE_SCHEMA).as_deref() == Ok("1") {
|
|
||||||
Mode::Write
|
|
||||||
} else {
|
|
||||||
Mode::Check
|
|
||||||
};
|
|
||||||
main(&Args { mode })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -387,31 +387,3 @@ impl Visit for CollectOptionsVisitor {
|
||||||
self.fields.push((name.to_owned(), field));
|
self.fields.push((name.to_owned(), field));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
use uv_static::EnvVars;
|
|
||||||
|
|
||||||
use crate::generate_all::Mode;
|
|
||||||
|
|
||||||
use super::{Args, main};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_generate_options_reference() -> Result<()> {
|
|
||||||
// Skip this test in CI to avoid redundancy with the dedicated CI job
|
|
||||||
if env::var_os(EnvVars::CI).is_some() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mode = if env::var(EnvVars::UV_UPDATE_SCHEMA).as_deref() == Ok("1") {
|
|
||||||
Mode::Write
|
|
||||||
} else {
|
|
||||||
Mode::Check
|
|
||||||
};
|
|
||||||
main(&Args { mode })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use crate::ROOT_DIR;
|
||||||
use crate::generate_all::Mode;
|
use crate::generate_all::Mode;
|
||||||
|
|
||||||
/// Contains current supported targets
|
/// Contains current supported targets
|
||||||
const TARGETS_YML_URL: &str = "https://raw.githubusercontent.com/astral-sh/python-build-standalone/refs/tags/20251120/cpython-unix/targets.yml";
|
const TARGETS_YML_URL: &str = "https://raw.githubusercontent.com/astral-sh/python-build-standalone/refs/tags/20251209/cpython-unix/targets.yml";
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
pub(crate) struct Args {
|
pub(crate) struct Args {
|
||||||
|
|
@ -130,7 +130,7 @@ async fn generate() -> Result<String> {
|
||||||
output.push_str("//! DO NOT EDIT\n");
|
output.push_str("//! DO NOT EDIT\n");
|
||||||
output.push_str("//!\n");
|
output.push_str("//!\n");
|
||||||
output.push_str("//! Generated with `cargo run dev generate-sysconfig-metadata`\n");
|
output.push_str("//! Generated with `cargo run dev generate-sysconfig-metadata`\n");
|
||||||
output.push_str("//! Targets from <https://github.com/astral-sh/python-build-standalone/blob/20251120/cpython-unix/targets.yml>\n");
|
output.push_str("//! Targets from <https://github.com/astral-sh/python-build-standalone/blob/20251209/cpython-unix/targets.yml>\n");
|
||||||
output.push_str("//!\n");
|
output.push_str("//!\n");
|
||||||
|
|
||||||
// Disable clippy/fmt
|
// Disable clippy/fmt
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ pub(crate) async fn list_packages(
|
||||||
args: ListPackagesArgs,
|
args: ListPackagesArgs,
|
||||||
environment: EnvironmentOptions,
|
environment: EnvironmentOptions,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let cache = Cache::try_from(args.cache_args)?.init()?;
|
let cache = Cache::try_from(args.cache_args)?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(
|
let client = RegistryClientBuilder::new(
|
||||||
BaseClientBuilder::default().timeout(environment.http_timeout),
|
BaseClientBuilder::default().timeout(environment.http_timeout),
|
||||||
cache,
|
cache,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ pub(crate) async fn validate_zip(
|
||||||
args: ValidateZipArgs,
|
args: ValidateZipArgs,
|
||||||
environment: EnvironmentOptions,
|
environment: EnvironmentOptions,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let cache = Cache::try_from(args.cache_args)?.init()?;
|
let cache = Cache::try_from(args.cache_args)?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(
|
let client = RegistryClientBuilder::new(
|
||||||
BaseClientBuilder::default().timeout(environment.http_timeout),
|
BaseClientBuilder::default().timeout(environment.http_timeout),
|
||||||
cache,
|
cache,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ pub(crate) async fn wheel_metadata(
|
||||||
args: WheelMetadataArgs,
|
args: WheelMetadataArgs,
|
||||||
environment: EnvironmentOptions,
|
environment: EnvironmentOptions,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let cache = Cache::try_from(args.cache_args)?.init()?;
|
let cache = Cache::try_from(args.cache_args)?.init().await?;
|
||||||
let client = RegistryClientBuilder::new(
|
let client = RegistryClientBuilder::new(
|
||||||
BaseClientBuilder::default().timeout(environment.http_timeout),
|
BaseClientBuilder::default().timeout(environment.http_timeout),
|
||||||
cache,
|
cache,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-dirs"
|
name = "uv-dirs"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-dirs).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-dispatch"
|
name = "uv-dispatch"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-dispatch).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -492,6 +492,7 @@ impl BuildContext for BuildDispatch<'_> {
|
||||||
environment_variables,
|
environment_variables,
|
||||||
build_output,
|
build_output,
|
||||||
self.concurrency.builds,
|
self.concurrency.builds,
|
||||||
|
self.client.credentials_cache(),
|
||||||
self.preview,
|
self.preview,
|
||||||
)
|
)
|
||||||
.boxed_local()
|
.boxed_local()
|
||||||
|
|
@ -504,6 +505,7 @@ impl BuildContext for BuildDispatch<'_> {
|
||||||
source: &'data Path,
|
source: &'data Path,
|
||||||
subdirectory: Option<&'data Path>,
|
subdirectory: Option<&'data Path>,
|
||||||
output_dir: &'data Path,
|
output_dir: &'data Path,
|
||||||
|
sources: SourceStrategy,
|
||||||
build_kind: BuildKind,
|
build_kind: BuildKind,
|
||||||
version_id: Option<&'data str>,
|
version_id: Option<&'data str>,
|
||||||
) -> Result<Option<DistFilename>, BuildDispatchError> {
|
) -> Result<Option<DistFilename>, BuildDispatchError> {
|
||||||
|
|
@ -532,6 +534,7 @@ impl BuildContext for BuildDispatch<'_> {
|
||||||
&output_dir,
|
&output_dir,
|
||||||
None,
|
None,
|
||||||
uv_version::version(),
|
uv_version::version(),
|
||||||
|
sources == SourceStrategy::Enabled,
|
||||||
)?;
|
)?;
|
||||||
DistFilename::WheelFilename(wheel)
|
DistFilename::WheelFilename(wheel)
|
||||||
}
|
}
|
||||||
|
|
@ -540,6 +543,7 @@ impl BuildContext for BuildDispatch<'_> {
|
||||||
&source_tree,
|
&source_tree,
|
||||||
&output_dir,
|
&output_dir,
|
||||||
uv_version::version(),
|
uv_version::version(),
|
||||||
|
sources == SourceStrategy::Enabled,
|
||||||
)?;
|
)?;
|
||||||
DistFilename::SourceDistFilename(source_dist)
|
DistFilename::SourceDistFilename(source_dist)
|
||||||
}
|
}
|
||||||
|
|
@ -549,6 +553,7 @@ impl BuildContext for BuildDispatch<'_> {
|
||||||
&output_dir,
|
&output_dir,
|
||||||
None,
|
None,
|
||||||
uv_version::version(),
|
uv_version::version(),
|
||||||
|
sources == SourceStrategy::Enabled,
|
||||||
)?;
|
)?;
|
||||||
DistFilename::WheelFilename(wheel)
|
DistFilename::WheelFilename(wheel)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-distribution-filename"
|
name = "uv-distribution-filename"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-distribution-filename).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-distribution-types"
|
name = "uv-distribution-types"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-distribution-types).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ use std::sync::{Arc, LazyLock, RwLock};
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::trace;
|
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
use uv_auth::RealmRef;
|
use uv_auth::RealmRef;
|
||||||
use uv_cache_key::CanonicalUrl;
|
use uv_cache_key::CanonicalUrl;
|
||||||
|
|
@ -440,26 +439,6 @@ impl<'a> IndexLocations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add all authenticated sources to the cache.
|
|
||||||
pub fn cache_index_credentials(&self) {
|
|
||||||
for index in self.known_indexes() {
|
|
||||||
if let Some(credentials) = index.credentials() {
|
|
||||||
trace!(
|
|
||||||
"Read credentials for index {}",
|
|
||||||
index
|
|
||||||
.name
|
|
||||||
.as_ref()
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.unwrap_or_else(|| index.url.to_string())
|
|
||||||
);
|
|
||||||
if let Some(root_url) = index.root_url() {
|
|
||||||
uv_auth::store_credentials(&root_url, credentials.clone());
|
|
||||||
}
|
|
||||||
uv_auth::store_credentials(index.raw_url(), credentials);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the Simple API cache control header for an [`IndexUrl`], if configured.
|
/// Return the Simple API cache control header for an [`IndexUrl`], if configured.
|
||||||
pub fn simple_api_cache_control_for(&self, url: &IndexUrl) -> Option<&str> {
|
pub fn simple_api_cache_control_for(&self, url: &IndexUrl) -> Option<&str> {
|
||||||
for index in &self.indexes {
|
for index in &self.indexes {
|
||||||
|
|
|
||||||
|
|
@ -159,9 +159,9 @@ pub enum InstalledVersion<'a> {
|
||||||
Url(&'a DisplaySafeUrl, &'a Version),
|
Url(&'a DisplaySafeUrl, &'a Version),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstalledVersion<'_> {
|
impl<'a> InstalledVersion<'a> {
|
||||||
/// If it is a URL, return its value.
|
/// If it is a URL, return its value.
|
||||||
pub fn url(&self) -> Option<&DisplaySafeUrl> {
|
pub fn url(&self) -> Option<&'a DisplaySafeUrl> {
|
||||||
match self {
|
match self {
|
||||||
Self::Version(_) => None,
|
Self::Version(_) => None,
|
||||||
Self::Url(url, _) => Some(url),
|
Self::Url(url, _) => Some(url),
|
||||||
|
|
@ -169,7 +169,7 @@ impl InstalledVersion<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If it is a version, return its value.
|
/// If it is a version, return its value.
|
||||||
pub fn version(&self) -> &Version {
|
pub fn version(&self) -> &'a Version {
|
||||||
match self {
|
match self {
|
||||||
Self::Version(version) => version,
|
Self::Version(version) => version,
|
||||||
Self::Url(_, version) => version,
|
Self::Url(_, version) => version,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use thiserror::Error;
|
||||||
use uv_cache_key::{CacheKey, CacheKeyHasher};
|
use uv_cache_key::{CacheKey, CacheKeyHasher};
|
||||||
use uv_distribution_filename::DistExtension;
|
use uv_distribution_filename::DistExtension;
|
||||||
use uv_fs::{CWD, PortablePath, PortablePathBuf, relative_to};
|
use uv_fs::{CWD, PortablePath, PortablePathBuf, relative_to};
|
||||||
use uv_git_types::{GitOid, GitReference, GitUrl, GitUrlParseError, OidParseError};
|
use uv_git_types::{GitLfs, GitOid, GitReference, GitUrl, GitUrlParseError, OidParseError};
|
||||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||||
use uv_pep440::VersionSpecifiers;
|
use uv_pep440::VersionSpecifiers;
|
||||||
use uv_pep508::{
|
use uv_pep508::{
|
||||||
|
|
@ -350,6 +350,13 @@ impl Display for Requirement {
|
||||||
if let Some(subdirectory) = subdirectory {
|
if let Some(subdirectory) = subdirectory {
|
||||||
writeln!(f, "#subdirectory={}", subdirectory.display())?;
|
writeln!(f, "#subdirectory={}", subdirectory.display())?;
|
||||||
}
|
}
|
||||||
|
if git.lfs().enabled() {
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"{}lfs=true",
|
||||||
|
if subdirectory.is_some() { "&" } else { "#" }
|
||||||
|
)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RequirementSource::Path { url, .. } => {
|
RequirementSource::Path { url, .. } => {
|
||||||
write!(f, " @ {url}")?;
|
write!(f, " @ {url}")?;
|
||||||
|
|
@ -436,6 +443,9 @@ impl CacheKey for Requirement {
|
||||||
} else {
|
} else {
|
||||||
0u8.cache_key(state);
|
0u8.cache_key(state);
|
||||||
}
|
}
|
||||||
|
if git.lfs().enabled() {
|
||||||
|
1u8.cache_key(state);
|
||||||
|
}
|
||||||
url.cache_key(state);
|
url.cache_key(state);
|
||||||
}
|
}
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
|
|
@ -765,6 +775,13 @@ impl Display for RequirementSource {
|
||||||
if let Some(subdirectory) = subdirectory {
|
if let Some(subdirectory) = subdirectory {
|
||||||
writeln!(f, "#subdirectory={}", subdirectory.display())?;
|
writeln!(f, "#subdirectory={}", subdirectory.display())?;
|
||||||
}
|
}
|
||||||
|
if git.lfs().enabled() {
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"{}lfs=true",
|
||||||
|
if subdirectory.is_some() { "&" } else { "#" }
|
||||||
|
)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Self::Path { url, .. } => {
|
Self::Path { url, .. } => {
|
||||||
write!(f, "{url}")?;
|
write!(f, "{url}")?;
|
||||||
|
|
@ -856,6 +873,11 @@ impl From<RequirementSource> for RequirementSourceWire {
|
||||||
.append_pair("subdirectory", &subdirectory);
|
.append_pair("subdirectory", &subdirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Persist lfs=true in the distribution metadata only when explicitly enabled.
|
||||||
|
if git.lfs().enabled() {
|
||||||
|
url.query_pairs_mut().append_pair("lfs", "true");
|
||||||
|
}
|
||||||
|
|
||||||
// Put the requested reference in the query.
|
// Put the requested reference in the query.
|
||||||
match git.reference() {
|
match git.reference() {
|
||||||
GitReference::Branch(branch) => {
|
GitReference::Branch(branch) => {
|
||||||
|
|
@ -932,6 +954,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
|
||||||
|
|
||||||
let mut reference = GitReference::DefaultBranch;
|
let mut reference = GitReference::DefaultBranch;
|
||||||
let mut subdirectory: Option<PortablePathBuf> = None;
|
let mut subdirectory: Option<PortablePathBuf> = None;
|
||||||
|
let mut lfs = GitLfs::Disabled;
|
||||||
for (key, val) in repository.query_pairs() {
|
for (key, val) in repository.query_pairs() {
|
||||||
match &*key {
|
match &*key {
|
||||||
"tag" => reference = GitReference::Tag(val.into_owned()),
|
"tag" => reference = GitReference::Tag(val.into_owned()),
|
||||||
|
|
@ -940,6 +963,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
|
||||||
"subdirectory" => {
|
"subdirectory" => {
|
||||||
subdirectory = Some(PortablePathBuf::from(val.as_ref()));
|
subdirectory = Some(PortablePathBuf::from(val.as_ref()));
|
||||||
}
|
}
|
||||||
|
"lfs" => lfs = GitLfs::from(val.eq_ignore_ascii_case("true")),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -959,13 +983,22 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
|
||||||
let path = format!("{}@{}", url.path(), rev);
|
let path = format!("{}@{}", url.path(), rev);
|
||||||
url.set_path(&path);
|
url.set_path(&path);
|
||||||
}
|
}
|
||||||
|
let mut frags: Vec<String> = Vec::new();
|
||||||
if let Some(subdirectory) = subdirectory.as_ref() {
|
if let Some(subdirectory) = subdirectory.as_ref() {
|
||||||
url.set_fragment(Some(&format!("subdirectory={subdirectory}")));
|
frags.push(format!("subdirectory={subdirectory}"));
|
||||||
}
|
}
|
||||||
|
// Preserve that we're using Git LFS in the Verbatim Url representations
|
||||||
|
if lfs.enabled() {
|
||||||
|
frags.push("lfs=true".to_string());
|
||||||
|
}
|
||||||
|
if !frags.is_empty() {
|
||||||
|
url.set_fragment(Some(&frags.join("&")));
|
||||||
|
}
|
||||||
|
|
||||||
let url = VerbatimUrl::from_url(url);
|
let url = VerbatimUrl::from_url(url);
|
||||||
|
|
||||||
Ok(Self::Git {
|
Ok(Self::Git {
|
||||||
git: GitUrl::from_fields(repository, reference, precise)?,
|
git: GitUrl::from_fields(repository, reference, precise, lfs)?,
|
||||||
subdirectory: subdirectory.map(Box::<Path>::from),
|
subdirectory: subdirectory.map(Box::<Path>::from),
|
||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use uv_git_types::GitReference;
|
use uv_git_types::{GitLfs, GitReference};
|
||||||
use uv_normalize::ExtraName;
|
use uv_normalize::ExtraName;
|
||||||
use uv_pep508::{MarkerEnvironment, MarkerTree, UnnamedRequirement};
|
use uv_pep508::{MarkerEnvironment, MarkerTree, UnnamedRequirement};
|
||||||
use uv_pypi_types::{Hashes, ParsedUrl};
|
use uv_pypi_types::{Hashes, ParsedUrl};
|
||||||
|
|
@ -75,6 +75,7 @@ impl UnresolvedRequirement {
|
||||||
rev: Option<&str>,
|
rev: Option<&str>,
|
||||||
tag: Option<&str>,
|
tag: Option<&str>,
|
||||||
branch: Option<&str>,
|
branch: Option<&str>,
|
||||||
|
lfs: Option<bool>,
|
||||||
marker: Option<MarkerTree>,
|
marker: Option<MarkerTree>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
#[allow(clippy::manual_map)]
|
#[allow(clippy::manual_map)]
|
||||||
|
|
@ -107,6 +108,11 @@ impl UnresolvedRequirement {
|
||||||
} else {
|
} else {
|
||||||
git
|
git
|
||||||
};
|
};
|
||||||
|
let git = if let Some(lfs) = lfs {
|
||||||
|
git.with_lfs(GitLfs::from(lfs))
|
||||||
|
} else {
|
||||||
|
git
|
||||||
|
};
|
||||||
RequirementSource::Git {
|
RequirementSource::Git {
|
||||||
git,
|
git,
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
|
@ -129,6 +135,9 @@ impl UnresolvedRequirement {
|
||||||
if let Some(git_reference) = git_reference {
|
if let Some(git_reference) = git_reference {
|
||||||
git.url = git.url.with_reference(git_reference);
|
git.url = git.url.with_reference(git_reference);
|
||||||
}
|
}
|
||||||
|
if let Some(lfs) = lfs {
|
||||||
|
git.url = git.url.with_lfs(GitLfs::from(lfs));
|
||||||
|
}
|
||||||
VerbatimParsedUrl {
|
VerbatimParsedUrl {
|
||||||
parsed_url: ParsedUrl::Git(git),
|
parsed_url: ParsedUrl::Git(git),
|
||||||
verbatim: requirement.url.verbatim,
|
verbatim: requirement.url.verbatim,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-distribution"
|
name = "uv-distribution"
|
||||||
version = "0.0.3"
|
version = "0.0.8"
|
||||||
description = "This is an internal component crate of uv"
|
description = "This is an internal component crate of uv"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
@ -24,6 +24,7 @@ uv-configuration = { workspace = true }
|
||||||
uv-distribution-filename = { workspace = true }
|
uv-distribution-filename = { workspace = true }
|
||||||
uv-distribution-types = { workspace = true }
|
uv-distribution-types = { workspace = true }
|
||||||
uv-extract = { workspace = true }
|
uv-extract = { workspace = true }
|
||||||
|
uv-flags = { workspace = true }
|
||||||
uv-fs = { workspace = true, features = ["tokio"] }
|
uv-fs = { workspace = true, features = ["tokio"] }
|
||||||
uv-git = { workspace = true }
|
uv-git = { workspace = true }
|
||||||
uv-git-types = { workspace = true }
|
uv-git-types = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
|
||||||
is unstable and will have frequent breaking changes.
|
is unstable and will have frequent breaking changes.
|
||||||
|
|
||||||
|
This version (0.0.8) is a component of [uv 0.9.18](https://crates.io/crates/uv/0.9.18). The source
|
||||||
|
can be found [here](https://github.com/astral-sh/uv/blob/0.9.18/crates/uv-distribution).
|
||||||
|
|
||||||
See uv's
|
See uv's
|
||||||
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
|
||||||
for details on versioning.
|
for details on versioning.
|
||||||
|
|
|
||||||
|
|
@ -385,6 +385,27 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
.boxed_local()
|
.boxed_local()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// Check that the wheel is compatible with its install target.
|
||||||
|
//
|
||||||
|
// When building a build dependency for a cross-install, the build dependency needs
|
||||||
|
// to install and run on the host instead of the target. In this case the `tags` are already
|
||||||
|
// for the host instead of the target, so this check passes.
|
||||||
|
if !built_wheel.filename.is_compatible(tags) {
|
||||||
|
return if tags.is_cross() {
|
||||||
|
Err(Error::BuiltWheelIncompatibleTargetPlatform {
|
||||||
|
filename: built_wheel.filename,
|
||||||
|
python_platform: tags.python_platform().clone(),
|
||||||
|
python_version: tags.python_version(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::BuiltWheelIncompatibleHostPlatform {
|
||||||
|
filename: built_wheel.filename,
|
||||||
|
python_platform: tags.python_platform().clone(),
|
||||||
|
python_version: tags.python_version(),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Acquire the advisory lock.
|
// Acquire the advisory lock.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _lock = {
|
let _lock = {
|
||||||
|
|
@ -395,7 +416,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
built_wheel.target.file_name().unwrap().to_str().unwrap()
|
built_wheel.target.file_name().unwrap().to_str().unwrap()
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
lock_entry.lock().await.map_err(Error::CacheWrite)?
|
lock_entry.lock().await.map_err(Error::CacheLock)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the wheel was unzipped previously, respect it. Source distributions are
|
// If the wheel was unzipped previously, respect it. Source distributions are
|
||||||
|
|
@ -554,7 +575,11 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
pyproject_toml: &PyProjectToml,
|
pyproject_toml: &PyProjectToml,
|
||||||
) -> Result<Option<RequiresDist>, Error> {
|
) -> Result<Option<RequiresDist>, Error> {
|
||||||
self.builder
|
self.builder
|
||||||
.source_tree_requires_dist(path, pyproject_toml)
|
.source_tree_requires_dist(
|
||||||
|
path,
|
||||||
|
pyproject_toml,
|
||||||
|
self.client.unmanaged.credentials_cache(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -574,7 +599,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _lock = {
|
let _lock = {
|
||||||
let lock_entry = wheel_entry.with_file(format!("{}.lock", filename.stem()));
|
let lock_entry = wheel_entry.with_file(format!("{}.lock", filename.stem()));
|
||||||
lock_entry.lock().await.map_err(Error::CacheWrite)?
|
lock_entry.lock().await.map_err(Error::CacheLock)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create an entry for the HTTP cache.
|
// Create an entry for the HTTP cache.
|
||||||
|
|
@ -745,7 +770,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _lock = {
|
let _lock = {
|
||||||
let lock_entry = wheel_entry.with_file(format!("{}.lock", filename.stem()));
|
let lock_entry = wheel_entry.with_file(format!("{}.lock", filename.stem()));
|
||||||
lock_entry.lock().await.map_err(Error::CacheWrite)?
|
lock_entry.lock().await.map_err(Error::CacheLock)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create an entry for the HTTP cache.
|
// Create an entry for the HTTP cache.
|
||||||
|
|
@ -947,7 +972,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _lock = {
|
let _lock = {
|
||||||
let lock_entry = wheel_entry.with_file(format!("{}.lock", filename.stem()));
|
let lock_entry = wheel_entry.with_file(format!("{}.lock", filename.stem()));
|
||||||
lock_entry.lock().await.map_err(Error::CacheWrite)?
|
lock_entry.lock().await.map_err(Error::CacheLock)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine the last-modified time of the wheel.
|
// Determine the last-modified time of the wheel.
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue