Skip to content

Commit

Permalink
feat: clean up previous dfx installation (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
ericswanson-dfinity authored Feb 5, 2024
1 parent ea1e0a1 commit 4ee4ecc
Show file tree
Hide file tree
Showing 15 changed files with 411 additions and 32 deletions.
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ 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.
- `dfxvm-init` now removes older dfx versions found on the path, by default.
- `dfxvm-init` deletes the uninstall.sh script that the dfx install script used to create.

## [0.2.0] - 2024-01-30

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

## [0.1.3] - 2024-01-19

Expand Down
6 changes: 3 additions & 3 deletions src/dfxvm/self_uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub fn self_uninstall(yes: bool, locations: &Locations) -> Result<(), SelfUninst

killall_dfx(locations);
delete_dir(&locations.network_dir())?;
delete_dir(locations.dfx_cache_dir())?;
delete_dir(locations.dfinity_cache_dir())?;
delete_dir(locations.versions_dir())?;
delete_file(&locations.dfx_proxy_path())?;
uninstall_from_profile_scripts()?;
Expand Down Expand Up @@ -78,14 +78,14 @@ fn killall_dfx(locations: &Locations) {

fn killany_dfx(locations: &Locations) -> bool {
let versions_dir = locations.versions_dir();
let dfx_cache_dir = locations.dfx_cache_dir();
let dfinity_cache_versions_dir = locations.dfinity_cache_versions_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) {
if exe.starts_with(versions_dir) || exe.starts_with(&dfinity_cache_versions_dir) {
info!("killing {} {}", pid.as_u32(), exe.display());
n += 1;
proc.kill();
Expand Down
75 changes: 69 additions & 6 deletions src/dfxvm_init/initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@ use crate::dfxvm;
use crate::dfxvm_init::{
plan::{DfxVersion, Plan, PlanOptions},
ui,
ui::Confirmation,
ui::{select_deletion_strategy, Confirmation, DeletionStrategy},
};
use crate::error::{
dfxvm_init,
dfxvm_init::{ExecutePlanError, UpdateProfileScriptsError},
fs::WriteFileError,
dfxvm_init::{
DeleteDfxOnPathError, DeleteDfxOnPathError::CallSudoRm, ExecutePlanError,
UpdateProfileScriptsError,
},
fs::{RemoveFileError, WriteFileError},
};
use crate::fs::{append_to_file, create_dir_all, read_to_string};
use crate::fs::{append_to_file, create_dir_all, read_to_string, remove_file};
use crate::installation::{env_file_contents, install_binaries, ProfileScript};
use crate::locations::Locations;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::process::Command;

pub async fn initialize(
options: PlanOptions,
confirmation: Option<Confirmation>,
locations: &Locations,
) -> Result<(), dfxvm_init::Error> {
let mut plan = Plan::new(options, locations);
let mut plan = Plan::new(options, locations)?;

ui::display::introduction(&plan);

Expand All @@ -46,6 +50,11 @@ pub async fn initialize(
}

pub async fn execute(plan: &Plan, locations: &Locations) -> Result<(), ExecutePlanError> {
remove_uninstall_script(locations)?;
if plan.options.delete_dfx_on_path {
delete_dfx_on_path(plan)?;
}

create_dir_all(&plan.bin_dir)?;

create_env_file(&plan.env_path)?;
Expand All @@ -64,6 +73,60 @@ pub async fn execute(plan: &Plan, locations: &Locations) -> Result<(), ExecutePl
Ok(())
}

fn remove_uninstall_script(locations: &Locations) -> Result<(), RemoveFileError> {
let path = locations.dfinity_cache_dir().join("uninstall.sh");
if path.exists() {
remove_file(&path)?;
info!("deleted: {}", path.display());
}
Ok(())
}

fn delete_dfx_on_path(plan: &Plan) -> Result<(), DeleteDfxOnPathError> {
loop {
let remaining = delete_and_filter(&plan.dfx_on_path);

if remaining.is_empty() {
break Ok(());
}

ui::display::need_to_delete_old_dfx(plan);

match select_deletion_strategy()? {
DeletionStrategy::Manual => {}
DeletionStrategy::CallSudo => sudo_rm(remaining)?,
DeletionStrategy::DontDelete => break Ok(()),
}
}
}

fn delete_and_filter(dfx_on_path: &[PathBuf]) -> Vec<PathBuf> {
dfx_on_path
.iter()
.filter(|p| {
if !p.exists() {
false
} else if remove_file(p).is_ok() {
info!("deleted: {}", p.display());
false
} else {
true
}
})
.cloned()
.collect()
}

fn sudo_rm(remaining: Vec<PathBuf>) -> Result<(), DeleteDfxOnPathError> {
let _ = Command::new("sudo")
.arg("rm")
.arg("-f")
.args(remaining.iter().map(|p| (*p).clone().into_os_string()))
.status()
.map_err(CallSudoRm)?;
Ok(())
}

fn create_env_file(path: &Path) -> Result<(), WriteFileError> {
info!("creating {}", path.display());
crate::fs::write(path, env_file_contents())
Expand Down
44 changes: 40 additions & 4 deletions src/dfxvm_init/plan.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::error::fs::CanonicalizePathError;
use crate::fs::canonicalize;
use crate::installation::{get_detected_profile_scripts, get_env_path_user_facing, ProfileScript};
use crate::locations::Locations;
use semver::Version;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

#[derive(Clone)]
pub enum DfxVersion {
Expand All @@ -13,13 +15,15 @@ pub enum DfxVersion {
pub struct PlanOptions {
pub dfx_version: DfxVersion,
pub modify_path: bool,
pub delete_dfx_on_path: bool,
}

impl PlanOptions {
pub fn new() -> Self {
Self {
dfx_version: DfxVersion::Latest,
modify_path: true,
delete_dfx_on_path: true,
}
}

Expand All @@ -36,6 +40,13 @@ impl PlanOptions {
..self
}
}

pub fn delete_dfx_on_path(self, delete_dfx_on_path: bool) -> Self {
Self {
delete_dfx_on_path,
..self
}
}
}

pub struct Plan {
Expand All @@ -51,25 +62,50 @@ pub struct Plan {
// altering profile scripts.
pub env_path_user_facing: String,

pub dfx_on_path: Vec<PathBuf>,
pub profile_scripts: Vec<ProfileScript>,
}

impl Plan {
pub fn new(options: PlanOptions, locations: &Locations) -> Self {
pub fn new(options: PlanOptions, locations: &Locations) -> Result<Self, CanonicalizePathError> {
let bin_dir = locations.bin_dir();
let env_path = locations.data_local_dir().join("env");
let env_path_user_facing = get_env_path_user_facing().to_string();
let profile_scripts = get_detected_profile_scripts();
Self {
let dfx_on_path = legacy_binaries(&locations.dfx_proxy_path())?;

Ok(Self {
options,
bin_dir,
env_path,
env_path_user_facing,
dfx_on_path,
profile_scripts,
}
})
}

pub fn with_options(self, options: PlanOptions) -> Self {
Self { options, ..self }
}
}

// find dfx binaries on PATH, excluding the dfx proxy
fn legacy_binaries(dfx_proxy: &Path) -> Result<Vec<PathBuf>, CanonicalizePathError> {
let all_dfx_on_path = std::env::split_paths(&std::env::var_os("PATH").unwrap_or_default())
.filter_map(|dir| {
let dfx_on_path = dir.join("dfx");
dfx_on_path.exists().then(|| canonicalize(&dfx_on_path))
})
.collect::<Result<Vec<_>, _>>()?;

let legacy_binaries = if dfx_proxy.exists() {
let canonical_dfx_proxy = canonicalize(dfx_proxy)?;
all_dfx_on_path
.into_iter()
.filter(|p| *p != canonical_dfx_proxy)
.collect()
} else {
all_dfx_on_path
};
Ok(legacy_binaries)
}
2 changes: 2 additions & 0 deletions src/dfxvm_init/ui.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod confirm;
mod customize;
mod deletion_strategy;
pub mod display;

pub use confirm::confirm;
pub use confirm::Confirmation;
pub use customize::customize;
pub use deletion_strategy::{select_deletion_strategy, DeletionStrategy};
13 changes: 13 additions & 0 deletions src/dfxvm_init/ui/customize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ pub fn customize(plan: Plan) -> Result<Plan, InteractError> {
let dfx_version = select_dfx_version(&options.dfx_version)?;
options = options.with_dfx_version(dfx_version);

if !plan.dfx_on_path.is_empty() {
let delete_dfx_on_path = delete_dfx_on_path(options.delete_dfx_on_path)?;
options = options.delete_dfx_on_path(delete_dfx_on_path);
}

let modify_path = select_modify_path(options.modify_path)?;
options = options.with_modify_path(modify_path);

Expand Down Expand Up @@ -49,6 +54,14 @@ fn select_dfx_version(install_dfx: &DfxVersion) -> Result<DfxVersion, InteractEr
Ok(dfx_version)
}

fn delete_dfx_on_path(current: bool) -> Result<bool, InteractError> {
let modify = Confirm::new()
.with_prompt("Delete dfx binaries found on PATH?")
.default(current)
.interact()?;
Ok(modify)
}

fn select_modify_path(current: bool) -> Result<bool, InteractError> {
let modify = Confirm::new()
.with_prompt("Modify PATH variable?")
Expand Down
31 changes: 31 additions & 0 deletions src/dfxvm_init/ui/deletion_strategy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::error::dfxvm_init::InteractError;
use dialoguer::Select;

pub enum DeletionStrategy {
Manual,
CallSudo,
DontDelete,
}

pub fn select_deletion_strategy() -> Result<DeletionStrategy, InteractError> {
let items = vec![
"I've deleted them manually (default)",
"Call sudo rm for me (I'll enter my password)",
"Don't delete anything. I'll do it later",
];

let index = Select::new()
.with_prompt("How would you like to proceed?")
.default(0)
.items(&items)
.interact()?;

let deletion_strategy = match index {
0 => DeletionStrategy::Manual,
1 => DeletionStrategy::CallSudo,
2 => DeletionStrategy::DontDelete,
_ => unreachable!(),
};

Ok(deletion_strategy)
}
Loading

0 comments on commit 4ee4ecc

Please sign in to comment.