Skip to content

Commit

Permalink
feat: self uninstall (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
ericswanson-dfinity authored Feb 1, 2024
1 parent 23d169f commit ea1e0a1
Show file tree
Hide file tree
Showing 16 changed files with 487 additions and 31 deletions.
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased] - ReleaseDate

- removed openssl dependencies
- Removed openssl dependencies
- Added `dfxvm self uninstall` command, which uninstalls dfxvm and all versions of dfx.

## [0.2.0] - 2024-01-30

- `dfxvm --version` now reports the version
- changed the dfxvm-init `--proceed` parameter to `--yes`
- static link to openssl
- Changed the dfxvm-init `--proceed` parameter to `--yes`
- Static link to openssl

## [0.1.3] - 2024-01-19

Expand Down
109 changes: 107 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ semver = { version = "1.0", features = [ "serde" ] }
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
sha2 = "0.10.8"
sysinfo = "0.30.5"
tar = "0.4.40"
tempfile = "3"
thiserror = "1.0"
Expand Down
20 changes: 20 additions & 0 deletions docs/cli-reference/dfxvm/dfxvm-self-uninstall.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# dfxvm self uninstall

Uninstalls dfxvm and all versions of dfx.

## Usage

```bash
dfxvm self uninstall [--yes]
```

### Options

| Option | Description |
|---------------------------| --- |
| `--yes` | Skip confirmation prompt |

## Examples

Uninstall dfxvm and all versions of dfx, with an opportunity to confirm or cancel.

```bash
dfxvm self uninstall
```
8 changes: 6 additions & 2 deletions src/dfxvm/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ pub struct SelfUpdateOpts {}

/// Uninstall dfxvm and all versions of dfx
#[derive(Parser)]
pub struct SelfUninstallOpts {}
pub struct SelfUninstallOpts {
/// Automatically confirm un-installation.
#[clap(long)]
yes: bool,
}

pub async fn main(args: &[OsString], locations: &Locations) -> Result<ExitCode, dfxvm::Error> {
cleanup_self_updater(locations)?;
Expand All @@ -88,7 +92,7 @@ pub async fn main(args: &[OsString], locations: &Locations) -> Result<ExitCode,
Command::List(_opts) => list(locations)?,
Command::SelfCmd(opts) => match opts.command {
SelfCommand::Update(_opts) => self_update(locations).await?,
SelfCommand::Uninstall(_opts) => self_uninstall(locations)?,
SelfCommand::Uninstall(opts) => self_uninstall(opts.yes, locations)?,
},
Command::Uninstall(opts) => uninstall(opts.version, locations)?,
Command::Update(_opts) => update(locations).await?,
Expand Down
119 changes: 116 additions & 3 deletions src/dfxvm/self_uninstall.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,120 @@
use crate::error::dfxvm::SelfUninstallError;
use crate::error::{
dfxvm::{self_uninstall::UninstallProfileScriptsError, SelfUninstallError},
dfxvm_init::InteractError,
fs::{RemoveDirAllError, RemoveFileError},
};
use crate::fs::{read, remove_dir_all, remove_file, write};
use crate::installation::get_all_profile_scripts;
use crate::locations::Locations;
use console::style;
use dialoguer::Confirm;
use std::path::Path;
use sysinfo::System;

pub fn self_uninstall(_locations: &Locations) -> Result<(), SelfUninstallError> {
println!("uninstall dfxvm and all dfx versions");
pub fn self_uninstall(yes: bool, locations: &Locations) -> Result<(), SelfUninstallError> {
println!();
println!("Thanks for developing with dfx and dfxvm!");
println!();
println!("This will uninstall dfxvm and all dfx versions, and remove");
println!(
"{} from your PATH environment variable.",
style(locations.bin_dir().display()).bold()
);
println!();

if !yes && !confirm()? {
info!("canceling uninstallation");
return Ok(());
}

killall_dfx(locations);
delete_dir(&locations.network_dir())?;
delete_dir(locations.dfx_cache_dir())?;
delete_dir(locations.versions_dir())?;
delete_file(&locations.dfx_proxy_path())?;
uninstall_from_profile_scripts()?;
delete_file(&locations.env_path())?;
delete_own_binary(locations)?;
remove_dir_all(&locations.bin_dir())?;
remove_dir_all(locations.data_local_dir())?;

if locations.config_dir().exists() {
info!("did not delete the config directory because it may contain identities.");
info!("config directory: {}", locations.config_dir().display());
}

Ok(())
}

fn delete_own_binary(locations: &Locations) -> Result<(), RemoveFileError> {
delete_file(&locations.dfxvm_path())
}

fn uninstall_from_profile_scripts() -> Result<(), UninstallProfileScriptsError> {
for ps in get_all_profile_scripts() {
if ps.is_file() {
let source_bytes = ps.source_string().into_bytes();
let file_bytes = read(&ps.path)?;
// This approach copied from https://github.com/rust-lang/rustup/blob/master/src/cli/self_update/unix.rs#L56
if let Some(idx) = file_bytes
.windows(source_bytes.len())
.position(|w| w == source_bytes.as_slice())
{
// Here we rewrite the file without the offending line.
let mut new_bytes = file_bytes[..idx].to_vec();
new_bytes.extend(&file_bytes[idx + source_bytes.len()..]);
write(&ps.path, &new_bytes)?;
}
}
}
Ok(())
}

fn killall_dfx(locations: &Locations) {
while killany_dfx(locations) {
continue;
}
}

fn killany_dfx(locations: &Locations) -> bool {
let versions_dir = locations.versions_dir();
let dfx_cache_dir = locations.dfx_cache_dir();

let mut info = System::new();
info.refresh_processes();
let mut n = 0;
for (pid, proc) in info.processes() {
if let Some(exe) = proc.exe() {
if exe.starts_with(versions_dir) || exe.starts_with(dfx_cache_dir) {
info!("killing {} {}", pid.as_u32(), exe.display());
n += 1;
proc.kill();
}
}
}
n > 0
}

fn confirm() -> Result<bool, InteractError> {
let uninstall = Confirm::new()
.with_prompt("Continue?")
.default(false)
.interact()?;
Ok(uninstall)
}

fn delete_dir(dir: &Path) -> Result<(), RemoveDirAllError> {
if dir.exists() {
info!("deleting {}", dir.display());
remove_dir_all(dir)?;
}
Ok(())
}

fn delete_file(path: &Path) -> Result<(), RemoveFileError> {
if path.exists() {
info!("deleting {}", path.display());
remove_file(path)?;
}
Ok(())
}
5 changes: 2 additions & 3 deletions src/error/dfxvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ use thiserror::Error;

pub mod default;
pub mod install;
pub mod self_uninstall;
pub mod self_update;

pub use default::DefaultError;
pub use default::SetDefaultError;
pub use install::InstallError;
pub use self_uninstall::SelfUninstallError;
pub use self_update::SelfUpdateError;

#[derive(Error, Debug)]
Expand Down Expand Up @@ -80,6 +82,3 @@ pub enum UpdateError {
#[error(transparent)]
SetDefault(#[from] SetDefaultError),
}

#[derive(Error, Debug)]
pub enum SelfUninstallError {}
Loading

0 comments on commit ea1e0a1

Please sign in to comment.