From 33754ae5e3740d022483b6164511c5c001a3c24b Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:23:50 +0800 Subject: [PATCH 1/4] fix(cli): unusable empty password private keys (#15022) * fix(cli): unusable empty password private keys * Bump minisign to 0.9 and revert other changes * Lock to `=0.7.3` --- .changes/sign-no-password.md | 6 + Cargo.lock | 149 ++++++++---------- crates/tauri-cli/Cargo.toml | 4 +- .../src/helpers/updater_signature.rs | 22 ++- 4 files changed, 98 insertions(+), 83 deletions(-) create mode 100644 .changes/sign-no-password.md diff --git a/.changes/sign-no-password.md b/.changes/sign-no-password.md new file mode 100644 index 000000000000..c9a30d9381de --- /dev/null +++ b/.changes/sign-no-password.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:bug +"@tauri-apps/cli": patch:bug +--- + +Fix updater signing private keys generated using `tauri signer generate` with empty password can't be used (The keys generated during tauri were broken between v2.9.3 and v2.10.0, you'll need to regenerate them) diff --git a/Cargo.lock b/Cargo.lock index 0166651d0884..059dabbeeaf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -397,7 +397,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -483,7 +483,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -858,7 +858,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1281,7 +1281,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1639,15 +1639,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.95", + "syn 2.0.117", ] -[[package]] -name = "ct-codecs" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10589d1a5e400d61f9f38f12f884cfd080ff345de8f17efda36fe0e4a02aa8" - [[package]] name = "ctor" version = "0.2.9" @@ -1655,7 +1649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1718,7 +1712,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1742,7 +1736,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1753,7 +1747,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1820,7 +1814,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1841,7 +1835,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1851,7 +1845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1864,7 +1858,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -1884,7 +1878,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", "unicode-xid", ] @@ -1996,7 +1990,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -2019,7 +2013,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -2271,7 +2265,7 @@ checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c" dependencies = [ "num-traits", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -2560,7 +2554,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -2685,7 +2679,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -2872,11 +2866,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", - "wasm-bindgen", ] [[package]] @@ -2971,7 +2963,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -3096,7 +3088,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -3602,7 +3594,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -3809,7 +3801,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -4503,7 +4495,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -4608,12 +4600,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "minisign" -version = "0.8.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6bf96cef396a17a96f7600281aa4da9229860b7a082601b1f6db6eaa5f99ee5" +checksum = "b23ef13ff1d745b1e52397daaa247e333c607f3cff96d4df2b798dc252db974b" dependencies = [ - "ct-codecs", - "getrandom 0.3.3", + "getrandom 0.2.15", "rpassword", "scrypt", ] @@ -4693,7 +4684,7 @@ dependencies = [ "napi-derive-backend", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -4706,7 +4697,7 @@ dependencies = [ "proc-macro2", "quote", "semver", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -4949,7 +4940,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -5026,7 +5017,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -5276,7 +5267,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -5383,7 +5374,7 @@ checksum = "e21f680e8c5f1900297d394627d495351b9e37761f7bbf90116bd5eeb6e80967" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -5422,7 +5413,7 @@ checksum = "ed71131e79889e226fb6510b90fa1a4a7495c8fdac43a4f505334fb0f1324e3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -5680,7 +5671,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -5739,7 +5730,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -5924,7 +5915,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -5977,7 +5968,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -6178,7 +6169,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", "version_check", "yansi", ] @@ -6199,7 +6190,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -6605,7 +6596,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -7295,7 +7286,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -7321,7 +7312,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -7511,7 +7502,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -7522,7 +7513,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -7566,7 +7557,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -7626,7 +7617,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -7664,7 +7655,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -7686,7 +7677,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -8051,7 +8042,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -8259,9 +8250,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.95" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -8303,7 +8294,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -8401,7 +8392,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -8666,7 +8657,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.95", + "syn 2.0.117", "tauri-utils", "thiserror 2.0.12", "time", @@ -8744,7 +8735,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", "tauri-codegen", "tauri-utils", ] @@ -9016,7 +9007,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -9027,7 +9018,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -9164,7 +9155,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -9426,7 +9417,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -10059,7 +10050,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -10185,7 +10176,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -10350,7 +10341,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -10361,7 +10352,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -10807,7 +10798,7 @@ dependencies = [ "async-trait", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -11049,7 +11040,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", "synstructure 0.13.1", ] @@ -11071,7 +11062,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -11091,7 +11082,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", "synstructure 0.13.1", ] @@ -11112,7 +11103,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] @@ -11134,7 +11125,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.117", ] [[package]] diff --git a/crates/tauri-cli/Cargo.toml b/crates/tauri-cli/Cargo.toml index 25cef598ea34..5122a0aa16e4 100644 --- a/crates/tauri-cli/Cargo.toml +++ b/crates/tauri-cli/Cargo.toml @@ -70,7 +70,9 @@ jsonschema = { version = "0.33", default-features = false } handlebars = "6" include_dir = "0.7" dirs = "6" -minisign = "0.8" +# 0.7.4 to 0.8.0 were broken, 0.9 pulls in getrandom 0.4 with a high MSRV +# see https://github.com/tauri-apps/tauri/pull/15022 +minisign = "=0.7.3" base64 = "0.22" ureq = { version = "3", default-features = false, features = ["gzip"] } os_info = "3" diff --git a/crates/tauri-cli/src/helpers/updater_signature.rs b/crates/tauri-cli/src/helpers/updater_signature.rs index ba9012359398..c27a2c0e555d 100644 --- a/crates/tauri-cli/src/helpers/updater_signature.rs +++ b/crates/tauri-cli/src/helpers/updater_signature.rs @@ -158,6 +158,8 @@ where } /// Gets the updater secret key from the given private key and password. +/// +/// If `password` is `None`, a password is going to be prompted interactively. pub fn secret_key>( private_key: S, password: Option, @@ -204,16 +206,30 @@ where #[cfg(test)] mod tests { + use super::*; + + // This was encrypted with an empty string const PRIVATE_KEY: &str = "dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5dkpDN09RZm5GeVAzc2RuYlNzWVVJelJRQnNIV2JUcGVXZUplWXZXYXpqUUFBQkFBQUFBQUFBQUFBQUlBQUFBQTZrN2RnWGh5dURxSzZiL1ZQSDdNcktiaHRxczQwMXdQelRHbjRNcGVlY1BLMTBxR2dpa3I3dDE1UTVDRDE4MXR4WlQwa1BQaXdxKy9UU2J2QmVSNXhOQWFDeG1GSVllbUNpTGJQRkhhTnROR3I5RmdUZi90OGtvaGhJS1ZTcjdZU0NyYzhQWlQ5cGM9Cg=="; - // minisign >=0.7.4,<0.8.0 couldn't handle empty passwords. + // minisign >=0.7.4,<0.8.0 couldn't handle empty passwords if the private key is encrypted with an empty string. #[test] fn empty_password_is_valid() { let path = std::env::temp_dir().join("minisign-password-text.txt"); std::fs::write(&path, b"TAURI").expect("failed to write test file"); let secret_key = - super::secret_key(PRIVATE_KEY, Some("".into())).expect("failed to resolve secret key"); - super::sign_file(&secret_key, &path).expect("failed to sign file"); + secret_key(PRIVATE_KEY, Some("".into())).expect("failed to resolve secret key"); + sign_file(&secret_key, &path).expect("failed to sign file"); + } + + // This tests the newly generated keys with empty string password works + // minisign >=0.7.4,<=0.8.0 generate keys unencrypted if the password is empty but is marked encrypted hence unusable + #[test] + fn generate_empty_password_keys_and_use() { + let KeyPair { pk, sk } = generate_key(Some("".to_owned())).unwrap(); + let pk = pub_key(pk).unwrap(); + let sk = secret_key(sk, Some("".into())).unwrap(); + let data = b"TAURI".as_slice(); + sign(Some(&pk), &sk, data, None, None).expect("failed to sign file"); } } From c3cbff3f7430161715f80f82128b345a6f7140c9 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:15:44 +0800 Subject: [PATCH 2/4] fix: resource path handles `./` path differently (#14662) * fix: resource path handles `./` path differently * Setup CI for tauri utils * Wrong job name * Fix tests * Always run tests and don't run doc tests * Add change file * Re-use `test-core` workflow * Format * Avoid path clone by calculating target first * Test tauri-utils first with step instead of matrix * Use `matrix.platform.command` * Document `current_dest` and `current_pattern` * More docs * Merge remote-tracking branch 'upstream/dev' into refactor-resource-path-iter * Test with doc tests * Revert "Test with doc tests" This reverts commit 388bee932865ac21e062e785db68b671184fdd3f. * Merge branch 'dev' into refactor-resource-path-iter * Merge branch 'dev' into refactor-resource-path-iter --- .changes/resource-path-cur-dir.md | 5 + .github/workflows/test-core.yml | 16 ++- crates/tauri-utils/src/resources.rs | 208 +++++++++++++++------------- 3 files changed, 130 insertions(+), 99 deletions(-) create mode 100644 .changes/resource-path-cur-dir.md diff --git a/.changes/resource-path-cur-dir.md b/.changes/resource-path-cur-dir.md new file mode 100644 index 000000000000..be4df9b3a88b --- /dev/null +++ b/.changes/resource-path-cur-dir.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:bug +--- + +Fix resource path handles `./` path differently (e.g. `./some-folder` should be the same as `some-folder`) diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index b84f4e09eb8c..59f393c314ef 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -93,11 +93,23 @@ jobs: prefix-key: v3 save-if: ${{ matrix.features.key == 'all' }} - - name: test + - name: test tauri-utils + if: ${{ !matrix.platform.cross }} + # Using --lib --bins --tests to skip doc tests + run: cargo ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --lib --bins --tests --manifest-path crates/tauri-utils/Cargo.toml + + - name: test tauri-utils (using cross) + if: ${{ matrix.platform.cross }} + # Using --lib --bins --tests to skip doc tests + run: | + cargo install cross --git https://github.com/cross-rs/cross --rev 51f46f296253d8122c927c5bb933e3c4f27cc317 --locked + cross ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --lib --bins --tests --manifest-path crates/tauri-utils/Cargo.toml + + - name: test tauri if: ${{ !matrix.platform.cross }} run: cargo ${{ matrix.features.key == 'no-default' && 'check' || matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --manifest-path crates/tauri/Cargo.toml - - name: test (using cross) + - name: test tauri (using cross) if: ${{ matrix.platform.cross }} run: | cargo install cross --git https://github.com/cross-rs/cross --rev 51f46f296253d8122c927c5bb933e3c4f27cc317 --locked diff --git a/crates/tauri-utils/src/resources.rs b/crates/tauri-utils/src/resources.rs index dfba0746c960..3fd0290b78ac 100644 --- a/crates/tauri-utils/src/resources.rs +++ b/crates/tauri-utils/src/resources.rs @@ -97,9 +97,8 @@ impl<'a> ResourcePaths<'a> { iter: ResourcePathsIter { pattern_iter: PatternIter::Slice(patterns.iter()), allow_walk, - current_pattern: None, - walk_iter: None, - glob_iter: None, + current_dest: None, + current_iter: None, }, } } @@ -110,9 +109,8 @@ impl<'a> ResourcePaths<'a> { iter: ResourcePathsIter { pattern_iter: PatternIter::Map(patterns.iter()), allow_walk, - current_pattern: None, - walk_iter: None, - glob_iter: None, + current_dest: None, + current_iter: None, }, } } @@ -132,122 +130,139 @@ pub struct ResourcePathsIter<'a> { /// whether the resource paths allows directories or not. allow_walk: bool, - /// The (key, value) of map when `pattern_iter` is a [`PatternIter::Map`], + /// The value of map when [`Self::pattern_iter`] is a [`PatternIter::Map`], /// used for determining [`Resource::target`] - current_pattern: Option<(String, PathBuf)>, - - walk_iter: Option, - glob_iter: Option, + current_dest: Option, + /// The iter for the current pattern. The cycle goes like this: + /// [`ResourcePaths::next`] -> [`Self::next`] -> [`Self::pattern_iter::next`] -> [`Self::current_iter::next`] + current_iter: Option, } -impl ResourcePathsIter<'_> { - fn next_glob_iter(&mut self) -> Option> { - let entry = self.glob_iter.as_mut().unwrap().next()?; +#[derive(Debug)] +enum ResourcePathsInnerIter { + Walk { + iter: walkdir::IntoIter, + /// The key of map when [`ResourcePathsIter::pattern_iter`] is a [`PatternIter::Map`], + /// used for determining [`Resource::target`] + current_pattern: Option, + }, + Glob { + iter: glob::Paths, + }, +} - let entry = match entry { - Ok(entry) => entry, - Err(err) => return Some(Err(err.into())), - }; +impl Iterator for ResourcePathsInnerIter { + type Item = crate::Result; - self.next_current_path(normalize(&entry)) + fn next(&mut self) -> Option> { + match self { + ResourcePathsInnerIter::Walk { iter, .. } => Some( + iter + .next()? + .map(|entry| entry.into_path()) + .map_err(Into::into), + ), + ResourcePathsInnerIter::Glob { iter } => Some(iter.next()?.map_err(Into::into)), + } } +} - fn next_walk_iter(&mut self) -> Option> { - let entry = self.walk_iter.as_mut().unwrap().next()?; - - let entry = match entry { - Ok(entry) => entry, - Err(err) => return Some(Err(err.into())), - }; - - self.next_current_path(normalize(entry.path())) +impl ResourcePathsIter<'_> { + fn next_current_iter(&mut self) -> Option> { + let current_iter = self.current_iter.as_mut().unwrap(); + let entry = current_iter.next()?; + + Some(match entry { + Ok(entry) => { + // Skip directories + if entry.is_dir() { + self.next_current_iter()? + } else { + self.resource_from_path(normalize(&entry)) + } + } + Err(error) => Err(error), + }) } - fn resource_from_path(&mut self, path: &Path) -> crate::Result { + fn resource_from_path(&self, path: PathBuf) -> crate::Result { if !path.exists() { - return Err(crate::Error::ResourcePathNotFound(path.to_path_buf())); + return Err(crate::Error::ResourcePathNotFound(path)); } Ok(Resource { - path: path.to_path_buf(), - target: if let Some((pattern, dest)) = &self.current_pattern { - // if processing a directory, preserve directory structure under current_dest - if self.walk_iter.is_some() { - dest.join(path.strip_prefix(pattern).unwrap_or(path)) - } else if dest.components().count() == 0 { - // if current_dest is empty while processing a file pattern or glob - // we preserve the file name as it is - PathBuf::from(path.file_name().unwrap()) - } else if self.glob_iter.is_some() { - // if processing a glob and current_dest is not empty - // we put all globbed paths under current_dest - // preserving the file name as it is - dest.join(path.file_name().unwrap()) - } else { - dest.clone() + target: if let Some(dest) = &self.current_dest { + match &self.current_iter { + Some(current_iter) => match current_iter { + // if processing a directory, preserve directory structure under current_dest + ResourcePathsInnerIter::Walk { + current_pattern, .. + } => { + if let Some(pattern) = current_pattern { + dest.join(path.strip_prefix(pattern).unwrap_or(&path)) + } else { + dest.join(&path) + } + } + // if processing a glob and current_dest is not empty + // we put all globbed paths under current_dest + // preserving the file name as it is + ResourcePathsInnerIter::Glob { .. } => dest.join(path.file_name().unwrap()), + }, + None => dest.clone(), } } else { - // If `pattern_iter` is a [`PatternIter::Slice`] - resource_relpath(path) + // If [`ResourcePathsIter::pattern_iter`] is a [`PatternIter::Slice`] + resource_relpath(&path) }, + path, }) } - fn next_current_path(&mut self, path: PathBuf) -> Option> { - let is_dir = path.is_dir(); - - if is_dir { - if self.glob_iter.is_some() { - return self.next(); - } - - if !self.allow_walk { - return Some(Err(crate::Error::NotAllowedToWalkDir(path.to_path_buf()))); - } - - if self.walk_iter.is_none() { - self.walk_iter = Some(WalkDir::new(&path).into_iter()); - } - - match self.next_walk_iter() { - Some(resource) => Some(resource), - None => { - self.walk_iter = None; - self.next() - } - } - } else { - Some(self.resource_from_path(&path)) - } - } - fn next_pattern(&mut self) -> Option> { - self.current_pattern = None; + self.current_dest = None; let pattern = match &mut self.pattern_iter { PatternIter::Slice(iter) => iter.next()?, PatternIter::Map(iter) => { let (pattern, dest) = iter.next()?; - self.current_pattern = Some((pattern.clone(), resource_relpath(Path::new(dest)))); + self.current_dest = Some(resource_relpath(Path::new(dest))); pattern } }; if pattern.contains('*') { - self.glob_iter = match glob::glob(pattern) { - Ok(glob) => Some(glob), + self.current_iter = match glob::glob(pattern) { + Ok(glob) => Some(ResourcePathsInnerIter::Glob { iter: glob }), Err(error) => return Some(Err(error.into())), }; - match self.next_glob_iter() { + match self.next_current_iter() { Some(r) => return Some(r), None => { - self.glob_iter = None; + self.current_iter = None; return Some(Err(crate::Error::GlobPathNotFound(pattern.clone()))); } } + } else { + let path = normalize(Path::new(pattern)); + if path.is_dir() { + if !self.allow_walk { + return Some(Err(crate::Error::NotAllowedToWalkDir(path))); + } + self.current_iter = Some(ResourcePathsInnerIter::Walk { + iter: WalkDir::new(&path).into_iter(), + current_pattern: if matches!(self.pattern_iter, PatternIter::Map(_)) { + Some(path) + } else { + None + }, + }); + } else { + return Some(self.resource_from_path(path)); + } } - self.next_current_path(normalize(Path::new(pattern))) + self.next_current_iter() } } @@ -263,17 +278,10 @@ impl Iterator for ResourcePathsIter<'_> { type Item = crate::Result; fn next(&mut self) -> Option> { - if self.walk_iter.is_some() { - match self.next_walk_iter() { + if self.current_iter.is_some() { + match self.next_current_iter() { Some(r) => return Some(r), - None => self.walk_iter = None, - } - } - - if self.glob_iter.is_some() { - match self.next_glob_iter() { - Some(r) => return Some(r), - None => self.glob_iter = None, + None => self.current_iter = None, } } @@ -322,6 +330,7 @@ mod tests { "src-tauri/Cargo.toml", "src-tauri/Tauri.toml", "src-tauri/build.rs", + "src-tauri/some-folder/some-file.txt", "src/assets/javascript.svg", "src/assets/tauri.svg", "src/assets/rust.svg", @@ -403,11 +412,11 @@ mod tests { // From `../src/textures/**/*` ( "../src/textures/ground/earth.tex", - "_up_/src/textures/earth.tex", + "_up_/src/textures/ground/earth.tex", ), ( "../src/textures/ground/sand.tex", - "_up_/src/textures/sand.tex", + "_up_/src/textures/ground/sand.tex", ), ("../src/textures/water.tex", "_up_/src/textures/water.tex"), ("../src/textures/fire.tex", "_up_/src/textures/fire.tex"), @@ -483,6 +492,7 @@ mod tests { ("../src/tiles/**/*", "tiles"), ("*.toml", ""), ("*.conf.json", "json"), + ("./some-folder/", "some-target-folder/"), ("../non-existent-file", "asd"), // invalid case ("../non/*", "asd"), // invalid case ]), @@ -511,6 +521,10 @@ mod tests { ("Cargo.toml", "Cargo.toml"), ("Tauri.toml", "Tauri.toml"), ("tauri.conf.json", "json/tauri.conf.json"), + ( + "some-folder/some-file.txt", + "some-target-folder/some-file.txt", + ), ]); assert_eq!(resources.len(), expected.len()); @@ -584,7 +598,7 @@ mod tests { .iter() .collect::>(); - assert_eq!(resources.len(), 4); + assert_eq!(resources.len(), 5); assert!(resources.iter().all(|r| r.is_err())); @@ -617,7 +631,7 @@ mod tests { .iter() .filter(|r| matches!(r, Err(crate::Error::GlobPathNotFound(_)))) .count(), - 1 + 2 ); } } From 52cf195b78e0b78f02d8aae997116c620355b095 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:27:55 +0800 Subject: [PATCH 3/4] refactor(cli): reduce some nesting code (#14844) * refactor(cli): reduce some nesting code * Handle all paths and config file change not first * Bring back is_ignore --- crates/tauri-cli/src/dev.rs | 4 +- crates/tauri-cli/src/interface/rust.rs | 77 ++++++++++++++------------ 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/crates/tauri-cli/src/dev.rs b/crates/tauri-cli/src/dev.rs index 3a6d58140547..468aa2f92f9f 100644 --- a/crates/tauri-cli/src/dev.rs +++ b/crates/tauri-cli/src/dev.rs @@ -241,9 +241,7 @@ pub fn setup( .canonicalize() .fs_context("failed to canonicalize path", path.to_path_buf())?; - let ip = options - .host - .unwrap_or_else(|| Ipv4Addr::new(127, 0, 0, 1).into()); + let ip = options.host.unwrap_or_else(|| Ipv4Addr::LOCALHOST.into()); let server_url = builtin_dev_server::start(path, ip, options.port) .context("failed to start builtin dev server")?; diff --git a/crates/tauri-cli/src/interface/rust.rs b/crates/tauri-cli/src/interface/rust.rs index d6026b3b24dd..9bd46c585d00 100644 --- a/crates/tauri-cli/src/interface/rust.rs +++ b/crates/tauri-cli/src/interface/rust.rs @@ -29,7 +29,7 @@ use tauri_utils::config::{parse::is_configuration_file, DeepLinkProtocol, Runner use super::{AppSettings, DevProcess, ExitReason}; use crate::{ - error::{Context, Error, ErrorExt}, + error::{bail, Context, Error, ErrorExt}, helpers::{ app_paths::Dirs, config::{nsis_settings, reload_config, wix_settings, BundleResources, Config, ConfigMetadata}, @@ -144,9 +144,10 @@ impl Rust { } }) .unwrap(); + let manifest_path = tauri_dir.join("Cargo.toml"); watcher - .watch(tauri_dir.join("Cargo.toml"), RecursiveMode::NonRecursive) - .with_context(|| format!("failed to watch {}", tauri_dir.join("Cargo.toml").display()))?; + .watch(&manifest_path, RecursiveMode::NonRecursive) + .with_context(|| format!("failed to watch {}", manifest_path.display()))?; let (manifest, modified) = rewrite_manifest(config, tauri_dir)?; if modified { // Wait for the modified event so we don't trigger a re-build later on @@ -555,43 +556,47 @@ impl Rust { } } - loop { - if let Ok(events) = rx.recv() { - for event in events { - if event.kind.is_access() { - continue; - } + while let Ok(events) = rx.recv() { + let paths: Vec = events + .into_iter() + .filter(|event| !event.kind.is_access()) + .flat_map(|event| event.event.paths) + .filter(|path| !ignore_matcher.is_ignore(path, path.is_dir())) + .collect(); - if let Some(event_path) = event.paths.first() { - if !ignore_matcher.is_ignore(event_path, event_path.is_dir()) { - if is_configuration_file(self.app_settings.target_platform, event_path) - && reload_config(config, merge_configs, dirs.tauri).is_ok() - { - let (manifest, modified) = rewrite_manifest(config, dirs.tauri)?; - if modified { - *self.app_settings.manifest.lock().unwrap() = manifest; - // no need to run the watcher logic, the manifest was modified - // and it will trigger the watcher again - continue; - } - } - - log::info!( - "File {} changed. Rebuilding application...", - display_path(event_path.strip_prefix(dirs.frontend).unwrap_or(event_path)) - ); - - child.kill().context("failed to kill app process")?; - - // wait for the process to exit - // note that on mobile, kill() already waits for the process to exit (duct implementation) - let _ = child.wait(); - child = run(self, config)?; - } - } + let config_file_changed = paths + .iter() + .any(|path| is_configuration_file(self.app_settings.target_platform, path)); + if config_file_changed && reload_config(config, merge_configs, dirs.tauri).is_ok() { + let (manifest, modified) = rewrite_manifest(config, dirs.tauri)?; + if modified { + *self.app_settings.manifest.lock().unwrap() = manifest; + // no need to run the watcher logic, the manifest was modified + // and it will trigger the watcher again + continue; } } + + let Some(first_changed_path) = paths.first() else { + continue; + }; + + log::info!( + "File {} changed. Rebuilding application...", + display_path( + first_changed_path + .strip_prefix(dirs.frontend) + .unwrap_or(first_changed_path) + ) + ); + + child.kill().context("failed to kill app process")?; + // wait for the process to exit + // note that on mobile, kill() already waits for the process to exit (duct implementation) + let _ = child.wait(); + child = run(self, config)?; } + bail!("File watcher exited unexpectedly") } } From 3a65cc6885ea61e35dc5be23b229043ab6e92372 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Tue, 3 Mar 2026 22:37:10 +0800 Subject: [PATCH 4/4] fix(test): disable `resolve_resource_dir` on Android (#15026) --- crates/tauri-utils/src/platform.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/tauri-utils/src/platform.rs b/crates/tauri-utils/src/platform.rs index 6eaaf3ffe38a..4ce3d2fac403 100644 --- a/crates/tauri-utils/src/platform.rs +++ b/crates/tauri-utils/src/platform.rs @@ -398,6 +398,7 @@ mod tests { use crate::{Env, PackageInfo}; #[test] + #[cfg(not(target_os = "android"))] fn resolve_resource_dir() { let package_info = PackageInfo { name: "MyApp".into(),