mirror of
https://github.com/di-sukharev/opencommit.git
synced 2026-01-12 23:28:16 -05:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7f7bfc2bd | ||
|
|
3885ae5893 | ||
|
|
b8e05a5852 | ||
|
|
677b7ecad9 | ||
|
|
6ab06f9db3 | ||
|
|
38ac20612b | ||
|
|
54b8ba7419 | ||
|
|
ff81d7e1ca | ||
|
|
a6ccdb5f77 | ||
|
|
fef8027959 | ||
|
|
0f48cc616e | ||
|
|
b54ff02930 | ||
|
|
7fb46de105 | ||
|
|
2f6e98dc30 | ||
|
|
2aa6582c52 | ||
|
|
2acf833cd0 | ||
|
|
3f7025d50a | ||
|
|
d793bf1340 | ||
|
|
0092e92061 | ||
|
|
0f33b74942 | ||
|
|
8f0a32275e | ||
|
|
b3509e34d0 | ||
|
|
5449d5cd61 | ||
|
|
b0d27e62ba | ||
|
|
5ae52cd8bb | ||
|
|
57baedd0b0 | ||
|
|
d4fc651fec | ||
|
|
04d40b5379 | ||
|
|
ec2e4c628c | ||
|
|
c787329710 | ||
|
|
0d1f72bdec |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,4 +10,5 @@ application.log
|
||||
logfile.log
|
||||
uncaughtExceptions.log
|
||||
.vscode
|
||||
src/*.json
|
||||
src/*.json
|
||||
.idea
|
||||
52
README.md
52
README.md
@@ -20,7 +20,7 @@ All the commits in this repo are done with OpenCommit — look into [the commits
|
||||
|
||||
## Setup
|
||||
|
||||
1. Install opencommit globally to use in any repository:
|
||||
1. Install OpenCommit globally to use in any repository:
|
||||
|
||||
```sh
|
||||
npm install -g opencommit
|
||||
@@ -28,7 +28,7 @@ All the commits in this repo are done with OpenCommit — look into [the commits
|
||||
|
||||
2. Get your API key from [OpenAI](https://platform.openai.com/account/api-keys). Make sure you add payment details, so API works.
|
||||
|
||||
3. Set the key to opencommit config:
|
||||
3. Set the key to OpenCommit config:
|
||||
|
||||
```sh
|
||||
opencommit config set OPENAI_API_KEY=<your_api_key>
|
||||
@@ -38,7 +38,7 @@ All the commits in this repo are done with OpenCommit — look into [the commits
|
||||
|
||||
## Usage
|
||||
|
||||
You can call `opencommit` directly to generate a commit message for your staged changes:
|
||||
You can call OpenCommit directly to generate a commit message for your staged changes:
|
||||
|
||||
```sh
|
||||
git add <files...>
|
||||
@@ -84,9 +84,53 @@ To remove description:
|
||||
oc config set description=false
|
||||
```
|
||||
|
||||
### Internationalization support
|
||||
|
||||
To specify the language used to generate commit messages:
|
||||
|
||||
```sh
|
||||
# de, German ,Deutsch
|
||||
oc config set language=de
|
||||
oc config set language=German
|
||||
oc config set language=Deutsch
|
||||
|
||||
# fr, French, française
|
||||
oc config set language=fr
|
||||
oc config set language=French
|
||||
oc config set language=française
|
||||
```
|
||||
The default language set is **English**
|
||||
All available languages are currently listed in the [i18n](https://github.com/di-sukharev/opencommit/tree/master/src/i18n) folder
|
||||
|
||||
### Git flags
|
||||
|
||||
The `opencommit` or `oc` commands can be used in place of the `git commit -m "${generatedMessage}"` command. This means that any regular flags that are used with the `git commit` command will also be applied when using `opencommit` or `oc`.
|
||||
|
||||
```sh
|
||||
oc --no-verify
|
||||
```
|
||||
|
||||
is translated to :
|
||||
|
||||
```sh
|
||||
git commit -m "${generatedMessage}" --no-verify
|
||||
```
|
||||
|
||||
### Ignore files
|
||||
You can ignore files from submission to OpenAI by creating a `.opencommitignore` file. For example:
|
||||
|
||||
```ignorelang
|
||||
path/to/large-asset.zip
|
||||
**/*.jpg
|
||||
```
|
||||
|
||||
This is useful for preventing opencommit from uploading artifacts and large files.
|
||||
|
||||
By default, opencommit ignores files matching: `*-lock.*` and `*.lock`
|
||||
|
||||
## Git hook
|
||||
|
||||
You can set opencommit as Git [`prepare-commit-msg`](https://git-scm.com/docs/githooks#_prepare_commit_msg) hook. Hook integrates with you IDE Source Control and allows you edit the message before commit.
|
||||
You can set OpenCommit as Git [`prepare-commit-msg`](https://git-scm.com/docs/githooks#_prepare_commit_msg) hook. Hook integrates with you IDE Source Control and allows you edit the message before commit.
|
||||
|
||||
To set the hook:
|
||||
|
||||
|
||||
22
package-lock.json
generated
22
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "opencommit",
|
||||
"version": "1.1.11",
|
||||
"version": "1.1.22",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "opencommit",
|
||||
"version": "1.1.11",
|
||||
"version": "1.1.22",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^0.6.1",
|
||||
@@ -14,6 +14,7 @@
|
||||
"chalk": "^5.2.0",
|
||||
"cleye": "^1.3.2",
|
||||
"execa": "^7.0.0",
|
||||
"ignore": "^5.2.4",
|
||||
"ini": "^3.0.1",
|
||||
"inquirer": "^9.1.4",
|
||||
"openai": "^3.2.1"
|
||||
@@ -31,6 +32,7 @@
|
||||
"dotenv": "^16.0.3",
|
||||
"esbuild": "^0.15.18",
|
||||
"eslint": "^8.28.0",
|
||||
"prettier": "^2.8.4",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.3"
|
||||
}
|
||||
@@ -1879,7 +1881,6 @@
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
@@ -2497,6 +2498,21 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
|
||||
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "opencommit",
|
||||
"version": "1.1.11",
|
||||
"version": "1.1.22",
|
||||
"description": "GPT CLI to auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫",
|
||||
"keywords": [
|
||||
"git",
|
||||
@@ -42,7 +42,8 @@
|
||||
"dev": "ts-node ./src/cli.ts",
|
||||
"build": "rimraf out && esbuild ./src/cli.ts --bundle --outfile=out/cli.cjs --format=cjs --platform=node",
|
||||
"deploy": "npm run build && npm version patch && npm publish --tag latest",
|
||||
"lint": "eslint src --ext ts && tsc --noEmit"
|
||||
"lint": "eslint src --ext ts && tsc --noEmit",
|
||||
"format": "prettier --write src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ini": "^1.3.31",
|
||||
@@ -53,6 +54,7 @@
|
||||
"dotenv": "^16.0.3",
|
||||
"esbuild": "^0.15.18",
|
||||
"eslint": "^8.28.0",
|
||||
"prettier": "^2.8.4",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
@@ -62,6 +64,7 @@
|
||||
"chalk": "^5.2.0",
|
||||
"cleye": "^1.3.2",
|
||||
"execa": "^7.0.0",
|
||||
"ignore": "^5.2.4",
|
||||
"ini": "^3.0.1",
|
||||
"inquirer": "^9.1.4",
|
||||
"openai": "^3.2.1"
|
||||
|
||||
19
src/api.ts
19
src/api.ts
@@ -53,7 +53,10 @@ class OpenAi {
|
||||
} catch (error: unknown) {
|
||||
outro(`${chalk.red('✖')} ${error}`);
|
||||
|
||||
if (axios.isAxiosError<{ error?: { message: string } }>(error) && error.response?.status === 401) {
|
||||
if (
|
||||
axios.isAxiosError<{ error?: { message: string } }>(error) &&
|
||||
error.response?.status === 401
|
||||
) {
|
||||
const openAiError = error.response.data.error;
|
||||
|
||||
if (openAiError?.message) outro(openAiError.message);
|
||||
@@ -67,4 +70,18 @@ class OpenAi {
|
||||
};
|
||||
}
|
||||
|
||||
export const getOpenCommitLatestVersion = async (): Promise<
|
||||
string | undefined
|
||||
> => {
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
'https://unpkg.com/opencommit/package.json'
|
||||
);
|
||||
return data.version;
|
||||
} catch (_) {
|
||||
outro('Error while getting the latest version of opencommit');
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const api = new OpenAi();
|
||||
|
||||
10
src/cli.ts
10
src/cli.ts
@@ -7,8 +7,9 @@ import { configCommand } from './commands/config';
|
||||
import { hookCommand, isHookCalled } from './commands/githook.js';
|
||||
import { prepareCommitMessageHook } from './commands/prepare-commit-msg-hook';
|
||||
import { commit } from './commands/commit';
|
||||
// import { checkIsLatestVersion } from './utils/checkIsLatestVersion';
|
||||
|
||||
const rawArgv = process.argv.slice(2);
|
||||
const extraArgs = process.argv.slice(2);
|
||||
|
||||
cli(
|
||||
{
|
||||
@@ -19,12 +20,13 @@ cli(
|
||||
ignoreArgv: (type) => type === 'unknown-flag' || type === 'argument',
|
||||
help: { description: packageJSON.description }
|
||||
},
|
||||
() => {
|
||||
async () => {
|
||||
// await checkIsLatestVersion();
|
||||
if (isHookCalled) {
|
||||
prepareCommitMessageHook();
|
||||
} else {
|
||||
commit();
|
||||
commit(extraArgs);
|
||||
}
|
||||
},
|
||||
rawArgv
|
||||
extraArgs
|
||||
);
|
||||
|
||||
@@ -3,12 +3,34 @@ import {
|
||||
GenerateCommitMessageErrorEnum,
|
||||
generateCommitMessageWithChatCompletion
|
||||
} from '../generateCommitMessageFromGitDiff';
|
||||
import { assertGitRepo, getStagedGitDiff } from '../utils/git';
|
||||
import { spinner, confirm, outro, isCancel, intro } from '@clack/prompts';
|
||||
import {
|
||||
assertGitRepo,
|
||||
getChangedFiles,
|
||||
getDiff,
|
||||
getStagedFiles,
|
||||
gitAdd
|
||||
} from '../utils/git';
|
||||
import {
|
||||
spinner,
|
||||
confirm,
|
||||
outro,
|
||||
isCancel,
|
||||
intro,
|
||||
multiselect,
|
||||
select
|
||||
} from '@clack/prompts';
|
||||
import chalk from 'chalk';
|
||||
import { trytm } from '../utils/trytm';
|
||||
|
||||
// Adding a function to get the list of remotes
|
||||
const getGitRemotes = async () => {
|
||||
const { stdout } = await execa('git', ['remote']);
|
||||
return stdout.split('\n').filter((remote) => remote.trim() !== '');
|
||||
};
|
||||
|
||||
const generateCommitMessageFromGitDiff = async (
|
||||
diff: string
|
||||
diff: string,
|
||||
extraArgs: string[]
|
||||
): Promise<void> => {
|
||||
await assertGitRepo();
|
||||
|
||||
@@ -45,55 +67,87 @@ ${chalk.grey('——————————————————')}`
|
||||
});
|
||||
|
||||
if (isCommitConfirmedByUser && !isCancel(isCommitConfirmedByUser)) {
|
||||
const { stdout } = await execa('git', ['commit', '-m', commitMessage]);
|
||||
const { stdout } = await execa('git', [
|
||||
'commit',
|
||||
'-m',
|
||||
commitMessage,
|
||||
...extraArgs
|
||||
]);
|
||||
|
||||
outro(`${chalk.green('✔')} successfully committed`);
|
||||
|
||||
outro(stdout);
|
||||
const isPushConfirmedByUser = await confirm({
|
||||
message: 'Do you want to run `git push`?'
|
||||
});
|
||||
const remotes = await getGitRemotes();
|
||||
|
||||
if (isPushConfirmedByUser && !isCancel(isPushConfirmedByUser)) {
|
||||
const pushSpinner = spinner();
|
||||
if (remotes.length === 1) {
|
||||
const isPushConfirmedByUser = await confirm({
|
||||
message: 'Do you want to run `git push`?'
|
||||
});
|
||||
|
||||
pushSpinner.start('Running `git push`');
|
||||
const { stdout } = await execa('git', ['push']);
|
||||
pushSpinner.stop(`${chalk.green('✔')} successfully pushed all commits`);
|
||||
if (isPushConfirmedByUser && !isCancel(isPushConfirmedByUser)) {
|
||||
const pushSpinner = spinner();
|
||||
pushSpinner.start(`Running \`git push ${remotes[0]}\``);
|
||||
const { stdout } = await execa('git', ['push', remotes[0]]);
|
||||
pushSpinner.stop(
|
||||
`${chalk.green('✔')} successfully pushed all commits to ${remotes[0]}`
|
||||
);
|
||||
if (stdout) outro(stdout);
|
||||
}
|
||||
} else {
|
||||
const selectedRemote = (await select({
|
||||
message: 'Choose a remote to push to',
|
||||
options: remotes.map((remote) => ({ value: remote, label: remote }))
|
||||
})) as string;
|
||||
|
||||
if (stdout) outro(stdout);
|
||||
if (!isCancel(selectedRemote)) {
|
||||
const pushSpinner = spinner();
|
||||
pushSpinner.start(`Running \`git push ${selectedRemote}\``);
|
||||
const { stdout } = await execa('git', ['push', selectedRemote]);
|
||||
pushSpinner.stop(
|
||||
`${chalk.green(
|
||||
'✔'
|
||||
)} successfully pushed all commits to ${selectedRemote}`
|
||||
);
|
||||
|
||||
if (stdout) outro(stdout);
|
||||
} else outro(`${chalk.gray('✖')} process cancelled`);
|
||||
}
|
||||
} else outro(`${chalk.gray('✖')} process cancelled`);
|
||||
}
|
||||
};
|
||||
|
||||
export async function commit(isStageAllFlag = false) {
|
||||
intro('open-commit');
|
||||
export async function commit(
|
||||
extraArgs: string[] = [],
|
||||
isStageAllFlag: Boolean = false
|
||||
) {
|
||||
if (isStageAllFlag) {
|
||||
const changedFiles = await getChangedFiles();
|
||||
|
||||
const stagedFilesSpinner = spinner();
|
||||
stagedFilesSpinner.start('Counting staged files');
|
||||
const staged = await getStagedGitDiff(isStageAllFlag);
|
||||
if (changedFiles) await gitAdd({ files: changedFiles });
|
||||
else {
|
||||
outro('No changes detected, write some code and run `oc` again');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!staged && isStageAllFlag) {
|
||||
outro(
|
||||
`${chalk.red(
|
||||
'No changes detected'
|
||||
)} — write some code, stage the files ${chalk
|
||||
.hex('0000FF')
|
||||
.bold('`git add .`')} and rerun ${chalk
|
||||
.hex('0000FF')
|
||||
.bold('`oc`')} command.`
|
||||
);
|
||||
const [stagedFiles, errorStagedFiles] = await trytm(getStagedFiles());
|
||||
const [changedFiles, errorChangedFiles] = await trytm(getChangedFiles());
|
||||
|
||||
if (!changedFiles?.length && !stagedFiles?.length) {
|
||||
outro(chalk.red('No changes detected'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!staged) {
|
||||
outro(
|
||||
`${chalk.red('Nothing to commit')} — stage the files ${chalk
|
||||
.hex('0000FF')
|
||||
.bold('`git add .`')} and rerun ${chalk
|
||||
.hex('0000FF')
|
||||
.bold('`oc`')} command.`
|
||||
);
|
||||
intro('open-commit');
|
||||
if (errorChangedFiles ?? errorStagedFiles) {
|
||||
outro(`${chalk.red('✖')} ${errorChangedFiles ?? errorStagedFiles}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const stagedFilesSpinner = spinner();
|
||||
|
||||
stagedFilesSpinner.start('Counting staged files');
|
||||
|
||||
if (!stagedFiles.length) {
|
||||
stagedFilesSpinner.stop('No files are staged');
|
||||
const isStageAllAndCommitConfirmedByUser = await confirm({
|
||||
message: 'Do you want to stage all files and generate commit message?'
|
||||
@@ -103,17 +157,45 @@ export async function commit(isStageAllFlag = false) {
|
||||
isStageAllAndCommitConfirmedByUser &&
|
||||
!isCancel(isStageAllAndCommitConfirmedByUser)
|
||||
) {
|
||||
await commit(true);
|
||||
await commit(extraArgs, true);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (stagedFiles.length === 0 && changedFiles.length > 0) {
|
||||
const files = (await multiselect({
|
||||
message: chalk.cyan('Select the files you want to add to the commit:'),
|
||||
options: changedFiles.map((file) => ({
|
||||
value: file,
|
||||
label: file
|
||||
}))
|
||||
})) as string[];
|
||||
|
||||
if (isCancel(files)) process.exit(1);
|
||||
|
||||
await gitAdd({ files });
|
||||
}
|
||||
|
||||
await commit(extraArgs, false);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
stagedFilesSpinner.stop(
|
||||
`${staged.files.length} staged files:\n${staged.files
|
||||
`${stagedFiles.length} staged files:\n${stagedFiles
|
||||
.map((file) => ` ${file}`)
|
||||
.join('\n')}`
|
||||
);
|
||||
|
||||
await generateCommitMessageFromGitDiff(staged.diff);
|
||||
const [, generateCommitError] = await trytm(
|
||||
generateCommitMessageFromGitDiff(
|
||||
await getDiff({ files: stagedFiles }),
|
||||
extraArgs
|
||||
)
|
||||
);
|
||||
|
||||
if (generateCommitError) {
|
||||
outro(`${chalk.red('✖')} ${generateCommitError}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ import { homedir } from 'os';
|
||||
import { intro, outro } from '@clack/prompts';
|
||||
import chalk from 'chalk';
|
||||
import { COMMANDS } from '../CommandsEnum';
|
||||
import { getI18nLocal } from '../i18n';
|
||||
|
||||
export enum CONFIG_KEYS {
|
||||
OPENAI_API_KEY = 'OPENAI_API_KEY',
|
||||
description = 'description',
|
||||
emoji = 'emoji'
|
||||
emoji = 'emoji',
|
||||
language = 'language'
|
||||
}
|
||||
|
||||
export enum CONFIG_MODES {
|
||||
@@ -64,6 +66,14 @@ export const configValidators = {
|
||||
);
|
||||
|
||||
return value;
|
||||
},
|
||||
[CONFIG_KEYS.language](value: any) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.language,
|
||||
getI18nLocal(value),
|
||||
`${value} is not supported yet`
|
||||
);
|
||||
return getI18nLocal(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from 'fs/promises';
|
||||
import chalk from 'chalk';
|
||||
import { intro, outro } from '@clack/prompts';
|
||||
import { getStagedGitDiff } from '../utils/git';
|
||||
import { getChangedFiles, getDiff, getStagedFiles, gitAdd } from '../utils/git';
|
||||
import { getConfig } from './config';
|
||||
import { generateCommitMessageWithChatCompletion } from '../generateCommitMessageFromGitDiff';
|
||||
|
||||
@@ -17,7 +17,14 @@ export const prepareCommitMessageHook = async () => {
|
||||
|
||||
if (commitSource) return;
|
||||
|
||||
const staged = await getStagedGitDiff();
|
||||
const changedFiles = await getChangedFiles();
|
||||
if (changedFiles) await gitAdd({ files: changedFiles });
|
||||
else {
|
||||
outro("No changes detected, write some code and run `oc` again");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const staged = await getStagedFiles();
|
||||
|
||||
if (!staged) return;
|
||||
|
||||
@@ -32,7 +39,7 @@ export const prepareCommitMessageHook = async () => {
|
||||
}
|
||||
|
||||
const commitMessage = await generateCommitMessageWithChatCompletion(
|
||||
staged.diff
|
||||
await getDiff({ files: staged })
|
||||
);
|
||||
|
||||
if (typeof commitMessage !== 'string') throw new Error(commitMessage.error);
|
||||
|
||||
@@ -5,8 +5,10 @@ import {
|
||||
import { api } from './api';
|
||||
import { getConfig } from './commands/config';
|
||||
import { mergeStrings } from './utils/mergeStrings';
|
||||
import { i18n, I18nLocals } from './i18n';
|
||||
|
||||
const config = getConfig();
|
||||
const translation = i18n[config?.language as I18nLocals || 'en']
|
||||
|
||||
const INIT_MESSAGES_PROMPT: Array<ChatCompletionRequestMessage> = [
|
||||
{
|
||||
@@ -18,8 +20,8 @@ const INIT_MESSAGES_PROMPT: Array<ChatCompletionRequestMessage> = [
|
||||
}, use the present tense. ${
|
||||
config?.description
|
||||
? 'Add a short description of what commit is about after the commit message. Don\'t start it with "This commit", just describe the changes.'
|
||||
: "Don't add any descriptions to the commit, only commit message."
|
||||
}`
|
||||
: 'Don\'t add any descriptions to the commit, only commit message.'
|
||||
} Use ${translation.localLanguage} to answer.`
|
||||
},
|
||||
{
|
||||
role: ChatCompletionRequestMessageRoleEnum.User,
|
||||
@@ -48,10 +50,9 @@ const INIT_MESSAGES_PROMPT: Array<ChatCompletionRequestMessage> = [
|
||||
},
|
||||
{
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
// prettier-ignore
|
||||
content: `${config?.emoji ? '🐛 ' : ''}fix(server.ts): change port variable case from lowercase port to uppercase PORT
|
||||
${config?.emoji ? '✨ ' : ''}feat(server.ts): add support for process.env.PORT environment variable
|
||||
${config?.description ? 'The port variable is now named PORT, which improves consistency with the naming conventions as PORT is a constant. Support for an environment variable allows the application to be more flexible as it can now run on any available port specified via the process.env.PORT environment variable.' : ''}`
|
||||
content: `${config?.emoji ? '🐛 ' : ''}${translation.commitFix}
|
||||
${config?.emoji ? '✨ ' : ''}${translation.commitFeat}
|
||||
${config?.description ? translation.commitDescription : ''}`
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
6
src/i18n/de.json
Normal file
6
src/i18n/de.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "Deutsch",
|
||||
"commitFix": "fix(server.ts): Ändere die Groß- und Kleinschreibung der Port-Variable von Kleinbuchstaben auf Großbuchstaben PORT.",
|
||||
"commitFeat": "Funktion(server.ts): Unterstützung für die Umgebungsvariable process.env.PORT hinzufügen",
|
||||
"commitDescription": "Die Port-Variable heißt jetzt PORT, was die Konsistenz mit den Namenskonventionen verbessert, da PORT eine Konstante ist. Die Unterstützung für eine Umgebungsvariable ermöglicht es der Anwendung, flexibler zu sein, da sie jetzt auf jedem verfügbaren Port laufen kann, der über die Umgebungsvariable process.env.PORT angegeben wird."
|
||||
}
|
||||
6
src/i18n/en.json
Normal file
6
src/i18n/en.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "english",
|
||||
"commitFix": "fix(server.ts): change port variable case from lowercase port to uppercase PORT",
|
||||
"commitFeat": "feat(server.ts): add support for process.env.PORT environment variable",
|
||||
"commitDescription": "The port variable is now named PORT, which improves consistency with the naming conventions as PORT is a constant. Support for an environment variable allows the application to be more flexible as it can now run on any available port specified via the process.env.PORT environment variable."
|
||||
}
|
||||
6
src/i18n/fr.json
Normal file
6
src/i18n/fr.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "française",
|
||||
"commitFix": "corriger(server.ts) : changer la casse de la variable de port de minuscules à majuscules (PORT)",
|
||||
"commitFeat": "fonctionnalité(server.ts) : ajouter la prise en charge de la variable d'environnement process.env.PORT",
|
||||
"commitDescription": "La variable de port est maintenant nommée PORT, ce qui améliore la cohérence avec les conventions de nommage car PORT est une constante. La prise en charge d'une variable d'environnement permet à l'application d'être plus flexible car elle peut maintenant s'exécuter sur n'importe quel port disponible spécifié via la variable d'environnement process.env.PORT."
|
||||
}
|
||||
55
src/i18n/index.ts
Normal file
55
src/i18n/index.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import en from '../i18n/en.json' assert { type: 'json' };
|
||||
import de from '../i18n/de.json' assert { type: 'json' };
|
||||
import fr from '../i18n/fr.json' assert { type: 'json' };
|
||||
import it from '../i18n/it.json' assert { type: 'json' };
|
||||
import ko from '../i18n/ko.json' assert { type: 'json' };
|
||||
import zh_CN from '../i18n/zh_CN.json' assert { type: 'json' };
|
||||
import zh_TW from '../i18n/zh_TW.json' assert { type: 'json' };
|
||||
import ja from '../i18n/ja.json' assert { type: 'json' };
|
||||
import pt_br from '../i18n/pt_br.json' assert { type: 'json' };
|
||||
|
||||
export enum I18nLocals {
|
||||
'en' = 'en',
|
||||
'zh_CN' = 'zh_CN',
|
||||
'zh_TW' = 'zh_TW',
|
||||
'ja' = 'ja',
|
||||
'de' = 'de',
|
||||
'fr' = 'fr',
|
||||
'it' = 'it',
|
||||
'ko' = 'ko',
|
||||
'pt_br' = 'pt_br'
|
||||
};
|
||||
|
||||
export const i18n = {
|
||||
en,
|
||||
zh_CN,
|
||||
zh_TW,
|
||||
ja,
|
||||
de,
|
||||
fr,
|
||||
it,
|
||||
ko,
|
||||
pt_br
|
||||
};
|
||||
|
||||
export const I18N_CONFIG_ALIAS: { [key: string]: string[] } = {
|
||||
zh_CN: ['zh_CN', '简体中文', '中文', '简体'],
|
||||
zh_TW: ['zh_TW', '繁體中文', '繁體'],
|
||||
ja: ['ja', 'Japanese', 'にほんご'],
|
||||
ko: ['ko', 'Korean', '한국어'],
|
||||
de: ['de', 'German' ,'Deutsch'],
|
||||
fr: ['fr', 'French', 'française'],
|
||||
it: ['it', 'Italian', 'italiano'],
|
||||
pt_br: ['pt_br', 'Portuguese', 'português'],
|
||||
en: ['en', 'English', 'english'],
|
||||
};
|
||||
|
||||
export function getI18nLocal(value: string): string | boolean {
|
||||
for (const key in I18N_CONFIG_ALIAS) {
|
||||
const aliases = I18N_CONFIG_ALIAS[key];
|
||||
if (aliases.includes(value)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
6
src/i18n/it.json
Normal file
6
src/i18n/it.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "italiano",
|
||||
"commitFix": "fix(server.ts): cambia il caso della variabile di porta da minuscolo port a maiuscolo PORT",
|
||||
"commitFeat": "funzionalità(server.ts): aggiungi supporto per la variabile di ambiente process.env.PORT",
|
||||
"commitDescription": "La variabile di porta è ora chiamata PORT, il che migliora la coerenza con le convenzioni di denominazione in quanto PORT è una costante. Il supporto per una variabile di ambiente consente all'applicazione di essere più flessibile poiché ora può essere eseguita su qualsiasi porta disponibile specificata tramite la variabile di ambiente process.env.PORT."
|
||||
}
|
||||
6
src/i18n/ja.json
Normal file
6
src/i18n/ja.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "にほんご",
|
||||
"commitFix": "修正(server.ts): ポート変数のケースを小文字のポートから大文字のPORTに変更",
|
||||
"commitFeat": "新機能(server.ts): process.env.PORT環境変数のサポートを追加する",
|
||||
"commitDescription": "ポート変数は現在PORTという名前になっており、PORTは定数であるため命名規則に一貫性があります。環境変数のサポートにより、アプリケーションはより柔軟になり、process.env.PORT環境変数で指定された任意の利用可能なポートで実行できるようになりまし"
|
||||
}
|
||||
6
src/i18n/ko.json
Normal file
6
src/i18n/ko.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "한국어",
|
||||
"commitFix": "fix(server.ts): 포트 변수를 소문자 port에서 대문자 PORT로 변경",
|
||||
"commitFeat": "피트(server.ts): process.env.PORT 환경 변수 지원 추가",
|
||||
"commitDescription": "포트 변수는 이제 PORT로 이름이 지정되어 상수인 PORT와 일관성 있는 이름 규칙을 따릅니다. 환경 변수 지원을 통해 애플리케이션은 이제 process.env.PORT 환경 변수로 지정된 사용 가능한 모든 포트에서 실행할 수 있으므로 더 유연해졌습니다."
|
||||
}
|
||||
7
src/i18n/pt_br.json
Normal file
7
src/i18n/pt_br.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"localLanguage": "português",
|
||||
"commitFix": "fix(server.ts): altera o caso da variável de porta de port minúscula para PORT maiúscula",
|
||||
"commitFeat": "feat(server.ts): adiciona suporte para a variável de ambiente process.env.PORT",
|
||||
"commitDescription": "A variável de porta agora é denominada PORT, o que melhora a consistência com as convenções de nomenclatura, pois PORT é uma constante. O suporte para uma variável de ambiente permite que o aplicativo seja mais flexível, pois agora pode ser executado em qualquer porta disponível especificada por meio da variável de ambiente process.env.PORT."
|
||||
}
|
||||
|
||||
6
src/i18n/zh_CN.json
Normal file
6
src/i18n/zh_CN.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "简体中文",
|
||||
"commitFix": "修复(server.ts):将端口变量从小写port改为大写PORT",
|
||||
"commitFeat": "功能(server.ts):添加对process.env.PORT环境变量的支持",
|
||||
"commitDescription": "现在端口变量被命名为PORT,这提高了命名约定的一致性,因为PORT是一个常量。环境变量的支持使应用程序更加灵活,因为它现在可以通过process.env.PORT环境变量在任何可用端口上运行。"
|
||||
}
|
||||
6
src/i18n/zh_TW.json
Normal file
6
src/i18n/zh_TW.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "繁體中文",
|
||||
"commitFix": "修正(server.ts):將端口變數從小寫端口改為大寫PORT",
|
||||
"commitFeat": "功能(server.ts):新增對process.env.PORT環境變數的支援",
|
||||
"commitDescription": "現在port變數已更名為PORT,以符合命名慣例,因為PORT是一個常量。支援環境變數可以使應用程序更靈活,因為它現在可以通過process.env.PORT環境變數運行在任何可用端口上。"
|
||||
}
|
||||
23
src/utils/checkIsLatestVersion.ts
Normal file
23
src/utils/checkIsLatestVersion.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { getOpenCommitLatestVersion } from '../api';
|
||||
import currentPackage from '../../package.json' assert { type: 'json' };
|
||||
import chalk from 'chalk';
|
||||
|
||||
export const checkIsLatestVersion = async () => {
|
||||
const latestVersion = await getOpenCommitLatestVersion();
|
||||
|
||||
if (latestVersion) {
|
||||
const currentVersion = currentPackage.version;
|
||||
|
||||
if (currentVersion !== latestVersion) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
`
|
||||
You are not using the latest stable version of OpenCommit with new features and bug fixes.
|
||||
Current version: ${currentVersion}. Latest version: ${latestVersion}.
|
||||
🚀 To update run: npm i -g opencommit@latest.
|
||||
`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
106
src/utils/git.ts
106
src/utils/git.ts
@@ -1,5 +1,7 @@
|
||||
import { execa } from 'execa';
|
||||
import { spinner } from '@clack/prompts';
|
||||
import { outro, spinner } from '@clack/prompts';
|
||||
import { readFileSync } from 'fs';
|
||||
import ignore, { Ignore } from 'ignore';
|
||||
|
||||
export const assertGitRepo = async () => {
|
||||
try {
|
||||
@@ -9,41 +11,85 @@ export const assertGitRepo = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const excludeBigFilesFromDiff = ['*-lock.*', '*.lock'].map(
|
||||
(file) => `:(exclude)${file}`
|
||||
);
|
||||
// const excludeBigFilesFromDiff = ['*-lock.*', '*.lock'].map(
|
||||
// (file) => `:(exclude)${file}`
|
||||
// );
|
||||
|
||||
export interface StagedDiff {
|
||||
files: string[];
|
||||
diff: string;
|
||||
}
|
||||
export const getOpenCommitIgnore = (): Ignore => {
|
||||
const ig = ignore();
|
||||
|
||||
export const getStagedGitDiff = async (
|
||||
isStageAllFlag = false
|
||||
): Promise<StagedDiff | null> => {
|
||||
if (isStageAllFlag) {
|
||||
const stageAllSpinner = spinner();
|
||||
stageAllSpinner.start('Staging all changes');
|
||||
await execa('git', ['add', '.']);
|
||||
stageAllSpinner.stop('Done');
|
||||
try {
|
||||
ig.add(readFileSync('.opencommitignore').toString().split('\n'));
|
||||
} catch (e) {}
|
||||
|
||||
return ig;
|
||||
};
|
||||
|
||||
export const getStagedFiles = async (): Promise<string[]> => {
|
||||
const { stdout: files } = await execa('git', [
|
||||
'diff',
|
||||
'--name-only',
|
||||
'--cached',
|
||||
'--relative'
|
||||
]);
|
||||
|
||||
if (!files) return [];
|
||||
|
||||
const filesList = files.split('\n');
|
||||
|
||||
const ig = getOpenCommitIgnore();
|
||||
const allowedFiles = filesList.filter((file) => !ig.ignores(file));
|
||||
|
||||
if (!allowedFiles) return [];
|
||||
|
||||
return allowedFiles.sort();
|
||||
};
|
||||
|
||||
export const getChangedFiles = async (): Promise<string[]> => {
|
||||
const { stdout: modified } = await execa('git', ['ls-files', '--modified']);
|
||||
const { stdout: others } = await execa('git', [
|
||||
'ls-files',
|
||||
'--others',
|
||||
'--exclude-standard'
|
||||
]);
|
||||
|
||||
const files = [...modified.split('\n'), ...others.split('\n')].filter(
|
||||
(file) => !!file
|
||||
);
|
||||
|
||||
return files.sort();
|
||||
};
|
||||
|
||||
export const gitAdd = async ({ files }: { files: string[] }) => {
|
||||
const gitAddSpinner = spinner();
|
||||
gitAddSpinner.start('Adding files to commit');
|
||||
await execa('git', ['add', ...files]);
|
||||
gitAddSpinner.stop('Done');
|
||||
};
|
||||
|
||||
export const getDiff = async ({ files }: { files: string[] }) => {
|
||||
const lockFiles = files.filter(
|
||||
(file) => file.includes('.lock') || file.includes('-lock.')
|
||||
);
|
||||
|
||||
if (lockFiles.length) {
|
||||
outro(
|
||||
`Some files are '.lock' files which are excluded by default from 'git diff'. No commit messages are generated for this files:\n${lockFiles.join(
|
||||
'\n'
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
const diffStaged = ['diff', '--staged'];
|
||||
const { stdout: files } = await execa('git', [
|
||||
...diffStaged,
|
||||
'--name-only',
|
||||
...excludeBigFilesFromDiff
|
||||
]);
|
||||
|
||||
if (!files) return null;
|
||||
const filesWithoutLocks = files.filter(
|
||||
(file) => !file.includes('.lock') && !file.includes('-lock.')
|
||||
);
|
||||
|
||||
const { stdout: diff } = await execa('git', [
|
||||
...diffStaged,
|
||||
...excludeBigFilesFromDiff
|
||||
'diff',
|
||||
'--staged',
|
||||
'--',
|
||||
...filesWithoutLocks
|
||||
]);
|
||||
|
||||
return {
|
||||
files: files.split('\n').sort(),
|
||||
diff
|
||||
};
|
||||
return diff;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,8 @@ export function mergeStrings(arr: string[], maxStringLength: number): string[] {
|
||||
currentItem = item;
|
||||
}
|
||||
}
|
||||
|
||||
mergedArr.push(currentItem);
|
||||
|
||||
return mergedArr;
|
||||
}
|
||||
|
||||
12
src/utils/trytm.ts
Normal file
12
src/utils/trytm.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export const trytm = async <T>(
|
||||
promise: Promise<T>
|
||||
): Promise<[T, null] | [null, Error]> => {
|
||||
try {
|
||||
const data = await promise;
|
||||
return [data, null];
|
||||
} catch (throwable) {
|
||||
if (throwable instanceof Error) return [null, throwable];
|
||||
|
||||
throw throwable;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user