Merge 8927e207e8 into 1040c3c306
This commit is contained in:
commit
dded90b153
|
|
@ -15,6 +15,7 @@ const path = require('node:path');
|
||||||
const os = require('node:os');
|
const os = require('node:os');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const { ConfigCollector } = require('../tools/cli/installers/lib/core/config-collector');
|
const { ConfigCollector } = require('../tools/cli/installers/lib/core/config-collector');
|
||||||
|
const { applyDefaultCoreConfig, clearCoreConfigDefaultsCache, getDefaultCoreConfig } = require('../tools/cli/lib/core-config-defaults');
|
||||||
const { ManifestGenerator } = require('../tools/cli/installers/lib/core/manifest-generator');
|
const { ManifestGenerator } = require('../tools/cli/installers/lib/core/manifest-generator');
|
||||||
const { IdeManager } = require('../tools/cli/installers/lib/ide/manager');
|
const { IdeManager } = require('../tools/cli/installers/lib/ide/manager');
|
||||||
const { clearCache, loadPlatformCodes } = require('../tools/cli/installers/lib/ide/platform-codes');
|
const { clearCache, loadPlatformCodes } = require('../tools/cli/installers/lib/ide/platform-codes');
|
||||||
|
|
@ -2028,6 +2029,38 @@ async function runTests() {
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Test Suite 34: Core Config Default Backfill
|
||||||
|
// ============================================================
|
||||||
|
console.log(`${colors.yellow}Test Suite 34: Core Config Default Backfill${colors.reset}\n`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
clearCoreConfigDefaultsCache();
|
||||||
|
const defaults = await getDefaultCoreConfig();
|
||||||
|
const normalized = applyDefaultCoreConfig(
|
||||||
|
{
|
||||||
|
user_name: '{user_name}',
|
||||||
|
communication_language: 'Spanish',
|
||||||
|
document_output_language: '',
|
||||||
|
output_folder: '{output_folder}',
|
||||||
|
},
|
||||||
|
defaults,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(normalized.appliedDefaults === true, 'Core config backfill reports when defaults were applied');
|
||||||
|
assert(normalized.coreConfig.user_name === defaults.user_name, 'Core config backfill replaces unresolved user_name placeholder');
|
||||||
|
assert(normalized.coreConfig.communication_language === 'Spanish', 'Core config backfill preserves existing valid values');
|
||||||
|
assert(
|
||||||
|
normalized.coreConfig.document_output_language === defaults.document_output_language,
|
||||||
|
'Core config backfill replaces blank document output language',
|
||||||
|
);
|
||||||
|
assert(normalized.coreConfig.output_folder === defaults.output_folder, 'Core config backfill replaces unresolved output_folder');
|
||||||
|
} catch (error) {
|
||||||
|
assert(false, 'Core config default backfill test succeeds', error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Summary
|
// Summary
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
const os = require('node:os');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const yaml = require('yaml');
|
||||||
|
const prompts = require('./prompts');
|
||||||
|
const { getModulePath } = require('./project-root');
|
||||||
|
|
||||||
|
let cachedCoreConfigDefaults = null;
|
||||||
|
|
||||||
|
function getFallbackUsername() {
|
||||||
|
let safeUsername;
|
||||||
|
try {
|
||||||
|
safeUsername = os.userInfo().username;
|
||||||
|
} catch {
|
||||||
|
safeUsername = process.env.USER || process.env.USERNAME || 'User';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof safeUsername !== 'string' || safeUsername.trim() === '') {
|
||||||
|
return 'User';
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedUsername = safeUsername.trim();
|
||||||
|
return normalizedUsername.charAt(0).toUpperCase() + normalizedUsername.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDefaultString(value, fallback) {
|
||||||
|
return typeof value === 'string' && value.trim() !== '' ? value.trim() : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMissingOrUnresolvedCoreConfigValue(value) {
|
||||||
|
return value == null || (typeof value === 'string' && (value.trim() === '' || /^\{[^}]+\}$/.test(value.trim())));
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDefaultCoreConfig(coreConfig = {}, defaults = {}) {
|
||||||
|
const normalizedConfig = { ...coreConfig };
|
||||||
|
let appliedDefaults = false;
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(defaults)) {
|
||||||
|
if (isMissingOrUnresolvedCoreConfigValue(normalizedConfig[key])) {
|
||||||
|
normalizedConfig[key] = value;
|
||||||
|
appliedDefaults = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { coreConfig: normalizedConfig, appliedDefaults };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDefaultCoreConfig() {
|
||||||
|
if (cachedCoreConfigDefaults) {
|
||||||
|
return { ...cachedCoreConfigDefaults };
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackDefaults = {
|
||||||
|
user_name: getFallbackUsername(),
|
||||||
|
communication_language: 'English',
|
||||||
|
document_output_language: 'English',
|
||||||
|
output_folder: '_bmad-output',
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const moduleYamlPath = getModulePath('core', 'module.yaml');
|
||||||
|
const moduleConfig = yaml.parse(await fs.readFile(moduleYamlPath, 'utf8')) || {};
|
||||||
|
|
||||||
|
cachedCoreConfigDefaults = {
|
||||||
|
user_name: normalizeDefaultString(moduleConfig.user_name?.default, fallbackDefaults.user_name),
|
||||||
|
communication_language: normalizeDefaultString(moduleConfig.communication_language?.default, fallbackDefaults.communication_language),
|
||||||
|
document_output_language: normalizeDefaultString(
|
||||||
|
moduleConfig.document_output_language?.default,
|
||||||
|
fallbackDefaults.document_output_language,
|
||||||
|
),
|
||||||
|
output_folder: normalizeDefaultString(moduleConfig.output_folder?.default, fallbackDefaults.output_folder),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
await prompts.log.warn(`Failed to load module.yaml, falling back to defaults: ${error.message}`);
|
||||||
|
cachedCoreConfigDefaults = fallbackDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...cachedCoreConfigDefaults };
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCoreConfigDefaultsCache() {
|
||||||
|
cachedCoreConfigDefaults = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
applyDefaultCoreConfig,
|
||||||
|
clearCoreConfigDefaultsCache,
|
||||||
|
getDefaultCoreConfig,
|
||||||
|
isMissingOrUnresolvedCoreConfigValue,
|
||||||
|
};
|
||||||
|
|
@ -2,6 +2,7 @@ const path = require('node:path');
|
||||||
const os = require('node:os');
|
const os = require('node:os');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const { CLIUtils } = require('./cli-utils');
|
const { CLIUtils } = require('./cli-utils');
|
||||||
|
const { applyDefaultCoreConfig, getDefaultCoreConfig: loadDefaultCoreConfig } = require('./core-config-defaults');
|
||||||
const { CustomHandler } = require('../installers/lib/custom/handler');
|
const { CustomHandler } = require('../installers/lib/custom/handler');
|
||||||
const { ExternalModuleManager } = require('../installers/lib/modules/external-manager');
|
const { ExternalModuleManager } = require('../installers/lib/modules/external-manager');
|
||||||
const prompts = require('./prompts');
|
const prompts = require('./prompts');
|
||||||
|
|
@ -47,6 +48,21 @@ class UI {
|
||||||
}
|
}
|
||||||
confirmedDirectory = expandedDir;
|
confirmedDirectory = expandedDir;
|
||||||
await prompts.log.info(`Using directory from command-line: ${confirmedDirectory}`);
|
await prompts.log.info(`Using directory from command-line: ${confirmedDirectory}`);
|
||||||
|
} else if (options.yes) {
|
||||||
|
// Default to current directory when --yes flag is set
|
||||||
|
let cwd;
|
||||||
|
try {
|
||||||
|
cwd = process.cwd();
|
||||||
|
} catch (error) {
|
||||||
|
await prompts.log.error(`Failed to resolve current directory (--yes flag): ${error.message}`);
|
||||||
|
throw new Error(`Unable to determine current directory: ${error.message}`);
|
||||||
|
}
|
||||||
|
const validation = this.validateDirectorySync(cwd);
|
||||||
|
if (validation) {
|
||||||
|
throw new Error(`Invalid current directory: ${validation}`);
|
||||||
|
}
|
||||||
|
confirmedDirectory = cwd;
|
||||||
|
await prompts.log.info(`Using current directory (--yes flag): ${confirmedDirectory}`);
|
||||||
} else {
|
} else {
|
||||||
confirmedDirectory = await this.getConfirmedDirectory();
|
confirmedDirectory = await this.getConfirmedDirectory();
|
||||||
}
|
}
|
||||||
|
|
@ -823,6 +839,14 @@ class UI {
|
||||||
return { existingInstall, installedModuleIds, bmadDir };
|
return { existingInstall, installedModuleIds, bmadDir };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get default core config values by reading from src/core/module.yaml
|
||||||
|
* @returns {Object} Default core config with user_name, communication_language, document_output_language, output_folder
|
||||||
|
*/
|
||||||
|
async getDefaultCoreConfig() {
|
||||||
|
return loadDefaultCoreConfig();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect core configuration
|
* Collect core configuration
|
||||||
* @param {string} directory - Installation directory
|
* @param {string} directory - Installation directory
|
||||||
|
|
@ -866,27 +890,21 @@ class UI {
|
||||||
(!options.userName || !options.communicationLanguage || !options.documentOutputLanguage || !options.outputFolder)
|
(!options.userName || !options.communicationLanguage || !options.documentOutputLanguage || !options.outputFolder)
|
||||||
) {
|
) {
|
||||||
await configCollector.collectModuleConfig('core', directory, false, true);
|
await configCollector.collectModuleConfig('core', directory, false, true);
|
||||||
|
} else if (options.yes) {
|
||||||
|
const defaults = await this.getDefaultCoreConfig();
|
||||||
|
const normalizedConfig = applyDefaultCoreConfig(configCollector.collectedConfig.core, defaults);
|
||||||
|
configCollector.collectedConfig.core = normalizedConfig.coreConfig;
|
||||||
|
if (normalizedConfig.appliedDefaults) {
|
||||||
|
await prompts.log.info('Using default configuration (--yes flag)');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (options.yes) {
|
} else if (options.yes) {
|
||||||
// Use all defaults when --yes flag is set
|
|
||||||
await configCollector.loadExistingConfig(directory);
|
await configCollector.loadExistingConfig(directory);
|
||||||
const existingConfig = configCollector.collectedConfig.core || {};
|
const existingConfig = configCollector.collectedConfig.core || {};
|
||||||
|
const defaults = await this.getDefaultCoreConfig();
|
||||||
// If no existing config, use defaults
|
const normalizedConfig = applyDefaultCoreConfig(existingConfig, defaults);
|
||||||
if (Object.keys(existingConfig).length === 0) {
|
configCollector.collectedConfig.core = normalizedConfig.coreConfig;
|
||||||
let safeUsername;
|
if (normalizedConfig.appliedDefaults) {
|
||||||
try {
|
|
||||||
safeUsername = os.userInfo().username;
|
|
||||||
} catch {
|
|
||||||
safeUsername = process.env.USER || process.env.USERNAME || 'User';
|
|
||||||
}
|
|
||||||
const defaultUsername = safeUsername.charAt(0).toUpperCase() + safeUsername.slice(1);
|
|
||||||
configCollector.collectedConfig.core = {
|
|
||||||
user_name: defaultUsername,
|
|
||||||
communication_language: 'English',
|
|
||||||
document_output_language: 'English',
|
|
||||||
output_folder: '_bmad-output',
|
|
||||||
};
|
|
||||||
await prompts.log.info('Using default configuration (--yes flag)');
|
await prompts.log.info('Using default configuration (--yes flag)');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue