fix(installer): address CodeRabbit review feedback on badge integration

- Derive BADGE_PATTERN from BADGE_URL constant (configurable)
- Fix git remote regex to support repo names with dots
- Add try/catch around badge injection so IO errors don't abort install
- Add try/catch around badge cleanup in uninstall (best-effort)
- Pass noBadge through quickUpdate flow

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
nick 2026-05-01 15:54:05 +08:00
parent 4d0b0fdcd1
commit a8666294cb
3 changed files with 22 additions and 10 deletions

View File

@ -140,14 +140,18 @@ module.exports = {
await installer.uninstallModules(projectDir); await installer.uninstallModules(projectDir);
s.stop('Modules & data removed'); s.stop('Modules & data removed');
// Remove BMAD badge from README // Remove BMAD badge from README (best-effort)
const badge = require('../core/badge'); try {
const readmePath = await badge.findReadme(projectDir); const badge = require('../core/badge');
if (readmePath) { const readmePath = await badge.findReadme(projectDir);
const content = await fs.readFile(readmePath, 'utf8'); if (readmePath) {
if (badge.hasBadge(content)) { const content = await fs.readFile(readmePath, 'utf8');
await fs.writeFile(readmePath, badge.removeBadge(content), 'utf8'); if (badge.hasBadge(content)) {
await fs.writeFile(readmePath, badge.removeBadge(content), 'utf8');
}
} }
} catch (error) {
await prompts.log.warn(`Badge cleanup skipped: ${error.message}`);
} }
} }

View File

@ -3,7 +3,10 @@ const { execSync } = require('node:child_process');
const fs = require('../fs-native'); const fs = require('../fs-native');
const BADGE_URL = 'https://bmad-badge.vercel.app'; const BADGE_URL = 'https://bmad-badge.vercel.app';
const BADGE_PATTERN = /\[!\[BMAD\]\(https:\/\/bmad-badge\.vercel\.app\/[^\)]+\)\]\(https:\/\/github\.com\/bmad-code-org\/BMAD-METHOD\)/; const escapedBadgeUrl = BADGE_URL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const BADGE_PATTERN = new RegExp(
`\\[!\\[BMAD\\]\\(${escapedBadgeUrl}/[^)]+\\)\\]\\(https://github\\.com/bmad-code-org/BMAD-METHOD\\)`,
);
const README_NAMES = ['README.md', 'readme.md', 'README', 'readme']; const README_NAMES = ['README.md', 'readme.md', 'README', 'readme'];
function resolveGitRemote(projectDir) { function resolveGitRemote(projectDir) {
@ -15,7 +18,7 @@ function resolveGitRemote(projectDir) {
}).trim(); }).trim();
// https://github.com/owner/repo.git // https://github.com/owner/repo.git
const httpsMatch = raw.match(/github\.com[:/]([^/]+)\/([^/.]+)/); const httpsMatch = raw.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
if (httpsMatch) { if (httpsMatch) {
return { owner: httpsMatch[1], repo: httpsMatch[2] }; return { owner: httpsMatch[1], repo: httpsMatch[2] };
} }

View File

@ -105,7 +105,11 @@ class Installer {
// Inject BMAD badge into README if applicable // Inject BMAD badge into README if applicable
if (!config.noBadge) { if (!config.noBadge) {
await this._injectBadgeIfNeeded(paths.projectRoot, addResult); try {
await this._injectBadgeIfNeeded(paths.projectRoot, addResult);
} catch (error) {
addResult('Badge', 'warn', `skipped: ${error.message}`);
}
} }
// Render consolidated summary // Render consolidated summary
@ -1337,6 +1341,7 @@ class Installer {
directory: projectDir, directory: projectDir,
modules: modulesToUpdate, modules: modulesToUpdate,
ides: configuredIdes, ides: configuredIdes,
noBadge: config.noBadge,
coreConfig: quickModules.collectedConfig.core, coreConfig: quickModules.collectedConfig.core,
moduleConfigs: quickModules.collectedConfig, moduleConfigs: quickModules.collectedConfig,
// Forward `--set` overrides so the post-install patch step // Forward `--set` overrides so the post-install patch step