Skip to content

Commit 6bf9179

Browse files
feat(cli): add tauri remove command (#11322)
1 parent 8e8312b commit 6bf9179

File tree

6 files changed

+133
-6
lines changed

6 files changed

+133
-6
lines changed

.changes/cli-remove-command.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-cli": minor:feat
3+
"@tauri-apps/cli": minor:feat
4+
---
5+
6+
Add `tauri remove` to remove plugins from projects.

crates/tauri-cli/src/acl/permission/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::Result;
99
pub mod add;
1010
mod ls;
1111
mod new;
12-
mod rm;
12+
pub mod rm;
1313

1414
#[derive(Debug, Parser)]
1515
#[clap(about = "Manage or create permissions for your app or plugin")]

crates/tauri-cli/src/acl/permission/rm.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ fn rm_permission_files(identifier: &str, dir: &Path) -> Result<()> {
4646
permission_file.default = None;
4747
} else {
4848
let set_len = permission_file.set.len();
49-
permission_file.set.retain(|s| s.identifier != identifier);
49+
permission_file
50+
.set
51+
.retain(|s| !identifier_match(identifier, &s.identifier));
5052
updated = permission_file.set.len() != set_len;
5153

5254
let permission_len = permission_file.permission.len();
5355
permission_file
5456
.permission
55-
.retain(|s| s.identifier != identifier);
57+
.retain(|s| !identifier_match(identifier, &s.identifier));
5658
updated = updated || permission_file.permission.len() != permission_len;
5759
}
5860

@@ -84,7 +86,11 @@ fn rm_permission_from_capabilities(identifier: &str, dir: &Path) -> Result<()> {
8486
if let Ok(mut value) = content.parse::<toml_edit::DocumentMut>() {
8587
if let Some(permissions) = value.get_mut("permissions").and_then(|p| p.as_array_mut()) {
8688
let prev_len = permissions.len();
87-
permissions.retain(|p| p.as_str().map(|p| p != identifier).unwrap_or(false));
89+
permissions.retain(|p| {
90+
p.as_str()
91+
.map(|p| !identifier_match(identifier, p))
92+
.unwrap_or(false)
93+
});
8894
if prev_len != permissions.len() {
8995
std::fs::write(&path, value.to_string())?;
9096
log::info!(action = "Removed"; "permission from capability at {}", dunce::simplified(&path).display());
@@ -97,7 +103,11 @@ fn rm_permission_from_capabilities(identifier: &str, dir: &Path) -> Result<()> {
97103
if let Ok(mut value) = serde_json::from_slice::<serde_json::Value>(&content) {
98104
if let Some(permissions) = value.get_mut("permissions").and_then(|p| p.as_array_mut()) {
99105
let prev_len = permissions.len();
100-
permissions.retain(|p| p.as_str().map(|p| p != identifier).unwrap_or(false));
106+
permissions.retain(|p| {
107+
p.as_str()
108+
.map(|p| !identifier_match(identifier, p))
109+
.unwrap_or(false)
110+
});
101111
if prev_len != permissions.len() {
102112
std::fs::write(&path, serde_json::to_vec_pretty(&value)?)?;
103113
log::info!(action = "Removed"; "permission from capability at {}", dunce::simplified(&path).display());
@@ -113,11 +123,20 @@ fn rm_permission_from_capabilities(identifier: &str, dir: &Path) -> Result<()> {
113123
Ok(())
114124
}
115125

126+
fn identifier_match(identifier: &str, permission: &str) -> bool {
127+
match identifier.split_once(':') {
128+
Some((plugin_name, "*")) => permission.contains(plugin_name),
129+
_ => permission == identifier,
130+
}
131+
}
132+
116133
#[derive(Debug, Parser)]
117134
#[clap(about = "Remove a permission file, and its reference from any capability")]
118135
pub struct Options {
119136
/// Permission to remove.
120-
identifier: String,
137+
///
138+
/// To remove all permissions for a given plugin, provide `<plugin-name>:*`
139+
pub identifier: String,
121140
}
122141

123142
pub fn command(options: Options) -> Result<()> {

crates/tauri-cli/src/helpers/cargo.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,33 @@ pub fn install_one(options: CargoInstallOptions) -> crate::Result<()> {
6161

6262
Ok(())
6363
}
64+
65+
#[derive(Debug, Default, Clone, Copy)]
66+
pub struct CargoUninstallOptions<'a> {
67+
pub name: &'a str,
68+
pub cwd: Option<&'a std::path::Path>,
69+
pub target: Option<&'a str>,
70+
}
71+
72+
pub fn uninstall_one(options: CargoUninstallOptions) -> crate::Result<()> {
73+
let mut cargo = Command::new("cargo");
74+
cargo.arg("remove");
75+
76+
cargo.arg(options.name);
77+
78+
if let Some(target) = options.target {
79+
cargo.args(["--target", target]);
80+
}
81+
82+
if let Some(cwd) = options.cwd {
83+
cargo.current_dir(cwd);
84+
}
85+
86+
log::info!("Uninstalling Cargo dependency \"{}\"...", options.name);
87+
let status = cargo.status().context("failed to run `cargo remove`")?;
88+
if !status.success() {
89+
anyhow::bail!("Failed to remove Cargo dependency");
90+
}
91+
92+
Ok(())
93+
}

crates/tauri-cli/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ mod interface;
3030
mod migrate;
3131
mod mobile;
3232
mod plugin;
33+
mod remove;
3334
mod signer;
3435

3536
use clap::{ArgAction, CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum};
@@ -146,6 +147,7 @@ enum Commands {
146147
Migrate,
147148
Info(info::Options),
148149
Add(add::Options),
150+
Remove(remove::Options),
149151
Plugin(plugin::Cli),
150152
Icon(icon::Options),
151153
Signer(signer::Cli),
@@ -265,6 +267,7 @@ where
265267
Commands::Bundle(options) => bundle::command(options, cli.verbose)?,
266268
Commands::Dev(options) => dev::command(options)?,
267269
Commands::Add(options) => add::command(options)?,
270+
Commands::Remove(options) => remove::command(options)?,
268271
Commands::Icon(options) => icon::command(options)?,
269272
Commands::Info(options) => info::command(options)?,
270273
Commands::Init(options) => init::command(options)?,

crates/tauri-cli/src/remove.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use clap::Parser;
6+
7+
use crate::{
8+
acl,
9+
helpers::{
10+
app_paths::{resolve_frontend_dir, tauri_dir},
11+
cargo,
12+
npm::PackageManager,
13+
},
14+
Result,
15+
};
16+
17+
#[derive(Debug, Parser)]
18+
#[clap(about = "Remove a tauri plugin from the project")]
19+
pub struct Options {
20+
/// The plugin to remove.
21+
pub plugin: String,
22+
}
23+
24+
pub fn command(options: Options) -> Result<()> {
25+
crate::helpers::app_paths::resolve();
26+
run(options)
27+
}
28+
29+
pub fn run(options: Options) -> Result<()> {
30+
let plugin = options.plugin;
31+
32+
let crate_name = format!("tauri-plugin-{plugin}");
33+
34+
let mut plugins = crate::helpers::plugins::known_plugins();
35+
let metadata = plugins.remove(plugin.as_str()).unwrap_or_default();
36+
37+
let frontend_dir = resolve_frontend_dir();
38+
let tauri_dir = tauri_dir();
39+
40+
let target_str = metadata
41+
.desktop_only
42+
.then_some(r#"cfg(not(any(target_os = "android", target_os = "ios")))"#)
43+
.or_else(|| {
44+
metadata
45+
.mobile_only
46+
.then_some(r#"cfg(any(target_os = "android", target_os = "ios"))"#)
47+
});
48+
49+
cargo::uninstall_one(cargo::CargoUninstallOptions {
50+
name: &crate_name,
51+
cwd: Some(tauri_dir),
52+
target: target_str,
53+
})?;
54+
55+
if !metadata.rust_only {
56+
if let Some(manager) = frontend_dir.map(PackageManager::from_project) {
57+
let npm_name = format!("@tauri-apps/plugin-{plugin}");
58+
manager.remove(&[npm_name], tauri_dir)?;
59+
}
60+
61+
acl::permission::rm::command(acl::permission::rm::Options {
62+
identifier: format!("{plugin}:*"),
63+
})?;
64+
}
65+
66+
log::info!("Now, you must manually remove the plugin from your Rust code.",);
67+
68+
Ok(())
69+
}

0 commit comments

Comments
 (0)