Skip to content

Commit 1d5ca4a

Browse files
committed
fix(installer): clean up version directory on failed install
Extract install steps into install_new_version() helper so the caller can remove the partial version directory on failure, matching the vp upgrade behavior. Without this, a failed extraction or dependency install would leave a corrupted directory that wastes disk space and could interfere with retries.
1 parent ad0bd49 commit 1d5ca4a

1 file changed

Lines changed: 63 additions & 36 deletions

File tree

crates/vite_installer/src/main.rs

Lines changed: 63 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -220,47 +220,22 @@ async fn do_install(
220220
let version_dir = install_dir.join(&target_version);
221221
tokio::fs::create_dir_all(&version_dir).await?;
222222

223-
if !opts.quiet {
224-
print_info("extracting binary...");
225-
}
226-
install::extract_platform_package(&platform_data, &version_dir).await?;
227-
228-
let binary_path = version_dir.join("bin").join(VP_BINARY_NAME);
229-
if !tokio::fs::try_exists(&binary_path).await.unwrap_or(false) {
230-
return Err("Binary not found after extraction. The download may be corrupted.".into());
231-
}
232-
233-
install::generate_wrapper_package_json(&version_dir, &target_version).await?;
234-
235-
if !opts.quiet {
236-
print_info("installing dependencies (this may take a moment)...");
237-
}
238-
install::install_production_deps(
223+
let result = install_new_version(
224+
opts,
225+
&platform_data,
239226
&version_dir,
240-
opts.registry.as_deref(),
241-
opts.yes,
227+
install_dir,
228+
&target_version,
242229
&resolved.version,
230+
current_version.is_some(),
243231
)
244-
.await?;
245-
246-
let previous_version = if current_version.is_some() {
247-
install::save_previous_version(install_dir).await?
248-
} else {
249-
None
250-
};
251-
install::swap_current_link(install_dir, &target_version).await?;
232+
.await;
252233

253-
// Cleanup with both new and previous versions protected (matches vp upgrade)
254-
let mut protected = vec![target_version.as_str()];
255-
if let Some(ref prev) = previous_version {
256-
protected.push(prev.as_str());
257-
}
258-
if let Err(e) =
259-
install::cleanup_old_versions(install_dir, vite_setup::MAX_VERSIONS_KEEP, &protected)
260-
.await
261-
{
262-
print_warn(&format!("Old version cleanup failed (non-fatal): {e}"));
234+
// On failure, clean up the partial version directory (matches vp upgrade behavior)
235+
if result.is_err() {
236+
let _ = tokio::fs::remove_dir_all(&version_dir).await;
263237
}
238+
result?;
264239
}
265240

266241
// --- Post-activation setup (always runs, even for same-version repair) ---
@@ -347,6 +322,58 @@ fn auto_detect_node_manager(install_dir: &vite_path::AbsolutePath, interactive:
347322
interactive
348323
}
349324

325+
/// Extract, install deps, and activate a new version. Separated so the caller
326+
/// can clean up the version directory on failure.
327+
async fn install_new_version(
328+
opts: &cli::Options,
329+
platform_data: &[u8],
330+
version_dir: &AbsolutePathBuf,
331+
install_dir: &AbsolutePathBuf,
332+
target_version: &str,
333+
resolved_version: &str,
334+
has_previous: bool,
335+
) -> Result<(), Box<dyn std::error::Error>> {
336+
if !opts.quiet {
337+
print_info("extracting binary...");
338+
}
339+
install::extract_platform_package(platform_data, version_dir).await?;
340+
341+
let binary_path = version_dir.join("bin").join(VP_BINARY_NAME);
342+
if !tokio::fs::try_exists(&binary_path).await.unwrap_or(false) {
343+
return Err("Binary not found after extraction. The download may be corrupted.".into());
344+
}
345+
346+
install::generate_wrapper_package_json(version_dir, target_version).await?;
347+
348+
if !opts.quiet {
349+
print_info("installing dependencies (this may take a moment)...");
350+
}
351+
install::install_production_deps(
352+
version_dir,
353+
opts.registry.as_deref(),
354+
opts.yes,
355+
resolved_version,
356+
)
357+
.await?;
358+
359+
let previous_version =
360+
if has_previous { install::save_previous_version(install_dir).await? } else { None };
361+
install::swap_current_link(install_dir, target_version).await?;
362+
363+
// Cleanup with both new and previous versions protected (matches vp upgrade)
364+
let mut protected = vec![target_version];
365+
if let Some(ref prev) = previous_version {
366+
protected.push(prev.as_str());
367+
}
368+
if let Err(e) =
369+
install::cleanup_old_versions(install_dir, vite_setup::MAX_VERSIONS_KEEP, &protected).await
370+
{
371+
print_warn(&format!("Old version cleanup failed (non-fatal): {e}"));
372+
}
373+
374+
Ok(())
375+
}
376+
350377
/// Windows locks running `.exe` files — rename the old one out of the way before copying.
351378
#[cfg(windows)]
352379
async fn replace_windows_exe(

0 commit comments

Comments
 (0)