Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/exit-with-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'tauri': 'patch:bug'
'tauri-runtime-wry': 'patch:bug'
---

Transfer the exit code from the `window.app_handle().exit(1)` call to the `run_return()` result instead of always returning 0.
5 changes: 5 additions & 0 deletions .changes/fix-deterministic-config-serialization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'tauri-utils': 'patch:bug'
---

Serialize the CSP directive map, header source maps and plugin config with sorted keys so writing the processed config (e.g. the `tauri.conf.json` embedded in Android/iOS projects) is deterministic across builds.
5 changes: 5 additions & 0 deletions .changes/fix-deterministic-embedded-assets-codegen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'tauri-codegen': 'patch:bug'
---

Emit embedded assets and CSP script/style hashes in sorted order so `generate_context!` output no longer depends on the filesystem walk order, which varies across machines and broke reproducible builds.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 14 additions & 8 deletions crates/tauri-codegen/src/embedded_assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use proc_macro2::TokenStream;
use quote::{quote, ToTokens, TokenStreamExt};
use sha2::{Digest, Sha256};
use std::{
collections::HashMap,
collections::BTreeMap,
fs::File,
path::{Path, PathBuf},
};
Expand Down Expand Up @@ -76,7 +76,7 @@ pub type EmbeddedAssetsResult<T> = Result<T, EmbeddedAssetsError>;
/// the compressed assets in that application's binary.
#[derive(Default)]
pub struct EmbeddedAssets {
assets: HashMap<AssetKey, (PathBuf, PathBuf)>,
assets: BTreeMap<AssetKey, (PathBuf, PathBuf)>,
csp_hashes: CspHashes,
}

Expand Down Expand Up @@ -158,7 +158,7 @@ pub struct CspHashes {
/// Scripts that are part of the asset collection (JS or MJS files).
pub(crate) scripts: Vec<String>,
/// Inline scripts (`<script>code</script>`). Maps a HTML path to a list of hashes.
pub(crate) inline_scripts: HashMap<String, Vec<String>>,
pub(crate) inline_scripts: BTreeMap<String, Vec<String>>,
/// A list of hashes of the contents of all `style` elements.
pub(crate) styles: Vec<String>,
}
Expand Down Expand Up @@ -266,13 +266,13 @@ impl EmbeddedAssets {

struct CompressState {
csp_hashes: CspHashes,
assets: HashMap<AssetKey, (PathBuf, PathBuf)>,
assets: BTreeMap<AssetKey, (PathBuf, PathBuf)>,
}

let CompressState { assets, csp_hashes } = paths.into_iter().try_fold(
CompressState {
csp_hashes,
assets: HashMap::new(),
assets: BTreeMap::new(),
},
move |mut state, (prefix, entry)| {
let (key, asset) =
Expand Down Expand Up @@ -302,7 +302,7 @@ impl EmbeddedAssets {
settings
}

/// Compress a file and spit out the information in a [`HashMap`] friendly form.
/// Compress a file and spit out the information in a [`BTreeMap`] friendly form.
fn compress_file(
prefix: &Path,
path: &Path,
Expand Down Expand Up @@ -404,12 +404,18 @@ impl ToTokens for EmbeddedAssets {
}

let mut global_hashes = TokenStream::new();
for script_hash in &self.csp_hashes.scripts {
// Sort the hashes so the generated code does not depend on the filesystem
// walk order the assets were collected in, which varies across machines
let mut script_hashes: Vec<_> = self.csp_hashes.scripts.iter().collect();
script_hashes.sort();
for script_hash in script_hashes {
let hash = script_hash.as_str();
global_hashes.append_all(quote!(CspHash::Script(#hash),));
}

for style_hash in &self.csp_hashes.styles {
let mut style_hashes: Vec<_> = self.csp_hashes.styles.iter().collect();
style_hashes.sort();
for style_hash in style_hashes {
let hash = style_hash.as_str();
global_hashes.append_all(quote!(CspHash::Style(#hash),));
}
Expand Down
2 changes: 1 addition & 1 deletion crates/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4304,7 +4304,7 @@ fn handle_event_loop<T: UserEvent>(
let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));

if !should_prevent {
*control_flow = ControlFlow::Exit;
*control_flow = ControlFlow::ExitWithCode(code);
}
}
Message::Window(id, WindowMessage::Close) => {
Expand Down
58 changes: 54 additions & 4 deletions crates/tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use serde_with::skip_serializing_none;
use url::Url;

use std::{
collections::{HashMap, HashSet},
collections::{BTreeMap, HashMap, HashSet},
fmt::{self, Display},
fs::read_to_string,
path::PathBuf,
Expand Down Expand Up @@ -2521,7 +2521,7 @@ impl CspDirectiveSources {

/// A Content-Security-Policy definition.
/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum Csp {
Expand All @@ -2531,6 +2531,24 @@ pub enum Csp {
DirectiveMap(HashMap<String, CspDirectiveSources>),
}

impl Serialize for Csp {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Policy(policy) => serializer.serialize_str(policy),
Self::DirectiveMap(map) => {
// Serialize through `BTreeMap` so the output is deterministic
// see: https://github.com/tauri-apps/tauri/issues/14978
// TODO: Remove this in v3, use a BTreeMap instead of a HashMap
let btree_map: BTreeMap<_, _> = map.iter().collect();
btree_map.serialize(serializer)
}
}
}
}

impl From<HashMap<String, CspDirectiveSources>> for Csp {
fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
Self::DirectiveMap(map)
Expand Down Expand Up @@ -2684,7 +2702,7 @@ pub struct AssetProtocolConfig {
/// definition of a header source
///
/// The header value to a header name
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum HeaderSource {
Expand All @@ -2696,6 +2714,25 @@ pub enum HeaderSource {
Map(HashMap<String, String>),
}

impl Serialize for HeaderSource {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Inline(s) => serializer.serialize_str(s),
Self::List(l) => l.serialize(serializer),
Self::Map(m) => {
// Serialize through `BTreeMap` so the output is deterministic
// see: https://github.com/tauri-apps/tauri/issues/14978
// TODO: Remove this in v3, use a BTreeMap instead of a HashMap
let btree_map: BTreeMap<_, _> = m.iter().collect();
btree_map.serialize(serializer)
}
}
}
}

impl Display for HeaderSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand Down Expand Up @@ -3765,10 +3802,23 @@ pub struct Config {
/// The plugin configs holds a HashMap mapping a plugin name to its configuration object.
///
/// See more: <https://v2.tauri.app/reference/config/#pluginconfig>
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct PluginConfig(pub HashMap<String, JsonValue>);

impl Serialize for PluginConfig {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
// Serialize through `BTreeMap` so the output is deterministic
// see: https://github.com/tauri-apps/tauri/issues/14978
// TODO: Remove this in v3, use a BTreeMap instead of a HashMap
let btree_map: BTreeMap<_, _> = self.0.iter().collect();
btree_map.serialize(serializer)
}
}

/// Implement `ToTokens` for all config structs, allowing a literal `Config` to be built.
///
/// This allows for a build script to output the values in a `Config` to a `TokenStream`, which can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -383,6 +384,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -53,6 +54,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [
Expand All @@ -52,6 +53,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
Pattern {
original: "child2",
Expand All @@ -76,6 +78,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
scope_id: None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -55,6 +56,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -82,6 +84,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -109,6 +112,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -146,6 +150,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -173,6 +178,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -200,6 +206,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -239,6 +246,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -55,6 +56,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -82,6 +84,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -109,6 +112,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -136,6 +140,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down Expand Up @@ -163,6 +168,7 @@ Resolved {
),
],
is_recursive: false,
has_metachars: false,
},
],
webviews: [],
Expand Down
Loading
Loading