diff --git a/_typos.toml b/_typos.toml index 4af054d17..4152afe98 100644 --- a/_typos.toml +++ b/_typos.toml @@ -4,6 +4,7 @@ extend-exclude = [ "ecosystem/**", "scripts/**/*.in", "crates/uv-build-frontend/src/pipreqs/mapping", + "crates/uv/src/commands/tool/top_packages.txt", ] ignore-hidden = false diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index 890f693fe..b92072c05 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -7831,7 +7831,7 @@ fn preview_features() { show_settings: true, preview: Preview { flags: PreviewFeatures( - PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA | WORKSPACE_DIR, + PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA | WORKSPACE_DIR | TOOL_INSTALL_CONFIRMATION, ), }, python_preference: Managed, @@ -8059,7 +8059,7 @@ fn preview_features() { show_settings: true, preview: Preview { flags: PreviewFeatures( - PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA | WORKSPACE_DIR, + PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA | WORKSPACE_DIR | TOOL_INSTALL_CONFIRMATION, ), }, python_preference: Managed, diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 59272a6fc..fa433d004 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -2362,7 +2362,9 @@ uv tool run [OPTIONS] [COMMAND]

Can be provided multiple times.

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

WARNING: Hosts included in this list will not be verified against the system's certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

-

May also be set with the UV_INSECURE_HOST environment variable.

--build-constraints, --build-constraint, -b build-constraints

Constrain build dependencies using the given requirements files when building source distributions.

+

May also be set with the UV_INSECURE_HOST environment variable.

--approve-all-tool-installs

Automatically approve all tool installations without prompting.

+

When enabled, skips confirmation prompts for installing uncached packages. This is useful for CI/CD environments or when you trust all packages.

+
--build-constraints, --build-constraint, -b build-constraints

Constrain build dependencies using the given requirements files when building source distributions.

Constraints files are requirements.txt-like files that only control the version of a requirement that's installed. However, including a package in a constraints file will not trigger the installation of that package.

May also be set with the UV_BUILD_CONSTRAINT environment variable.

--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 2073d8244..c880fd081 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -2204,6 +2204,73 @@ Accepts both standalone package names (`ruff`) and version specifiers (`ruff<0.5 --- +### `install-prompt` + +Settings for tool install confirmation prompts. + +#### [`approve-all-heuristics`](#install-prompt_approve-all-heuristics) {: #install-prompt_approve-all-heuristics } + + +A list of heuristics to use when deciding whether to show a confirmation prompt. + +Each heuristic checks a different condition. If all enabled heuristics pass (i.e., the +package matches all checks), the prompt is skipped. Available heuristics: +- `top-packages`: Skip prompt if package is in the top Python packages list +- `previously-installed`: Skip prompt if package has been previously approved (not yet implemented) +- `user-allowlist`: Skip prompt if package is in user's allowlist file (not yet implemented) + +Defaults to `["top-packages"]`. + +**Default value**: `["top-packages"]` + +**Type**: `list[str]` + +**Example usage**: + +=== "pyproject.toml" + + ```toml + [tool.uv.install-prompt] + approve-all-heuristics = ["top-packages", "previously-installed"] + ``` +=== "uv.toml" + + ```toml + [install-prompt] + approve-all-heuristics = ["top-packages", "previously-installed"] + ``` + +--- + +#### [`approve-all-tool-installs`](#install-prompt_approve-all-tool-installs) {: #install-prompt_approve-all-tool-installs } + + +Automatically approve all tool installations without prompting. + +When enabled, `uvx` and `uv tool run` will skip confirmation prompts for installing +uncached packages. This is useful for CI/CD environments or when you trust all packages. + +**Default value**: `false` + +**Type**: `bool` + +**Example usage**: + +=== "pyproject.toml" + + ```toml + [tool.uv.install-prompt] + approve-all-tool-installs = true + ``` +=== "uv.toml" + + ```toml + [install-prompt] + approve-all-tool-installs = true + ``` + +--- + ### `pip` Settings that are specific to the `uv pip` command-line interface. diff --git a/scripts/uvx_usage_on_gh/fetch_uvx_usage.py b/scripts/uvx_usage_on_gh/fetch_uvx_usage.py index 54e84a635..eaf26b321 100755 --- a/scripts/uvx_usage_on_gh/fetch_uvx_usage.py +++ b/scripts/uvx_usage_on_gh/fetch_uvx_usage.py @@ -128,7 +128,12 @@ def extract_package_name(match_text: str) -> Optional[str]: package = package.split("@")[0] # Validation checks - if package.startswith("--") or "/" in package or "\\" in package or len(package) < 2: + if ( + package.startswith("--") + or "/" in package + or "\\" in package + or len(package) < 2 + ): return None return package @@ -303,7 +308,9 @@ async def wait_for_rate_limit(rate_limit: RateLimitInfo) -> None: await asyncio.sleep(RATE_LIMIT_DELAY) -def build_size_query(base_query: str, start_bytes: int, end_bytes: Optional[int]) -> str: +def build_size_query( + base_query: str, start_bytes: int, end_bytes: Optional[int] +) -> str: """Build a GitHub Code Search query with size filter.""" if end_bytes is None: return f"{base_query} size:>={start_bytes}" @@ -365,6 +372,7 @@ async def check_packages_batch( Returns: Dictionary mapping package names to their existence status """ + async def check_one(package: str) -> tuple[str, bool]: async with semaphore: async with httpx.AsyncClient() as client: @@ -422,7 +430,7 @@ async def search_uvx_usage( current_rate_limit = RateLimitInfo(None, None) # Size buckets to work around GitHub's 1000 result limit - # It would be way smarter to do this dynamically (query a given size range and do a + # It would be way smarter to do this dynamically (query a given size range and do a # binary/proportional split on the number of results) but I already got this far # so I'm not going to change it for now. markdown_size_buckets = [ @@ -500,7 +508,9 @@ async def search_uvx_usage( packages_to_check = list(set(unknown_packages_queue)) unknown_packages_queue.clear() - logger.info(f"Checking {len(packages_to_check)} unknown packages against PyPI...") + logger.info( + f"Checking {len(packages_to_check)} unknown packages against PyPI..." + ) results = await check_packages_batch(packages_to_check, pypi_cache, semaphore) # Update valid package counts based on results @@ -509,7 +519,9 @@ async def search_uvx_usage( count = all_package_counts.get(package, 0) if count > 0: valid_package_counts[package] = count - logger.debug(f"Added {package} to valid packages ({count} occurrences)") + logger.debug( + f"Added {package} to valid packages ({count} occurrences)" + ) else: logger.warning(f"Package {package} validated but has no count") @@ -639,9 +651,7 @@ def write_top_packages( ) # Sort by count descending, then alphabetically - sorted_packages = sorted( - packages.items(), key=lambda x: (-x[1], x[0]) - ) + sorted_packages = sorted(packages.items(), key=lambda x: (-x[1], x[0])) for package, count in sorted_packages: f.write(f"{package}\n") diff --git a/uv.schema.json b/uv.schema.json index b475bbd7a..f0e18ef33 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -321,6 +321,16 @@ } ] }, + "install-prompt": { + "anyOf": [ + { + "$ref": "#/definitions/InstallPromptOptions" + }, + { + "type": "null" + } + ] + }, "keyring-provider": { "description": "Attempt to use `keyring` for authentication for index URLs.\n\nAt present, only `--keyring-provider subprocess` is supported, which configures uv to\nuse the `keyring` CLI to handle authentication.", "anyOf": [ @@ -1137,6 +1147,37 @@ "description": "The URL of an index to use for fetching packages (e.g., `https://pypi.org/simple`), or a local path.", "type": "string" }, + "InstallPromptHeuristic": { + "type": "string", + "enum": [ + "top-packages", + "previously-installed", + "user-allowlist" + ] + }, + "InstallPromptOptions": { + "description": "Settings for tool install confirmation prompts.", + "type": "object", + "properties": { + "approve-all-heuristics": { + "description": "A list of heuristics to use when deciding whether to show a confirmation prompt.\n\nEach heuristic checks a different condition. If all enabled heuristics pass (i.e., the\npackage matches all checks), the prompt is skipped. Available heuristics:\n- `top-packages`: Skip prompt if package is in the top Python packages list\n- `previously-installed`: Skip prompt if package has been previously approved (not yet implemented)\n- `user-allowlist`: Skip prompt if package is in user's allowlist file (not yet implemented)\n\nDefaults to `[\"top-packages\"]`.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/InstallPromptHeuristic" + } + }, + "approve-all-tool-installs": { + "description": "Automatically approve all tool installations without prompting.\n\nWhen enabled, `uvx` and `uv tool run` will skip confirmation prompts for installing\nuncached packages. This is useful for CI/CD environments or when you trust all packages.", + "type": [ + "boolean", + "null" + ] + } + } + }, "KeyringProviderType": { "description": "Keyring provider type to use for credential lookup.", "oneOf": [