From 206984f3db4845518352db65cc83cfeed910dbf5 Mon Sep 17 00:00:00 2001 From: Alex Verkhovsky Date: Sat, 21 Mar 2026 20:02:52 -0600 Subject: [PATCH] refactor(installer): extract _backupUserFiles to deduplicate backup logic The identical custom-file and modified-file backup loops appeared in both the regular update and quick-update branches of install(). Extract into a single _backupUserFiles method that returns temp directory paths. Co-Authored-By: Claude Opus 4.6 (1M context) --- tools/cli/installers/lib/core/installer.js | 114 +++++++++------------ 1 file changed, 50 insertions(+), 64 deletions(-) diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 22a450b16..58008a2e1 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -184,39 +184,9 @@ class Installer { await this._scanCachedCustomModules(paths); - // If there are custom files, back them up temporarily - if (customFiles.length > 0) { - const tempBackupDir = path.join(paths.projectRoot, '_bmad-custom-backup-temp'); - await fs.ensureDir(tempBackupDir); - - spinner.start(`Backing up ${customFiles.length} custom files...`); - for (const customFile of customFiles) { - const relativePath = path.relative(paths.bmadDir, customFile); - const backupPath = path.join(tempBackupDir, relativePath); - await fs.ensureDir(path.dirname(backupPath)); - await fs.copy(customFile, backupPath); - } - spinner.stop(`Backed up ${customFiles.length} custom files`); - - customConfig._tempBackupDir = tempBackupDir; - } - - // For modified files, back them up to temp directory (will be restored as .bak files after install) - if (modifiedFiles.length > 0) { - const tempModifiedBackupDir = path.join(paths.projectRoot, '_bmad-modified-backup-temp'); - await fs.ensureDir(tempModifiedBackupDir); - - spinner.start(`Backing up ${modifiedFiles.length} modified files...`); - for (const modifiedFile of modifiedFiles) { - const relativePath = path.relative(paths.bmadDir, modifiedFile.path); - const tempBackupPath = path.join(tempModifiedBackupDir, relativePath); - await fs.ensureDir(path.dirname(tempBackupPath)); - await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true }); - } - spinner.stop(`Backed up ${modifiedFiles.length} modified files`); - - customConfig._tempModifiedBackupDir = tempModifiedBackupDir; - } + const backupDirs = await this._backupUserFiles(paths, customFiles, modifiedFiles, spinner); + customConfig._tempBackupDir = backupDirs.tempBackupDir; + customConfig._tempModifiedBackupDir = backupDirs.tempModifiedBackupDir; } } else if (existingInstall.installed && config.isQuickUpdate()) { // Quick update mode - automatically treat as update without prompting @@ -233,37 +203,9 @@ class Installer { await this._scanCachedCustomModules(paths); - // Back up custom files - if (customFiles.length > 0) { - const tempBackupDir = path.join(paths.projectRoot, '_bmad-custom-backup-temp'); - await fs.ensureDir(tempBackupDir); - - spinner.start(`Backing up ${customFiles.length} custom files...`); - for (const customFile of customFiles) { - const relativePath = path.relative(paths.bmadDir, customFile); - const backupPath = path.join(tempBackupDir, relativePath); - await fs.ensureDir(path.dirname(backupPath)); - await fs.copy(customFile, backupPath); - } - spinner.stop(`Backed up ${customFiles.length} custom files`); - customConfig._tempBackupDir = tempBackupDir; - } - - // Back up modified files - if (modifiedFiles.length > 0) { - const tempModifiedBackupDir = path.join(paths.projectRoot, '_bmad-modified-backup-temp'); - await fs.ensureDir(tempModifiedBackupDir); - - spinner.start(`Backing up ${modifiedFiles.length} modified files...`); - for (const modifiedFile of modifiedFiles) { - const relativePath = path.relative(paths.bmadDir, modifiedFile.path); - const tempBackupPath = path.join(tempModifiedBackupDir, relativePath); - await fs.ensureDir(path.dirname(tempBackupPath)); - await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true }); - } - spinner.stop(`Backed up ${modifiedFiles.length} modified files`); - customConfig._tempModifiedBackupDir = tempModifiedBackupDir; - } + const backupDirs = await this._backupUserFiles(paths, customFiles, modifiedFiles, spinner); + customConfig._tempBackupDir = backupDirs.tempBackupDir; + customConfig._tempModifiedBackupDir = backupDirs.tempModifiedBackupDir; } // Now collect tool configurations after we know if it's a reinstall @@ -872,6 +814,50 @@ class Installer { } } + /** + * Back up custom and modified files to temp directories before overwriting. + * Returns the temp directory paths (or undefined if no files to back up). + * @param {Object} paths - InstallPaths instance + * @param {string[]} customFiles - Absolute paths of custom (user-added) files + * @param {Object[]} modifiedFiles - Array of { path, relativePath } for modified files + * @param {Object} spinner - Spinner instance for progress display + * @returns {Object} { tempBackupDir, tempModifiedBackupDir } — undefined if no files + */ + async _backupUserFiles(paths, customFiles, modifiedFiles, spinner) { + let tempBackupDir; + let tempModifiedBackupDir; + + if (customFiles.length > 0) { + tempBackupDir = path.join(paths.projectRoot, '_bmad-custom-backup-temp'); + await fs.ensureDir(tempBackupDir); + + spinner.start(`Backing up ${customFiles.length} custom files...`); + for (const customFile of customFiles) { + const relativePath = path.relative(paths.bmadDir, customFile); + const backupPath = path.join(tempBackupDir, relativePath); + await fs.ensureDir(path.dirname(backupPath)); + await fs.copy(customFile, backupPath); + } + spinner.stop(`Backed up ${customFiles.length} custom files`); + } + + if (modifiedFiles.length > 0) { + tempModifiedBackupDir = path.join(paths.projectRoot, '_bmad-modified-backup-temp'); + await fs.ensureDir(tempModifiedBackupDir); + + spinner.start(`Backing up ${modifiedFiles.length} modified files...`); + for (const modifiedFile of modifiedFiles) { + const relativePath = path.relative(paths.bmadDir, modifiedFile.path); + const tempBackupPath = path.join(tempModifiedBackupDir, relativePath); + await fs.ensureDir(path.dirname(tempBackupPath)); + await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true }); + } + spinner.stop(`Backed up ${modifiedFiles.length} modified files`); + } + + return { tempBackupDir, tempModifiedBackupDir }; + } + /** * Install official (non-custom) modules. * @param {Object} config - Installation configuration