diff --git a/backend/tests/dependency_map.rs b/backend/tests/dependency_map.rs index f4b74525fb..59895b7a3a 100644 --- a/backend/tests/dependency_map.rs +++ b/backend/tests/dependency_map.rs @@ -539,4 +539,57 @@ def main(): .await; Ok(()) } + + #[sqlx::test(fixtures("base"))] + async fn relock_of_importer_runs_on_its_tag(db: Pool) -> anyhow::Result<()> { + for (path, hash, tag) in [ + ("f/tags/tagged", 7_000_001_i64, Some("mytag")), + ("f/tags/plain", 7_000_002_i64, None), + ] { + sqlx::query( + "INSERT INTO script(workspace_id, created_by, content, schema, summary, description, path, hash, language, tag) + VALUES ('test-workspace', 'test-user', 'def main(): pass', '{}', '', '', $1, $2, 'python3', $3)", + ) + .bind(path) + .bind(hash) + .bind(tag) + .execute(&db) + .await?; + } + let importers = ["f/tags/tagged", "f/tags/plain"] + .map( + |p| windmill_dep_map::scoped_dependency_map::DependencyDependent { + importer_path: p.to_string(), + importer_kind: "script".to_string(), + importer_node_ids: None, + }, + ) + .to_vec(); + windmill_dep_map::trigger_dependents::trigger_dependents_to_recompute_dependencies( + "test-workspace", + importers, + None, + None, + "test@windmill.dev", + "test-user", + "u/test-user", + &db, + vec![], + ) + .await?; + + let tags: Vec<(String, String)> = sqlx::query_as( + "SELECT runnable_path, tag FROM v2_job WHERE kind = 'dependencies' ORDER BY runnable_path", + ) + .fetch_all(&db) + .await?; + assert_eq!( + tags, + vec![ + ("f/tags/plain".to_string(), "python3".to_string()), + ("f/tags/tagged".to_string(), "mytag".to_string()), + ] + ); + Ok(()) + } } diff --git a/backend/windmill-api-scripts/src/scripts.rs b/backend/windmill-api-scripts/src/scripts.rs index 631e8606e6..42617b249b 100644 --- a/backend/windmill-api-scripts/src/scripts.rs +++ b/backend/windmill-api-scripts/src/scripts.rs @@ -2858,19 +2858,7 @@ async fn create_script_internal<'c>( tracing::info!("creating script {hash:?} at path {script_path} on workspace {w_id}",); } if needs_lock_gen { - let tag = if ns.dedicated_worker.is_some_and(|x| x) { - Some(windmill_common::worker::dedicated_worker_tag( - &w_id, &ns.path, - )) - } else if ns.tag.as_ref().is_some_and(|x| x.contains("$args[")) { - None - } else if lang == ScriptLang::Bunnative { - // if a custom tag is set for a bunnative script, this prevents the custom tag to be used for the dependency job - // forcing the bundling to run on a worker with the bun tag - None - } else { - ns.tag - }; + let tag = windmill_common::scripts::dependency_job_tag(ns.tag, &lang); let mut args: HashMap> = HashMap::new(); if let Some(dm) = ns.deployment_message { diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs index 628ae2a709..9726065c6f 100644 --- a/backend/windmill-common/src/scripts.rs +++ b/backend/windmill-common/src/scripts.rs @@ -24,6 +24,22 @@ use regex::Regex; use crate::utils::StripPath; +/// The tag a script's dependency job runs on, given the script's own tag; `None` leaves it to +/// `push`'s default (the language tag). Every relock of a script must use this, or a relock +/// lands on a worker pool other than the one its deploy locked on and resolves packages +/// differently. +/// Dedicated workers are handled by `push`, which gives `dedicated_worker` precedence. +pub fn dependency_job_tag(tag: Option, language: &ScriptLang) -> Option { + if tag.as_ref().is_some_and(|x| x.contains("$args[")) { + None + } else if *language == ScriptLang::Bunnative { + // the bundle must be built on a worker with the bun tag, whatever the script's tag + None + } else { + tag + } +} + pub fn extract_workspace_dependencies_annotated_refs( lang: &ScriptLang, code: &str, diff --git a/backend/windmill-dep-map/src/trigger_dependents.rs b/backend/windmill-dep-map/src/trigger_dependents.rs index 51a7fd4b90..d5cfd8e5a4 100644 --- a/backend/windmill-dep-map/src/trigger_dependents.rs +++ b/backend/windmill-dep-map/src/trigger_dependents.rs @@ -66,6 +66,7 @@ pub async fn trigger_dependents_to_recompute_dependencies( ..Default::default() }; + let mut tag = None; let job_payload = match importer_kind.as_str() { "script" => match sqlx::query_scalar!( "SELECT hash FROM script WHERE path = $1 AND workspace_id = $2 AND deleted = false AND archived = false ORDER BY created_at DESC LIMIT 1", @@ -80,6 +81,7 @@ pub async fn trigger_dependents_to_recompute_dependencies( let info = windmill_common::get_script_info_for_hash(None, db, w_id, hash).await?; + tag = windmill_common::scripts::dependency_job_tag(info.tag, &info.language); JobPayload::Dependencies { path: importer_path.clone(), @@ -199,7 +201,7 @@ pub async fn trigger_dependents_to_recompute_dependencies( false, None, true, - Some("dependency".into()), + tag, None, None, None,