@waron97/prbot 1.0.2 → 1.1.1

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.
@@ -0,0 +1,5 @@
1
+ {
2
+ "permissions": {
3
+ "allow": ["Bash(node *)"]
4
+ }
5
+ }
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # prbot
2
+
3
+ CLI tool for fetching Odoo workflow XML files from the RIP API, writing them into the addons repo, and committing the result.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g .
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ```bash
14
+ prbot init
15
+ ```
16
+
17
+ Prompts for all required config values, writes them to `~/.config/prbot/config`, installs shell tab completion, and patches `~/.bashrc`. Run once, re-run anytime to update config.
18
+
19
+ After first run:
20
+
21
+ ```bash
22
+ source ~/.bashrc
23
+ ```
24
+
25
+ ### Config keys
26
+
27
+ | Key | Description |
28
+ |-----|-------------|
29
+ | `ADDONS_PATH` | Path to local Odoo addons repo |
30
+ | `KC_URL` | Keycloak token endpoint URL |
31
+ | `KC_USER` | Keycloak username |
32
+ | `KC_PASSWORD` | Keycloak password |
33
+ | `KC_ID` | Keycloak client ID |
34
+ | `KC_SECRET` | Keycloak client secret |
35
+ | `RIP_URL` | RIP API base URL |
36
+
37
+ ## Commands
38
+
39
+ ### `prbot pr <module>`
40
+
41
+ Fetches workflow XML for `<module>` from RIP, writes files into `ADDONS_PATH/config/<module>/data/`, and commits.
42
+
43
+ ```bash
44
+ prbot pr config_wf_contestazione
45
+ ```
46
+
47
+ Options:
48
+
49
+ | Flag | Description |
50
+ |------|-------------|
51
+ | `-b, --bump <level>` | Also bump manifest version after commit. Level: `major`, `minor`, `patch` |
52
+
53
+ ```bash
54
+ prbot pr config_wf_contestazione -b minor
55
+ ```
56
+
57
+ ### `prbot ver <module>`
58
+
59
+ Bumps the version in `__manifest__.py` for `<module>` and commits.
60
+
61
+ ```bash
62
+ prbot ver config_wf_contestazione --bump patch
63
+ ```
64
+
65
+ ### `prbot init`
66
+
67
+ Interactive setup: writes `~/.config/prbot/config` and installs shell completion.
68
+
69
+ ## Tab completion
70
+
71
+ After `prbot init` and sourcing `~/.bashrc`, `<module>` arguments autocomplete from directories in `ADDONS_PATH/config/`.
72
+
73
+ ```
74
+ prbot pr config_wf_<TAB> # lists all workflow modules
75
+ ```
package/index.js CHANGED
@@ -3,11 +3,56 @@
3
3
  import { configDotenv } from "dotenv";
4
4
  import fetch from "node-fetch";
5
5
  import fs from "fs/promises";
6
+ import {
7
+ readdirSync,
8
+ readFileSync,
9
+ appendFileSync,
10
+ existsSync,
11
+ mkdirSync,
12
+ writeFileSync,
13
+ } from "fs";
6
14
  import path from "path";
7
15
  import { execFile } from "child_process";
8
16
  import { program } from "commander";
17
+ import omelette from "omelette";
18
+ import inquirer from "inquirer";
19
+
20
+ const CONFIG_DIR = path.join(process.env.HOME || "", ".config", "prbot");
21
+ const CONFIG_FILE = path.join(CONFIG_DIR, "config");
22
+ const COMPLETION_SCRIPT = path.join(CONFIG_DIR, "completion.sh");
23
+
24
+ const completion = omelette("prbot <command> <module>");
25
+ completion.on("command", ({ reply }) => {
26
+ reply(["pr", "ver", "init"]);
27
+ });
28
+
29
+ completion.on("module", ({ before, reply }) => {
30
+ if (before === "init") {
31
+ reply([]);
32
+ return;
33
+ }
34
+ try {
35
+ const raw = readFileSync(CONFIG_FILE, "utf-8");
36
+ const match = raw.match(/^ADDONS_PATH=(.+)$/m);
37
+ if (!match) {
38
+ reply([]);
39
+ return;
40
+ }
41
+ const addonsPath = match[1].trim().replace(/^~/, process.env.HOME || "");
42
+ reply(readdirSync(path.join(addonsPath, "config")));
43
+ } catch {
44
+ reply([]);
45
+ }
46
+ });
47
+
48
+ completion.init();
49
+
50
+ const isCompletionMode =
51
+ process.argv.includes("--compbash") || process.argv.includes("--compzsh");
9
52
 
10
- configDotenv({ path: path.join(process.env.HOME, ".prbot") });
53
+ if (!isCompletionMode) {
54
+ configDotenv({ path: CONFIG_FILE });
55
+ }
11
56
 
12
57
  async function getToken() {
13
58
  const url = process.env.KC_URL;
@@ -234,17 +279,13 @@ async function verbot(module_name, level) {
234
279
  }
235
280
 
236
281
  program
237
- .command("pr")
238
- .option("-m, --module <module>")
282
+ .command("pr <module>")
239
283
  .option("-b, --bump <level>")
240
- .action((opts) => {
241
- if (!opts.module) {
242
- throw new Error("No module specified");
243
- }
244
- main(opts.module)
284
+ .action((module, opts) => {
285
+ main(module)
245
286
  .then(() => {
246
287
  if (opts.bump) {
247
- return verbot(opts.module, opts.bump);
288
+ return verbot(module, opts.bump);
248
289
  }
249
290
  })
250
291
  .catch((err) => {
@@ -253,14 +294,100 @@ program
253
294
  });
254
295
 
255
296
  program
256
- .command("ver")
257
- .option("-m, --module <module>")
297
+ .command("init")
298
+ .description("Create config file and install shell completion")
299
+ .action(async () => {
300
+ if (!existsSync(CONFIG_DIR)) {
301
+ mkdirSync(CONFIG_DIR, { recursive: true });
302
+ }
303
+
304
+ const existing = existsSync(CONFIG_FILE)
305
+ ? Object.fromEntries(
306
+ readFileSync(CONFIG_FILE, "utf-8")
307
+ .split("\n")
308
+ .flatMap((line) => {
309
+ const m = line.match(/^([A-Z_]+)=(.*)$/);
310
+ return m ? [[m[1], m[2]]] : [];
311
+ }),
312
+ )
313
+ : {};
314
+
315
+ const answers = await inquirer.prompt([
316
+ {
317
+ type: "input",
318
+ name: "ADDONS_PATH",
319
+ message: "Addons path:",
320
+ default: existing.ADDONS_PATH ?? "~/codebase/sorgenia/addons",
321
+ },
322
+ {
323
+ type: "input",
324
+ name: "KC_URL",
325
+ message: "Keycloak URL:",
326
+ default: existing.KC_URL ?? "",
327
+ },
328
+ {
329
+ type: "input",
330
+ name: "KC_USER",
331
+ message: "Keycloak user:",
332
+ default: existing.KC_USER ?? "",
333
+ },
334
+ {
335
+ type: "password",
336
+ name: "KC_PASSWORD",
337
+ message: "Keycloak password:",
338
+ default: existing.KC_PASSWORD ?? "",
339
+ mask: "*",
340
+ },
341
+ {
342
+ type: "input",
343
+ name: "KC_ID",
344
+ message: "Keycloak client ID:",
345
+ default: existing.KC_ID ?? "",
346
+ },
347
+ {
348
+ type: "input",
349
+ name: "KC_SECRET",
350
+ message: "Keycloak client secret:",
351
+ default: existing.KC_SECRET ?? "",
352
+ },
353
+ {
354
+ type: "input",
355
+ name: "RIP_URL",
356
+ message: "RIP URL:",
357
+ default: existing.RIP_URL ?? "",
358
+ },
359
+ ]);
360
+
361
+ writeFileSync(
362
+ CONFIG_FILE,
363
+ Object.entries(answers)
364
+ .map(([k, v]) => `${k}=${v}`)
365
+ .join("\n") + "\n",
366
+ );
367
+ console.log(`Config written to ${CONFIG_FILE}`);
368
+
369
+ writeFileSync(COMPLETION_SCRIPT, completion.generateCompletionCode());
370
+ console.log(`Completion script written to ${COMPLETION_SCRIPT}`);
371
+
372
+ const rcFile = path.join(process.env.HOME || "", ".bashrc");
373
+ const sourceLine = `source ${COMPLETION_SCRIPT}`;
374
+ const rcContent = existsSync(rcFile) ? readFileSync(rcFile, "utf-8") : "";
375
+ if (!rcContent.includes(sourceLine)) {
376
+ appendFileSync(rcFile, `\n# prbot completion\n${sourceLine}\n`);
377
+ console.log(`Registered completion in ${rcFile} — run: source ~/.bashrc`);
378
+ } else {
379
+ console.log("Completion already registered in ~/.bashrc");
380
+ }
381
+ });
382
+
383
+ program
384
+ .command("ver <module>")
258
385
  .option("-b, --bump <level>")
259
- .action((opts) => {
260
- if (!opts.module || !opts.bump) {
261
- throw new Error("No module or level specified");
386
+ .action((module, opts) => {
387
+ if (!opts.bump) {
388
+ throw new Error("No bump level specified");
262
389
  }
263
- verbot(opts.module, opts.bump);
390
+ verbot(module, opts.bump);
264
391
  });
265
392
 
266
393
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waron97/prbot",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,7 +14,9 @@
14
14
  "commander": "^14.0.2",
15
15
  "dotenv": "^17.2.3",
16
16
  "eslint-plugin-es5": "^1.5.0",
17
+ "inquirer": "^13.4.2",
17
18
  "node-fetch": "^3.3.2",
19
+ "omelette": "^0.4.17",
18
20
  "prettier": "^3.5.3"
19
21
  },
20
22
  "devDependencies": {