fix(cli): broken v1 updater migration, add TOML support, closes #10508 (#10539)

- make the v1 config migration more resilient by checking null values
- fix "targets: all" incorrectly migrating createUpdaterArtifacts when there's no updater configuration (this is problematic because this targets config is the default)
- migrate Tauri.toml
- add more tests
This commit is contained in:
Lucas Fernandes Nogueira 2024-08-11 23:56:13 -03:00 committed by GitHub
parent 71d00646a9
commit f3837d5b98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 361 additions and 24 deletions

View File

@ -0,0 +1,6 @@
---
"tauri-cli": patch:bug
"@tauri-apps/cli": patch:bug
---
Improve migration tooling by supporting TOML configs, handle nulls and properly check for updater migration.

View File

@ -21,7 +21,11 @@ pub fn migrate(tauri_dir: &Path) -> Result<MigratedConfig> {
tauri_utils_v1::config::parse::parse_value(tauri_dir.join("tauri.conf.json"))
{
let migrated = migrate_config(&mut config)?;
if config_path.extension().map_or(false, |ext| ext == "toml") {
fs::write(&config_path, toml::to_string_pretty(&config)?)?;
} else {
fs::write(&config_path, serde_json::to_string_pretty(&config)?)?;
}
let mut permissions: Vec<PermissionEntry> = vec!["core:default"]
.into_iter()
@ -101,8 +105,12 @@ fn migrate_config(config: &mut Value) -> Result<MigratedConfig> {
}
// system tray
if let Some(tray) = tauri_config.remove("systemTray") {
tauri_config.insert("trayIcon".into(), tray);
if let Some((tray, key)) = tauri_config
.remove("systemTray")
.map(|v| (v, "trayIcon"))
.or_else(|| tauri_config.remove("system-tray").map(|v| (v, "tray-icon")))
{
tauri_config.insert(key.into(), tray);
}
// cli
@ -114,9 +122,21 @@ fn migrate_config(config: &mut Value) -> Result<MigratedConfig> {
process_updater(tauri_config, &mut plugins, &mut migrated)?;
}
config.insert("plugins".into(), plugins.into());
process_bundle(config, &migrated);
process_bundle(config);
// if we have migrated the updater config, let's ensure createUpdaterArtifacts is set
if plugins.contains_key("updater") {
let bundle_config = config
.entry("bundle")
.or_insert_with(|| Value::Object(Default::default()))
.as_object_mut()
.unwrap();
if !bundle_config.contains_key("createUpdaterArtifacts") {
bundle_config.insert("createUpdaterArtifacts".to_owned(), "v1Compatible".into());
}
}
config.insert("plugins".into(), plugins.into());
if let Some(tauri_config) = config.remove("tauri") {
config.insert("app".into(), tauri_config);
@ -129,8 +149,16 @@ fn migrate_config(config: &mut Value) -> Result<MigratedConfig> {
fn process_package_metadata(config: &mut Map<String, Value>) {
if let Some(mut package_config) = config.remove("package") {
if let Some(package_config) = package_config.as_object_mut() {
if let Some(product_name) = package_config.remove("productName") {
config.insert("productName".into(), product_name);
if let Some((product_name, key)) = package_config
.remove("productName")
.map(|v| (v, "productName"))
.or_else(|| {
package_config
.remove("product-name")
.map(|v| (v, "product-name"))
})
{
config.insert(key.into(), product_name);
}
if let Some(version) = package_config.remove("version") {
@ -152,25 +180,45 @@ fn process_package_metadata(config: &mut Map<String, Value>) {
fn process_build(config: &mut Map<String, Value>) {
if let Some(build_config) = config.get_mut("build").and_then(|b| b.as_object_mut()) {
if let Some(dist_dir) = build_config.remove("distDir") {
build_config.insert("frontendDist".into(), dist_dir);
if let Some((dist_dir, key)) = build_config
.remove("distDir")
.map(|v| (v, "frontendDist"))
.or_else(|| {
build_config
.remove("dist-dir")
.map(|v| (v, "frontend-dist"))
})
{
build_config.insert(key.into(), dist_dir);
}
if let Some(dev_path) = build_config.remove("devPath") {
if let Some((dev_path, key)) = build_config
.remove("devPath")
.map(|v| (v, "devUrl"))
.or_else(|| build_config.remove("dev-path").map(|v| (v, "dev-url")))
{
let is_url = url::Url::parse(dev_path.as_str().unwrap_or_default()).is_ok();
if is_url {
build_config.insert("devUrl".into(), dev_path);
build_config.insert(key.into(), dev_path);
}
}
if let Some(with_global_tauri) = build_config.remove("withGlobalTauri") {
if let Some((with_global_tauri, key)) = build_config
.remove("withGlobalTauri")
.map(|v| (v, "withGlobalTauri"))
.or_else(|| {
build_config
.remove("with-global-tauri")
.map(|v| (v, "with-global-tauri"))
})
{
config
.get_mut("tauri")
.and_then(|t| t.as_object_mut())
.map(|t| t.insert("withGlobalTauri".into(), with_global_tauri));
.map(|t| t.insert(key.into(), with_global_tauri));
}
}
}
fn process_bundle(config: &mut Map<String, Value>) {
fn process_bundle(config: &mut Map<String, Value>, migrated: &MigratedConfig) {
let mut license_file = None;
if let Some(mut bundle_config) = config
@ -196,19 +244,22 @@ fn process_bundle(config: &mut Map<String, Value>) {
}
// license file
if let Some(macos) = bundle_config.get_mut("macOS") {
if let Some(license) = macos.as_object_mut().unwrap().remove("license") {
if let Some(macos) = bundle_config
.get_mut("macOS")
.and_then(|v| v.as_object_mut())
{
if let Some(license) = macos.remove("license") {
license_file = Some(license);
}
}
if let Some(windows) = bundle_config.get_mut("windows") {
if let Some(wix) = windows.get_mut("wix") {
if let Some(license_path) = wix.as_object_mut().unwrap().remove("license") {
if let Some(wix) = windows.get_mut("wix").and_then(|v| v.as_object_mut()) {
if let Some(license_path) = wix.remove("license") {
license_file = Some(license_path);
}
}
if let Some(nsis) = windows.get_mut("nsis") {
if let Some(license_path) = nsis.as_object_mut().unwrap().remove("license") {
if let Some(nsis) = windows.get_mut("nsis").and_then(|v| v.as_object_mut()) {
if let Some(license_path) = nsis.remove("license") {
license_file = Some(license_path);
}
}
@ -219,7 +270,7 @@ fn process_bundle(config: &mut Map<String, Value>) {
// Migrate updater from targets to update field
if let Some(targets) = bundle_config.get_mut("targets") {
let shuold_migrate = if let Some(targets) = targets.as_array_mut() {
let should_migrate = if let Some(targets) = targets.as_array_mut() {
// targets: ["updater", ...]
if let Some(index) = targets
.iter()
@ -232,17 +283,19 @@ fn process_bundle(config: &mut Map<String, Value>) {
}
} else if let Some(target) = targets.as_str() {
// targets: "updater"
// targets: "all"
if target == "updater" {
bundle_config.remove("targets");
true
} else {
target == "all"
// note that target == "all" is the default from the v1 tauri CLI
// so we shouldn't bindly force updater bundles to be created
// instead we only migrate if the updater has been migrated
target == "all" && migrated.plugins.contains("updater")
}
} else {
false
};
if shuold_migrate {
if should_migrate {
bundle_config.insert("createUpdaterArtifacts".to_owned(), "v1Compatible".into());
}
}
@ -287,6 +340,18 @@ fn process_security(security: &mut Map<String, Value>) -> Result<()> {
security.insert("csp".into(), csp);
}
// dangerous_remote_domain_ipc_access no longer exists
if let Some(dangerous_remote_domain_ipc_access) = security
.remove("dangerousRemoteDomainIpcAccess")
.or_else(|| security.remove("dangerous-remote-domain-ipc-access"))
{
println!("dangerous remote domain IPC access config ({dangerous_remote_domain_ipc_access:?}) no longer exists, see documentation for capabilities and remote access: https://v2.tauri.app/security/capabilities/#remote-api-access")
}
security
.remove("dangerousUseHttpScheme")
.or_else(|| security.remove("dangerous-use-http-scheme"));
Ok(())
}
@ -797,8 +862,47 @@ mod test {
);
}
#[test]
fn can_migrate_default_config() {
let original = serde_json::to_value(tauri_utils_v1::config::Config::default()).unwrap();
migrate(&original);
}
#[test]
fn can_migrate_api_example_config() {
let original =
serde_json::from_str(include_str!("./fixtures/api-example.tauri.conf.json")).unwrap();
migrate(&original);
}
#[test]
fn can_migrate_cli_template_config() {
let original =
serde_json::from_str(include_str!("./fixtures/cli-template.tauri.conf.json")).unwrap();
migrate(&original);
}
#[test]
fn migrate_updater_target() {
let original = serde_json::json!({});
let migrated = migrate(&original);
assert_eq!(
migrated["bundle"]["createUpdaterArtifacts"],
serde_json::Value::Null
);
let original = serde_json::json!({
"tauri": {
"updater": {
"active": true
}
}
});
let migrated = migrate(&original);
assert_eq!(migrated["bundle"]["createUpdaterArtifacts"], "v1Compatible");
let original = serde_json::json!({
"tauri": {
"bundle": {
@ -814,6 +918,14 @@ mod test {
Some(&vec!["nsis".into()])
);
let original =
serde_json::from_str(include_str!("./fixtures/cli-template.tauri.conf.json")).unwrap();
let migrated = migrate(&original);
assert_eq!(
migrated["bundle"]["createUpdaterArtifacts"],
serde_json::Value::Null
);
let original = serde_json::json!({
"tauri": {
"bundle": {
@ -822,6 +934,24 @@ mod test {
}
});
let migrated = migrate(&original);
assert_eq!(
migrated["bundle"]["createUpdaterArtifacts"],
serde_json::Value::Null
);
assert_eq!(migrated["bundle"]["targets"], "all");
let original = serde_json::json!({
"tauri": {
"bundle": {
"targets": "all"
},
"updater": {
"active": true
}
}
});
let migrated = migrate(&original);
assert_eq!(migrated["bundle"]["createUpdaterArtifacts"], "v1Compatible");
assert_eq!(migrated["bundle"]["targets"], "all");

View File

@ -0,0 +1,136 @@
{
"$schema": "../../../core/tauri-config-schema/schema.json",
"build": {
"distDir": "../dist",
"devPath": "http://localhost:5173",
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn build"
},
"package": {
"productName": "Tauri API",
"version": "1.0.0"
},
"tauri": {
"pattern": {
"use": "isolation",
"options": {
"dir": "../isolation-dist/"
}
},
"macOSPrivateApi": true,
"cli": {
"description": "Tauri API example",
"args": [
{
"short": "c",
"name": "config",
"takesValue": true,
"description": "Config path"
},
{
"short": "t",
"name": "theme",
"takesValue": true,
"description": "App theme",
"possibleValues": ["light", "dark", "system"]
},
{
"short": "v",
"name": "verbose",
"multipleOccurrences": true,
"description": "Verbosity level"
}
],
"subcommands": {
"update": {
"description": "Updates the app",
"args": [
{
"short": "b",
"name": "background",
"description": "Update in background"
}
]
}
}
},
"bundle": {
"active": true,
"identifier": "com.tauri.api",
"icon": [
"../../.icons/32x32.png",
"../../.icons/128x128.png",
"../../.icons/128x128@2x.png",
"../../.icons/icon.icns",
"../../.icons/icon.ico"
],
"windows": {
"wix": {
"language": {
"en-US": {},
"pt-BR": {
"localePath": "locales/pt-BR.wxl"
}
}
}
}
},
"updater": {
"active": true,
"dialog": false,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDE5QzMxNjYwNTM5OEUwNTgKUldSWTRKaFRZQmJER1h4d1ZMYVA3dnluSjdpN2RmMldJR09hUFFlZDY0SlFqckkvRUJhZDJVZXAK",
"endpoints": [
"https://tauri-update-server.vercel.app/update/{{target}}/{{current_version}}"
]
},
"allowlist": {
"all": true,
"fs": {
"scope": {
"allow": ["$APPDATA/db/**", "$DOWNLOAD/**", "$RESOURCE/**"],
"deny": ["$APPDATA/db/*.stronghold"]
}
},
"shell": {
"open": true,
"scope": [
{
"name": "sh",
"cmd": "sh",
"args": ["-c", { "validator": "\\S+" }]
},
{
"name": "cmd",
"cmd": "cmd",
"args": ["/C", { "validator": "\\S+" }]
}
]
},
"protocol": {
"asset": true,
"assetScope": {
"allow": ["$APPDATA/db/**", "$RESOURCE/**"],
"deny": ["$APPDATA/db/*.stronghold"]
}
},
"http": {
"scope": ["http://localhost:3003"]
}
},
"windows": [],
"security": {
"csp": {
"default-src": "'self' customprotocol: asset:",
"font-src": ["https://fonts.gstatic.com"],
"img-src": "'self' asset: https://asset.localhost blob: data:",
"style-src": "'unsafe-inline' 'self' https://fonts.googleapis.com"
},
"freezePrototype": true
},
"systemTray": {
"iconPath": "../../.icons/tray_icon_with_transparency.png",
"iconAsTemplate": true,
"menuOnLeftClick": false
}
}
}

View File

@ -0,0 +1,65 @@
{
"package": {
"productName": "{{ app_name }}",
"version": "0.1.0"
},
"build": {
"distDir": "{{ dist_dir }}",
"devPath": "{{ dev_path }}",
"beforeDevCommand": "{{ before_dev_command }}",
"beforeBuildCommand": "{{ before_build_command }}"
},
"tauri": {
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool",
"shortDescription": "",
"longDescription": "",
"deb": {
"depends": []
},
"macOS": {
"frameworks": [],
"exceptionDomain": "",
"signingIdentity": null,
"providerShortName": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"updater": {
"active": false
},
"allowlist": {
"all": false
},
"windows": [
{
"title": "{{ window_title }}",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": null
}
}
}