@waron97/prbot 2.6.1 → 3.0.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/index.js DELETED
@@ -1,376 +0,0 @@
1
- #!/usr/bin/env node
2
- import { execFile } from 'child_process';
3
- import {
4
- appendFileSync,
5
- existsSync,
6
- mkdirSync,
7
- readdirSync,
8
- readFileSync,
9
- writeFileSync,
10
- } from 'fs';
11
- import fs from 'fs/promises';
12
- import path from 'path';
13
- import { program } from 'commander';
14
- import { configDotenv } from 'dotenv';
15
- import inquirer from 'inquirer';
16
- import fetch from 'node-fetch';
17
- import omelette from 'omelette';
18
-
19
- const CONFIG_DIR = path.join(process.env.HOME || '', '.config', 'prbot');
20
- const CONFIG_FILE = path.join(CONFIG_DIR, 'config');
21
- const COMPLETION_SCRIPT = path.join(CONFIG_DIR, 'completion.sh');
22
-
23
- const completion = omelette('prbot <command> <module>');
24
- completion.on('command', ({ reply }) => {
25
- reply(['pr', 'ver', 'init']);
26
- });
27
-
28
- completion.on('module', ({ before, reply }) => {
29
- if (before === 'init') {
30
- reply([]);
31
- return;
32
- }
33
- try {
34
- const raw = readFileSync(CONFIG_FILE, 'utf-8');
35
- const match = raw.match(/^ADDONS_PATH=(.+)$/m);
36
- if (!match) {
37
- reply([]);
38
- return;
39
- }
40
- const addonsPath = match[1].trim().replace(/^~/, process.env.HOME || '');
41
- reply(readdirSync(path.join(addonsPath, 'config')));
42
- } catch {
43
- reply([]);
44
- }
45
- });
46
-
47
- completion.init();
48
-
49
- const isCompletionMode = process.argv.includes('--compbash') || process.argv.includes('--compzsh');
50
-
51
- if (!isCompletionMode) {
52
- configDotenv({ path: CONFIG_FILE });
53
- }
54
-
55
- async function getToken() {
56
- const url = process.env.KC_URL;
57
- const payload = new URLSearchParams();
58
-
59
- payload.append('username', process.env.KC_USER);
60
- payload.append('password', process.env.KC_PASSWORD);
61
- payload.append('client_id', process.env.KC_ID);
62
- payload.append('client_secret', process.env.KC_SECRET);
63
- payload.append('grant_type', 'password');
64
-
65
- const response = await fetch(url, {
66
- method: 'POST',
67
- headers: {
68
- 'Content-Type': 'application/x-www-form-urlencoded',
69
- },
70
- body: payload.toString(),
71
- });
72
-
73
- const json = await response.json();
74
- return json.access_token;
75
- }
76
-
77
- async function getFiles(module_name, token) {
78
- const url = `${process.env.RIP_URL}/ir.model/xml_prbot`;
79
- const body = JSON.stringify({ module_name });
80
- const headers = {
81
- Authorization: `Bearer ${token}`,
82
- 'Content-Type': 'application/json',
83
- };
84
-
85
- const response = await fetch(url, { method: 'POST', body, headers });
86
- if (!response.ok) {
87
- throw new Error(await response.text());
88
- }
89
- return await response.json();
90
- }
91
-
92
- async function main(module_name) {
93
- const token = await getToken();
94
- const files = await getFiles(module_name, token);
95
-
96
- let ADDONS_PATH = process.env.ADDONS_PATH;
97
- if (ADDONS_PATH.startsWith('~')) {
98
- ADDONS_PATH = ADDONS_PATH.replace('~', process.env.HOME);
99
- }
100
-
101
- // Create out directory
102
-
103
- // Write files temporarily and process them
104
- for (const file of files) {
105
- const buffer = Buffer.from(file.data, 'base64');
106
-
107
- // Read and process the file
108
- let content = buffer.toString();
109
-
110
- // Remove the last two lines
111
- const lines = content.split('\n');
112
-
113
- // Check for skipped records section appended after </odoo>
114
- const odooCloseIndex = lines.findIndex((l) => l.trim() === '</odoo>');
115
- if (odooCloseIndex !== -1 && odooCloseIndex < lines.length - 1) {
116
- const footer = lines.slice(odooCloseIndex + 1).join('\n');
117
- const skippedMatch = footer.match(/Skipped records:\s*(\d+)/);
118
- if (skippedMatch && parseInt(skippedMatch[1]) > 0) {
119
- throw new Error(
120
- `[${file.name}] Export contains skipped records:\n${footer.trim()}`
121
- );
122
- }
123
- const duplicatedMatch = footer.match(/Duplicated records:\s*(\d+)/);
124
- if (duplicatedMatch && parseInt(duplicatedMatch[1]) > 0) {
125
- throw new Error(
126
- `[${file.name}] Export contains duplicated records:\n${footer.trim()}`
127
- );
128
- }
129
- }
130
-
131
- if (lines.length > 2) {
132
- lines.splice(-2);
133
- }
134
- content = lines.join('\n');
135
-
136
- // Remove the bpmn_diagram field pattern
137
- content = content.replace(
138
- /<field name="bpmn_diagram"><!\[CDATA\[[\s\S]*?\]\]><\/field>/g,
139
- ''
140
- );
141
-
142
- // Determine the destination path based on filename
143
- let destPath;
144
- if (file.name.includes('Relazioni mancanti')) {
145
- destPath = `${ADDONS_PATH}/config/${module_name}/data/workflow_missing_relations.xml`;
146
- } else {
147
- destPath = `${ADDONS_PATH}/config/${module_name}/data/workflow_configuration.xml`;
148
- }
149
-
150
- // Create destination directory if needed
151
- await fs.mkdir(path.dirname(destPath), { recursive: true });
152
-
153
- // Write the processed file
154
- await fs.writeFile(destPath, content);
155
- console.log(`Processed: ${file.name} -> ${destPath}`);
156
- }
157
-
158
- // Git operations
159
- const workflowDir = path.join(ADDONS_PATH, 'config', module_name, 'data');
160
- const filesToAdd = [
161
- path.join(workflowDir, 'workflow_missing_relations.xml'),
162
- path.join(workflowDir, 'workflow_configuration.xml'),
163
- ];
164
-
165
- // Add files to git
166
- for (const filePath of filesToAdd) {
167
- await new Promise((resolve, reject) => {
168
- execFile('git', ['add', filePath], { cwd: ADDONS_PATH }, (error) => {
169
- if (error) reject(error);
170
- else resolve();
171
- });
172
- });
173
- }
174
-
175
- // Commit changes
176
- const commitMessage = `[IMP][${module_name}] Update workflow`;
177
- await new Promise((resolve, reject) => {
178
- execFile('git', ['commit', '-m', commitMessage], { cwd: ADDONS_PATH }, (error) => {
179
- if (error) reject(error);
180
- else resolve();
181
- });
182
- });
183
-
184
- console.log(`Committed with message: ${commitMessage}`);
185
- }
186
-
187
- async function verbot(module_name, level) {
188
- if (!['major', 'minor', 'patch'].includes(level)) {
189
- throw new Error('Level must be one of major, minor, patch');
190
- }
191
-
192
- let ADDONS_PATH = process.env.ADDONS_PATH;
193
- if (ADDONS_PATH.startsWith('~')) {
194
- ADDONS_PATH = ADDONS_PATH.replace('~', process.env.HOME);
195
- }
196
-
197
- // Try to find manifest file in either location
198
- let manifestPath = path.join(ADDONS_PATH, module_name, '__manifest__.py');
199
- try {
200
- await fs.access(manifestPath);
201
- } catch {
202
- manifestPath = path.join(ADDONS_PATH, 'config', module_name, '__manifest__.py');
203
- try {
204
- await fs.access(manifestPath);
205
- } catch {
206
- throw new Error(`__manifest__.py not found for module ${module_name}`);
207
- }
208
- }
209
-
210
- // Read the manifest file
211
- const content = await fs.readFile(manifestPath, 'utf-8');
212
-
213
- // Find and increment version
214
- const versionMatch = content.match(/"version":\s*"(15\.0\.\d+\.\d+\.\d+)"/);
215
- if (!versionMatch) {
216
- throw new Error('Version not found in manifest');
217
- }
218
-
219
- const currentVersion = versionMatch[1];
220
- const parts = currentVersion.split('.');
221
- const base = `${parts[0]}.${parts[1]}`;
222
- const major = parseInt(parts[2]);
223
- const minor = parseInt(parts[3]);
224
- const patch = parseInt(parts[4]);
225
-
226
- let newVersion;
227
- if (level === 'patch') {
228
- newVersion = `${base}.${major}.${minor}.${patch + 1}`;
229
- } else if (level === 'minor') {
230
- newVersion = `${base}.${major}.${minor + 1}.0`;
231
- } else if (level === 'major') {
232
- newVersion = `${base}.${major + 1}.0.0`;
233
- }
234
-
235
- // Replace only the version line
236
- const newContent = content.replace(
237
- `"version": "${currentVersion}"`,
238
- `"version": "${newVersion}"`
239
- );
240
-
241
- // Write back the manifest
242
- await fs.writeFile(manifestPath, newContent);
243
- console.log(`Updated version: ${currentVersion} -> ${newVersion}`);
244
-
245
- // Git add and commit
246
- await new Promise((resolve, reject) => {
247
- execFile('git', ['add', manifestPath], { cwd: ADDONS_PATH }, (error) => {
248
- if (error) reject(error);
249
- else resolve();
250
- });
251
- });
252
-
253
- const commitMessage = `[VER][${module_name}] Bump`;
254
- await new Promise((resolve, reject) => {
255
- execFile('git', ['commit', '-m', commitMessage], { cwd: ADDONS_PATH }, (error) => {
256
- if (error) reject(error);
257
- else resolve();
258
- });
259
- });
260
-
261
- console.log(`Committed with message: ${commitMessage}`);
262
- }
263
-
264
- program
265
- .command('pr <module>')
266
- .option('-b, --bump <level>')
267
- .action((module, opts) => {
268
- main(module)
269
- .then(() => {
270
- if (opts.bump) {
271
- return verbot(module, opts.bump);
272
- }
273
- })
274
- .catch((err) => {
275
- throw err;
276
- });
277
- });
278
-
279
- program
280
- .command('init')
281
- .description('Create config file and install shell completion')
282
- .action(async () => {
283
- if (!existsSync(CONFIG_DIR)) {
284
- mkdirSync(CONFIG_DIR, { recursive: true });
285
- }
286
-
287
- const existing = existsSync(CONFIG_FILE)
288
- ? Object.fromEntries(
289
- readFileSync(CONFIG_FILE, 'utf-8')
290
- .split('\n')
291
- .flatMap((line) => {
292
- const m = line.match(/^([A-Z_]+)=(.*)$/);
293
- return m ? [[m[1], m[2]]] : [];
294
- })
295
- )
296
- : {};
297
-
298
- const answers = await inquirer.prompt([
299
- {
300
- type: 'input',
301
- name: 'ADDONS_PATH',
302
- message: 'Addons path:',
303
- default: existing.ADDONS_PATH ?? '~/codebase/sorgenia/addons',
304
- },
305
- {
306
- type: 'input',
307
- name: 'KC_URL',
308
- message: 'Keycloak URL:',
309
- default: existing.KC_URL ?? '',
310
- },
311
- {
312
- type: 'input',
313
- name: 'KC_USER',
314
- message: 'Keycloak user:',
315
- default: existing.KC_USER ?? '',
316
- },
317
- {
318
- type: 'password',
319
- name: 'KC_PASSWORD',
320
- message: 'Keycloak password:',
321
- default: existing.KC_PASSWORD ?? '',
322
- mask: '*',
323
- },
324
- {
325
- type: 'input',
326
- name: 'KC_ID',
327
- message: 'Keycloak client ID:',
328
- default: existing.KC_ID ?? '',
329
- },
330
- {
331
- type: 'input',
332
- name: 'KC_SECRET',
333
- message: 'Keycloak client secret:',
334
- default: existing.KC_SECRET ?? '',
335
- },
336
- {
337
- type: 'input',
338
- name: 'RIP_URL',
339
- message: 'RIP URL:',
340
- default: existing.RIP_URL ?? '',
341
- },
342
- ]);
343
-
344
- writeFileSync(
345
- CONFIG_FILE,
346
- Object.entries(answers)
347
- .map(([k, v]) => `${k}=${v}`)
348
- .join('\n') + '\n'
349
- );
350
- console.log(`Config written to ${CONFIG_FILE}`);
351
-
352
- writeFileSync(COMPLETION_SCRIPT, completion.generateCompletionCode());
353
- console.log(`Completion script written to ${COMPLETION_SCRIPT}`);
354
-
355
- const rcFile = path.join(process.env.HOME || '', '.bashrc');
356
- const sourceLine = `source ${COMPLETION_SCRIPT}`;
357
- const rcContent = existsSync(rcFile) ? readFileSync(rcFile, 'utf-8') : '';
358
- if (!rcContent.includes(sourceLine)) {
359
- appendFileSync(rcFile, `\n# prbot completion\n${sourceLine}\n`);
360
- console.log(`Registered completion in ${rcFile} — run: source ~/.bashrc`);
361
- } else {
362
- console.log('Completion already registered in ~/.bashrc');
363
- }
364
- });
365
-
366
- program
367
- .command('ver <module>')
368
- .option('-b, --bump <level>')
369
- .action((module, opts) => {
370
- if (!opts.bump) {
371
- throw new Error('No bump level specified');
372
- }
373
- verbot(module, opts.bump);
374
- });
375
-
376
- program.parse();