fix: relock importers on their own tag, not the bare dependency tag (#11359)

* fix: relock importers on their own tag, not the bare dependency tag

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test: pin the tag of relocks triggered by a changed import

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-09-25 17:32:25 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 53a5cfd17a
commit a1abb36d9f
4 changed files with 73 additions and 14 deletions
+53
View File
@@ -539,4 +539,57 @@ def main():
.await;
Ok(())
}
#[sqlx::test(fixtures("base"))]
async fn relock_of_importer_runs_on_its_tag(db: Pool<Postgres>) -> 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(())
}
}
+1 -13
View File
@@ -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<String, Box<serde_json::value::RawValue>> = HashMap::new();
if let Some(dm) = ns.deployment_message {
+16
View File
@@ -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<String>, language: &ScriptLang) -> Option<String> {
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,
@@ -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,