refactor: reuse config validators on cli config
This commit is contained in:
@@ -21,9 +21,8 @@ cli(
|
|||||||
flags: {
|
flags: {
|
||||||
generate: {
|
generate: {
|
||||||
type: Number,
|
type: Number,
|
||||||
description: 'Number of messages to generate. (Warning: generating multiple costs more)',
|
description: 'Number of messages to generate. (Warning: generating multiple costs more) (default: 1)',
|
||||||
alias: 'g',
|
alias: 'g',
|
||||||
default: 1,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { generateCommitMessage } from '../utils/openai.js';
|
|||||||
import { KnownError, handleCliError } from '../utils/error.js';
|
import { KnownError, handleCliError } from '../utils/error.js';
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
generate: number,
|
generate: number | undefined,
|
||||||
rawArgv: string[],
|
rawArgv: string[],
|
||||||
) => (async () => {
|
) => (async () => {
|
||||||
intro(bgCyan(black(' aicommits ')));
|
intro(bgCyan(black(' aicommits ')));
|
||||||
@@ -34,20 +34,18 @@ export default async (
|
|||||||
staged.files.map(file => ` ${file}`).join('\n')
|
staged.files.map(file => ` ${file}`).join('\n')
|
||||||
}`);
|
}`);
|
||||||
|
|
||||||
const config = await getConfig();
|
const config = await getConfig({
|
||||||
const OPENAI_KEY = process.env.OPENAI_KEY ?? process.env.OPENAI_API_KEY ?? config.OPENAI_KEY;
|
OPENAI_KEY: process.env.OPENAI_KEY ?? process.env.OPENAI_API_KEY,
|
||||||
const locale = config.locale ?? 'en';
|
generate: generate?.toString(),
|
||||||
if (!OPENAI_KEY) {
|
});
|
||||||
throw new KnownError('Please set your OpenAI API key via `aicommits config set OPENAI_KEY=<your token>`');
|
|
||||||
}
|
|
||||||
|
|
||||||
const s = spinner();
|
const s = spinner();
|
||||||
s.start('The AI is analyzing your changes');
|
s.start('The AI is analyzing your changes');
|
||||||
const messages = await generateCommitMessage(
|
const messages = await generateCommitMessage(
|
||||||
OPENAI_KEY,
|
config.OPENAI_KEY,
|
||||||
locale,
|
config.locale,
|
||||||
staged.diff,
|
staged.diff,
|
||||||
generate,
|
config.generate,
|
||||||
);
|
);
|
||||||
s.stop('Changes analyzed');
|
s.stop('Changes analyzed');
|
||||||
|
|
||||||
|
|||||||
@@ -30,18 +30,15 @@ export default () => (async () => {
|
|||||||
|
|
||||||
intro(bgCyan(black(' aicommits ')));
|
intro(bgCyan(black(' aicommits ')));
|
||||||
|
|
||||||
const { OPENAI_KEY, locale = 'en', generate } = await getConfig();
|
const config = await getConfig();
|
||||||
if (!OPENAI_KEY) {
|
|
||||||
throw new KnownError('Please set your OpenAI API key in ~/.aicommits');
|
|
||||||
}
|
|
||||||
|
|
||||||
const s = spinner();
|
const s = spinner();
|
||||||
s.start('The AI is analyzing your changes');
|
s.start('The AI is analyzing your changes');
|
||||||
const messages = await generateCommitMessage(
|
const messages = await generateCommitMessage(
|
||||||
OPENAI_KEY,
|
config.OPENAI_KEY,
|
||||||
locale,
|
config.locale,
|
||||||
staged!.diff,
|
staged!.diff,
|
||||||
generate || 1,
|
config.generate,
|
||||||
);
|
);
|
||||||
s.stop('Changes analyzed');
|
s.stop('Changes analyzed');
|
||||||
|
|
||||||
|
|||||||
@@ -19,24 +19,33 @@ const parseAssert = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const configParsers = {
|
const configParsers = {
|
||||||
OPENAI_KEY(key: string) {
|
OPENAI_KEY(key?: string) {
|
||||||
parseAssert('OPENAI_KEY', key, 'Cannot be empty');
|
if (!key) {
|
||||||
|
throw new KnownError('Please set your OpenAI API key via `aicommits config set OPENAI_KEY=<your token>`');
|
||||||
|
}
|
||||||
parseAssert('OPENAI_KEY', key.startsWith('sk-'), 'Must start with "sk-"');
|
parseAssert('OPENAI_KEY', key.startsWith('sk-'), 'Must start with "sk-"');
|
||||||
parseAssert('OPENAI_KEY', key.length === 51, 'Must be 51 characters long');
|
parseAssert('OPENAI_KEY', key.length === 51, 'Must be 51 characters long');
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
},
|
},
|
||||||
locale(key: string) {
|
locale(locale?: string) {
|
||||||
parseAssert('locale', key, 'Cannot be empty');
|
if (!locale) {
|
||||||
parseAssert('locale', /^[a-z-]+$/i.test(key), 'Must be a valid locale (letters and dashes/underscores). You can consult the list of codes in: https://wikipedia.org/wiki/List_of_ISO_639-1_codes');
|
return 'en';
|
||||||
|
}
|
||||||
|
|
||||||
return key;
|
parseAssert('locale', locale, 'Cannot be empty');
|
||||||
|
parseAssert('locale', /^[a-z-]+$/i.test(locale), 'Must be a valid locale (letters and dashes/underscores). You can consult the list of codes in: https://wikipedia.org/wiki/List_of_ISO_639-1_codes');
|
||||||
|
|
||||||
|
return locale;
|
||||||
},
|
},
|
||||||
generate(key: string) {
|
generate(count?: string) {
|
||||||
parseAssert('generate', key, 'Cannot be empty');
|
if (!count) {
|
||||||
parseAssert('generate', /^\d+$/.test(key), 'Must be an integer');
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
const parsed = Number(key);
|
parseAssert('generate', /^\d+$/.test(count), 'Must be an integer');
|
||||||
|
|
||||||
|
const parsed = Number(count);
|
||||||
parseAssert('generate', parsed > 0, 'Must be greater than 0');
|
parseAssert('generate', parsed > 0, 'Must be greater than 0');
|
||||||
parseAssert('generate', parsed <= 5, 'Must be less or equal to 5');
|
parseAssert('generate', parsed <= 5, 'Must be less or equal to 5');
|
||||||
|
|
||||||
@@ -44,45 +53,53 @@ const configParsers = {
|
|||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
type ValidKeys = keyof typeof configParsers;
|
type ConfigKeys = keyof typeof configParsers;
|
||||||
type ConfigType = {
|
|
||||||
[key in ValidKeys]?: ReturnType<typeof configParsers[key]>;
|
type RawConfig = {
|
||||||
|
[key in ConfigKeys]?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ValidConfig = {
|
||||||
|
[Key in ConfigKeys]: ReturnType<typeof configParsers[Key]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const configPath = path.join(os.homedir(), '.aicommits');
|
const configPath = path.join(os.homedir(), '.aicommits');
|
||||||
|
|
||||||
export const getConfig = async (): Promise<ConfigType> => {
|
const readConfigFile = async (): Promise<RawConfig> => {
|
||||||
const configExists = await fileExists(configPath);
|
const configExists = await fileExists(configPath);
|
||||||
if (!configExists) {
|
if (!configExists) {
|
||||||
return {};
|
return Object.create(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const configString = await fs.readFile(configPath, 'utf8');
|
const configString = await fs.readFile(configPath, 'utf8');
|
||||||
const config = ini.parse(configString);
|
return ini.parse(configString);
|
||||||
for (const key of Object.keys(config)) {
|
};
|
||||||
if (hasOwn(configParsers, key)) {
|
|
||||||
const parsed = configParsers[key as ValidKeys](config[key]);
|
export const getConfig = async (cliConfig?: RawConfig): Promise<ValidConfig> => {
|
||||||
config[key as ValidKeys] = parsed;
|
const config = await readConfigFile();
|
||||||
} else {
|
const parsedConfig: Record<string, unknown> = {};
|
||||||
console.warn(`\n⚠️ Unknown config property "${key}" found in ${configPath}`);
|
|
||||||
}
|
for (const key of Object.keys(configParsers) as ConfigKeys[]) {
|
||||||
|
const parser = configParsers[key];
|
||||||
|
const value = cliConfig?.[key] ?? config[key];
|
||||||
|
parsedConfig[key] = parser(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return parsedConfig as ValidConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setConfigs = async (
|
export const setConfigs = async (
|
||||||
keyValues: [key: string, value: string][],
|
keyValues: [key: string, value: string][],
|
||||||
) => {
|
) => {
|
||||||
const config = await getConfig();
|
const config = await readConfigFile();
|
||||||
|
|
||||||
for (const [key, value] of keyValues) {
|
for (const [key, value] of keyValues) {
|
||||||
if (!hasOwn(configParsers, key)) {
|
if (!hasOwn(configParsers, key)) {
|
||||||
throw new KnownError(`Invalid config property: ${key}`);
|
throw new KnownError(`Invalid config property: ${key}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = configParsers[key as ValidKeys](value);
|
const parsed = configParsers[key as ConfigKeys](value);
|
||||||
config[key as ValidKeys] = parsed as any;
|
config[key as ConfigKeys] = parsed as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(configPath, ini.stringify(config), 'utf8');
|
await fs.writeFile(configPath, ini.stringify(config), 'utf8');
|
||||||
|
|||||||
Reference in New Issue
Block a user