@waron97/prbot 2.1.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.
@@ -1,189 +1,193 @@
1
- import fs from "fs/promises";
2
- import { readFileSync } from "fs";
3
- import inquirer from "inquirer";
4
- import search from "@inquirer/search";
5
- import { resolveAddonsPath } from "../lib/addons.js";
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
- const refs = [];
8
+ const refs = [];
9
9
 
10
- if (tridents && tridents.length > 0) {
11
- refs.push(`Trident ${tridents.map((t) => `#${t}`).join(", #")}`);
12
- }
10
+ if (tridents && tridents.length > 0) {
11
+ refs.push(`Trident ${tridents.map((t) => `#${t}`).join(', #')}`);
12
+ }
13
13
 
14
- if (jiras && jiras.length > 0) {
15
- refs.push(`JIRA ${jiras.join(", ")}`);
16
- }
14
+ if (jiras && jiras.length > 0) {
15
+ refs.push(`JIRA ${jiras.join(', ')}`);
16
+ }
17
17
 
18
- if (prNumber) {
19
- refs.push(`PR sorgenia_addons #${prNumber}ADO`);
20
- }
18
+ if (prNumber) {
19
+ refs.push(`PR sorgenia_addons #${prNumber}ADO`);
20
+ }
21
21
 
22
- return refs.length > 0 ? `(${refs.join(", ")})` : "";
22
+ return refs.length > 0 ? `(${refs.join(', ')})` : '';
23
23
  }
24
24
 
25
25
  function appendPrToLine(line, prNumber) {
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)`;
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
- const lines = content.split("\n");
38
+ const lines = content.split('\n');
39
39
 
40
- for (let i = 0; i < lines.length; i++) {
41
- const line = lines[i];
40
+ for (let i = 0; i < lines.length; i++) {
41
+ const line = lines[i];
42
42
 
43
- if (tridents && tridents.length > 0) {
44
- for (const t of tridents) {
45
- if (line.includes(`Trident #${t}`)) {
46
- return { lineNumber: i, line };
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
- if (jiras && jiras.length > 0) {
52
- for (const j of jiras) {
53
- if (line.includes(j)) {
54
- return { lineNumber: i, line };
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
- return null;
60
+ return null;
61
61
  }
62
62
 
63
63
  function extractSections(content) {
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
- });
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
- return sections;
76
+ return sections;
77
77
  }
78
78
 
79
79
  function findSectionEndLine(lines, sectionStartLine) {
80
- const nextSectionLine = lines.findIndex(
81
- (l, i) => i > sectionStartLine && l.startsWith("### "),
82
- );
80
+ const nextSectionLine = lines.findIndex((l, i) => i > sectionStartLine && l.startsWith('### '));
83
81
 
84
- if (nextSectionLine === -1) {
85
- return lines.length - 1;
86
- }
82
+ if (nextSectionLine === -1) {
83
+ return lines.length - 1;
84
+ }
87
85
 
88
- let endLine = nextSectionLine - 1;
89
- while (endLine > sectionStartLine && lines[endLine].trim() === "") {
90
- endLine--;
91
- }
86
+ let endLine = nextSectionLine - 1;
87
+ while (endLine > sectionStartLine && lines[endLine].trim() === '') {
88
+ endLine--;
89
+ }
92
90
 
93
- return endLine;
91
+ return endLine;
94
92
  }
95
93
 
96
94
  function detectIndentation(lines, sectionStartLine, sectionEndLine) {
97
- for (let i = sectionStartLine + 1; i <= sectionEndLine; i++) {
98
- const line = lines[i];
99
- const match = line.match(/^(\s*)-\s/);
100
- if (match) {
101
- return match[1];
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
- const tridents = options.trident || [];
109
- const jiras = options.jira || [];
110
- let message = options.message;
111
-
112
- let ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
113
- const changelogPath = `${ADDONS_PATH}/CHANGELOG.md`;
114
-
115
- const content = readFileSync(changelogPath, "utf-8");
116
-
117
- const duplicate = findDuplicateLine(content, tridents, jiras);
118
- let appendMode = false;
119
- if (duplicate) {
120
- console.log(`\nExisting entry (line ${duplicate.lineNumber + 1}):\n ${duplicate.line}`);
121
- const { confirm } = await inquirer.prompt([
122
- {
123
- type: "confirm",
124
- name: "confirm",
125
- message: "Append PR ref to existing entry?",
126
- default: true,
127
- },
128
- ]);
129
- appendMode = confirm;
130
- }
131
-
132
- if (appendMode) {
133
- const lines = content.split("\n");
134
- lines[duplicate.lineNumber] = appendPrToLine(lines[duplicate.lineNumber], prNumber);
135
- await fs.writeFile(changelogPath, lines.join("\n"));
136
- console.log("Updated existing line");
137
- return;
138
- }
139
-
140
- if (!message) {
141
- const answer = await inquirer.prompt([
142
- {
143
- type: "input",
144
- name: "message",
145
- message: "Changelog entry message:",
146
- },
147
- ]);
148
- message = answer.message;
149
- }
150
-
151
- const sections = extractSections(content);
152
- const sectionChoices = sections.map((s) => ({
153
- name: s.heading.replace(/^## /, ""),
154
- value: s.heading,
155
- }));
156
-
157
- const selectedSection = await search({
158
- message: "Select section to add entry:",
159
- source: async (input) => {
160
- if (!input) {
161
- return sectionChoices;
162
- }
163
-
164
- const filtered = sectionChoices.filter((choice) =>
165
- choice.name.toLowerCase().includes(input.toLowerCase()),
166
- );
167
-
168
- return filtered;
169
- },
170
- });
171
-
172
- const selectedSectionObj = sections.find(
173
- (s) => s.heading === selectedSection,
174
- );
175
-
176
- const lines = content.split("\n");
177
- const endLine = findSectionEndLine(lines, selectedSectionObj.startLine);
178
- const indent = detectIndentation(lines, selectedSectionObj.startLine, endLine);
179
-
180
- const refString = buildRefString(tridents, jiras, prNumber);
181
- const newEntry = `${indent}- ${message}${refString ? " " + refString : ""}`;
182
-
183
- lines.splice(endLine + 1, 0, newEntry);
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 { changelog, extractSections, findSectionEndLine, detectIndentation, buildRefString, findDuplicateLine, appendPrToLine };
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 };