@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.
- package/.claude/settings.local.json +5 -0
- package/README.md +75 -0
- package/index.js +142 -15
- package/package.json +3 -1
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
|
-
|
|
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
|
-
|
|
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(
|
|
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("
|
|
257
|
-
.
|
|
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.
|
|
261
|
-
throw new Error("No
|
|
386
|
+
.action((module, opts) => {
|
|
387
|
+
if (!opts.bump) {
|
|
388
|
+
throw new Error("No bump level specified");
|
|
262
389
|
}
|
|
263
|
-
verbot(
|
|
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.
|
|
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": {
|