From 7b1bfbf2b94fe8308a61cb1f7b970a8b2cba1bbc Mon Sep 17 00:00:00 2001 From: Dov Benyomin Sohacheski Date: Thu, 11 Jun 2026 23:19:35 +0300 Subject: [PATCH] fix(installer): track _bmad/.gitignore in manifest on every install run Address review: the early return when config.user.toml was already ignored skipped installedFiles.add, dropping _bmad/.gitignore from files-manifest.csv on re-installs. Suite 46 now uses a fresh Installer per run, as production does, so the regression is actually exercised. --- test/test-installation-components.js | 11 ++++++++--- tools/installer/core/installer.js | 9 ++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/test/test-installation-components.js b/test/test-installation-components.js index 862100b8f..5e9b25f41 100644 --- a/test/test-installation-components.js +++ b/test/test-installation-components.js @@ -3352,17 +3352,22 @@ async function runTests() { // The pre-existing custom/*.user.toml rule is still seeded alongside it. assert(await fs.pathExists(path.join(customDir46, '.gitignore')), '_installSharedScripts still seeds _bmad/custom/.gitignore'); - // Idempotent: a second install must not duplicate the entry. - await installer46._installSharedScripts(paths46); + // Idempotent: a second install must not duplicate the entry. Fresh Installer + // per run, as in production — reuse would hide a failure to re-track the file. + const installer46b = new Installer(); + await installer46b._installSharedScripts(paths46); const occurrences46 = (await fs.readFile(bmadGitignore46, 'utf8')).split(/\r?\n/).filter((line) => line === 'config.user.toml').length; assert(occurrences46 === 1, 'second install does not duplicate the config.user.toml entry'); + assert(installer46b.installedFiles.has(bmadGitignore46), 're-install tracks _bmad/.gitignore even when the entry already exists'); // An existing .gitignore with unrelated rules is topped up, not clobbered. await fs.writeFile(bmadGitignore46, 'notes.local.md\n'); - await installer46._installSharedScripts(paths46); + const installer46c = new Installer(); + await installer46c._installSharedScripts(paths46); const toppedUp46 = await fs.readFile(bmadGitignore46, 'utf8'); assert(toppedUp46.includes('notes.local.md'), 'existing .gitignore rules are preserved'); assert(toppedUp46.split(/\r?\n/).includes('config.user.toml'), 'config.user.toml is appended to an existing .gitignore'); + assert(installer46c.installedFiles.has(bmadGitignore46), 'appending install tracks _bmad/.gitignore'); } catch (error) { console.log(`${colors.red}Test Suite 46 setup failed: ${error.message}${colors.reset}`); console.log(error.stack); diff --git a/tools/installer/core/installer.js b/tools/installer/core/installer.js index c779be52b..ee38a8281 100644 --- a/tools/installer/core/installer.js +++ b/tools/installer/core/installer.js @@ -702,12 +702,15 @@ class Installer { if (await fs.pathExists(gitignorePath)) { const existing = await fs.readFile(gitignorePath, 'utf8'); - if (existing.split(/\r?\n/).some((line) => line.trim() === entry)) return; - const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n' : ''; - await fs.writeFile(gitignorePath, `${existing}${separator}${entry}\n`, 'utf8'); + if (!existing.split(/\r?\n/).some((line) => line.trim() === entry)) { + const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n' : ''; + await fs.writeFile(gitignorePath, `${existing}${separator}${entry}\n`, 'utf8'); + } } else { await fs.writeFile(gitignorePath, `${entry}\n`, 'utf8'); } + // Track on every path — each run starts a fresh Installer, so skipping the + // already-correct file would drop it from files-manifest.csv on re-installs. this.installedFiles.add(gitignorePath); }