From 68cb6b3d7d87d97dcf2edd4b47df3a629ebf5ce6 Mon Sep 17 00:00:00 2001 From: Will Chen Date: Mon, 19 May 2025 14:59:55 -0700 Subject: [PATCH] Robust app folder rename (#200) Fixes #89 --- src/ipc/handlers/app_handlers.ts | 44 +++++++++++++++++++++++++------- src/ipc/utils/file_utils.ts | 20 --------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/ipc/handlers/app_handlers.ts b/src/ipc/handlers/app_handlers.ts index 5344437..44ec8fc 100644 --- a/src/ipc/handlers/app_handlers.ts +++ b/src/ipc/handlers/app_handlers.ts @@ -12,10 +12,7 @@ import { promises as fsPromises } from "node:fs"; // Import our utility modules import { withLock } from "../utils/lock_utils"; -import { - copyDirectoryRecursive, - getFilesRecursively, -} from "../utils/file_utils"; +import { getFilesRecursively } from "../utils/file_utils"; import { runningApps, processCounter, @@ -192,10 +189,15 @@ export function registerAppHandlers() { // Start async operations in background try { - // Copy scaffold asynchronously - await copyDirectoryRecursive( + await fsPromises.cp( path.join(__dirname, "..", "..", "scaffold"), fullAppPath, + { + recursive: true, + // Scaffold should *not* have node_modules anyways, but + // just in case, we filter it out. + filter: (source) => !source.includes("node_modules"), + }, ); // Initialize git repo and create first commit await git.init({ @@ -666,8 +668,11 @@ export function registerAppHandlers() { recursive: true, }); - // Move the files - await fsPromises.rename(oldAppPath, newAppPath); + // Copy the directory without node_modules + await fsPromises.cp(oldAppPath, newAppPath, { + recursive: true, + filter: (source) => !source.includes("node_modules"), + }); } catch (error: any) { logger.error( `Error moving app files from ${oldAppPath} to ${newAppPath}:`, @@ -675,6 +680,21 @@ export function registerAppHandlers() { ); throw new Error(`Failed to move app files: ${error.message}`); } + + try { + // Delete the old directory + await fsPromises.rm(oldAppPath, { recursive: true, force: true }); + } catch (error: any) { + // Why is this just a warning? This happens quite often on Windows + // because it has an aggressive file lock. + // + // Not deleting the old directory is annoying, but not a big deal + // since the user can do it themselves if they need to. + logger.warn( + `Error deleting old app directory ${oldAppPath}:`, + error, + ); + } } // Update app in database @@ -693,7 +713,13 @@ export function registerAppHandlers() { // Attempt to rollback the file move if (newAppPath !== oldAppPath) { try { - await fsPromises.rename(newAppPath, oldAppPath); + // Copy back from new to old + await fsPromises.cp(newAppPath, oldAppPath, { + recursive: true, + filter: (source) => !source.includes("node_modules"), + }); + // Delete the new directory + await fsPromises.rm(newAppPath, { recursive: true, force: true }); } catch (rollbackError) { logger.error( `Failed to rollback file move during rename error:`, diff --git a/src/ipc/utils/file_utils.ts b/src/ipc/utils/file_utils.ts index a4680b9..9feba02 100644 --- a/src/ipc/utils/file_utils.ts +++ b/src/ipc/utils/file_utils.ts @@ -1,6 +1,5 @@ import fs from "node:fs"; import path from "node:path"; -import { promises as fsPromises } from "node:fs"; /** * Recursively gets all files in a directory, excluding node_modules and .git @@ -32,22 +31,3 @@ export function getFilesRecursively(dir: string, baseDir: string): string[] { return files; } - -export async function copyDirectoryRecursive( - source: string, - destination: string, -) { - await fsPromises.mkdir(destination, { recursive: true }); - const entries = await fsPromises.readdir(source, { withFileTypes: true }); - - for (const entry of entries) { - const srcPath = path.join(source, entry.name); - const destPath = path.join(destination, entry.name); - - if (entry.isDirectory()) { - await copyDirectoryRecursive(srcPath, destPath); - } else { - await fsPromises.copyFile(srcPath, destPath); - } - } -}