From 7b16dafb1dc417536ebb62df8eb66154dd97109d Mon Sep 17 00:00:00 2001 From: montyc1999 <211852937+montyc1999@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:14:56 -0500 Subject: [PATCH] fix(tauri-utils): sort csp/plugin/header config maps during codegen so `generate_context!` is deterministic (#14986) * fix(tauri-utils): sort csp/plugin/header config maps during codegen so generate_context! is deterministic * add comments explaining rationale, and todo for removing the hack in v3 --- .../fix-nondeterministic-context-codegen.md | 7 +++++ crates/tauri-utils/src/config.rs | 26 ++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 .changes/fix-nondeterministic-context-codegen.md diff --git a/.changes/fix-nondeterministic-context-codegen.md b/.changes/fix-nondeterministic-context-codegen.md new file mode 100644 index 000000000..5b4d8237a --- /dev/null +++ b/.changes/fix-nondeterministic-context-codegen.md @@ -0,0 +1,7 @@ +--- +'tauri-utils': 'patch:bug' +--- + +Sort csp/plugin/header configs when generating HashMap constructors so that `generate_context!` is deterministic. + +See: https://github.com/tauri-apps/tauri/issues/14978 for more information \ No newline at end of file diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index 8826e3f51..78f0ed3ec 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -3842,9 +3842,14 @@ mod build { quote!(#prefix::Policy(#policy.into())) } Self::DirectiveMap(list) => { + // Pass a sorted vec so the HashMap constructor is deterministic + // see: https://github.com/tauri-apps/tauri/issues/14978 + // TODO: Remove this in v3, use a BTreeMap instead of a HashMap + let mut sorted: Vec<_> = list.iter().collect(); + sorted.sort_by_key(|(k, _)| *k); let map = map_lit( quote! { ::std::collections::HashMap }, - list, + sorted, str_lit, identity, ); @@ -3900,7 +3905,17 @@ mod build { quote!(#prefix::List(#list)) } Self::Map(m) => { - let map = map_lit(quote! { ::std::collections::HashMap }, m, str_lit, str_lit); + // Pass a sorted vec so the HashMap constructor is deterministic + // see: https://github.com/tauri-apps/tauri/issues/14978 + // TODO: Remove this in v3, use a BTreeMap instead of a HashMap + let mut sorted: Vec<_> = m.iter().collect(); + sorted.sort_by_key(|(k, _)| *k); + let map = map_lit( + quote! { ::std::collections::HashMap }, + sorted, + str_lit, + str_lit, + ); quote!(#prefix::Map(#map)) } }) @@ -4047,9 +4062,14 @@ mod build { impl ToTokens for PluginConfig { fn to_tokens(&self, tokens: &mut TokenStream) { + // Pass a sorted vec so the HashMap constructor is deterministic + // see: https://github.com/tauri-apps/tauri/issues/14978 + // TODO: Remove this in v3, use a BTreeMap instead of a HashMap + let mut sorted: Vec<_> = self.0.iter().collect(); + sorted.sort_by_key(|(k, _)| *k); let config = map_lit( quote! { ::std::collections::HashMap }, - &self.0, + sorted, str_lit, json_value_lit, );