Skip to content

Commit 73c1c2d

Browse files
authored
fix(cli/migrate): tolerate non-UTF-8 in migration (#9457)
1 parent 9331435 commit 73c1c2d

File tree

7 files changed

+40
-28
lines changed

7 files changed

+40
-28
lines changed

.changes/cli-migrate-non-utf8.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'tauri-cli': 'patch:bug'
3+
'@tauri-apps/cli': 'patch:bug'
4+
---
5+
6+
Gracefully handle Non-UTF8 files when using `tauri migrate`

core/tests/restart/tests/restart.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fn symlink_runner(create_symlinks: impl Fn(&Path) -> io::Result<Symlink>) -> Res
5555

5656
if output.status.success() {
5757
// gather the output into a string
58-
let stdout = String::from_utf8(output.stdout)?;
58+
let stdout = String::from_utf8_lossy(&output.stdout);
5959

6060
// we expect the output to be the bin path, twice
6161
assert_eq!(stdout, format!("{bin}\n{bin}\n", bin = bin.display()));
@@ -64,7 +64,7 @@ fn symlink_runner(create_symlinks: impl Fn(&Path) -> io::Result<Symlink>) -> Res
6464
not(feature = "process-relaunch-dangerous-allow-symlink-macos")
6565
)) {
6666
// we expect this to fail on macOS without the dangerous symlink flag set
67-
let stderr = String::from_utf8(output.stderr)?;
67+
let stderr = String::from_utf8_lossy(&output.stderr);
6868

6969
// make sure it's the error that we expect
7070
assert!(stderr.contains(

tooling/bench/src/utils.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ pub fn run_collect(cmd: &[&str]) -> (String, String) {
9292
stderr,
9393
status,
9494
} = prog.wait_with_output().expect("failed to wait on child");
95-
let stdout = String::from_utf8(stdout).unwrap();
96-
let stderr = String::from_utf8(stderr).unwrap();
95+
let stdout = String::from_utf8_lossy(&stdout);
96+
let stderr = String::from_utf8_lossy(&stderr);
9797
if !status.success() {
9898
eprintln!("stdout: <<<{}>>>", stdout);
9999
eprintln!("stderr: <<<{}>>>", stderr);

tooling/cli/src/interface/rust.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1127,7 +1127,7 @@ fn get_cargo_metadata() -> crate::Result<CargoMetadata> {
11271127
if !output.status.success() {
11281128
return Err(anyhow::anyhow!(
11291129
"cargo metadata command exited with a non zero exit code: {}",
1130-
String::from_utf8(output.stderr)?
1130+
String::from_utf8_lossy(&output.stderr)
11311131
));
11321132
}
11331133

tooling/cli/src/migrate/config.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use tauri_utils::acl::{
1212

1313
use std::{
1414
collections::{BTreeMap, HashSet},
15-
fs::{create_dir_all, write},
15+
fs,
1616
path::Path,
1717
};
1818

@@ -21,7 +21,7 @@ pub fn migrate(tauri_dir: &Path) -> Result<MigratedConfig> {
2121
tauri_utils_v1::config::parse::parse_value(tauri_dir.join("tauri.conf.json"))
2222
{
2323
let migrated = migrate_config(&mut config)?;
24-
write(&config_path, serde_json::to_string_pretty(&config)?)?;
24+
fs::write(&config_path, serde_json::to_string_pretty(&config)?)?;
2525

2626
let mut permissions: Vec<PermissionEntry> = vec![
2727
"path:default",
@@ -38,8 +38,8 @@ pub fn migrate(tauri_dir: &Path) -> Result<MigratedConfig> {
3838
permissions.extend(migrated.permissions.clone());
3939

4040
let capabilities_path = config_path.parent().unwrap().join("capabilities");
41-
create_dir_all(&capabilities_path)?;
42-
write(
41+
fs::create_dir_all(&capabilities_path)?;
42+
fs::write(
4343
capabilities_path.join("migrated.json"),
4444
serde_json::to_string_pretty(&Capability {
4545
identifier: "migrated".to_string(),

tooling/cli/src/migrate/frontend.rs

+19-15
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ use crate::{
66
helpers::{app_paths::walk_builder, cargo, npm::PackageManager},
77
Result,
88
};
9+
use anyhow::Context;
910

10-
use std::{
11-
fs::{read_to_string, write},
12-
path::Path,
13-
};
11+
use std::{fs, path::Path};
1412

1513
const CORE_API_MODULES: &[&str] = &["dpi", "event", "path", "core", "window", "mocks"];
1614
const JS_EXTENSIONS: &[&str] = &["js", "jsx", "ts", "tsx", "mjs"];
1715

16+
/// Returns a list of paths that could not be migrated
1817
pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
1918
let mut new_npm_packages = Vec::new();
2019
let mut new_cargo_packages = Vec::new();
@@ -24,19 +23,21 @@ pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
2423
.next()
2524
.unwrap_or(PackageManager::Npm);
2625

27-
let tauri_api_import_regex = regex::Regex::new(r"@tauri-apps/api/(\w+)").unwrap();
26+
let tauri_api_import_regex = regex::bytes::Regex::new(r"@tauri-apps/api/(\w+)").unwrap();
2827

2928
for entry in walk_builder(app_dir).build().flatten() {
3029
if entry.file_type().map(|t| t.is_file()).unwrap_or_default() {
3130
let path = entry.path();
3231
let ext = path.extension().unwrap_or_default();
3332
if JS_EXTENSIONS.iter().any(|e| e == &ext) {
34-
let js_contents = read_to_string(path)?;
33+
let js_contents = fs::read(path)?;
3534

3635
let new_contents =
37-
tauri_api_import_regex.replace_all(&js_contents, |cap: &regex::Captures<'_>| {
38-
let module = cap.get(1).unwrap().as_str();
39-
let original = cap.get(0).unwrap().as_str();
36+
tauri_api_import_regex.replace_all(&js_contents, |cap: &regex::bytes::Captures<'_>| {
37+
let module = cap.get(1).unwrap().as_bytes();
38+
let module = String::from_utf8_lossy(module).to_string();
39+
let original = cap.get(0).unwrap().as_bytes();
40+
let original = String::from_utf8_lossy(original).to_string();
4041

4142
if module == "tauri" {
4243
let new = "@tauri-apps/api/core".to_string();
@@ -46,8 +47,8 @@ pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
4647
let new = "@tauri-apps/api/webviewWindow".to_string();
4748
log::info!("Replacing `{original}` with `{new}` on {}", path.display());
4849
new
49-
} else if CORE_API_MODULES.contains(&module) {
50-
original.to_string()
50+
} else if CORE_API_MODULES.contains(&module.as_str()) {
51+
original
5152
} else {
5253
let plugin = format!("@tauri-apps/plugin-{module}");
5354
log::info!(
@@ -61,7 +62,7 @@ pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
6162
if module == "clipboard" {
6263
"clipboard-manager"
6364
} else {
64-
module
65+
&module
6566
}
6667
));
6768

@@ -70,18 +71,21 @@ pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
7071
});
7172

7273
if new_contents != js_contents {
73-
write(path, new_contents.as_bytes())?;
74+
fs::write(path, new_contents)
75+
.with_context(|| format!("Error writing {}", path.display()))?;
7476
}
7577
}
7678
}
7779
}
7880

7981
if !new_npm_packages.is_empty() {
80-
pm.install(&new_npm_packages)?;
82+
pm.install(&new_npm_packages)
83+
.context("Error installing new npm packages")?;
8184
}
8285

8386
if !new_cargo_packages.is_empty() {
84-
cargo::install(&new_cargo_packages, Some(tauri_dir))?;
87+
cargo::install(&new_cargo_packages, Some(tauri_dir))
88+
.context("Error installing new Cargo packages")?;
8589
}
8690

8791
Ok(())

tooling/cli/src/migrate/mod.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
helpers::app_paths::{app_dir, tauri_dir},
77
Result,
88
};
9+
use anyhow::Context;
910

1011
mod config;
1112
mod frontend;
@@ -15,18 +16,19 @@ pub fn command() -> Result<()> {
1516
let tauri_dir = tauri_dir();
1617
let app_dir = app_dir();
1718

18-
let migrated = config::migrate(&tauri_dir)?;
19-
manifest::migrate(&tauri_dir)?;
19+
let migrated = config::migrate(&tauri_dir).context("Could not migrate config")?;
20+
manifest::migrate(&tauri_dir).context("Could not migrate manifest")?;
2021
frontend::migrate(app_dir, &tauri_dir)?;
2122

2223
// Add plugins
2324
for plugin in migrated.plugins {
2425
crate::add::command(crate::add::Options {
25-
plugin,
26+
plugin: plugin.clone(),
2627
branch: None,
2728
tag: None,
2829
rev: None,
29-
})?
30+
})
31+
.with_context(|| format!("Could not migrate plugin '{plugin}'"))?
3032
}
3133

3234
Ok(())

0 commit comments

Comments
 (0)