Compare commits

...

4 Commits

Author SHA1 Message Date
Takanori Matsumoto
68dc9c36eb fix(utils.ts): add existsSync check before removing temp directory to prevent errors if directory does not exist (#401) 2024-09-01 11:56:13 +03:00
di-sukharev
e063faa491 refactor(config.ts): extract default configuration to a constant for better maintainability and readability
refactor(config.ts): improve initGlobalConfig function to accept a configPath parameter for flexibility
feat(config.ts): enhance getConfig function to support separate paths for global and environment configurations
test(config.test.ts): update tests to reflect changes in config handling and ensure proper functionality
style(utils.ts): clean up code formatting for consistency and readability
style(tsconfig.json): adjust formatting in tsconfig.json for better clarity and maintainability
2024-08-30 17:37:31 +03:00
di-sukharev
c7b1a5bcdc refactor(commit.ts): rename spinner variables for clarity and consistency in commit message generation process
fix(commit.ts): ensure proper stopping of spinners in case of errors during commit message generation and committing process
2024-08-30 14:24:18 +03:00
di-sukharev
92943d3da8 fix(commit.ts): improve user confirmation handling by exiting on cancel actions to prevent unintended behavior
refactor(commit.ts): streamline conditional checks for user confirmations to enhance code readability and maintainability
2024-08-30 14:18:22 +03:00
7 changed files with 463 additions and 306 deletions

View File

@@ -25193,7 +25193,7 @@ function G3(t2, e3) {
// package.json // package.json
var package_default = { var package_default = {
name: "opencommit", name: "opencommit",
version: "3.0.20", version: "3.1.0",
description: "Auto-generate impressive commits in 1 second. Killing lame commits with AI \u{1F92F}\u{1F52B}", description: "Auto-generate impressive commits in 1 second. Killing lame commits with AI \u{1F92F}\u{1F52B}",
keywords: [ keywords: [
"git", "git",
@@ -28029,44 +28029,24 @@ var configValidators = {
}; };
var defaultConfigPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit"); var defaultConfigPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit");
var defaultEnvPath = (0, import_path.resolve)(process.cwd(), ".env"); var defaultEnvPath = (0, import_path.resolve)(process.cwd(), ".env");
var assertConfigsAreValid = (config7) => { var DEFAULT_CONFIG = {
for (const [key, value] of Object.entries(config7)) { OCO_TOKENS_MAX_INPUT: 40960 /* DEFAULT_MAX_TOKENS_INPUT */,
if (!value) OCO_TOKENS_MAX_OUTPUT: 4096 /* DEFAULT_MAX_TOKENS_OUTPUT */,
continue; OCO_DESCRIPTION: false,
if (typeof value === "string" && ["null", "undefined"].includes(value)) { OCO_EMOJI: false,
config7[key] = void 0; OCO_MODEL: getDefaultModel("openai"),
continue; OCO_LANGUAGE: "en",
} OCO_MESSAGE_TEMPLATE_PLACEHOLDER: "$msg",
try { OCO_PROMPT_MODULE: "conventional-commit" /* CONVENTIONAL_COMMIT */,
const validate = configValidators[key]; OCO_AI_PROVIDER: "openai" /* OPENAI */,
validate(value, config7); OCO_ONE_LINE_COMMIT: false,
} catch (error) { OCO_TEST_MOCK_TYPE: "commit-message",
ce(`Unknown '${key}' config option or missing validator.`); OCO_FLOWISE_ENDPOINT: ":",
ce( OCO_GITPUSH: true
`Manually fix the '.env' file or global '~/.opencommit' config file.`
);
process.exit(1);
}
}
}; };
var initGlobalConfig = () => { var initGlobalConfig = (configPath = defaultConfigPath) => {
const defaultConfig = { (0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(DEFAULT_CONFIG), "utf8");
OCO_TOKENS_MAX_INPUT: 40960 /* DEFAULT_MAX_TOKENS_INPUT */, return DEFAULT_CONFIG;
OCO_TOKENS_MAX_OUTPUT: 4096 /* DEFAULT_MAX_TOKENS_OUTPUT */,
OCO_DESCRIPTION: false,
OCO_EMOJI: false,
OCO_MODEL: getDefaultModel("openai"),
OCO_LANGUAGE: "en",
OCO_MESSAGE_TEMPLATE_PLACEHOLDER: "$msg",
OCO_PROMPT_MODULE: "conventional-commit" /* CONVENTIONAL_COMMIT */,
OCO_AI_PROVIDER: "openai" /* OPENAI */,
OCO_ONE_LINE_COMMIT: false,
OCO_TEST_MOCK_TYPE: "commit-message",
OCO_FLOWISE_ENDPOINT: ":",
OCO_GITPUSH: true
};
(0, import_fs.writeFileSync)(defaultConfigPath, (0, import_ini.stringify)(defaultConfig), "utf8");
return defaultConfig;
}; };
var parseEnvVarValue = (value) => { var parseEnvVarValue = (value) => {
try { try {
@@ -28075,12 +28055,9 @@ var parseEnvVarValue = (value) => {
return value; return value;
} }
}; };
var getConfig = ({ var getEnvConfig = (envPath) => {
configPath = defaultConfigPath,
envPath = defaultEnvPath
} = {}) => {
dotenv.config({ path: envPath }); dotenv.config({ path: envPath });
const envConfig = { return {
OCO_MODEL: process.env.OCO_MODEL, OCO_MODEL: process.env.OCO_MODEL,
OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY,
OCO_ANTHROPIC_API_KEY: process.env.OCO_ANTHROPIC_API_KEY, OCO_ANTHROPIC_API_KEY: process.env.OCO_ANTHROPIC_API_KEY,
@@ -28104,23 +28081,35 @@ var getConfig = ({
OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE, OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE,
OCO_GITPUSH: parseEnvVarValue(process.env.OCO_GITPUSH) OCO_GITPUSH: parseEnvVarValue(process.env.OCO_GITPUSH)
}; };
};
var getGlobalConfig = (configPath) => {
let globalConfig; let globalConfig;
const isGlobalConfigFileExist = (0, import_fs.existsSync)(configPath); const isGlobalConfigFileExist = (0, import_fs.existsSync)(configPath);
if (!isGlobalConfigFileExist) if (!isGlobalConfigFileExist)
globalConfig = initGlobalConfig(); globalConfig = initGlobalConfig(configPath);
else { else {
const configFile = (0, import_fs.readFileSync)(configPath, "utf8"); const configFile = (0, import_fs.readFileSync)(configPath, "utf8");
globalConfig = (0, import_ini.parse)(configFile); globalConfig = (0, import_ini.parse)(configFile);
} }
const mergeObjects = (main, fallback) => Object.keys(CONFIG_KEYS).reduce((acc, key) => { return globalConfig;
acc[key] = parseEnvVarValue(main[key] ?? fallback[key]); };
return acc; var mergeConfigs = (main, fallback) => Object.keys(CONFIG_KEYS).reduce((acc, key) => {
}, {}); acc[key] = parseEnvVarValue(main[key] ?? fallback[key]);
const config7 = mergeObjects(envConfig, globalConfig); return acc;
}, {});
var getConfig = ({
envPath = defaultEnvPath,
globalPath = defaultConfigPath
} = {}) => {
const envConfig = getEnvConfig(envPath);
const globalConfig = getGlobalConfig(globalPath);
const config7 = mergeConfigs(envConfig, globalConfig);
return config7; return config7;
}; };
var setConfig = (keyValues, configPath = defaultConfigPath) => { var setConfig = (keyValues, globalConfigPath = defaultConfigPath) => {
const config7 = getConfig(); const config7 = getConfig({
globalPath: globalConfigPath
});
for (let [key, value] of keyValues) { for (let [key, value] of keyValues) {
if (!configValidators.hasOwnProperty(key)) { if (!configValidators.hasOwnProperty(key)) {
const supportedKeys = Object.keys(configValidators).join("\n"); const supportedKeys = Object.keys(configValidators).join("\n");
@@ -28144,8 +28133,7 @@ For more help refer to our docs: https://github.com/di-sukharev/opencommit`
); );
config7[key] = validValue; config7[key] = validValue;
} }
(0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(config7), "utf8"); (0, import_fs.writeFileSync)(globalConfigPath, (0, import_ini.stringify)(config7), "utf8");
assertConfigsAreValid(config7);
ce(`${source_default.green("\u2714")} config successfully set`); ce(`${source_default.green("\u2714")} config successfully set`);
}; };
var configCommand = G3( var configCommand = G3(
@@ -42400,7 +42388,7 @@ var OpenAiEngine = class {
function getEngine() { function getEngine() {
const config7 = getConfig(); const config7 = getConfig();
const provider = config7.OCO_AI_PROVIDER; const provider = config7.OCO_AI_PROVIDER;
const DEFAULT_CONFIG = { const DEFAULT_CONFIG2 = {
model: config7.OCO_MODEL, model: config7.OCO_MODEL,
maxTokensOutput: config7.OCO_TOKENS_MAX_OUTPUT, maxTokensOutput: config7.OCO_TOKENS_MAX_OUTPUT,
maxTokensInput: config7.OCO_TOKENS_MAX_INPUT, maxTokensInput: config7.OCO_TOKENS_MAX_INPUT,
@@ -42409,37 +42397,37 @@ function getEngine() {
switch (provider) { switch (provider) {
case "ollama" /* OLLAMA */: case "ollama" /* OLLAMA */:
return new OllamaAi({ return new OllamaAi({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: "", apiKey: "",
baseURL: config7.OCO_OLLAMA_API_URL baseURL: config7.OCO_OLLAMA_API_URL
}); });
case "anthropic" /* ANTHROPIC */: case "anthropic" /* ANTHROPIC */:
return new AnthropicEngine({ return new AnthropicEngine({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: config7.OCO_ANTHROPIC_API_KEY apiKey: config7.OCO_ANTHROPIC_API_KEY
}); });
case "test" /* TEST */: case "test" /* TEST */:
return new TestAi(config7.OCO_TEST_MOCK_TYPE); return new TestAi(config7.OCO_TEST_MOCK_TYPE);
case "gemini" /* GEMINI */: case "gemini" /* GEMINI */:
return new Gemini({ return new Gemini({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: config7.OCO_GEMINI_API_KEY, apiKey: config7.OCO_GEMINI_API_KEY,
baseURL: config7.OCO_GEMINI_BASE_PATH baseURL: config7.OCO_GEMINI_BASE_PATH
}); });
case "azure" /* AZURE */: case "azure" /* AZURE */:
return new AzureEngine({ return new AzureEngine({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: config7.OCO_AZURE_API_KEY apiKey: config7.OCO_AZURE_API_KEY
}); });
case "flowise" /* FLOWISE */: case "flowise" /* FLOWISE */:
return new FlowiseAi({ return new FlowiseAi({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
baseURL: config7.OCO_FLOWISE_ENDPOINT || DEFAULT_CONFIG.baseURL, baseURL: config7.OCO_FLOWISE_ENDPOINT || DEFAULT_CONFIG2.baseURL,
apiKey: config7.OCO_FLOWISE_API_KEY apiKey: config7.OCO_FLOWISE_API_KEY
}); });
default: default:
return new OpenAiEngine({ return new OpenAiEngine({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: config7.OCO_OPENAI_API_KEY apiKey: config7.OCO_OPENAI_API_KEY
}); });
} }
@@ -43172,8 +43160,8 @@ var generateCommitMessageFromGitDiff = async ({
skipCommitConfirmation = false skipCommitConfirmation = false
}) => { }) => {
await assertGitRepo(); await assertGitRepo();
const commitSpinner = le(); const commitGenerationSpinner = le();
commitSpinner.start("Generating the commit message"); commitGenerationSpinner.start("Generating the commit message");
try { try {
let commitMessage = await generateCommitMessageByDiff( let commitMessage = await generateCommitMessageByDiff(
diff, diff,
@@ -43188,7 +43176,7 @@ var generateCommitMessageFromGitDiff = async ({
commitMessage commitMessage
); );
} }
commitSpinner.stop("\u{1F4DD} Commit message generated"); commitGenerationSpinner.stop("\u{1F4DD} Commit message generated");
ce( ce(
`Generated commit message: `Generated commit message:
${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014")} ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014")}
@@ -43198,14 +43186,20 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2
const isCommitConfirmedByUser = skipCommitConfirmation || await Q3({ const isCommitConfirmedByUser = skipCommitConfirmation || await Q3({
message: "Confirm the commit message?" message: "Confirm the commit message?"
}); });
if (isCommitConfirmedByUser && !hD2(isCommitConfirmedByUser)) { if (hD2(isCommitConfirmedByUser))
process.exit(1);
if (isCommitConfirmedByUser) {
const committingChangesSpinner = le();
committingChangesSpinner.start("Committing the changes");
const { stdout } = await execa("git", [ const { stdout } = await execa("git", [
"commit", "commit",
"-m", "-m",
commitMessage, commitMessage,
...extraArgs2 ...extraArgs2
]); ]);
ce(`${source_default.green("\u2714")} Successfully committed`); committingChangesSpinner.stop(
`${source_default.green("\u2714")} Successfully committed`
);
ce(stdout); ce(stdout);
const remotes = await getGitRemotes(); const remotes = await getGitRemotes();
if (!remotes.length) { if (!remotes.length) {
@@ -43218,7 +43212,9 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2
const isPushConfirmedByUser = await Q3({ const isPushConfirmedByUser = await Q3({
message: "Do you want to run `git push`?" message: "Do you want to run `git push`?"
}); });
if (isPushConfirmedByUser && !hD2(isPushConfirmedByUser)) { if (hD2(isPushConfirmedByUser))
process.exit(1);
if (isPushConfirmedByUser) {
const pushSpinner = le(); const pushSpinner = le();
pushSpinner.start(`Running 'git push ${remotes[0]}'`); pushSpinner.start(`Running 'git push ${remotes[0]}'`);
const { stdout: stdout2 } = await execa("git", [ const { stdout: stdout2 } = await execa("git", [
@@ -43240,26 +43236,26 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2
message: "Choose a remote to push to", message: "Choose a remote to push to",
options: remotes.map((remote) => ({ value: remote, label: remote })) options: remotes.map((remote) => ({ value: remote, label: remote }))
}); });
if (!hD2(selectedRemote)) { if (hD2(selectedRemote))
const pushSpinner = le(); process.exit(1);
pushSpinner.start(`Running 'git push ${selectedRemote}'`); const pushSpinner = le();
const { stdout: stdout2 } = await execa("git", ["push", selectedRemote]); pushSpinner.start(`Running 'git push ${selectedRemote}'`);
pushSpinner.stop( const { stdout: stdout2 } = await execa("git", ["push", selectedRemote]);
`${source_default.green( pushSpinner.stop(
"\u2714" `${source_default.green(
)} Successfully pushed all commits to ${selectedRemote}` "\u2714"
); )} Successfully pushed all commits to ${selectedRemote}`
if (stdout2) );
ce(stdout2); if (stdout2)
} else ce(stdout2);
ce(`${source_default.gray("\u2716")} process cancelled`);
} }
} } else {
if (!isCommitConfirmedByUser && !hD2(isCommitConfirmedByUser)) {
const regenerateMessage = await Q3({ const regenerateMessage = await Q3({
message: "Do you want to regenerate the message?" message: "Do you want to regenerate the message?"
}); });
if (regenerateMessage && !hD2(isCommitConfirmedByUser)) { if (hD2(regenerateMessage))
process.exit(1);
if (regenerateMessage) {
await generateCommitMessageFromGitDiff({ await generateCommitMessageFromGitDiff({
diff, diff,
extraArgs: extraArgs2, extraArgs: extraArgs2,
@@ -43268,7 +43264,7 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2
} }
} }
} catch (error) { } catch (error) {
commitSpinner.stop("\u{1F4DD} Commit message generated"); commitGenerationSpinner.stop("\u{1F4DD} Commit message generated");
const err = error; const err = error;
ce(`${source_default.red("\u2716")} ${err?.message || err}`); ce(`${source_default.red("\u2716")} ${err?.message || err}`);
process.exit(1); process.exit(1);
@@ -43302,7 +43298,9 @@ async function commit(extraArgs2 = [], isStageAllFlag = false, fullGitMojiSpec =
const isStageAllAndCommitConfirmedByUser = await Q3({ const isStageAllAndCommitConfirmedByUser = await Q3({
message: "Do you want to stage all files and generate commit message?" message: "Do you want to stage all files and generate commit message?"
}); });
if (isStageAllAndCommitConfirmedByUser && !hD2(isStageAllAndCommitConfirmedByUser)) { if (hD2(isStageAllAndCommitConfirmedByUser))
process.exit(1);
if (isStageAllAndCommitConfirmedByUser) {
await commit(extraArgs2, true, fullGitMojiSpec); await commit(extraArgs2, true, fullGitMojiSpec);
process.exit(1); process.exit(1);
} }

View File

@@ -46841,44 +46841,24 @@ var configValidators = {
}; };
var defaultConfigPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit"); var defaultConfigPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit");
var defaultEnvPath = (0, import_path.resolve)(process.cwd(), ".env"); var defaultEnvPath = (0, import_path.resolve)(process.cwd(), ".env");
var assertConfigsAreValid = (config6) => { var DEFAULT_CONFIG = {
for (const [key, value] of Object.entries(config6)) { OCO_TOKENS_MAX_INPUT: 40960 /* DEFAULT_MAX_TOKENS_INPUT */,
if (!value) OCO_TOKENS_MAX_OUTPUT: 4096 /* DEFAULT_MAX_TOKENS_OUTPUT */,
continue; OCO_DESCRIPTION: false,
if (typeof value === "string" && ["null", "undefined"].includes(value)) { OCO_EMOJI: false,
config6[key] = void 0; OCO_MODEL: getDefaultModel("openai"),
continue; OCO_LANGUAGE: "en",
} OCO_MESSAGE_TEMPLATE_PLACEHOLDER: "$msg",
try { OCO_PROMPT_MODULE: "conventional-commit" /* CONVENTIONAL_COMMIT */,
const validate = configValidators[key]; OCO_AI_PROVIDER: "openai" /* OPENAI */,
validate(value, config6); OCO_ONE_LINE_COMMIT: false,
} catch (error) { OCO_TEST_MOCK_TYPE: "commit-message",
ce(`Unknown '${key}' config option or missing validator.`); OCO_FLOWISE_ENDPOINT: ":",
ce( OCO_GITPUSH: true
`Manually fix the '.env' file or global '~/.opencommit' config file.`
);
process.exit(1);
}
}
}; };
var initGlobalConfig = () => { var initGlobalConfig = (configPath = defaultConfigPath) => {
const defaultConfig = { (0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(DEFAULT_CONFIG), "utf8");
OCO_TOKENS_MAX_INPUT: 40960 /* DEFAULT_MAX_TOKENS_INPUT */, return DEFAULT_CONFIG;
OCO_TOKENS_MAX_OUTPUT: 4096 /* DEFAULT_MAX_TOKENS_OUTPUT */,
OCO_DESCRIPTION: false,
OCO_EMOJI: false,
OCO_MODEL: getDefaultModel("openai"),
OCO_LANGUAGE: "en",
OCO_MESSAGE_TEMPLATE_PLACEHOLDER: "$msg",
OCO_PROMPT_MODULE: "conventional-commit" /* CONVENTIONAL_COMMIT */,
OCO_AI_PROVIDER: "openai" /* OPENAI */,
OCO_ONE_LINE_COMMIT: false,
OCO_TEST_MOCK_TYPE: "commit-message",
OCO_FLOWISE_ENDPOINT: ":",
OCO_GITPUSH: true
};
(0, import_fs.writeFileSync)(defaultConfigPath, (0, import_ini.stringify)(defaultConfig), "utf8");
return defaultConfig;
}; };
var parseEnvVarValue = (value) => { var parseEnvVarValue = (value) => {
try { try {
@@ -46887,12 +46867,9 @@ var parseEnvVarValue = (value) => {
return value; return value;
} }
}; };
var getConfig = ({ var getEnvConfig = (envPath) => {
configPath = defaultConfigPath,
envPath = defaultEnvPath
} = {}) => {
dotenv.config({ path: envPath }); dotenv.config({ path: envPath });
const envConfig = { return {
OCO_MODEL: process.env.OCO_MODEL, OCO_MODEL: process.env.OCO_MODEL,
OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY,
OCO_ANTHROPIC_API_KEY: process.env.OCO_ANTHROPIC_API_KEY, OCO_ANTHROPIC_API_KEY: process.env.OCO_ANTHROPIC_API_KEY,
@@ -46916,23 +46893,35 @@ var getConfig = ({
OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE, OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE,
OCO_GITPUSH: parseEnvVarValue(process.env.OCO_GITPUSH) OCO_GITPUSH: parseEnvVarValue(process.env.OCO_GITPUSH)
}; };
};
var getGlobalConfig = (configPath) => {
let globalConfig; let globalConfig;
const isGlobalConfigFileExist = (0, import_fs.existsSync)(configPath); const isGlobalConfigFileExist = (0, import_fs.existsSync)(configPath);
if (!isGlobalConfigFileExist) if (!isGlobalConfigFileExist)
globalConfig = initGlobalConfig(); globalConfig = initGlobalConfig(configPath);
else { else {
const configFile = (0, import_fs.readFileSync)(configPath, "utf8"); const configFile = (0, import_fs.readFileSync)(configPath, "utf8");
globalConfig = (0, import_ini.parse)(configFile); globalConfig = (0, import_ini.parse)(configFile);
} }
const mergeObjects = (main, fallback) => Object.keys(CONFIG_KEYS).reduce((acc, key) => { return globalConfig;
acc[key] = parseEnvVarValue(main[key] ?? fallback[key]); };
return acc; var mergeConfigs = (main, fallback) => Object.keys(CONFIG_KEYS).reduce((acc, key) => {
}, {}); acc[key] = parseEnvVarValue(main[key] ?? fallback[key]);
const config6 = mergeObjects(envConfig, globalConfig); return acc;
}, {});
var getConfig = ({
envPath = defaultEnvPath,
globalPath = defaultConfigPath
} = {}) => {
const envConfig = getEnvConfig(envPath);
const globalConfig = getGlobalConfig(globalPath);
const config6 = mergeConfigs(envConfig, globalConfig);
return config6; return config6;
}; };
var setConfig = (keyValues, configPath = defaultConfigPath) => { var setConfig = (keyValues, globalConfigPath = defaultConfigPath) => {
const config6 = getConfig(); const config6 = getConfig({
globalPath: globalConfigPath
});
for (let [key, value] of keyValues) { for (let [key, value] of keyValues) {
if (!configValidators.hasOwnProperty(key)) { if (!configValidators.hasOwnProperty(key)) {
const supportedKeys = Object.keys(configValidators).join("\n"); const supportedKeys = Object.keys(configValidators).join("\n");
@@ -46956,8 +46945,7 @@ For more help refer to our docs: https://github.com/di-sukharev/opencommit`
); );
config6[key] = validValue; config6[key] = validValue;
} }
(0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(config6), "utf8"); (0, import_fs.writeFileSync)(globalConfigPath, (0, import_ini.stringify)(config6), "utf8");
assertConfigsAreValid(config6);
ce(`${source_default.green("\u2714")} config successfully set`); ce(`${source_default.green("\u2714")} config successfully set`);
}; };
var configCommand = G2( var configCommand = G2(
@@ -61212,7 +61200,7 @@ var OpenAiEngine = class {
function getEngine() { function getEngine() {
const config6 = getConfig(); const config6 = getConfig();
const provider = config6.OCO_AI_PROVIDER; const provider = config6.OCO_AI_PROVIDER;
const DEFAULT_CONFIG = { const DEFAULT_CONFIG2 = {
model: config6.OCO_MODEL, model: config6.OCO_MODEL,
maxTokensOutput: config6.OCO_TOKENS_MAX_OUTPUT, maxTokensOutput: config6.OCO_TOKENS_MAX_OUTPUT,
maxTokensInput: config6.OCO_TOKENS_MAX_INPUT, maxTokensInput: config6.OCO_TOKENS_MAX_INPUT,
@@ -61221,37 +61209,37 @@ function getEngine() {
switch (provider) { switch (provider) {
case "ollama" /* OLLAMA */: case "ollama" /* OLLAMA */:
return new OllamaAi({ return new OllamaAi({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: "", apiKey: "",
baseURL: config6.OCO_OLLAMA_API_URL baseURL: config6.OCO_OLLAMA_API_URL
}); });
case "anthropic" /* ANTHROPIC */: case "anthropic" /* ANTHROPIC */:
return new AnthropicEngine({ return new AnthropicEngine({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: config6.OCO_ANTHROPIC_API_KEY apiKey: config6.OCO_ANTHROPIC_API_KEY
}); });
case "test" /* TEST */: case "test" /* TEST */:
return new TestAi(config6.OCO_TEST_MOCK_TYPE); return new TestAi(config6.OCO_TEST_MOCK_TYPE);
case "gemini" /* GEMINI */: case "gemini" /* GEMINI */:
return new Gemini({ return new Gemini({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: config6.OCO_GEMINI_API_KEY, apiKey: config6.OCO_GEMINI_API_KEY,
baseURL: config6.OCO_GEMINI_BASE_PATH baseURL: config6.OCO_GEMINI_BASE_PATH
}); });
case "azure" /* AZURE */: case "azure" /* AZURE */:
return new AzureEngine({ return new AzureEngine({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: config6.OCO_AZURE_API_KEY apiKey: config6.OCO_AZURE_API_KEY
}); });
case "flowise" /* FLOWISE */: case "flowise" /* FLOWISE */:
return new FlowiseAi({ return new FlowiseAi({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
baseURL: config6.OCO_FLOWISE_ENDPOINT || DEFAULT_CONFIG.baseURL, baseURL: config6.OCO_FLOWISE_ENDPOINT || DEFAULT_CONFIG2.baseURL,
apiKey: config6.OCO_FLOWISE_API_KEY apiKey: config6.OCO_FLOWISE_API_KEY
}); });
default: default:
return new OpenAiEngine({ return new OpenAiEngine({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG2,
apiKey: config6.OCO_OPENAI_API_KEY apiKey: config6.OCO_OPENAI_API_KEY
}); });
} }

View File

@@ -50,8 +50,8 @@ const generateCommitMessageFromGitDiff = async ({
skipCommitConfirmation = false skipCommitConfirmation = false
}: GenerateCommitMessageFromGitDiffParams): Promise<void> => { }: GenerateCommitMessageFromGitDiffParams): Promise<void> => {
await assertGitRepo(); await assertGitRepo();
const commitSpinner = spinner(); const commitGenerationSpinner = spinner();
commitSpinner.start('Generating the commit message'); commitGenerationSpinner.start('Generating the commit message');
try { try {
let commitMessage = await generateCommitMessageByDiff( let commitMessage = await generateCommitMessageByDiff(
@@ -73,7 +73,7 @@ const generateCommitMessageFromGitDiff = async ({
); );
} }
commitSpinner.stop('📝 Commit message generated'); commitGenerationSpinner.stop('📝 Commit message generated');
outro( outro(
`Generated commit message: `Generated commit message:
@@ -88,15 +88,20 @@ ${chalk.grey('——————————————————')}`
message: 'Confirm the commit message?' message: 'Confirm the commit message?'
})); }));
if (isCommitConfirmedByUser && !isCancel(isCommitConfirmedByUser)) { if (isCancel(isCommitConfirmedByUser)) process.exit(1);
if (isCommitConfirmedByUser) {
const committingChangesSpinner = spinner();
committingChangesSpinner.start('Committing the changes');
const { stdout } = await execa('git', [ const { stdout } = await execa('git', [
'commit', 'commit',
'-m', '-m',
commitMessage, commitMessage,
...extraArgs ...extraArgs
]); ]);
committingChangesSpinner.stop(
outro(`${chalk.green('✔')} Successfully committed`); `${chalk.green('✔')} Successfully committed`
);
outro(stdout); outro(stdout);
@@ -113,7 +118,9 @@ ${chalk.grey('——————————————————')}`
message: 'Do you want to run `git push`?' message: 'Do you want to run `git push`?'
}); });
if (isPushConfirmedByUser && !isCancel(isPushConfirmedByUser)) { if (isCancel(isPushConfirmedByUser)) process.exit(1);
if (isPushConfirmedByUser) {
const pushSpinner = spinner(); const pushSpinner = spinner();
pushSpinner.start(`Running 'git push ${remotes[0]}'`); pushSpinner.start(`Running 'git push ${remotes[0]}'`);
@@ -141,28 +148,30 @@ ${chalk.grey('——————————————————')}`
options: remotes.map((remote) => ({ value: remote, label: remote })) options: remotes.map((remote) => ({ value: remote, label: remote }))
})) as string; })) as string;
if (!isCancel(selectedRemote)) { if (isCancel(selectedRemote)) process.exit(1);
const pushSpinner = spinner();
pushSpinner.start(`Running 'git push ${selectedRemote}'`); const pushSpinner = spinner();
const { stdout } = await execa('git', ['push', selectedRemote]); pushSpinner.start(`Running 'git push ${selectedRemote}'`);
pushSpinner.stop( const { stdout } = await execa('git', ['push', selectedRemote]);
`${chalk.green(
'✔'
)} Successfully pushed all commits to ${selectedRemote}`
);
if (stdout) outro(stdout); pushSpinner.stop(
} else outro(`${chalk.gray('✖')} process cancelled`); `${chalk.green(
'✔'
)} Successfully pushed all commits to ${selectedRemote}`
);
if (stdout) outro(stdout);
} }
} } else {
if (!isCommitConfirmedByUser && !isCancel(isCommitConfirmedByUser)) {
const regenerateMessage = await confirm({ const regenerateMessage = await confirm({
message: 'Do you want to regenerate the message?' message: 'Do you want to regenerate the message?'
}); });
if (regenerateMessage && !isCancel(isCommitConfirmedByUser)) {
if (isCancel(regenerateMessage)) process.exit(1);
if (regenerateMessage) {
await generateCommitMessageFromGitDiff({ await generateCommitMessageFromGitDiff({
diff, diff,
extraArgs, extraArgs,
@@ -171,7 +180,7 @@ ${chalk.grey('——————————————————')}`
} }
} }
} catch (error) { } catch (error) {
commitSpinner.stop('📝 Commit message generated'); commitGenerationSpinner.stop('📝 Commit message generated');
const err = error as Error; const err = error as Error;
outro(`${chalk.red('✖')} ${err?.message || err}`); outro(`${chalk.red('✖')} ${err?.message || err}`);
@@ -219,10 +228,9 @@ export async function commit(
message: 'Do you want to stage all files and generate commit message?' message: 'Do you want to stage all files and generate commit message?'
}); });
if ( if (isCancel(isStageAllAndCommitConfirmedByUser)) process.exit(1);
isStageAllAndCommitConfirmedByUser &&
!isCancel(isStageAllAndCommitConfirmedByUser) if (isStageAllAndCommitConfirmedByUser) {
) {
await commit(extraArgs, true, fullGitMojiSpec); await commit(extraArgs, true, fullGitMojiSpec);
process.exit(1); process.exit(1);
} }

View File

@@ -422,25 +422,25 @@ enum OCO_PROMPT_MODULE_ENUM {
COMMITLINT = '@commitlint' COMMITLINT = '@commitlint'
} }
const initGlobalConfig = () => { export const DEFAULT_CONFIG = {
const defaultConfig = { OCO_TOKENS_MAX_INPUT: DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_INPUT,
OCO_TOKENS_MAX_INPUT: DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_INPUT, OCO_TOKENS_MAX_OUTPUT: DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_OUTPUT,
OCO_TOKENS_MAX_OUTPUT: DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_OUTPUT, OCO_DESCRIPTION: false,
OCO_DESCRIPTION: false, OCO_EMOJI: false,
OCO_EMOJI: false, OCO_MODEL: getDefaultModel('openai'),
OCO_MODEL: getDefaultModel('openai'), OCO_LANGUAGE: 'en',
OCO_LANGUAGE: 'en', OCO_MESSAGE_TEMPLATE_PLACEHOLDER: '$msg',
OCO_MESSAGE_TEMPLATE_PLACEHOLDER: '$msg', OCO_PROMPT_MODULE: OCO_PROMPT_MODULE_ENUM.CONVENTIONAL_COMMIT,
OCO_PROMPT_MODULE: OCO_PROMPT_MODULE_ENUM.CONVENTIONAL_COMMIT, OCO_AI_PROVIDER: OCO_AI_PROVIDER_ENUM.OPENAI,
OCO_AI_PROVIDER: OCO_AI_PROVIDER_ENUM.OPENAI, OCO_ONE_LINE_COMMIT: false,
OCO_ONE_LINE_COMMIT: false, OCO_TEST_MOCK_TYPE: 'commit-message',
OCO_TEST_MOCK_TYPE: 'commit-message', OCO_FLOWISE_ENDPOINT: ':',
OCO_FLOWISE_ENDPOINT: ':', OCO_GITPUSH: true // todo: deprecate
OCO_GITPUSH: true // todo: deprecate };
};
writeFileSync(defaultConfigPath, iniStringify(defaultConfig), 'utf8'); const initGlobalConfig = (configPath: string = defaultConfigPath) => {
return defaultConfig; writeFileSync(configPath, iniStringify(DEFAULT_CONFIG), 'utf8');
return DEFAULT_CONFIG;
}; };
const parseEnvVarValue = (value?: any) => { const parseEnvVarValue = (value?: any) => {
@@ -451,16 +451,10 @@ const parseEnvVarValue = (value?: any) => {
} }
}; };
export const getConfig = ({ const getEnvConfig = (envPath: string) => {
configPath = defaultConfigPath,
envPath = defaultEnvPath
}: {
configPath?: string;
envPath?: string;
} = {}): ConfigType => {
dotenv.config({ path: envPath }); dotenv.config({ path: envPath });
const envConfig = { return {
OCO_MODEL: process.env.OCO_MODEL, OCO_MODEL: process.env.OCO_MODEL,
OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY,
@@ -491,33 +485,59 @@ export const getConfig = ({
OCO_GITPUSH: parseEnvVarValue(process.env.OCO_GITPUSH) // todo: deprecate OCO_GITPUSH: parseEnvVarValue(process.env.OCO_GITPUSH) // todo: deprecate
}; };
};
const getGlobalConfig = (configPath: string) => {
let globalConfig: ConfigType; let globalConfig: ConfigType;
const isGlobalConfigFileExist = existsSync(configPath); const isGlobalConfigFileExist = existsSync(configPath);
if (!isGlobalConfigFileExist) globalConfig = initGlobalConfig(); if (!isGlobalConfigFileExist) globalConfig = initGlobalConfig(configPath);
else { else {
const configFile = readFileSync(configPath, 'utf8'); const configFile = readFileSync(configPath, 'utf8');
globalConfig = iniParse(configFile) as ConfigType; globalConfig = iniParse(configFile) as ConfigType;
} }
const mergeObjects = (main: Partial<ConfigType>, fallback: ConfigType) => return globalConfig;
Object.keys(CONFIG_KEYS).reduce((acc, key) => { };
acc[key] = parseEnvVarValue(main[key] ?? fallback[key]);
return acc; /**
}, {} as ConfigType); * Merges two configs.
* Env config takes precedence over global ~/.opencommit config file
* @param main - env config
* @param fallback - global ~/.opencommit config file
* @returns merged config
*/
const mergeConfigs = (main: Partial<ConfigType>, fallback: ConfigType) =>
Object.keys(CONFIG_KEYS).reduce((acc, key) => {
acc[key] = parseEnvVarValue(main[key] ?? fallback[key]);
// env config takes precedence over global ~/.opencommit config file return acc;
const config = mergeObjects(envConfig, globalConfig); }, {} as ConfigType);
interface GetConfigOptions {
globalPath?: string;
envPath?: string;
}
export const getConfig = ({
envPath = defaultEnvPath,
globalPath = defaultConfigPath
}: GetConfigOptions = {}): ConfigType => {
const envConfig = getEnvConfig(envPath);
const globalConfig = getGlobalConfig(globalPath);
const config = mergeConfigs(envConfig, globalConfig);
return config; return config;
}; };
export const setConfig = ( export const setConfig = (
keyValues: [key: string, value: string][], keyValues: [key: string, value: string][],
configPath: string = defaultConfigPath globalConfigPath: string = defaultConfigPath
) => { ) => {
const config = getConfig(); const config = getConfig({
globalPath: globalConfigPath
});
for (let [key, value] of keyValues) { for (let [key, value] of keyValues) {
if (!configValidators.hasOwnProperty(key)) { if (!configValidators.hasOwnProperty(key)) {
@@ -543,9 +563,7 @@ export const setConfig = (
config[key] = validValue; config[key] = validValue;
} }
writeFileSync(configPath, iniStringify(config), 'utf8'); writeFileSync(globalConfigPath, iniStringify(config), 'utf8');
assertConfigsAreValid(config);
outro(`${chalk.green('✔')} config successfully set`); outro(`${chalk.green('✔')} config successfully set`);
}; };

View File

@@ -1,10 +1,16 @@
import { getConfig } from '../../src/commands/config'; import { existsSync, readFileSync, rmSync } from 'fs';
import {
DEFAULT_CONFIG,
getConfig,
setConfig
} from '../../src/commands/config';
import { prepareFile } from './utils'; import { prepareFile } from './utils';
import { dirname } from 'path';
describe('getConfig', () => { describe('config', () => {
const originalEnv = { ...process.env }; const originalEnv = { ...process.env };
let globalConfigFile: { filePath: string; cleanup: () => Promise<void> }; let globalConfigFile: { filePath: string; cleanup: () => Promise<void> };
let localEnvFile: { filePath: string; cleanup: () => Promise<void> }; let envConfigFile: { filePath: string; cleanup: () => Promise<void> };
function resetEnv(env: NodeJS.ProcessEnv) { function resetEnv(env: NodeJS.ProcessEnv) {
Object.keys(process.env).forEach((key) => { Object.keys(process.env).forEach((key) => {
@@ -19,7 +25,12 @@ describe('getConfig', () => {
beforeEach(async () => { beforeEach(async () => {
resetEnv(originalEnv); resetEnv(originalEnv);
if (globalConfigFile) await globalConfigFile.cleanup(); if (globalConfigFile) await globalConfigFile.cleanup();
if (localEnvFile) await localEnvFile.cleanup(); if (envConfigFile) await envConfigFile.cleanup();
});
afterEach(async () => {
if (globalConfigFile) await globalConfigFile.cleanup();
if (envConfigFile) await envConfigFile.cleanup();
}); });
afterAll(() => { afterAll(() => {
@@ -36,115 +47,249 @@ describe('getConfig', () => {
return await prepareFile(fileName, fileContent); return await prepareFile(fileName, fileContent);
}; };
it('should prioritize local .env over global .opencommit config', async () => { describe('getConfig', () => {
globalConfigFile = await generateConfig('.opencommit', { it('should prioritize local .env over global .opencommit config', async () => {
OCO_OPENAI_API_KEY: 'global-key', globalConfigFile = await generateConfig('.opencommit', {
OCO_MODEL: 'gpt-3.5-turbo', OCO_OPENAI_API_KEY: 'global-key',
OCO_LANGUAGE: 'en' OCO_MODEL: 'gpt-3.5-turbo',
OCO_LANGUAGE: 'en'
});
envConfigFile = await generateConfig('.env', {
OCO_OPENAI_API_KEY: 'local-key',
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key',
OCO_LANGUAGE: 'fr'
});
const config = getConfig({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual('local-key');
expect(config.OCO_MODEL).toEqual('gpt-3.5-turbo');
expect(config.OCO_LANGUAGE).toEqual('fr');
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key');
}); });
localEnvFile = await generateConfig('.env', { it('should fallback to global config when local config is not set', async () => {
OCO_OPENAI_API_KEY: 'local-key', globalConfigFile = await generateConfig('.opencommit', {
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key', OCO_OPENAI_API_KEY: 'global-key',
OCO_LANGUAGE: 'fr' OCO_MODEL: 'gpt-4',
OCO_LANGUAGE: 'de',
OCO_DESCRIPTION: 'true'
});
envConfigFile = await generateConfig('.env', {
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key'
});
const config = getConfig({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key');
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key');
expect(config.OCO_MODEL).toEqual('gpt-4');
expect(config.OCO_LANGUAGE).toEqual('de');
expect(config.OCO_DESCRIPTION).toEqual(true);
}); });
const config = getConfig({ it('should handle boolean and numeric values correctly', async () => {
configPath: globalConfigFile.filePath, globalConfigFile = await generateConfig('.opencommit', {
envPath: localEnvFile.filePath OCO_TOKENS_MAX_INPUT: '4096',
OCO_TOKENS_MAX_OUTPUT: '500',
OCO_GITPUSH: 'true'
});
envConfigFile = await generateConfig('.env', {
OCO_TOKENS_MAX_INPUT: '8192',
OCO_ONE_LINE_COMMIT: 'false'
});
const config = getConfig({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_TOKENS_MAX_INPUT).toEqual(8192);
expect(config.OCO_TOKENS_MAX_OUTPUT).toEqual(500);
expect(config.OCO_GITPUSH).toEqual(true);
expect(config.OCO_ONE_LINE_COMMIT).toEqual(false);
}); });
expect(config).not.toEqual(null); it('should handle empty local config correctly', async () => {
expect(config.OCO_OPENAI_API_KEY).toEqual('local-key'); globalConfigFile = await generateConfig('.opencommit', {
expect(config.OCO_MODEL).toEqual('gpt-3.5-turbo'); OCO_OPENAI_API_KEY: 'global-key',
expect(config.OCO_LANGUAGE).toEqual('fr'); OCO_MODEL: 'gpt-4',
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key'); OCO_LANGUAGE: 'es'
});
envConfigFile = await generateConfig('.env', {});
const config = getConfig({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key');
expect(config.OCO_MODEL).toEqual('gpt-4');
expect(config.OCO_LANGUAGE).toEqual('es');
});
it('should override global config with null values in local .env', async () => {
globalConfigFile = await generateConfig('.opencommit', {
OCO_OPENAI_API_KEY: 'global-key',
OCO_MODEL: 'gpt-4',
OCO_LANGUAGE: 'es'
});
envConfigFile = await generateConfig('.env', {
OCO_OPENAI_API_KEY: 'null'
});
const config = getConfig({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual(null);
});
it('should handle empty global config', async () => {
globalConfigFile = await generateConfig('.opencommit', {});
envConfigFile = await generateConfig('.env', {});
const config = getConfig({
globalPath: globalConfigFile.filePath,
envPath: envConfigFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual(undefined);
});
}); });
it('should fallback to global config when local config is not set', async () => { describe('setConfig', () => {
globalConfigFile = await generateConfig('.opencommit', { beforeEach(async () => {
OCO_OPENAI_API_KEY: 'global-key', // we create and delete the file to have the parent directory, but not the file, to test the creation of the file
OCO_MODEL: 'gpt-4', globalConfigFile = await generateConfig('.opencommit', {});
OCO_LANGUAGE: 'de', rmSync(globalConfigFile.filePath);
OCO_DESCRIPTION: 'true'
}); });
localEnvFile = await generateConfig('.env', { it('should create .opencommit file with DEFAULT CONFIG if it does not exist on first setConfig run', async () => {
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key' const isGlobalConfigFileExist = existsSync(globalConfigFile.filePath);
expect(isGlobalConfigFileExist).toBe(false);
await setConfig(
[['OCO_OPENAI_API_KEY', 'persisted-key_1']],
globalConfigFile.filePath
);
const fileContent = readFileSync(globalConfigFile.filePath, 'utf8');
expect(fileContent).toContain('OCO_OPENAI_API_KEY=persisted-key_1');
Object.entries(DEFAULT_CONFIG).forEach(([key, value]) => {
expect(fileContent).toContain(`${key}=${value}`);
});
}); });
const config = getConfig({ it('should set new config values', async () => {
configPath: globalConfigFile.filePath, globalConfigFile = await generateConfig('.opencommit', {});
envPath: localEnvFile.filePath await setConfig(
[
['OCO_OPENAI_API_KEY', 'new-key'],
['OCO_MODEL', 'gpt-4']
],
globalConfigFile.filePath
);
const config = getConfig({ globalPath: globalConfigFile.filePath });
expect(config.OCO_OPENAI_API_KEY).toEqual('new-key');
expect(config.OCO_MODEL).toEqual('gpt-4');
}); });
expect(config).not.toEqual(null); it('should update existing config values', async () => {
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key'); globalConfigFile = await generateConfig('.opencommit', {
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key'); OCO_OPENAI_API_KEY: 'initial-key'
expect(config.OCO_MODEL).toEqual('gpt-4'); });
expect(config.OCO_LANGUAGE).toEqual('de'); await setConfig(
expect(config.OCO_DESCRIPTION).toEqual(true); [['OCO_OPENAI_API_KEY', 'updated-key']],
}); globalConfigFile.filePath
);
it('should handle boolean and numeric values correctly', async () => { const config = getConfig({ globalPath: globalConfigFile.filePath });
globalConfigFile = await generateConfig('.opencommit', { expect(config.OCO_OPENAI_API_KEY).toEqual('updated-key');
OCO_TOKENS_MAX_INPUT: '4096',
OCO_TOKENS_MAX_OUTPUT: '500',
OCO_GITPUSH: 'true'
}); });
localEnvFile = await generateConfig('.env', { it('should handle boolean and numeric values correctly', async () => {
OCO_TOKENS_MAX_INPUT: '8192', globalConfigFile = await generateConfig('.opencommit', {});
OCO_ONE_LINE_COMMIT: 'false' await setConfig(
[
['OCO_TOKENS_MAX_INPUT', '8192'],
['OCO_DESCRIPTION', 'true'],
['OCO_ONE_LINE_COMMIT', 'false']
],
globalConfigFile.filePath
);
const config = getConfig({ globalPath: globalConfigFile.filePath });
expect(config.OCO_TOKENS_MAX_INPUT).toEqual(8192);
expect(config.OCO_DESCRIPTION).toEqual(true);
expect(config.OCO_ONE_LINE_COMMIT).toEqual(false);
}); });
const config = getConfig({ it('should throw an error for unsupported config keys', async () => {
configPath: globalConfigFile.filePath, globalConfigFile = await generateConfig('.opencommit', {});
envPath: localEnvFile.filePath
try {
await setConfig(
[['UNSUPPORTED_KEY', 'value']],
globalConfigFile.filePath
);
throw new Error('NEVER_REACHED');
} catch (error) {
expect(error.message).toContain(
'Unsupported config key: UNSUPPORTED_KEY'
);
expect(error.message).not.toContain('NEVER_REACHED');
}
}); });
expect(config).not.toEqual(null); it('should persist changes to the config file', async () => {
expect(config.OCO_TOKENS_MAX_INPUT).toEqual(8192); const isGlobalConfigFileExist = existsSync(globalConfigFile.filePath);
expect(config.OCO_TOKENS_MAX_OUTPUT).toEqual(500); expect(isGlobalConfigFileExist).toBe(false);
expect(config.OCO_GITPUSH).toEqual(true);
expect(config.OCO_ONE_LINE_COMMIT).toEqual(false);
});
it('should handle empty local config correctly', async () => { await setConfig(
globalConfigFile = await generateConfig('.opencommit', { [['OCO_OPENAI_API_KEY', 'persisted-key']],
OCO_OPENAI_API_KEY: 'global-key', globalConfigFile.filePath
OCO_MODEL: 'gpt-4', );
OCO_LANGUAGE: 'es'
const fileContent = readFileSync(globalConfigFile.filePath, 'utf8');
expect(fileContent).toContain('OCO_OPENAI_API_KEY=persisted-key');
}); });
localEnvFile = await generateConfig('.env', {}); it('should set multiple configs in a row and keep the changes', async () => {
const isGlobalConfigFileExist = existsSync(globalConfigFile.filePath);
expect(isGlobalConfigFileExist).toBe(false);
const config = getConfig({ await setConfig(
configPath: globalConfigFile.filePath, [['OCO_OPENAI_API_KEY', 'persisted-key']],
envPath: localEnvFile.filePath globalConfigFile.filePath
);
const fileContent1 = readFileSync(globalConfigFile.filePath, 'utf8');
expect(fileContent1).toContain('OCO_OPENAI_API_KEY=persisted-key');
await setConfig([['OCO_MODEL', 'gpt-4']], globalConfigFile.filePath);
const fileContent2 = readFileSync(globalConfigFile.filePath, 'utf8');
expect(fileContent2).toContain('OCO_MODEL=gpt-4');
}); });
expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key');
expect(config.OCO_MODEL).toEqual('gpt-4');
expect(config.OCO_LANGUAGE).toEqual('es');
});
it('should override global config with null values in local .env', async () => {
globalConfigFile = await generateConfig('.opencommit', {
OCO_OPENAI_API_KEY: 'global-key',
OCO_MODEL: 'gpt-4',
OCO_LANGUAGE: 'es'
});
localEnvFile = await generateConfig('.env', { OCO_OPENAI_API_KEY: 'null' });
const config = getConfig({
configPath: globalConfigFile.filePath,
envPath: localEnvFile.filePath
});
expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual(null);
}); });
}); });

View File

@@ -1,7 +1,7 @@
import path from 'path'; import { existsSync, mkdtemp, rm, writeFile } from 'fs';
import { mkdtemp, rm, writeFile } from 'fs';
import { promisify } from 'util';
import { tmpdir } from 'os'; import { tmpdir } from 'os';
import path from 'path';
import { promisify } from 'util';
const fsMakeTempDir = promisify(mkdtemp); const fsMakeTempDir = promisify(mkdtemp);
const fsRemove = promisify(rm); const fsRemove = promisify(rm);
const fsWriteFile = promisify(writeFile); const fsWriteFile = promisify(writeFile);
@@ -20,7 +20,9 @@ export async function prepareFile(
const filePath = path.resolve(tempDir, fileName); const filePath = path.resolve(tempDir, fileName);
await fsWriteFile(filePath, content); await fsWriteFile(filePath, content);
const cleanup = async () => { const cleanup = async () => {
return fsRemove(tempDir, { recursive: true }); if (existsSync(tempDir)) {
await fsRemove(tempDir, { recursive: true });
}
}; };
return { return {

View File

@@ -4,7 +4,7 @@
"lib": ["ES6", "ES2020"], "lib": ["ES6", "ES2020"],
"module": "CommonJS", "module": "CommonJS",
"resolveJsonModule": true, "resolveJsonModule": true,
"moduleResolution": "Node", "moduleResolution": "Node",
@@ -21,9 +21,7 @@
"skipLibCheck": true "skipLibCheck": true
}, },
"include": [ "include": ["test/jest-setup.ts"],
"test/jest-setup.ts"
],
"exclude": ["node_modules"], "exclude": ["node_modules"],
"ts-node": { "ts-node": {
"esm": true, "esm": true,