feat(cli/migrate): add plugins to Cargo.toml (#8951)

* feat(cli/migrate): add plugins to Cargo.toml

closes #8933

* small cleanup

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
Amr Bashir 2024-02-26 18:29:16 +02:00 committed by GitHub
parent bc5b5e671a
commit 9be314f07a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 134 additions and 97 deletions

View File

@ -0,0 +1,6 @@
---
'tauri-cli': 'patch:enhance'
'@tauri-apps/cli': 'patch:enhance'
---
Add plugins to `Cargo.toml` when using `tauri migrate`

View File

@ -22,16 +22,16 @@ use std::{collections::HashMap, process::Command};
#[clap(about = "Add a tauri plugin to the project")] #[clap(about = "Add a tauri plugin to the project")]
pub struct Options { pub struct Options {
/// The plugin to add. /// The plugin to add.
plugin: String, pub plugin: String,
/// Git tag to use. /// Git tag to use.
#[clap(short, long)] #[clap(short, long)]
tag: Option<String>, pub tag: Option<String>,
/// Git rev to use. /// Git rev to use.
#[clap(short, long)] #[clap(short, long)]
rev: Option<String>, pub rev: Option<String>,
/// Git branch to use. /// Git branch to use.
#[clap(short, long)] #[clap(short, long)]
branch: Option<String>, pub branch: Option<String>,
} }
pub fn command(options: Options) -> Result<()> { pub fn command(options: Options) -> Result<()> {

View File

@ -14,24 +14,12 @@ use tauri_utils::{
}; };
use std::{ use std::{
collections::HashSet,
fs::{create_dir_all, write}, fs::{create_dir_all, write},
path::Path, path::Path,
}; };
macro_rules! move_allowlist_object { pub fn migrate(tauri_dir: &Path) -> Result<MigratedConfig> {
($plugins: ident, $value: expr, $plugin: literal, $field: literal) => {{
if $value != Default::default() {
$plugins
.entry($plugin)
.or_insert_with(|| Value::Object(Default::default()))
.as_object_mut()
.unwrap()
.insert($field.into(), serde_json::to_value($value.clone())?);
}
}};
}
pub fn migrate(tauri_dir: &Path) -> Result<()> {
if let Ok((mut config, config_path)) = if let Ok((mut config, config_path)) =
tauri_utils_v1::config::parse::parse_value(tauri_dir.join("tauri.conf.json")) tauri_utils_v1::config::parse::parse_value(tauri_dir.join("tauri.conf.json"))
{ {
@ -50,7 +38,7 @@ pub fn migrate(tauri_dir: &Path) -> Result<()> {
.into_iter() .into_iter()
.map(|p| PermissionEntry::PermissionRef(p.to_string().try_into().unwrap())) .map(|p| PermissionEntry::PermissionRef(p.to_string().try_into().unwrap()))
.collect(); .collect();
permissions.extend(migrated.permissions); permissions.extend(migrated.permissions.clone());
let capabilities_path = config_path.parent().unwrap().join("capabilities"); let capabilities_path = config_path.parent().unwrap().join("capabilities");
create_dir_all(&capabilities_path)?; create_dir_all(&capabilities_path)?;
@ -73,18 +61,23 @@ pub fn migrate(tauri_dir: &Path) -> Result<()> {
], ],
})?, })?,
)?; )?;
return Ok(migrated);
} }
Ok(()) Ok(Default::default())
} }
struct MigratedConfig { #[derive(Default)]
permissions: Vec<PermissionEntry>, pub struct MigratedConfig {
pub permissions: Vec<PermissionEntry>,
pub plugins: HashSet<String>,
} }
fn migrate_config(config: &mut Value) -> Result<MigratedConfig> { fn migrate_config(config: &mut Value) -> Result<MigratedConfig> {
let mut migrated = MigratedConfig { let mut migrated = MigratedConfig {
permissions: Vec::new(), permissions: Vec::new(),
plugins: HashSet::new(),
}; };
if let Some(config) = config.as_object_mut() { if let Some(config) = config.as_object_mut() {
@ -101,8 +94,9 @@ fn migrate_config(config: &mut Value) -> Result<MigratedConfig> {
if let Some(tauri_config) = config.get_mut("tauri").and_then(|c| c.as_object_mut()) { if let Some(tauri_config) = config.get_mut("tauri").and_then(|c| c.as_object_mut()) {
// allowlist // allowlist
if let Some(allowlist) = tauri_config.remove("allowlist") { if let Some(allowlist) = tauri_config.remove("allowlist") {
let allowlist = process_allowlist(tauri_config, &mut plugins, allowlist)?; let allowlist = process_allowlist(tauri_config, allowlist)?;
let permissions = allowlist_to_permissions(allowlist); let permissions = allowlist_to_permissions(allowlist);
migrated.plugins = plugins_from_permissions(&permissions);
migrated.permissions = permissions; migrated.permissions = permissions;
} }
@ -178,8 +172,11 @@ fn process_build(config: &mut Map<String, Value>) {
if let Some(dist_dir) = build_config.remove("distDir") { if let Some(dist_dir) = build_config.remove("distDir") {
build_config.insert("frontendDist".into(), dist_dir); build_config.insert("frontendDist".into(), dist_dir);
} }
if let Some(dist_dir) = build_config.remove("devPath") { if let Some(dev_path) = build_config.remove("devPath") {
build_config.insert("devUrl".into(), dist_dir); 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);
}
} }
if let Some(with_global_tauri) = build_config.remove("withGlobalTauri") { if let Some(with_global_tauri) = build_config.remove("withGlobalTauri") {
config config
@ -282,13 +279,10 @@ fn process_security(security: &mut Map<String, Value>) -> Result<()> {
fn process_allowlist( fn process_allowlist(
tauri_config: &mut Map<String, Value>, tauri_config: &mut Map<String, Value>,
plugins: &mut Map<String, Value>,
allowlist: Value, allowlist: Value,
) -> Result<tauri_utils_v1::config::AllowlistConfig> { ) -> Result<tauri_utils_v1::config::AllowlistConfig> {
let allowlist: tauri_utils_v1::config::AllowlistConfig = serde_json::from_value(allowlist)?; let allowlist: tauri_utils_v1::config::AllowlistConfig = serde_json::from_value(allowlist)?;
move_allowlist_object!(plugins, allowlist.shell.open, "shell", "open");
if allowlist.protocol.asset_scope != Default::default() { if allowlist.protocol.asset_scope != Default::default() {
let security = tauri_config let security = tauri_config
.entry("security") .entry("security")
@ -524,6 +518,34 @@ fn process_updater(
Ok(()) Ok(())
} }
const KNOWN_PLUGINS: &[&str] = &[
"fs",
"shell",
"dialog",
"http",
"notification",
"global-shortcut",
"os",
"process",
"clipboard-manager",
];
fn plugins_from_permissions(permissions: &Vec<PermissionEntry>) -> HashSet<String> {
let mut plugins = HashSet::new();
for permission in permissions {
let permission = permission.identifier().get();
for plugin in KNOWN_PLUGINS {
if permission.starts_with(plugin) {
plugins.insert(plugin.to_string());
break;
}
}
}
plugins
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
fn migrate(original: &serde_json::Value) -> serde_json::Value { fn migrate(original: &serde_json::Value) -> serde_json::Value {
@ -846,4 +868,22 @@ mod test {
"connect-src migration failed" "connect-src migration failed"
); );
} }
#[test]
fn migrate_invalid_url_dev_path() {
let original = serde_json::json!({
"build": {
"devPath": "../src",
"distDir": "../src"
}
});
let migrated = migrate(&original);
assert!(migrated["build"].get("devUrl").is_none());
assert_eq!(
migrated["build"]["distDir"],
original["build"]["frontendDist"]
);
}
} }

View File

@ -11,7 +11,7 @@ use toml_edit::{Document, Entry, Item, Table, TableLike, Value};
use std::{fs::File, io::Write, path::Path}; use std::{fs::File, io::Write, path::Path};
const CRATE_TYPES: &[&str] = &["staticlib", "cdylib", "rlib"]; const CRATE_TYPES: [&str; 3] = ["lib", "staticlib", "cdylib"];
pub fn migrate(tauri_dir: &Path) -> Result<()> { pub fn migrate(tauri_dir: &Path) -> Result<()> {
let manifest_path = tauri_dir.join("Cargo.toml"); let manifest_path = tauri_dir.join("Cargo.toml");
@ -57,34 +57,34 @@ fn migrate_manifest(manifest: &mut Document) -> Result<()> {
migrate_dependency(build_dependencies, "tauri-build", &version, &[]); migrate_dependency(build_dependencies, "tauri-build", &version, &[]);
let lib = manifest if let Some(lib) = manifest
.as_table_mut() .as_table_mut()
.entry("lib") .get_mut("lib")
.or_insert(Item::Table(Table::new())) .and_then(|l| l.as_table_mut())
.as_table_mut() {
.expect("manifest lib isn't a table"); match lib.entry("crate-type") {
match lib.entry("crate-type") { Entry::Occupied(mut e) => {
Entry::Occupied(mut e) => { if let Item::Value(Value::Array(types)) = e.get_mut() {
if let Item::Value(Value::Array(types)) = e.get_mut() { let mut crate_types_to_add = CRATE_TYPES.to_vec();
let mut crate_types_to_add = CRATE_TYPES.to_vec(); for t in types.iter() {
for t in types.iter() { // type is already in the manifest, skip adding it
// type is already in the manifest, skip adding it if let Some(i) = crate_types_to_add
if let Some(i) = crate_types_to_add .iter()
.iter() .position(|ty| Some(ty) == t.as_str().as_ref())
.position(|ty| Some(ty) == t.as_str().as_ref()) {
{ crate_types_to_add.remove(i);
crate_types_to_add.remove(i); }
}
for t in crate_types_to_add {
types.push(t);
} }
} }
for t in crate_types_to_add {
types.push(t);
}
} }
} Entry::Vacant(e) => {
Entry::Vacant(e) => { let mut arr = toml_edit::Array::new();
let mut arr = toml_edit::Array::new(); arr.extend(CRATE_TYPES.to_vec());
arr.extend(CRATE_TYPES.to_vec()); e.insert(Item::Value(arr.into()));
e.insert(Item::Value(arr.into())); }
} }
} }
@ -265,35 +265,6 @@ mod tests {
} }
} }
fn migrate_lib(toml: &str) {
let mut manifest = toml.parse::<toml_edit::Document>().expect("invalid toml");
super::migrate_manifest(&mut manifest).expect("failed to migrate manifest");
let lib = manifest
.as_table()
.get("lib")
.expect("missing manifest lib")
.as_table()
.expect("manifest lib isn't a table");
let crate_types = lib
.get("crate-type")
.expect("missing lib crate-type")
.as_array()
.expect("crate-type must be an array");
let mut not_added_crate_types = super::CRATE_TYPES.to_vec();
for t in crate_types {
let t = t.as_str().expect("crate-type must be a string");
if let Some(i) = not_added_crate_types.iter().position(|ty| ty == &t) {
not_added_crate_types.remove(i);
}
}
assert!(
not_added_crate_types.is_empty(),
"missing crate-type: {not_added_crate_types:?}"
);
}
#[test] #[test]
fn migrate_table() { fn migrate_table() {
migrate_deps(|features| { migrate_deps(|features| {
@ -332,22 +303,32 @@ mod tests {
}) })
} }
#[test]
fn migrate_missing_lib() {
migrate_lib("[dependencies]");
}
#[test]
fn migrate_missing_crate_types() {
migrate_lib("[lib]");
}
#[test] #[test]
fn migrate_add_crate_types() { fn migrate_add_crate_types() {
migrate_lib( let toml = r#"
r#"
[lib] [lib]
crate-type = ["something"]"#, crate-type = ["something"]"#;
);
let mut manifest = toml.parse::<toml_edit::Document>().expect("invalid toml");
super::migrate_manifest(&mut manifest).expect("failed to migrate manifest");
if let Some(crate_types) = manifest
.as_table()
.get("lib")
.and_then(|l| l.get("crate-type"))
.and_then(|c| c.as_array())
{
let mut not_added_crate_types = super::CRATE_TYPES.to_vec();
for t in crate_types {
let t = t.as_str().expect("crate-type must be a string");
if let Some(i) = not_added_crate_types.iter().position(|ty| ty == &t) {
not_added_crate_types.remove(i);
}
}
assert!(
not_added_crate_types.is_empty(),
"missing crate-type: {not_added_crate_types:?}"
);
}
} }
} }

View File

@ -15,9 +15,19 @@ pub fn command() -> Result<()> {
let tauri_dir = tauri_dir(); let tauri_dir = tauri_dir();
let app_dir = app_dir(); let app_dir = app_dir();
config::migrate(&tauri_dir)?; let migrated = config::migrate(&tauri_dir)?;
manifest::migrate(&tauri_dir)?; manifest::migrate(&tauri_dir)?;
frontend::migrate(app_dir, &tauri_dir)?; frontend::migrate(app_dir, &tauri_dir)?;
// Add plugins
for plugin in migrated.plugins {
crate::add::command(crate::add::Options {
plugin,
branch: None,
tag: None,
rev: None,
})?
}
Ok(()) Ok(())
} }