1
0
Fork 0
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:
Sylvain Cau 2021-09-30 23:38:07 +08:00 committed by GitHub
parent f602d63f48
commit ee2e25fba7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 145 additions and 0 deletions

View file

@ -110,6 +110,12 @@ pub struct InstallFlags {
pub force: bool, 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)] #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct LintFlags { pub struct LintFlags {
pub files: Vec<PathBuf>, pub files: Vec<PathBuf>,
@ -166,6 +172,7 @@ pub enum DenoSubcommand {
Fmt(FmtFlags), Fmt(FmtFlags),
Info(InfoFlags), Info(InfoFlags),
Install(InstallFlags), Install(InstallFlags),
Uninstall(UninstallFlags),
Lsp, Lsp,
Lint(LintFlags), Lint(LintFlags),
Repl(ReplFlags), Repl(ReplFlags),
@ -428,6 +435,8 @@ pub fn flags_from_vec(args: Vec<String>) -> clap::Result<Flags> {
bundle_parse(&mut flags, m); bundle_parse(&mut flags, m);
} else if let Some(m) = matches.subcommand_matches("install") { } else if let Some(m) = matches.subcommand_matches("install") {
install_parse(&mut flags, m); 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") { } else if let Some(m) = matches.subcommand_matches("completions") {
completions_parse(&mut flags, m); completions_parse(&mut flags, m);
} else if let Some(m) = matches.subcommand_matches("test") { } 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(fmt_subcommand())
.subcommand(info_subcommand()) .subcommand(info_subcommand())
.subcommand(install_subcommand()) .subcommand(install_subcommand())
.subcommand(uninstall_subcommand())
.subcommand(lsp_subcommand()) .subcommand(lsp_subcommand())
.subcommand(lint_subcommand()) .subcommand(lint_subcommand())
.subcommand(repl_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.") 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> { fn lsp_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("lsp") SubCommand::with_name("lsp")
.about("Start the language server") .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) { fn lsp_parse(flags: &mut Flags, _matches: &clap::ArgMatches) {
flags.subcommand = DenoSubcommand::Lsp; flags.subcommand = DenoSubcommand::Lsp;
} }

View file

@ -53,6 +53,7 @@ use crate::flags::LintFlags;
use crate::flags::ReplFlags; use crate::flags::ReplFlags;
use crate::flags::RunFlags; use crate::flags::RunFlags;
use crate::flags::TestFlags; use crate::flags::TestFlags;
use crate::flags::UninstallFlags;
use crate::flags::UpgradeFlags; use crate::flags::UpgradeFlags;
use crate::fmt_errors::PrettyJsError; use crate::fmt_errors::PrettyJsError;
use crate::module_loader::CliModuleLoader; 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> { async fn lsp_command() -> Result<(), AnyError> {
lsp::start().await lsp::start().await
} }
@ -1149,6 +1156,9 @@ fn get_subcommand(
DenoSubcommand::Install(install_flags) => { DenoSubcommand::Install(install_flags) => {
install_command(flags, install_flags).boxed_local() install_command(flags, install_flags).boxed_local()
} }
DenoSubcommand::Uninstall(uninstall_flags) => {
uninstall_command(uninstall_flags).boxed_local()
}
DenoSubcommand::Lsp => lsp_command().boxed_local(), DenoSubcommand::Lsp => lsp_command().boxed_local(),
DenoSubcommand::Lint(lint_flags) => { DenoSubcommand::Lint(lint_flags) => {
lint_command(flags, lint_flags).boxed_local() lint_command(flags, lint_flags).boxed_local()

View file

@ -139,6 +139,57 @@ pub fn infer_name_from_url(url: &Url) -> Option<String> {
Some(stem) 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( pub fn install(
flags: Flags, flags: Flags,
module_url: &str, module_url: &str,
@ -926,4 +977,36 @@ mod tests {
let content = fs::read_to_string(file_path).unwrap(); let content = fs::read_to_string(file_path).unwrap();
assert!(content.contains(&expected_string)); 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());
}
}
} }