mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
feat(cli/uninstall): add uninstall command (#12209)
This commit is contained in:
parent
f602d63f48
commit
ee2e25fba7
3 changed files with 145 additions and 0 deletions
52
cli/flags.rs
52
cli/flags.rs
|
@ -110,6 +110,12 @@ pub struct InstallFlags {
|
|||
pub force: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct UninstallFlags {
|
||||
pub name: String,
|
||||
pub root: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct LintFlags {
|
||||
pub files: Vec<PathBuf>,
|
||||
|
@ -166,6 +172,7 @@ pub enum DenoSubcommand {
|
|||
Fmt(FmtFlags),
|
||||
Info(InfoFlags),
|
||||
Install(InstallFlags),
|
||||
Uninstall(UninstallFlags),
|
||||
Lsp,
|
||||
Lint(LintFlags),
|
||||
Repl(ReplFlags),
|
||||
|
@ -428,6 +435,8 @@ pub fn flags_from_vec(args: Vec<String>) -> clap::Result<Flags> {
|
|||
bundle_parse(&mut flags, m);
|
||||
} else if let Some(m) = matches.subcommand_matches("install") {
|
||||
install_parse(&mut flags, m);
|
||||
} else if let Some(m) = matches.subcommand_matches("uninstall") {
|
||||
uninstall_parse(&mut flags, m);
|
||||
} else if let Some(m) = matches.subcommand_matches("completions") {
|
||||
completions_parse(&mut flags, m);
|
||||
} else if let Some(m) = matches.subcommand_matches("test") {
|
||||
|
@ -499,6 +508,7 @@ If the flag is set, restrict these messages to errors.",
|
|||
.subcommand(fmt_subcommand())
|
||||
.subcommand(info_subcommand())
|
||||
.subcommand(install_subcommand())
|
||||
.subcommand(uninstall_subcommand())
|
||||
.subcommand(lsp_subcommand())
|
||||
.subcommand(lint_subcommand())
|
||||
.subcommand(repl_subcommand())
|
||||
|
@ -995,6 +1005,36 @@ The installation root is determined, in order of precedence:
|
|||
These must be added to the path manually if required.")
|
||||
}
|
||||
|
||||
fn uninstall_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
SubCommand::with_name("uninstall")
|
||||
.setting(AppSettings::TrailingVarArg)
|
||||
.arg(
|
||||
Arg::with_name("name")
|
||||
.required(true)
|
||||
.multiple(false)
|
||||
.allow_hyphen_values(true))
|
||||
.arg(
|
||||
Arg::with_name("root")
|
||||
.long("root")
|
||||
.help("Installation root")
|
||||
.takes_value(true)
|
||||
.multiple(false))
|
||||
.about("Uninstall a script previously installed with deno install")
|
||||
.long_about(
|
||||
"Uninstalls an executable script in the installation root's bin directory.
|
||||
|
||||
deno uninstall serve
|
||||
|
||||
To change the installation root, use --root:
|
||||
|
||||
deno uninstall --root /usr/local serve
|
||||
|
||||
The installation root is determined, in order of precedence:
|
||||
- --root option
|
||||
- DENO_INSTALL_ROOT environment variable
|
||||
- $HOME/.deno")
|
||||
}
|
||||
|
||||
fn lsp_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
SubCommand::with_name("lsp")
|
||||
.about("Start the language server")
|
||||
|
@ -1896,6 +1936,18 @@ fn install_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
|||
});
|
||||
}
|
||||
|
||||
fn uninstall_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
||||
let root = if matches.is_present("root") {
|
||||
let install_root = matches.value_of("root").unwrap();
|
||||
Some(PathBuf::from(install_root))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let name = matches.value_of("name").unwrap().to_string();
|
||||
flags.subcommand = DenoSubcommand::Uninstall(UninstallFlags { name, root });
|
||||
}
|
||||
|
||||
fn lsp_parse(flags: &mut Flags, _matches: &clap::ArgMatches) {
|
||||
flags.subcommand = DenoSubcommand::Lsp;
|
||||
}
|
||||
|
|
10
cli/main.rs
10
cli/main.rs
|
@ -53,6 +53,7 @@ use crate::flags::LintFlags;
|
|||
use crate::flags::ReplFlags;
|
||||
use crate::flags::RunFlags;
|
||||
use crate::flags::TestFlags;
|
||||
use crate::flags::UninstallFlags;
|
||||
use crate::flags::UpgradeFlags;
|
||||
use crate::fmt_errors::PrettyJsError;
|
||||
use crate::module_loader::CliModuleLoader;
|
||||
|
@ -487,6 +488,12 @@ async fn install_command(
|
|||
)
|
||||
}
|
||||
|
||||
async fn uninstall_command(
|
||||
uninstall_flags: UninstallFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
tools::installer::uninstall(uninstall_flags.name, uninstall_flags.root)
|
||||
}
|
||||
|
||||
async fn lsp_command() -> Result<(), AnyError> {
|
||||
lsp::start().await
|
||||
}
|
||||
|
@ -1149,6 +1156,9 @@ fn get_subcommand(
|
|||
DenoSubcommand::Install(install_flags) => {
|
||||
install_command(flags, install_flags).boxed_local()
|
||||
}
|
||||
DenoSubcommand::Uninstall(uninstall_flags) => {
|
||||
uninstall_command(uninstall_flags).boxed_local()
|
||||
}
|
||||
DenoSubcommand::Lsp => lsp_command().boxed_local(),
|
||||
DenoSubcommand::Lint(lint_flags) => {
|
||||
lint_command(flags, lint_flags).boxed_local()
|
||||
|
|
|
@ -139,6 +139,57 @@ pub fn infer_name_from_url(url: &Url) -> Option<String> {
|
|||
Some(stem)
|
||||
}
|
||||
|
||||
pub fn uninstall(name: String, root: Option<PathBuf>) -> Result<(), AnyError> {
|
||||
let root = if let Some(root) = root {
|
||||
canonicalize_path(&root)?
|
||||
} else {
|
||||
get_installer_root()?
|
||||
};
|
||||
let installation_dir = root.join("bin");
|
||||
|
||||
// ensure directory exists
|
||||
if let Ok(metadata) = fs::metadata(&installation_dir) {
|
||||
if !metadata.is_dir() {
|
||||
return Err(generic_error("Installation path is not a directory"));
|
||||
}
|
||||
}
|
||||
|
||||
let mut file_path = installation_dir.join(&name);
|
||||
|
||||
let mut removed = false;
|
||||
|
||||
if file_path.exists() {
|
||||
fs::remove_file(&file_path)?;
|
||||
println!("deleted {}", file_path.to_string_lossy());
|
||||
removed = true
|
||||
};
|
||||
|
||||
if cfg!(windows) {
|
||||
file_path = file_path.with_extension("cmd");
|
||||
if file_path.exists() {
|
||||
fs::remove_file(&file_path)?;
|
||||
println!("deleted {}", file_path.to_string_lossy());
|
||||
removed = true
|
||||
}
|
||||
}
|
||||
|
||||
if !removed {
|
||||
return Err(generic_error(format!("No installation found for {}", name)));
|
||||
}
|
||||
|
||||
// There might be some extra files to delete
|
||||
for ext in ["tsconfig.json", "lock.json"] {
|
||||
file_path = file_path.with_extension(ext);
|
||||
if file_path.exists() {
|
||||
fs::remove_file(&file_path)?;
|
||||
println!("deleted {}", file_path.to_string_lossy());
|
||||
}
|
||||
}
|
||||
|
||||
println!("✅ Successfully uninstalled {}", name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install(
|
||||
flags: Flags,
|
||||
module_url: &str,
|
||||
|
@ -926,4 +977,36 @@ mod tests {
|
|||
let content = fs::read_to_string(file_path).unwrap();
|
||||
assert!(content.contains(&expected_string));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninstall_basic() {
|
||||
let temp_dir = TempDir::new().expect("tempdir fail");
|
||||
let bin_dir = temp_dir.path().join("bin");
|
||||
std::fs::create_dir(&bin_dir).unwrap();
|
||||
|
||||
let mut file_path = bin_dir.join("echo_test");
|
||||
File::create(&file_path).unwrap();
|
||||
if cfg!(windows) {
|
||||
file_path = file_path.with_extension("cmd");
|
||||
File::create(&file_path).unwrap();
|
||||
}
|
||||
|
||||
// create extra files
|
||||
file_path = file_path.with_extension("tsconfig.json");
|
||||
File::create(&file_path).unwrap();
|
||||
file_path = file_path.with_extension("lock.json");
|
||||
File::create(&file_path).unwrap();
|
||||
|
||||
uninstall("echo_test".to_string(), Some(temp_dir.path().to_path_buf()))
|
||||
.expect("Uninstall failed");
|
||||
|
||||
assert!(!file_path.exists());
|
||||
assert!(!file_path.with_extension("tsconfig.json").exists());
|
||||
assert!(!file_path.with_extension("lock.json").exists());
|
||||
|
||||
if cfg!(windows) {
|
||||
file_path = file_path.with_extension("cmd");
|
||||
assert!(!file_path.exists());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue