Ver código fonte

fix(cli/migrate): tolerate non-UTF-8 in migration (#9457)

Kornel 1 ano atrás
pai
commit
73c1c2d338

+ 6 - 0
.changes/cli-migrate-non-utf8.md

@@ -0,0 +1,6 @@
+---
+'tauri-cli': 'patch:bug'
+'@tauri-apps/cli': 'patch:bug'
+---
+
+Gracefully handle Non-UTF8 files when using `tauri migrate`

+ 2 - 2
core/tests/restart/tests/restart.rs

@@ -55,7 +55,7 @@ fn symlink_runner(create_symlinks: impl Fn(&Path) -> io::Result<Symlink>) -> Res
 
     if output.status.success() {
       // gather the output into a string
-      let stdout = String::from_utf8(output.stdout)?;
+      let stdout = String::from_utf8_lossy(&output.stdout);
 
       // we expect the output to be the bin path, twice
       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
       not(feature = "process-relaunch-dangerous-allow-symlink-macos")
     )) {
       // we expect this to fail on macOS without the dangerous symlink flag set
-      let stderr = String::from_utf8(output.stderr)?;
+      let stderr = String::from_utf8_lossy(&output.stderr);
 
       // make sure it's the error that we expect
       assert!(stderr.contains(

+ 2 - 2
tooling/bench/src/utils.rs

@@ -92,8 +92,8 @@ pub fn run_collect(cmd: &[&str]) -> (String, String) {
     stderr,
     status,
   } = prog.wait_with_output().expect("failed to wait on child");
-  let stdout = String::from_utf8(stdout).unwrap();
-  let stderr = String::from_utf8(stderr).unwrap();
+  let stdout = String::from_utf8_lossy(&stdout);
+  let stderr = String::from_utf8_lossy(&stderr);
   if !status.success() {
     eprintln!("stdout: <<<{}>>>", stdout);
     eprintln!("stderr: <<<{}>>>", stderr);

+ 1 - 1
tooling/cli/src/interface/rust.rs

@@ -1127,7 +1127,7 @@ fn get_cargo_metadata() -> crate::Result<CargoMetadata> {
   if !output.status.success() {
     return Err(anyhow::anyhow!(
       "cargo metadata command exited with a non zero exit code: {}",
-      String::from_utf8(output.stderr)?
+      String::from_utf8_lossy(&output.stderr)
     ));
   }
 

+ 4 - 4
tooling/cli/src/migrate/config.rs

@@ -12,7 +12,7 @@ use tauri_utils::acl::{
 
 use std::{
   collections::{BTreeMap, HashSet},
-  fs::{create_dir_all, write},
+  fs,
   path::Path,
 };
 
@@ -21,7 +21,7 @@ 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)?;
-    write(&config_path, serde_json::to_string_pretty(&config)?)?;
+    fs::write(&config_path, serde_json::to_string_pretty(&config)?)?;
 
     let mut permissions: Vec<PermissionEntry> = vec![
       "path:default",
@@ -38,8 +38,8 @@ pub fn migrate(tauri_dir: &Path) -> Result<MigratedConfig> {
     permissions.extend(migrated.permissions.clone());
 
     let capabilities_path = config_path.parent().unwrap().join("capabilities");
-    create_dir_all(&capabilities_path)?;
-    write(
+    fs::create_dir_all(&capabilities_path)?;
+    fs::write(
       capabilities_path.join("migrated.json"),
       serde_json::to_string_pretty(&Capability {
         identifier: "migrated".to_string(),

+ 19 - 15
tooling/cli/src/migrate/frontend.rs

@@ -6,15 +6,14 @@ use crate::{
   helpers::{app_paths::walk_builder, cargo, npm::PackageManager},
   Result,
 };
+use anyhow::Context;
 
-use std::{
-  fs::{read_to_string, write},
-  path::Path,
-};
+use std::{fs, path::Path};
 
 const CORE_API_MODULES: &[&str] = &["dpi", "event", "path", "core", "window", "mocks"];
 const JS_EXTENSIONS: &[&str] = &["js", "jsx", "ts", "tsx", "mjs"];
 
+/// Returns a list of paths that could not be migrated
 pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
   let mut new_npm_packages = Vec::new();
   let mut new_cargo_packages = Vec::new();
@@ -24,19 +23,21 @@ pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
     .next()
     .unwrap_or(PackageManager::Npm);
 
-  let tauri_api_import_regex = regex::Regex::new(r"@tauri-apps/api/(\w+)").unwrap();
+  let tauri_api_import_regex = regex::bytes::Regex::new(r"@tauri-apps/api/(\w+)").unwrap();
 
   for entry in walk_builder(app_dir).build().flatten() {
     if entry.file_type().map(|t| t.is_file()).unwrap_or_default() {
       let path = entry.path();
       let ext = path.extension().unwrap_or_default();
       if JS_EXTENSIONS.iter().any(|e| e == &ext) {
-        let js_contents = read_to_string(path)?;
+        let js_contents = fs::read(path)?;
 
         let new_contents =
-          tauri_api_import_regex.replace_all(&js_contents, |cap: &regex::Captures<'_>| {
-            let module = cap.get(1).unwrap().as_str();
-            let original = cap.get(0).unwrap().as_str();
+          tauri_api_import_regex.replace_all(&js_contents, |cap: &regex::bytes::Captures<'_>| {
+            let module = cap.get(1).unwrap().as_bytes();
+            let module = String::from_utf8_lossy(module).to_string();
+            let original = cap.get(0).unwrap().as_bytes();
+            let original = String::from_utf8_lossy(original).to_string();
 
             if module == "tauri" {
               let new = "@tauri-apps/api/core".to_string();
@@ -46,8 +47,8 @@ pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
               let new = "@tauri-apps/api/webviewWindow".to_string();
               log::info!("Replacing `{original}` with `{new}` on {}", path.display());
               new
-            } else if CORE_API_MODULES.contains(&module) {
-              original.to_string()
+            } else if CORE_API_MODULES.contains(&module.as_str()) {
+              original
             } else {
               let plugin = format!("@tauri-apps/plugin-{module}");
               log::info!(
@@ -61,7 +62,7 @@ pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
                 if module == "clipboard" {
                   "clipboard-manager"
                 } else {
-                  module
+                  &module
                 }
               ));
 
@@ -70,18 +71,21 @@ pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> {
           });
 
         if new_contents != js_contents {
-          write(path, new_contents.as_bytes())?;
+          fs::write(path, new_contents)
+            .with_context(|| format!("Error writing {}", path.display()))?;
         }
       }
     }
   }
 
   if !new_npm_packages.is_empty() {
-    pm.install(&new_npm_packages)?;
+    pm.install(&new_npm_packages)
+      .context("Error installing new npm packages")?;
   }
 
   if !new_cargo_packages.is_empty() {
-    cargo::install(&new_cargo_packages, Some(tauri_dir))?;
+    cargo::install(&new_cargo_packages, Some(tauri_dir))
+      .context("Error installing new Cargo packages")?;
   }
 
   Ok(())

+ 6 - 4
tooling/cli/src/migrate/mod.rs

@@ -6,6 +6,7 @@ use crate::{
   helpers::app_paths::{app_dir, tauri_dir},
   Result,
 };
+use anyhow::Context;
 
 mod config;
 mod frontend;
@@ -15,18 +16,19 @@ pub fn command() -> Result<()> {
   let tauri_dir = tauri_dir();
   let app_dir = app_dir();
 
-  let migrated = config::migrate(&tauri_dir)?;
-  manifest::migrate(&tauri_dir)?;
+  let migrated = config::migrate(&tauri_dir).context("Could not migrate config")?;
+  manifest::migrate(&tauri_dir).context("Could not migrate manifest")?;
   frontend::migrate(app_dir, &tauri_dir)?;
 
   // Add plugins
   for plugin in migrated.plugins {
     crate::add::command(crate::add::Options {
-      plugin,
+      plugin: plugin.clone(),
       branch: None,
       tag: None,
       rev: None,
-    })?
+    })
+    .with_context(|| format!("Could not migrate plugin '{plugin}'"))?
   }
 
   Ok(())