review-copilot
Version:
ReviewCopilot - AI-powered code review assistant with customizable prompts
141 lines (140 loc) • 4.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigManager = void 0;
const promises_1 = require("fs/promises");
const yaml_1 = require("yaml");
const zod_1 = require("zod");
const providerSchema = zod_1.z
.object({
enabled: zod_1.z.boolean(),
apiKey: zod_1.z.string(),
model: zod_1.z.string(),
baseURL: zod_1.z.string(),
defaultHeaders: zod_1.z.record(zod_1.z.string()).optional(),
timeout: zod_1.z.number().optional(),
reviewLanguage: zod_1.z.enum(['en', 'zh', 'ja', 'ko']).optional(),
})
.transform((config) => ({
enabled: config.enabled,
apiKey: config.apiKey,
model: config.model,
baseURL: config.baseURL,
reviewLanguage: config.reviewLanguage,
}));
const triggerSchema = zod_1.z.object({
on: zod_1.z.enum(['pull_request', 'merge_request', 'push']),
actions: zod_1.z.array(zod_1.z.string()).optional(),
});
const configSchema = zod_1.z
.object({
providers: zod_1.z.record(zod_1.z.string(), providerSchema).transform((providers) => {
const enabledProvider = Object.entries(providers).find(([_, p]) => p.enabled)?.[0] || 'openai';
if (!['openai', 'deepseek'].includes(enabledProvider)) {
throw new Error(`Unsupported provider type: ${enabledProvider}`);
}
return { [enabledProvider]: providers[enabledProvider] };
}),
triggers: zod_1.z.array(triggerSchema),
rules: zod_1.z.object({
commitMessage: zod_1.z
.object({
enabled: zod_1.z.boolean(),
pattern: zod_1.z.string().optional(),
prompt: zod_1.z.string(),
})
.transform((rule) => ({
...rule,
pattern: rule.pattern || '',
})),
branchName: zod_1.z
.object({
enabled: zod_1.z.boolean(),
pattern: zod_1.z.string().optional(),
prompt: zod_1.z.string(),
})
.transform((rule) => ({
...rule,
pattern: rule.pattern || '',
})),
codeChanges: zod_1.z
.object({
enabled: zod_1.z.boolean(),
filePatterns: zod_1.z.array(zod_1.z.string()).optional(),
prompt: zod_1.z.string(),
})
.transform((rule) => ({
...rule,
filePatterns: rule.filePatterns || [],
})),
}),
customReviewPoints: zod_1.z
.array(zod_1.z.object({
name: zod_1.z.string(),
prompt: zod_1.z.string(),
}))
.optional()
.transform((points) => points || []),
})
.transform((config) => ({
providers: config.providers,
triggers: config.triggers,
rules: config.rules,
customReviewPoints: config.customReviewPoints,
}));
class ConfigManager {
constructor() {
this.config = null;
this.configPromise = null;
}
static async getInstance(configPath) {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager();
await ConfigManager.instance.loadConfig(configPath);
}
return ConfigManager.instance;
}
replaceEnvVariables(obj) {
if (typeof obj === 'string') {
return obj.replace(/\${([^}]+)}/g, (_, key) => process.env[key] || '');
}
if (Array.isArray(obj)) {
return obj.map((item) => this.replaceEnvVariables(item));
}
if (typeof obj === 'object' && obj !== null) {
const result = {};
for (const [key, value] of Object.entries(obj)) {
result[key] = this.replaceEnvVariables(value);
}
return result;
}
return obj;
}
async loadConfig(path = '.review-copilot.yaml') {
if (this.configPromise) {
return this.configPromise;
}
this.configPromise = (async () => {
try {
const configFile = await (0, promises_1.readFile)(path, 'utf8');
const parsedConfig = (0, yaml_1.parse)(configFile);
const configWithEnv = this.replaceEnvVariables(parsedConfig);
this.config = configSchema.parse(configWithEnv);
return this.config;
}
catch (error) {
// Reset the promise on error
this.configPromise = null;
this.config = null;
throw new Error('Failed to load config');
}
})();
return this.configPromise;
}
getConfig() {
if (!this.config) {
throw new Error('Config not loaded. Please ensure getInstance() was and awaited.');
}
return this.config;
}
}
exports.ConfigManager = ConfigManager;