feat(uv): index resolve strategy (#7885)

* fix

* fix

Signed-off-by: pyranota <pyra@duck.com>

* Update backend/windmill-worker/src/python_executor.rs

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* Update backend/windmill-worker/src/python_executor.rs

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

---------

Signed-off-by: pyranota <pyra@duck.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
This commit is contained in:
Pyra
2026-02-11 12:18:51 +01:00
committed by GitHub
parent 92c601860f
commit 097d9288c5
8 changed files with 66 additions and 24 deletions

View File

@@ -135,6 +135,7 @@ use crate::{
worker_utils::ping_job_status,
PyV, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, PIP_EXTRA_INDEX_URL,
PIP_INDEX_URL, PROXY_ENVS, PY_INSTALL_DIR, TRACING_PROXY_CA_CERT_PATH, TZ_ENV, UV_CACHE_DIR,
UV_INDEX_STRATEGY,
};
use windmill_common::client::AuthedClient;
@@ -221,6 +222,9 @@ pub async fn uv_pip_compile(
requirements.to_string()
};
let uv_index_strategy = UV_INDEX_STRATEGY.read().await.clone();
let uv_index_strategy = uv_index_strategy.as_deref().unwrap_or("unsafe-best-match");
let py_version_str = py_version.clone().to_string();
// Include python version to requirements.in
// We need it because same hash based on requirements.in can get calculated even for different python versions
@@ -230,7 +234,7 @@ pub async fn uv_pip_compile(
#[cfg(feature = "enterprise")]
let requirements = replace_pip_secret(conn, w_id, &requirements, worker_name, job_id).await?;
let req_hash = format!("py-{}", calculate_hash(&requirements));
let req_hash = format!("py-{}-{uv_index_strategy}", calculate_hash(&requirements));
if !no_cache {
if let Some(db) = conn.as_sql() {
@@ -271,11 +275,6 @@ pub async fn uv_pip_compile(
"--strip-extras",
"-o",
"requirements.txt",
// Prefer main index over extra
// https://docs.astral.sh/uv/pip/compatibility/#packages-that-exist-on-multiple-indexes
// TODO: Use env variable that can be toggled from UI
"--index-strategy",
"unsafe-best-match",
// Target to /tmp/windmill/cache/uv
"--cache-dir",
UV_CACHE_DIR,
@@ -321,6 +320,7 @@ pub async fn uv_pip_compile(
#[cfg(unix)]
let uv_cmd = UV_PATH.as_str();
let mut child_cmd = Command::new(uv_cmd);
child_cmd
.current_dir(job_dir)
@@ -328,6 +328,7 @@ pub async fn uv_pip_compile(
.env("HOME", HOME_ENV.to_string())
.env("PATH", PATH_ENV.to_string())
.env("UV_PYTHON_INSTALL_DIR", PY_INSTALL_DIR.to_string())
.env("UV_INDEX_STRATEGY", uv_index_strategy)
.envs(PROXY_ENVS.clone())
.args(&args)
.stdout(Stdio::piped())
@@ -1353,6 +1354,9 @@ async fn spawn_uv_install(
py_path: Option<String>,
worker_dir: &str,
) -> Result<Box<dyn TokioChildWrapper>, Error> {
let uv_index_strategy_guard = UV_INDEX_STRATEGY.read().await.clone();
let uv_index_strategy = uv_index_strategy_guard.as_deref().unwrap_or("unsafe-best-match");
if !*DISABLE_NSJAIL {
tracing::info!(
workspace_id = %w_id,
@@ -1375,6 +1379,7 @@ async fn spawn_uv_install(
if *NATIVE_CERT {
vars.push(("UV_NATIVE_TLS", "true"));
}
let _owner;
if let Some(py_path) = py_path.as_ref() {
_owner = format!(
@@ -1385,6 +1390,7 @@ async fn spawn_uv_install(
}
vars.push(("REQ", &req));
vars.push(("TARGET", venv_p));
vars.push(("UV_INDEX_STRATEGY", uv_index_strategy));
std::fs::create_dir_all(venv_p)?;
let nsjail_proto = format!("{req}.config.proto");
@@ -1430,11 +1436,6 @@ async fn spawn_uv_install(
"--no-config",
"--link-mode=copy",
"--system",
// Prefer main index over extra
// https://docs.astral.sh/uv/pip/compatibility/#packages-that-exist-on-multiple-indexes
// TODO: Use env variable that can be toggled from UI
"--index-strategy",
"unsafe-best-match",
"--target",
venv_p,
"--no-cache",
@@ -1464,6 +1465,7 @@ async fn spawn_uv_install(
let mut envs = vec![("PATH", PATH_ENV.as_str())];
envs.push(("HOME", HOME_ENV.as_str()));
envs.push(("UV_INDEX_STRATEGY", uv_index_strategy));
if let Some(url) = pip_index_url.as_ref() {
command_args.extend(["--index-url", url]);