@waron97/prbot 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +9 -3
- package/.prettierrc.mjs +11 -0
- package/README.md +30 -30
- package/eslint.config.mjs +16 -0
- package/index.js +327 -344
- package/package.json +33 -32
- package/src/commands/autopr.js +238 -259
- package/src/commands/changelog.js +154 -150
- package/src/commands/commit.js +146 -0
- package/src/commands/init.js +137 -134
- package/src/commands/pr.js +101 -106
- package/src/commands/ver.js +54 -64
- package/src/config.js +4 -4
- package/src/index.js +83 -78
- package/src/lib/addons.js +4 -4
- package/src/lib/git.js +6 -6
|
@@ -1,189 +1,193 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { resolveAddonsPath } from
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import search from '@inquirer/search';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { resolveAddonsPath } from '../lib/addons.js';
|
|
6
6
|
|
|
7
7
|
function buildRefString(tridents, jiras, prNumber) {
|
|
8
|
-
|
|
8
|
+
const refs = [];
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
if (tridents && tridents.length > 0) {
|
|
11
|
+
refs.push(`Trident ${tridents.map((t) => `#${t}`).join(', #')}`);
|
|
12
|
+
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if (jiras && jiras.length > 0) {
|
|
15
|
+
refs.push(`JIRA ${jiras.join(', ')}`);
|
|
16
|
+
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (prNumber) {
|
|
19
|
+
refs.push(`PR sorgenia_addons #${prNumber}ADO`);
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
return refs.length > 0 ? `(${refs.join(', ')})` : '';
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function appendPrToLine(line, prNumber) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
const parenMatch = line.match(/\(([^)]*)\)\s*$/);
|
|
27
|
+
if (parenMatch) {
|
|
28
|
+
const inner = parenMatch[1];
|
|
29
|
+
const suffix = inner.includes('PR sorgenia_addons')
|
|
30
|
+
? `, #${prNumber}ADO`
|
|
31
|
+
: `, PR sorgenia_addons #${prNumber}ADO`;
|
|
32
|
+
return line.replace(/\(([^)]*)\)\s*$/, `(${inner}${suffix})`);
|
|
33
|
+
}
|
|
34
|
+
return `${line.trimEnd()} (PR sorgenia_addons #${prNumber}ADO)`;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function findDuplicateLine(content, tridents, jiras) {
|
|
38
|
-
|
|
38
|
+
const lines = content.split('\n');
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
for (let i = 0; i < lines.length; i++) {
|
|
41
|
+
const line = lines[i];
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
if (tridents && tridents.length > 0) {
|
|
44
|
+
for (const t of tridents) {
|
|
45
|
+
if (line.includes(`Trident #${t}`)) {
|
|
46
|
+
return { lineNumber: i, line };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
47
49
|
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
if (jiras && jiras.length > 0) {
|
|
52
|
+
for (const j of jiras) {
|
|
53
|
+
if (line.includes(j)) {
|
|
54
|
+
return { lineNumber: i, line };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
55
57
|
}
|
|
56
|
-
}
|
|
57
58
|
}
|
|
58
|
-
}
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
return null;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
function extractSections(content) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
const lines = content.split('\n');
|
|
65
|
+
const sections = [];
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < lines.length; i++) {
|
|
68
|
+
if (lines[i].startsWith('### ')) {
|
|
69
|
+
sections.push({
|
|
70
|
+
heading: lines[i],
|
|
71
|
+
startLine: i,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
73
74
|
}
|
|
74
|
-
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
return sections;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function findSectionEndLine(lines, sectionStartLine) {
|
|
80
|
-
|
|
81
|
-
(l, i) => i > sectionStartLine && l.startsWith("### "),
|
|
82
|
-
);
|
|
80
|
+
const nextSectionLine = lines.findIndex((l, i) => i > sectionStartLine && l.startsWith('### '));
|
|
83
81
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
if (nextSectionLine === -1) {
|
|
83
|
+
return lines.length - 1;
|
|
84
|
+
}
|
|
87
85
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
let endLine = nextSectionLine - 1;
|
|
87
|
+
while (endLine > sectionStartLine && lines[endLine].trim() === '') {
|
|
88
|
+
endLine--;
|
|
89
|
+
}
|
|
92
90
|
|
|
93
|
-
|
|
91
|
+
return endLine;
|
|
94
92
|
}
|
|
95
93
|
|
|
96
94
|
function detectIndentation(lines, sectionStartLine, sectionEndLine) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
for (let i = sectionStartLine + 1; i <= sectionEndLine; i++) {
|
|
96
|
+
const line = lines[i];
|
|
97
|
+
const match = line.match(/^(\s*)-\s/);
|
|
98
|
+
if (match) {
|
|
99
|
+
return match[1];
|
|
100
|
+
}
|
|
102
101
|
}
|
|
103
|
-
|
|
104
|
-
return " ";
|
|
102
|
+
return ' ';
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
async function changelog(prNumber, options) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
await fs.writeFile(changelogPath, lines.join("\n"));
|
|
186
|
-
console.log("Changelog entry added");
|
|
106
|
+
const tridents = options.trident || [];
|
|
107
|
+
const jiras = options.jira || [];
|
|
108
|
+
let message = options.message;
|
|
109
|
+
|
|
110
|
+
let ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
111
|
+
const changelogPath = `${ADDONS_PATH}/CHANGELOG.md`;
|
|
112
|
+
|
|
113
|
+
const content = readFileSync(changelogPath, 'utf-8');
|
|
114
|
+
|
|
115
|
+
const duplicate = findDuplicateLine(content, tridents, jiras);
|
|
116
|
+
let appendMode = false;
|
|
117
|
+
if (duplicate) {
|
|
118
|
+
console.log(`\nExisting entry (line ${duplicate.lineNumber + 1}):\n ${duplicate.line}`);
|
|
119
|
+
const { confirm } = await inquirer.prompt([
|
|
120
|
+
{
|
|
121
|
+
type: 'confirm',
|
|
122
|
+
name: 'confirm',
|
|
123
|
+
message: 'Append PR ref to existing entry?',
|
|
124
|
+
default: true,
|
|
125
|
+
},
|
|
126
|
+
]);
|
|
127
|
+
appendMode = confirm;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (appendMode) {
|
|
131
|
+
const lines = content.split('\n');
|
|
132
|
+
lines[duplicate.lineNumber] = appendPrToLine(lines[duplicate.lineNumber], prNumber);
|
|
133
|
+
await fs.writeFile(changelogPath, lines.join('\n'));
|
|
134
|
+
console.log('Updated existing line');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!message) {
|
|
139
|
+
const answer = await inquirer.prompt([
|
|
140
|
+
{
|
|
141
|
+
type: 'input',
|
|
142
|
+
name: 'message',
|
|
143
|
+
message: 'Changelog entry message:',
|
|
144
|
+
},
|
|
145
|
+
]);
|
|
146
|
+
message = answer.message;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const sections = extractSections(content);
|
|
150
|
+
const sectionChoices = sections.map((s) => ({
|
|
151
|
+
name: s.heading.replace(/^## /, ''),
|
|
152
|
+
value: s.heading,
|
|
153
|
+
}));
|
|
154
|
+
|
|
155
|
+
const selectedSection = await search({
|
|
156
|
+
message: 'Select section to add entry:',
|
|
157
|
+
source: async (input) => {
|
|
158
|
+
if (!input) {
|
|
159
|
+
return sectionChoices;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const filtered = sectionChoices.filter((choice) =>
|
|
163
|
+
choice.name.toLowerCase().includes(input.toLowerCase())
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return filtered;
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const selectedSectionObj = sections.find((s) => s.heading === selectedSection);
|
|
171
|
+
|
|
172
|
+
const lines = content.split('\n');
|
|
173
|
+
const endLine = findSectionEndLine(lines, selectedSectionObj.startLine);
|
|
174
|
+
const indent = detectIndentation(lines, selectedSectionObj.startLine, endLine);
|
|
175
|
+
|
|
176
|
+
const refString = buildRefString(tridents, jiras, prNumber);
|
|
177
|
+
const newEntry = `${indent}- ${message}${refString ? ' ' + refString : ''}`;
|
|
178
|
+
|
|
179
|
+
lines.splice(endLine + 1, 0, newEntry);
|
|
180
|
+
|
|
181
|
+
await fs.writeFile(changelogPath, lines.join('\n'));
|
|
182
|
+
console.log('Changelog entry added');
|
|
187
183
|
}
|
|
188
184
|
|
|
189
|
-
export {
|
|
185
|
+
export {
|
|
186
|
+
changelog,
|
|
187
|
+
extractSections,
|
|
188
|
+
findSectionEndLine,
|
|
189
|
+
detectIndentation,
|
|
190
|
+
buildRefString,
|
|
191
|
+
findDuplicateLine,
|
|
192
|
+
appendPrToLine,
|
|
193
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import searchList from 'inquirer-search-list';
|
|
4
|
+
import { resolveAddonsPath } from '../lib/addons.js';
|
|
5
|
+
import { execGit } from '../lib/git.js';
|
|
6
|
+
|
|
7
|
+
inquirer.registerPrompt('search-list', searchList);
|
|
8
|
+
|
|
9
|
+
const commitOperations = [
|
|
10
|
+
{
|
|
11
|
+
name: `✅ ADD ${chalk.gray('- Adding something that was previously missing')}`,
|
|
12
|
+
value: '[ADD]',
|
|
13
|
+
},
|
|
14
|
+
{ name: `📖 DOC ${chalk.gray('- Documentation changes')}`, value: '[DOC]' },
|
|
15
|
+
{ name: `🩹 FIX ${chalk.gray('- Bugfix or hotfix')}`, value: '[FIX]' },
|
|
16
|
+
{ name: `🛠️ IMP ${chalk.gray('- Improvement of existing code')}`, value: '[IMP]' },
|
|
17
|
+
{ name: `💬 I18N ${chalk.gray('- Translation changes')}`, value: '[I18N]' },
|
|
18
|
+
{ name: `📦 MIG ${chalk.gray('- Module migration')}`, value: '[MIG]' },
|
|
19
|
+
{ name: `🔄 REF ${chalk.gray('- Code refactoring')}`, value: '[REF]' },
|
|
20
|
+
{ name: `🎉 REL ${chalk.gray('- Release commit')}`, value: '[REL]' },
|
|
21
|
+
{ name: `🗑️ REM ${chalk.gray('- Removing unnecessary files')}`, value: '[REM]' },
|
|
22
|
+
{ name: `✏️ REN ${chalk.gray('- Renaming files/variables/models/etc.')}`, value: '[REN]' },
|
|
23
|
+
{ name: `🔙 REV ${chalk.gray('- Revert of an existing commit')}`, value: '[REV]' },
|
|
24
|
+
{ name: `🧳 SUB ${chalk.gray('- Submodule adding/updating')}`, value: '[SUB]' },
|
|
25
|
+
{ name: `#️⃣ VER ${chalk.gray('- Versioning')}`, value: '[VER]' },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const commitTypes = [
|
|
29
|
+
{ name: '🔧 Workflow', value: 'workflow' },
|
|
30
|
+
{ name: '📋 Module', value: 'module' },
|
|
31
|
+
{ name: '👤 Wizard', value: 'wizard' },
|
|
32
|
+
{ name: '⚙️ Symphony Process', value: 'symphony' },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
async function commit() {
|
|
36
|
+
const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
37
|
+
|
|
38
|
+
const unstagedChanges = await execGit(['diff', '--name-only'], ADDONS_PATH);
|
|
39
|
+
const stagedChanges = await execGit(['diff', '--cached', '--name-only'], ADDONS_PATH);
|
|
40
|
+
|
|
41
|
+
let stageFilesAnswers = [];
|
|
42
|
+
|
|
43
|
+
if (!stagedChanges.trim()) {
|
|
44
|
+
console.log(chalk.yellow('No staged changes found.'));
|
|
45
|
+
console.log(chalk.yellow('Unstaged changes:'));
|
|
46
|
+
console.log(unstagedChanges);
|
|
47
|
+
|
|
48
|
+
stageFilesAnswers = await inquirer.prompt([
|
|
49
|
+
{
|
|
50
|
+
message: 'Select unstaged files to stage for commit:',
|
|
51
|
+
type: 'checkbox',
|
|
52
|
+
name: 'filesToStage',
|
|
53
|
+
choices: unstagedChanges
|
|
54
|
+
.trim()
|
|
55
|
+
.split('\n')
|
|
56
|
+
.map((file) => file),
|
|
57
|
+
},
|
|
58
|
+
]);
|
|
59
|
+
} else {
|
|
60
|
+
console.log(chalk.green('Staged changes:'));
|
|
61
|
+
console.log(stagedChanges);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const answers = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
type: 'search-list',
|
|
67
|
+
message: 'Select an operation:',
|
|
68
|
+
name: 'commitOperation',
|
|
69
|
+
choices: commitOperations,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'search-list',
|
|
73
|
+
message: 'Select what has been changed:',
|
|
74
|
+
name: 'commitType',
|
|
75
|
+
choices: commitTypes,
|
|
76
|
+
when(answers) {
|
|
77
|
+
return answers.commitOperation !== '[DOC]';
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'input',
|
|
82
|
+
message(answers) {
|
|
83
|
+
return `Please enter the name of the ${answers.commitType}:`;
|
|
84
|
+
},
|
|
85
|
+
name: 'commitModule',
|
|
86
|
+
default: 'config_wf_',
|
|
87
|
+
when(answers) {
|
|
88
|
+
return answers.commitType === 'workflow' || answers.commitType === 'module';
|
|
89
|
+
},
|
|
90
|
+
filter(input) {
|
|
91
|
+
let value = input.trim();
|
|
92
|
+
|
|
93
|
+
if (!value.startsWith('[')) {
|
|
94
|
+
value = `[${value}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!value.endsWith(']')) {
|
|
98
|
+
value = `${value}]`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return value;
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: 'input',
|
|
106
|
+
message: 'Please enter your message:',
|
|
107
|
+
name: 'commitMessage',
|
|
108
|
+
filter: (input) => input.trim(),
|
|
109
|
+
when(answers) {
|
|
110
|
+
return answers.commitOperation !== '[DOC]';
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
if (answers.commitOperation === '[DOC]') {
|
|
116
|
+
answers.commitModule = '[CHANGELOG]';
|
|
117
|
+
answers.commitMessage = 'Changelog';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (answers.commitType === 'wizard') {
|
|
121
|
+
answers.commitModule = '[.cloudbuild/pb/B2WA/processes/all]';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (answers.commitType === 'symphony') {
|
|
125
|
+
answers.commitModule = '[.cloudbuild/symphony/B2WA/processes/all]';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const commitMessage = `${answers.commitOperation}${answers.commitModule} ${answers.commitMessage}`;
|
|
129
|
+
|
|
130
|
+
const finalPrompt = await inquirer.prompt([
|
|
131
|
+
{
|
|
132
|
+
type: 'confirm',
|
|
133
|
+
message: `${chalk.red(commitMessage)}\nDo you want to proceed with this commit message?`,
|
|
134
|
+
name: 'confirmCommit',
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
if (finalPrompt.confirmCommit) {
|
|
139
|
+
if (stageFilesAnswers.filesToStage && stageFilesAnswers.filesToStage.length > 0) {
|
|
140
|
+
await execGit(['add', ...stageFilesAnswers.filesToStage], ADDONS_PATH);
|
|
141
|
+
}
|
|
142
|
+
await execGit(['commit', '-m', commitMessage], ADDONS_PATH);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { commit };
|