Skip to content

Commit 8e3e7fc

Browse files
authored
feat(cli): improve bundle identifier validation, closes #4589 (#4596)
1 parent 92aca55 commit 8e3e7fc

File tree

5 files changed

+122
-18
lines changed

5 files changed

+122
-18
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"cli.rs": patch
3+
"cli.js": patch
4+
---
5+
6+
Improved bundle identifier validation showing the exact source of the configuration value.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-utils": patch
3+
---
4+
5+
Added `config::parse::read_platform` and `config::parse::get_platform_config_filename`.

core/tauri-utils/src/config/parse.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,34 @@ pub enum ConfigError {
8282
/// [JSON Merge Patch (RFC 7396)]: https://datatracker.ietf.org/doc/html/rfc7396.
8383
pub fn read_from(root_dir: PathBuf) -> Result<Value, ConfigError> {
8484
let mut config: Value = parse_value(root_dir.join("tauri.conf.json"))?;
85+
if let Some(platform_config) = read_platform(root_dir)? {
86+
merge(&mut config, &platform_config);
87+
}
88+
Ok(config)
89+
}
8590

86-
let platform_config_filename = if cfg!(target_os = "macos") {
91+
/// Gets the platform configuration file name.
92+
pub fn get_platform_config_filename() -> &'static str {
93+
if cfg!(target_os = "macos") {
8794
"tauri.macos.conf.json"
8895
} else if cfg!(windows) {
8996
"tauri.windows.conf.json"
9097
} else {
9198
"tauri.linux.conf.json"
92-
};
93-
let platform_config_path = root_dir.join(platform_config_filename);
99+
}
100+
}
101+
102+
/// Reads the platform-specific configuration file from the given root directory if it exists.
103+
///
104+
/// Check [`read_from`] for more information.
105+
pub fn read_platform(root_dir: PathBuf) -> Result<Option<Value>, ConfigError> {
106+
let platform_config_path = root_dir.join(get_platform_config_filename());
94107
if does_supported_extension_exist(&platform_config_path) {
95108
let platform_config: Value = parse_value(platform_config_path)?;
96-
merge(&mut config, &platform_config);
109+
Ok(Some(platform_config))
110+
} else {
111+
Ok(None)
97112
}
98-
Ok(config)
99113
}
100114

101115
/// Check if a supported config file exists at path.

tooling/cli/src/build.rs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
helpers::{
77
app_paths::{app_dir, tauri_dir},
88
command_env,
9-
config::{get as get_config, AppUrl, WindowUrl},
9+
config::{get as get_config, AppUrl, WindowUrl, MERGE_CONFIG_EXTENSION_NAME},
1010
updater_signature::sign_file_from_env_variables,
1111
},
1212
interface::{AppInterface, AppSettings, Interface},
@@ -54,15 +54,22 @@ pub struct Options {
5454
}
5555

5656
pub fn command(mut options: Options) -> Result<()> {
57-
options.config = if let Some(config) = &options.config {
58-
Some(if config.starts_with('{') {
59-
config.to_string()
57+
let (merge_config, merge_config_path) = if let Some(config) = &options.config {
58+
if config.starts_with('{') {
59+
(Some(config.to_string()), None)
6060
} else {
61-
std::fs::read_to_string(&config).with_context(|| "failed to read custom configuration")?
62-
})
61+
(
62+
Some(
63+
std::fs::read_to_string(&config)
64+
.with_context(|| "failed to read custom configuration")?,
65+
),
66+
Some(config.clone()),
67+
)
68+
}
6369
} else {
64-
None
70+
(None, None)
6571
};
72+
options.config = merge_config;
6673

6774
let tauri_path = tauri_dir();
6875
set_current_dir(&tauri_path).with_context(|| "failed to change current working directory")?;
@@ -72,8 +79,19 @@ pub fn command(mut options: Options) -> Result<()> {
7279
let config_guard = config.lock().unwrap();
7380
let config_ = config_guard.as_ref().unwrap();
7481

82+
let bundle_identifier_source = match config_.find_bundle_identifier_overwriter() {
83+
Some(source) if source == MERGE_CONFIG_EXTENSION_NAME => {
84+
merge_config_path.unwrap_or_else(|| source.into())
85+
}
86+
Some(source) => source.into(),
87+
None => "tauri.conf.json".into(),
88+
};
89+
7590
if config_.tauri.bundle.identifier == "com.tauri.dev" {
76-
error!("You must change the bundle identifier in `tauri.conf.json > tauri > bundle > identifier`. The default value `com.tauri.dev` is not allowed as it must be unique across applications.");
91+
error!(
92+
"You must change the bundle identifier in `{} > tauri > bundle > identifier`. The default value `com.tauri.dev` is not allowed as it must be unique across applications.",
93+
bundle_identifier_source
94+
);
7795
std::process::exit(1);
7896
}
7997

@@ -84,7 +102,11 @@ pub fn command(mut options: Options) -> Result<()> {
84102
.chars()
85103
.any(|ch| !(ch.is_alphanumeric() || ch == '-' || ch == '.'))
86104
{
87-
error!("You must change the bundle identifier in `tauri.conf.json > tauri > bundle > identifier`. The bundle identifier string must contain only alphanumeric characters (A–Z, a–z, and 0–9), hyphens (-), and periods (.).");
105+
error!(
106+
"The bundle identifier \"{}\" set in `{} > tauri > bundle > identifier`. The bundle identifier string must contain only alphanumeric characters (A–Z, a–z, and 0–9), hyphens (-), and periods (.).",
107+
config_.tauri.bundle.identifier,
108+
bundle_identifier_source
109+
);
88110
std::process::exit(1);
89111
}
90112

tooling/cli/src/helpers/config.rs

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,54 @@ use serde_json::Value as JsonValue;
1010
pub use tauri_utils::config::*;
1111

1212
use std::{
13+
collections::HashMap,
1314
env::set_var,
1415
process::exit,
1516
sync::{Arc, Mutex},
1617
};
1718

18-
pub type ConfigHandle = Arc<Mutex<Option<Config>>>;
19+
pub const MERGE_CONFIG_EXTENSION_NAME: &str = "--config";
20+
21+
pub struct ConfigMetadata {
22+
/// The actual configuration, merged with any extension.
23+
inner: Config,
24+
/// The config extensions (platform-specific config files or the config CLI argument).
25+
/// Maps the extension name to its value.
26+
extensions: HashMap<&'static str, JsonValue>,
27+
}
28+
29+
impl std::ops::Deref for ConfigMetadata {
30+
type Target = Config;
31+
32+
#[inline(always)]
33+
fn deref(&self) -> &Config {
34+
&self.inner
35+
}
36+
}
37+
38+
impl ConfigMetadata {
39+
/// Checks which config is overwriting the bundle identifier.
40+
pub fn find_bundle_identifier_overwriter(&self) -> Option<&'static str> {
41+
for (ext, config) in &self.extensions {
42+
if let Some(identifier) = config
43+
.as_object()
44+
.and_then(|config| config.get("tauri"))
45+
.and_then(|tauri_config| tauri_config.as_object())
46+
.and_then(|tauri_config| tauri_config.get("bundle"))
47+
.and_then(|bundle_config| bundle_config.as_object())
48+
.and_then(|bundle_config| bundle_config.get("identifier"))
49+
.and_then(|id| id.as_str())
50+
{
51+
if identifier == self.inner.tauri.bundle.identifier {
52+
return Some(ext);
53+
}
54+
}
55+
}
56+
None
57+
}
58+
}
59+
60+
pub type ConfigHandle = Arc<Mutex<Option<ConfigMetadata>>>;
1961

2062
pub fn wix_settings(config: WixConfig) -> tauri_bundler::WixSettings {
2163
tauri_bundler::WixSettings {
@@ -63,13 +105,24 @@ fn get_internal(merge_config: Option<&str>, reload: bool) -> crate::Result<Confi
63105
return Ok(config_handle().clone());
64106
}
65107

66-
let mut config = tauri_utils::config::parse::read_from(super::app_paths::tauri_dir())?;
108+
let tauri_dir = super::app_paths::tauri_dir();
109+
let mut config = tauri_utils::config::parse::parse_value(tauri_dir.join("tauri.conf.json"))?;
110+
let mut extensions = HashMap::new();
111+
112+
if let Some(platform_config) = tauri_utils::config::parse::read_platform(tauri_dir)? {
113+
merge(&mut config, &platform_config);
114+
extensions.insert(
115+
tauri_utils::config::parse::get_platform_config_filename(),
116+
platform_config,
117+
);
118+
}
67119

68120
if let Some(merge_config) = merge_config {
69121
let merge_config: JsonValue =
70122
serde_json::from_str(merge_config).with_context(|| "failed to parse config to merge")?;
71123
merge(&mut config, &merge_config);
72-
}
124+
extensions.insert(MERGE_CONFIG_EXTENSION_NAME, merge_config);
125+
};
73126

74127
let schema: JsonValue = serde_json::from_str(include_str!("../../schema.json"))?;
75128
let mut scope = valico::json_schema::Scope::new();
@@ -93,7 +146,11 @@ fn get_internal(merge_config: Option<&str>, reload: bool) -> crate::Result<Confi
93146

94147
let config: Config = serde_json::from_value(config)?;
95148
set_var("TAURI_CONFIG", serde_json::to_string(&config)?);
96-
*config_handle().lock().unwrap() = Some(config);
149+
150+
*config_handle().lock().unwrap() = Some(ConfigMetadata {
151+
inner: config,
152+
extensions,
153+
});
97154

98155
Ok(config_handle().clone())
99156
}

0 commit comments

Comments
 (0)