@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.
package/package.json CHANGED
@@ -1,34 +1,35 @@
1
1
  {
2
- "name": "@waron97/prbot",
3
- "version": "2.1.0",
4
- "description": "",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "bin": {
10
- "prbot": "src/index.js"
11
- },
12
- "dependencies": {
13
- "@inquirer/search": "^2.0.1",
14
- "commander": "^14.0.2",
15
- "dotenv": "^17.2.3",
16
- "eslint-plugin-es5": "^1.5.0",
17
- "inquirer": "^13.4.2",
18
- "node-fetch": "^3.3.2",
19
- "omelette": "^0.4.17",
20
- "prettier": "^3.5.3"
21
- },
22
- "devDependencies": {
23
- "@eslint/js": "^9.21.0",
24
- "@types/node": "^25.0.10",
25
- "@typescript-eslint/parser": "^8.26.0",
26
- "eslint": "^9.21.0",
27
- "typescript": "^5.8.2",
28
- "typescript-eslint": "^8.26.0"
29
- },
30
- "keywords": [],
31
- "author": "",
32
- "license": "ISC",
33
- "type": "module"
2
+ "name": "@waron97/prbot",
3
+ "version": "2.2.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "bin": {
10
+ "prbot": "src/index.js"
11
+ },
12
+ "dependencies": {
13
+ "@inquirer/search": "^2.0.1",
14
+ "commander": "^14.0.2",
15
+ "dotenv": "^17.2.3",
16
+ "eslint-plugin-es5": "^1.5.0",
17
+ "inquirer": "^13.4.2",
18
+ "inquirer-search-list": "^1.2.6",
19
+ "node-fetch": "^3.3.2",
20
+ "omelette": "^0.4.17",
21
+ "prettier": "^3.5.3"
22
+ },
23
+ "devDependencies": {
24
+ "@eslint/js": "^9.21.0",
25
+ "@types/node": "^25.0.10",
26
+ "@typescript-eslint/parser": "^8.26.0",
27
+ "eslint": "^9.21.0",
28
+ "typescript": "^5.8.2",
29
+ "typescript-eslint": "^8.26.0"
30
+ },
31
+ "keywords": [],
32
+ "author": "",
33
+ "license": "ISC",
34
+ "type": "module"
34
35
  }
@@ -1,313 +1,288 @@
1
- import fs from "fs/promises";
2
- import { readFileSync } from "fs";
3
- import fetch from "node-fetch";
4
- import inquirer from "inquirer";
5
- import search from "@inquirer/search";
6
- import { resolveAddonsPath } from "../lib/addons.js";
7
- import { execGit } from "../lib/git.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 fetch from 'node-fetch';
6
+ import { resolveAddonsPath } from '../lib/addons.js';
7
+ import { execGit } from '../lib/git.js';
8
8
  import {
9
- extractSections,
10
- findSectionEndLine,
11
- detectIndentation,
12
- buildRefString,
13
- findDuplicateLine,
14
- appendPrToLine,
15
- } from "./changelog.js";
9
+ appendPrToLine,
10
+ buildRefString,
11
+ detectIndentation,
12
+ extractSections,
13
+ findDuplicateLine,
14
+ findSectionEndLine,
15
+ } from './changelog.js';
16
16
 
17
17
  function devopsHeaders() {
18
- const token = Buffer.from(`:${process.env.DEVOPS_TOKEN}`).toString("base64");
19
- return {
20
- Authorization: `Basic ${token}`,
21
- "Content-Type": "application/json",
22
- };
18
+ const token = Buffer.from(`:${process.env.DEVOPS_TOKEN}`).toString('base64');
19
+ return {
20
+ Authorization: `Basic ${token}`,
21
+ 'Content-Type': 'application/json',
22
+ };
23
23
  }
24
24
 
25
25
  async function fetchTask(taskId) {
26
- const url = `${process.env.TRIDENT_URL}/jsonrpc`;
27
- const body = {
28
- jsonrpc: "2.0",
29
- method: "call",
30
- id: 1,
31
- params: {
32
- service: "object",
33
- method: "execute_kw",
34
- args: [
35
- process.env.TRIDENT_DB,
36
- parseInt(process.env.TRIDENT_UID, 10),
37
- process.env.TRIDENT_TOKEN,
38
- "project.task",
39
- "read",
40
- [[parseInt(taskId, 10)]],
41
- {
42
- fields: [
43
- "name",
44
- "x_subpackage_id",
45
- "x_workflow",
46
- "x_cluster_id",
47
- "x_release_checklist",
48
- ],
26
+ const url = `${process.env.TRIDENT_URL}/jsonrpc`;
27
+ const body = {
28
+ jsonrpc: '2.0',
29
+ method: 'call',
30
+ id: 1,
31
+ params: {
32
+ service: 'object',
33
+ method: 'execute_kw',
34
+ args: [
35
+ process.env.TRIDENT_DB,
36
+ parseInt(process.env.TRIDENT_UID, 10),
37
+ process.env.TRIDENT_TOKEN,
38
+ 'project.task',
39
+ 'read',
40
+ [[parseInt(taskId, 10)]],
41
+ {
42
+ fields: [
43
+ 'name',
44
+ 'x_subpackage_id',
45
+ 'x_workflow',
46
+ 'x_cluster_id',
47
+ 'x_release_checklist',
48
+ ],
49
+ },
50
+ ],
49
51
  },
50
- ],
51
- },
52
- };
53
- const res = await fetch(url, {
54
- method: "POST",
55
- headers: { "Content-Type": "application/json" },
56
- body: JSON.stringify(body),
57
- });
58
- const data = await res.json();
59
- if (data.error)
60
- throw new Error(`Trident error: ${JSON.stringify(data.error)}`);
61
- if (!data.result || !data.result[0])
62
- throw new Error(`Task ${taskId} not found`);
63
- return data.result[0];
52
+ };
53
+ const res = await fetch(url, {
54
+ method: 'POST',
55
+ headers: { 'Content-Type': 'application/json' },
56
+ body: JSON.stringify(body),
57
+ });
58
+ const data = await res.json();
59
+ if (data.error) throw new Error(`Trident error: ${JSON.stringify(data.error)}`);
60
+ if (!data.result || !data.result[0]) throw new Error(`Task ${taskId} not found`);
61
+ return data.result[0];
64
62
  }
65
63
 
66
64
  function buildPrDescription(taskIds, jiras) {
67
- const lines = taskIds.map(
68
- (id) => `${process.env.TRIDENT_URL}/odoo/my-tasks/${id}`,
69
- );
70
- for (const jira of jiras ?? []) {
71
- lines.push(`https://sorgenia.atlassian.net/browse/${jira}`);
72
- }
73
- return lines.join("\n");
65
+ const lines = taskIds.map((id) => `${process.env.TRIDENT_URL}/odoo/my-tasks/${id}`);
66
+ for (const jira of jiras ?? []) {
67
+ lines.push(`https://sorgenia.atlassian.net/browse/${jira}`);
68
+ }
69
+ return lines.join('\n');
74
70
  }
75
71
 
76
72
  async function createDevopsPR(branch, title, description) {
77
- const { DEVOPS_ORG, DEVOPS_PROJECT, DEVOPS_REPO, AUTOPR_TARGET_BRANCH } =
78
- process.env;
79
- const apiUrl = `https://dev.azure.com/${DEVOPS_ORG}/${DEVOPS_PROJECT}/_apis/git/repositories/${DEVOPS_REPO}/pullrequests?api-version=7.0`;
80
- const res = await fetch(apiUrl, {
81
- method: "POST",
82
- headers: devopsHeaders(),
83
- body: JSON.stringify({
84
- title,
85
- description,
86
- isDraft: true,
87
- sourceRefName: `refs/heads/${branch}`,
88
- targetRefName: `refs/heads/${AUTOPR_TARGET_BRANCH}`,
89
- }),
90
- });
91
- const data = await res.json();
92
- if (!res.ok)
93
- throw new Error(`DevOps PR creation failed: ${JSON.stringify(data)}`);
94
- const prUrl = `https://dev.azure.com/${DEVOPS_ORG}/${DEVOPS_PROJECT}/_git/${DEVOPS_REPO}/pullrequest/${data.pullRequestId}`;
95
- return { id: data.pullRequestId, url: prUrl };
73
+ const { DEVOPS_ORG, DEVOPS_PROJECT, DEVOPS_REPO, AUTOPR_TARGET_BRANCH } = process.env;
74
+ const apiUrl = `https://dev.azure.com/${DEVOPS_ORG}/${DEVOPS_PROJECT}/_apis/git/repositories/${DEVOPS_REPO}/pullrequests?api-version=7.0`;
75
+ const res = await fetch(apiUrl, {
76
+ method: 'POST',
77
+ headers: devopsHeaders(),
78
+ body: JSON.stringify({
79
+ title,
80
+ description,
81
+ isDraft: true,
82
+ sourceRefName: `refs/heads/${branch}`,
83
+ targetRefName: `refs/heads/${AUTOPR_TARGET_BRANCH}`,
84
+ }),
85
+ });
86
+ const data = await res.json();
87
+ if (!res.ok) throw new Error(`DevOps PR creation failed: ${JSON.stringify(data)}`);
88
+ const prUrl = `https://dev.azure.com/${DEVOPS_ORG}/${DEVOPS_PROJECT}/_git/${DEVOPS_REPO}/pullrequest/${data.pullRequestId}`;
89
+ return { id: data.pullRequestId, url: prUrl };
96
90
  }
97
91
 
98
92
  async function appendChecklistPrLink(taskId, currentChecklist, prUrl, prNumber) {
99
- const link = `<a href="${prUrl}">${prUrl}</a><br/>`;
100
- const updated = currentChecklist ? `${currentChecklist}\n${link}` : link;
101
- const url = `${process.env.TRIDENT_URL}/jsonrpc`;
102
- const res = await fetch(url, {
103
- method: "POST",
104
- headers: { "Content-Type": "application/json" },
105
- body: JSON.stringify({
106
- jsonrpc: "2.0",
107
- method: "call",
108
- id: 2,
109
- params: {
110
- service: "object",
111
- method: "execute_kw",
112
- args: [
113
- process.env.TRIDENT_DB,
114
- parseInt(process.env.TRIDENT_UID, 10),
115
- process.env.TRIDENT_TOKEN,
116
- "project.task",
117
- "write",
118
- [[parseInt(taskId, 10)], { x_release_checklist: updated }],
119
- ],
120
- },
121
- }),
122
- });
123
- const data = await res.json();
124
- if (data.error)
125
- throw new Error(`Trident write error: ${JSON.stringify(data.error)}`);
93
+ const link = `<a href="${prUrl}">${prUrl}</a><br/>`;
94
+ const updated = currentChecklist ? `${currentChecklist}\n${link}` : link;
95
+ const url = `${process.env.TRIDENT_URL}/jsonrpc`;
96
+ const res = await fetch(url, {
97
+ method: 'POST',
98
+ headers: { 'Content-Type': 'application/json' },
99
+ body: JSON.stringify({
100
+ jsonrpc: '2.0',
101
+ method: 'call',
102
+ id: 2,
103
+ params: {
104
+ service: 'object',
105
+ method: 'execute_kw',
106
+ args: [
107
+ process.env.TRIDENT_DB,
108
+ parseInt(process.env.TRIDENT_UID, 10),
109
+ process.env.TRIDENT_TOKEN,
110
+ 'project.task',
111
+ 'write',
112
+ [[parseInt(taskId, 10)], { x_release_checklist: updated }],
113
+ ],
114
+ },
115
+ }),
116
+ });
117
+ const data = await res.json();
118
+ if (data.error) throw new Error(`Trident write error: ${JSON.stringify(data.error)}`);
126
119
  }
127
120
 
128
121
  function scoreSections(sections, candidates) {
129
- const results = [];
130
- for (const section of sections) {
131
- const heading = section.heading.replace(/^### /, "").toLowerCase().trim();
132
- let score = 0;
133
- for (const candidate of candidates) {
134
- const lc = candidate.toLowerCase().trim();
135
- if (heading === lc) {
136
- score += 100;
137
- continue;
138
- }
139
- if (heading.includes(lc) || lc.includes(heading)) {
140
- score += 50;
141
- continue;
142
- }
143
- // Match by first pipe-segment (e.g. "CROSS_31.1 - ML")
144
- const candidatePrefix = lc.split("|")[0].trim();
145
- const headingPrefix = heading.split("|")[0].trim();
146
- if (
147
- headingPrefix.includes(candidatePrefix) ||
148
- candidatePrefix.includes(headingPrefix)
149
- ) {
150
- score += 20;
151
- continue;
152
- }
153
- // Token fallback
154
- const tokens = lc
155
- .split(/[\s|_\-.]+/)
156
- .filter((t) => t.length > 2);
157
- for (const token of tokens) {
158
- if (heading.includes(token)) score++;
159
- }
122
+ const results = [];
123
+ for (const section of sections) {
124
+ const heading = section.heading.replace(/^### /, '').toLowerCase().trim();
125
+ let score = 0;
126
+ for (const candidate of candidates) {
127
+ const lc = candidate.toLowerCase().trim();
128
+ if (heading === lc) {
129
+ score += 100;
130
+ continue;
131
+ }
132
+ if (heading.includes(lc) || lc.includes(heading)) {
133
+ score += 50;
134
+ continue;
135
+ }
136
+ // Match by first pipe-segment (e.g. "CROSS_31.1 - ML")
137
+ const candidatePrefix = lc.split('|')[0].trim();
138
+ const headingPrefix = heading.split('|')[0].trim();
139
+ if (
140
+ headingPrefix.includes(candidatePrefix) ||
141
+ candidatePrefix.includes(headingPrefix)
142
+ ) {
143
+ score += 20;
144
+ continue;
145
+ }
146
+ // Token fallback
147
+ const tokens = lc.split(/[\s|_\-.]+/).filter((t) => t.length > 2);
148
+ for (const token of tokens) {
149
+ if (heading.includes(token)) score++;
150
+ }
151
+ }
152
+ if (score > 0) results.push({ ...section, score });
160
153
  }
161
- if (score > 0) results.push({ ...section, score });
162
- }
163
- return results.sort((a, b) => b.score - a.score).slice(0, 3);
154
+ return results.sort((a, b) => b.score - a.score).slice(0, 3);
164
155
  }
165
156
 
166
157
  async function selectSection(sections, candidates) {
167
- const scored = candidates.length > 0 ? scoreSections(sections, candidates) : [];
158
+ const scored = candidates.length > 0 ? scoreSections(sections, candidates) : [];
168
159
 
169
- if (candidates.length > 0) {
170
- console.log(`\nTask fields: ${candidates.join(" | ")}`);
171
- if (scored.length === 0) console.log("No matching sections found.");
172
- }
160
+ if (candidates.length > 0) {
161
+ console.log(`\nTask fields: ${candidates.join(' | ')}`);
162
+ if (scored.length === 0) console.log('No matching sections found.');
163
+ }
173
164
 
174
- const scoredHeadings = new Set(scored.map((s) => s.heading));
175
- const topChoices = scored.map((s) => ({
176
- name: `${s.heading.replace(/^### /, "")} ✓`,
177
- value: s.heading,
178
- }));
179
- const restChoices = sections
180
- .filter((s) => !scoredHeadings.has(s.heading))
181
- .map((s) => ({ name: s.heading.replace(/^### /, ""), value: s.heading }));
182
- const allChoices = [...topChoices, ...restChoices];
165
+ const scoredHeadings = new Set(scored.map((s) => s.heading));
166
+ const topChoices = scored.map((s) => ({
167
+ name: `${s.heading.replace(/^### /, '')} ✓`,
168
+ value: s.heading,
169
+ }));
170
+ const restChoices = sections
171
+ .filter((s) => !scoredHeadings.has(s.heading))
172
+ .map((s) => ({ name: s.heading.replace(/^### /, ''), value: s.heading }));
173
+ const allChoices = [...topChoices, ...restChoices];
183
174
 
184
- const selected = await search({
185
- message: "Select changelog section:",
186
- source: async (input) => {
187
- if (!input) return allChoices;
188
- return allChoices.filter((c) =>
189
- c.value.toLowerCase().includes(input.toLowerCase()),
190
- );
191
- },
192
- });
193
- return sections.find((s) => s.heading === selected);
175
+ const selected = await search({
176
+ message: 'Select changelog section:',
177
+ source: async (input) => {
178
+ if (!input) return allChoices;
179
+ return allChoices.filter((c) => c.value.toLowerCase().includes(input.toLowerCase()));
180
+ },
181
+ });
182
+ return sections.find((s) => s.heading === selected);
194
183
  }
195
184
 
196
185
  async function autopr(options) {
197
- const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
198
- const changelogPath = `${ADDONS_PATH}/CHANGELOG.md`;
186
+ const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
187
+ const changelogPath = `${ADDONS_PATH}/CHANGELOG.md`;
199
188
 
200
- const ids = options.trident ?? [];
201
- const hasTridents = ids.length > 0;
189
+ const ids = options.trident ?? [];
190
+ const hasTridents = ids.length > 0;
202
191
 
203
- const tasks = hasTridents
204
- ? await Promise.all(ids.map((id) => fetchTask(id)))
205
- : [];
206
- tasks.forEach((t) => console.log(`Task: ${t.name}`));
192
+ const tasks = hasTridents ? await Promise.all(ids.map((id) => fetchTask(id))) : [];
193
+ tasks.forEach((t) => console.log(`Task: ${t.name}`));
207
194
 
208
- const content = readFileSync(changelogPath, "utf-8");
195
+ const content = readFileSync(changelogPath, 'utf-8');
209
196
 
210
- const duplicate = hasTridents
211
- ? findDuplicateLine(content, ids, [])
212
- : null;
197
+ const duplicate = hasTridents ? findDuplicateLine(content, ids, []) : null;
213
198
 
214
- let appendMode = false;
215
- if (duplicate) {
216
- console.log(`\nExisting entry (line ${duplicate.lineNumber + 1}):\n ${duplicate.line}`);
217
- const { confirm } = await inquirer.prompt([
218
- {
219
- type: "confirm",
220
- name: "confirm",
221
- message: "Append PR ref to existing entry?",
222
- default: true,
223
- },
224
- ]);
225
- appendMode = confirm;
226
- }
199
+ let appendMode = false;
200
+ if (duplicate) {
201
+ console.log(`\nExisting entry (line ${duplicate.lineNumber + 1}):\n ${duplicate.line}`);
202
+ const { confirm } = await inquirer.prompt([
203
+ {
204
+ type: 'confirm',
205
+ name: 'confirm',
206
+ message: 'Append PR ref to existing entry?',
207
+ default: true,
208
+ },
209
+ ]);
210
+ appendMode = confirm;
211
+ }
227
212
 
228
- const branch =
229
- options.branch ??
230
- (ids[0]
231
- ? `autopr_${ids[0]}`
232
- : options.jira?.[0]
233
- ? `autopr_${options.jira[0]}`
234
- : null);
235
- if (!branch)
236
- throw new Error(
237
- "No branch name: provide --branch, a task ID, or --jira",
238
- );
213
+ const branch =
214
+ options.branch ??
215
+ (ids[0] ? `autopr_${ids[0]}` : options.jira?.[0] ? `autopr_${options.jira[0]}` : null);
216
+ if (!branch) throw new Error('No branch name: provide --branch, a task ID, or --jira');
239
217
 
240
- let message = null;
241
- if (!appendMode) {
242
- message = options.message;
243
- if (!message) {
244
- const answer = await inquirer.prompt([
245
- { type: "input", name: "message", message: "Changelog entry message:" },
246
- ]);
247
- message = answer.message;
218
+ let message = null;
219
+ if (!appendMode) {
220
+ message = options.message;
221
+ if (!message) {
222
+ const answer = await inquirer.prompt([
223
+ { type: 'input', name: 'message', message: 'Changelog entry message:' },
224
+ ]);
225
+ message = answer.message;
226
+ }
248
227
  }
249
- }
250
228
 
251
- await execGit(["checkout", "-b", branch], ADDONS_PATH);
252
- console.log(`Branch created: ${branch}`);
253
- await execGit(["push", "-u", "origin", branch], ADDONS_PATH);
229
+ await execGit(['checkout', '-b', branch], ADDONS_PATH);
230
+ console.log(`Branch created: ${branch}`);
231
+ await execGit(['push', '-u', 'origin', branch], ADDONS_PATH);
254
232
 
255
- const prTitle = options.name ?? (tasks[0]?.name ?? branch);
256
- const prDescription = buildPrDescription(ids, options.jira ?? []);
257
- const { id: prNumber, url: prUrl } = await createDevopsPR(branch, prTitle, prDescription);
258
- console.log(`PR opened: #${prNumber} — ${prUrl}`);
233
+ const prTitle = options.name ?? tasks[0]?.name ?? branch;
234
+ const prDescription = buildPrDescription(ids, options.jira ?? []);
235
+ const { id: prNumber, url: prUrl } = await createDevopsPR(branch, prTitle, prDescription);
236
+ console.log(`PR opened: #${prNumber} — ${prUrl}`);
259
237
 
260
- if (hasTridents) {
261
- for (let i = 0; i < ids.length; i++) {
262
- await appendChecklistPrLink(ids[i], tasks[i].x_release_checklist, prUrl, prNumber);
238
+ if (hasTridents) {
239
+ for (let i = 0; i < ids.length; i++) {
240
+ await appendChecklistPrLink(ids[i], tasks[i].x_release_checklist, prUrl, prNumber);
241
+ }
242
+ console.log('Checklist updated');
263
243
  }
264
- console.log("Checklist updated");
265
- }
266
244
 
267
- const lines = content.split("\n");
245
+ const lines = content.split('\n');
268
246
 
269
- if (appendMode) {
270
- lines[duplicate.lineNumber] = appendPrToLine(lines[duplicate.lineNumber], prNumber);
271
- } else {
272
- const sections = extractSections(content);
273
- const candidates = [];
274
- if (hasTridents) {
275
- const anchor = tasks.find(
276
- (t) =>
277
- (Array.isArray(t.x_subpackage_id) && t.x_subpackage_id[1]) ||
278
- (Array.isArray(t.x_workflow) && t.x_workflow[1]) ||
279
- (Array.isArray(t.x_cluster_id) && t.x_cluster_id[1]),
280
- );
281
- if (anchor) {
282
- if (Array.isArray(anchor.x_subpackage_id) && anchor.x_subpackage_id[1])
283
- candidates.push(anchor.x_subpackage_id[1]);
284
- if (Array.isArray(anchor.x_workflow) && anchor.x_workflow[1])
285
- candidates.push(anchor.x_workflow[1]);
286
- if (Array.isArray(anchor.x_cluster_id) && anchor.x_cluster_id[1])
287
- candidates.push(anchor.x_cluster_id[1]);
288
- }
289
- }
247
+ if (appendMode) {
248
+ lines[duplicate.lineNumber] = appendPrToLine(lines[duplicate.lineNumber], prNumber);
249
+ } else {
250
+ const sections = extractSections(content);
251
+ const candidates = [];
252
+ if (hasTridents) {
253
+ const anchor = tasks.find(
254
+ (t) =>
255
+ (Array.isArray(t.x_subpackage_id) && t.x_subpackage_id[1]) ||
256
+ (Array.isArray(t.x_workflow) && t.x_workflow[1]) ||
257
+ (Array.isArray(t.x_cluster_id) && t.x_cluster_id[1])
258
+ );
259
+ if (anchor) {
260
+ if (Array.isArray(anchor.x_subpackage_id) && anchor.x_subpackage_id[1])
261
+ candidates.push(anchor.x_subpackage_id[1]);
262
+ if (Array.isArray(anchor.x_workflow) && anchor.x_workflow[1])
263
+ candidates.push(anchor.x_workflow[1]);
264
+ if (Array.isArray(anchor.x_cluster_id) && anchor.x_cluster_id[1])
265
+ candidates.push(anchor.x_cluster_id[1]);
266
+ }
267
+ }
290
268
 
291
- const selectedSection = await selectSection(sections, candidates);
292
- const endLine = findSectionEndLine(lines, selectedSection.startLine);
293
- const indent = detectIndentation(lines, selectedSection.startLine, endLine);
269
+ const selectedSection = await selectSection(sections, candidates);
270
+ const endLine = findSectionEndLine(lines, selectedSection.startLine);
271
+ const indent = detectIndentation(lines, selectedSection.startLine, endLine);
294
272
 
295
- const jiras = options.jira ?? [];
296
- const refString = buildRefString(ids, jiras, prNumber);
297
- const newEntry = `${indent}- ${message}${refString ? " " + refString : ""}`;
298
- lines.splice(endLine + 1, 0, newEntry);
299
- }
273
+ const jiras = options.jira ?? [];
274
+ const refString = buildRefString(ids, jiras, prNumber);
275
+ const newEntry = `${indent}- ${message}${refString ? ' ' + refString : ''}`;
276
+ lines.splice(endLine + 1, 0, newEntry);
277
+ }
300
278
 
301
- await fs.writeFile(changelogPath, lines.join("\n"));
302
- console.log("Changelog entry written");
279
+ await fs.writeFile(changelogPath, lines.join('\n'));
280
+ console.log('Changelog entry written');
303
281
 
304
- await execGit(["add", "CHANGELOG.md"], ADDONS_PATH);
305
- await execGit(
306
- ["commit", "-m", "[DOC][CHANGELOG] Changelog"],
307
- ADDONS_PATH,
308
- );
309
- await execGit(["push"], ADDONS_PATH);
310
- console.log("Changelog committed and pushed");
282
+ await execGit(['add', 'CHANGELOG.md'], ADDONS_PATH);
283
+ await execGit(['commit', '-m', '[DOC][CHANGELOG] Changelog'], ADDONS_PATH);
284
+ await execGit(['push'], ADDONS_PATH);
285
+ console.log('Changelog committed and pushed');
311
286
  }
312
287
 
313
288
  export { autopr };