@waron97/prbot 3.3.0 → 3.4.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 +5 -1
- package/src/agrippa/commands/clone.js +10 -9
- package/src/agrippa/commands/cloneLrp.js +9 -9
- package/src/agrippa/commands/clonePb.js +10 -10
- package/src/agrippa/commands/diff.js +9 -8
- package/src/agrippa/commands/init.js +14 -13
- package/src/agrippa/commands/initPhase.js +10 -11
- package/src/agrippa/commands/pb.js +25 -26
- package/src/agrippa/commands/pull.js +67 -48
- package/src/agrippa/commands/pullLrp.js +2 -3
- package/src/agrippa/commands/pullPb.js +2 -3
- package/src/agrippa/commands/push.js +33 -24
- package/src/agrippa/commands/repair.js +4 -3
- package/src/agrippa/index.js +20 -11
- package/src/agrippa/lib/pbLayout.js +8 -1
- package/src/agrippa/lib/pbModel.js +1 -0
- package/src/agrippa/lib/pbPreview.js +6 -2
- package/src/agrippa/lib/pbProject.js +71 -3
- package/src/commands/autopr.js +20 -21
- package/src/commands/changelog.js +4 -3
- package/src/commands/commit.js +11 -10
- package/src/commands/export.js +2 -1
- package/src/commands/exportPb.js +19 -2
- package/src/commands/init.js +2 -1
- package/src/commands/routine.js +19 -8
- package/src/index.js +137 -112
- package/src/lib/auth.js +19 -1
- package/src/lib/logger.js +12 -1
package/src/index.js
CHANGED
|
@@ -18,54 +18,102 @@ import { main as prMain } from './commands/pr.js';
|
|
|
18
18
|
import { routine } from './commands/routine.js';
|
|
19
19
|
import { verbot } from './commands/ver.js';
|
|
20
20
|
import { CONFIG_FILE } from './config.js';
|
|
21
|
-
import { setSilent } from './lib/logger.js';
|
|
21
|
+
import { error, log, setSilent } from './lib/logger.js';
|
|
22
22
|
import { checkForUpdate, currentVersion } from './lib/updateCheck.js';
|
|
23
23
|
|
|
24
|
+
// Commands that never talk to RIP/DevOps/Trident/Keycloak. They still read
|
|
25
|
+
// non-secret config (e.g. ADDONS_PATH) from the same file, but must not end
|
|
26
|
+
// up holding credentials in their process env — a coding agent invoking one
|
|
27
|
+
// of these should not even technically be able to leak or reuse a secret it
|
|
28
|
+
// never needed.
|
|
29
|
+
const LOCAL_COMMANDS = new Set(['init', 'ver', 'commit', 'changelog']);
|
|
30
|
+
const SECRET_ENV_KEYS = [
|
|
31
|
+
'KC_USER',
|
|
32
|
+
'KC_PASSWORD',
|
|
33
|
+
'KC_ID',
|
|
34
|
+
'KC_SECRET',
|
|
35
|
+
'DEVOPS_TOKEN',
|
|
36
|
+
'TRIDENT_TOKEN',
|
|
37
|
+
];
|
|
38
|
+
const OFFLINE = process.env.PRBOT_OFFLINE === '1' || process.argv.includes('--offline');
|
|
39
|
+
|
|
24
40
|
configDotenv({ path: CONFIG_FILE, quiet: true });
|
|
25
41
|
|
|
42
|
+
const invokedCommand = process.argv[2];
|
|
43
|
+
if (LOCAL_COMMANDS.has(invokedCommand)) {
|
|
44
|
+
for (const key of SECRET_ENV_KEYS) delete process.env[key];
|
|
45
|
+
}
|
|
46
|
+
|
|
26
47
|
let _updateAvailable = null;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
48
|
+
if (!OFFLINE) {
|
|
49
|
+
checkForUpdate().then((v) => {
|
|
50
|
+
_updateAvailable = v;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
30
53
|
|
|
31
54
|
process.on('exit', () => {
|
|
32
55
|
if (_updateAvailable) {
|
|
33
|
-
|
|
34
|
-
`\nUpdate available: ${currentVersion} → ${_updateAvailable}\nRun: prbot update`
|
|
35
|
-
);
|
|
56
|
+
log(`\nUpdate available: ${currentVersion} → ${_updateAvailable}\nRun: prbot update`);
|
|
36
57
|
}
|
|
37
58
|
});
|
|
38
59
|
|
|
60
|
+
// Single point of convergence for every command's failure. Exit code 0 must
|
|
61
|
+
// mean the requested operation actually succeeded; nothing downstream of
|
|
62
|
+
// this should ever swallow an error into a zero exit.
|
|
63
|
+
function fail(err) {
|
|
64
|
+
error(`Error: ${err?.message ?? err}`);
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
process.on('unhandledRejection', (err) => {
|
|
69
|
+
fail(err instanceof Error ? err : new Error(String(err)));
|
|
70
|
+
});
|
|
71
|
+
process.on('uncaughtException', (err) => {
|
|
72
|
+
fail(err);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Adds `--quiet`/`--silent` to a command and returns a helper that reads
|
|
77
|
+
* both, warning once if the deprecated `--silent` alias is used. `--silent`
|
|
78
|
+
* used to also swallow errors (so a failed export could report success);
|
|
79
|
+
* it no longer does — both flags only suppress informational output.
|
|
80
|
+
*/
|
|
81
|
+
function withQuiet(cmd) {
|
|
82
|
+
return cmd
|
|
83
|
+
.option('-q, --quiet', 'Suppress informational output (errors still fail the command)')
|
|
84
|
+
.option('-s, --silent', 'Deprecated alias of --quiet; no longer swallows errors');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resolveQuiet(opts) {
|
|
88
|
+
if (opts.silent) {
|
|
89
|
+
error('Warning: --silent is deprecated and no longer swallows errors; use --quiet.');
|
|
90
|
+
}
|
|
91
|
+
return Boolean(opts.quiet || opts.silent);
|
|
92
|
+
}
|
|
93
|
+
|
|
39
94
|
program
|
|
40
95
|
.command('pr <module>')
|
|
41
96
|
.option('-b, --bump <level>')
|
|
42
|
-
.action((module, opts) => {
|
|
43
|
-
prMain(module)
|
|
44
|
-
|
|
45
|
-
if (opts.bump) {
|
|
46
|
-
return verbot(module, opts.bump);
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
.catch((err) => {
|
|
50
|
-
throw err;
|
|
51
|
-
});
|
|
97
|
+
.action(async (module, opts) => {
|
|
98
|
+
await prMain(module);
|
|
99
|
+
if (opts.bump) await verbot(module, opts.bump);
|
|
52
100
|
});
|
|
53
101
|
|
|
54
102
|
program
|
|
55
103
|
.command('ver <module>')
|
|
56
104
|
.option('-b, --bump <level>')
|
|
57
|
-
.action((module, opts) => {
|
|
105
|
+
.action(async (module, opts) => {
|
|
58
106
|
if (!opts.bump) {
|
|
59
107
|
throw new Error('No bump level specified');
|
|
60
108
|
}
|
|
61
|
-
verbot(module, opts.bump);
|
|
109
|
+
await verbot(module, opts.bump);
|
|
62
110
|
});
|
|
63
111
|
|
|
64
112
|
program
|
|
65
113
|
.command('init')
|
|
66
114
|
.description('Create config file')
|
|
67
|
-
.action(() => {
|
|
68
|
-
init();
|
|
115
|
+
.action(async () => {
|
|
116
|
+
await init();
|
|
69
117
|
});
|
|
70
118
|
|
|
71
119
|
const collect = (val, prev) => [...(prev ?? []), val];
|
|
@@ -75,10 +123,8 @@ program
|
|
|
75
123
|
.option('-t, --trident <code>', 'Trident issue codes (repeatable)', collect)
|
|
76
124
|
.option('-j, --jira <code>', 'JIRA issue codes (repeatable)', collect)
|
|
77
125
|
.option('-m, --message <text>', 'Changelog entry message')
|
|
78
|
-
.action((prNumber, opts) => {
|
|
79
|
-
changelog(prNumber, opts)
|
|
80
|
-
throw err;
|
|
81
|
-
});
|
|
126
|
+
.action(async (prNumber, opts) => {
|
|
127
|
+
await changelog(prNumber, opts);
|
|
82
128
|
});
|
|
83
129
|
|
|
84
130
|
program
|
|
@@ -89,107 +135,86 @@ program
|
|
|
89
135
|
.option('-b, --branch <name>', 'Branch name (default: autopr_<taskId>)')
|
|
90
136
|
.option('-n, --name <text>', 'PR title (default: task name from Odoo)')
|
|
91
137
|
.option('--amend', 'Amend existing PR on current branch with new trident/jira refs')
|
|
92
|
-
.action((opts) => {
|
|
93
|
-
autopr(opts)
|
|
94
|
-
throw err;
|
|
95
|
-
});
|
|
138
|
+
.action(async (opts) => {
|
|
139
|
+
await autopr(opts);
|
|
96
140
|
});
|
|
97
141
|
|
|
98
|
-
program.command('commit').action((opts) => {
|
|
99
|
-
commit(opts)
|
|
100
|
-
throw err;
|
|
101
|
-
});
|
|
142
|
+
program.command('commit').action(async (opts) => {
|
|
143
|
+
await commit(opts);
|
|
102
144
|
});
|
|
103
145
|
|
|
104
146
|
const exportCmd = program.command('export');
|
|
105
147
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
});
|
|
121
|
-
});
|
|
148
|
+
withQuiet(
|
|
149
|
+
exportCmd
|
|
150
|
+
.command('workflow')
|
|
151
|
+
.option('--no-commit')
|
|
152
|
+
.option('-b, --bump <level>', 'Version bump level (patch, minor, major)')
|
|
153
|
+
.option('-m, --module <id>', 'Module/workflow ID to export (skips interactive selection)')
|
|
154
|
+
.option(
|
|
155
|
+
'--auto-premigrate',
|
|
156
|
+
'Auto-generate pre-migrate script when XML ID renames are detected (no prompt)'
|
|
157
|
+
)
|
|
158
|
+
).action(async (opts) => {
|
|
159
|
+
if (resolveQuiet(opts)) setSilent(true);
|
|
160
|
+
await exportWorkflow(opts);
|
|
161
|
+
});
|
|
122
162
|
|
|
123
163
|
exportCmd.command('rip').action(() => exportRip());
|
|
124
164
|
|
|
125
|
-
exportCmd
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
.action((opts) => {
|
|
130
|
-
if (opts.silent) setSilent(true);
|
|
131
|
-
exportPb(opts).catch((err) => {
|
|
132
|
-
if (!opts.silent) throw err;
|
|
133
|
-
});
|
|
134
|
-
});
|
|
165
|
+
withQuiet(exportCmd.command('pb').option('--no-commit')).action(async (opts) => {
|
|
166
|
+
if (resolveQuiet(opts)) setSilent(true);
|
|
167
|
+
await exportPb(opts);
|
|
168
|
+
});
|
|
135
169
|
|
|
136
|
-
exportCmd
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
.action((opts) => {
|
|
141
|
-
if (opts.silent) setSilent(true);
|
|
142
|
-
exportImperex(opts).catch((err) => {
|
|
143
|
-
if (!opts.silent) throw err;
|
|
144
|
-
});
|
|
145
|
-
});
|
|
170
|
+
withQuiet(exportCmd.command('imperex').option('--no-commit')).action(async (opts) => {
|
|
171
|
+
if (resolveQuiet(opts)) setSilent(true);
|
|
172
|
+
await exportImperex(opts);
|
|
173
|
+
});
|
|
146
174
|
|
|
147
|
-
exportCmd
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
.action((opts) => {
|
|
152
|
-
if (opts.silent) setSilent(true);
|
|
153
|
-
exportLrp(opts).catch((err) => {
|
|
154
|
-
if (!opts.silent) throw err;
|
|
155
|
-
});
|
|
156
|
-
});
|
|
175
|
+
withQuiet(exportCmd.command('lrp').option('--no-commit')).action(async (opts) => {
|
|
176
|
+
if (resolveQuiet(opts)) setSilent(true);
|
|
177
|
+
await exportLrp(opts);
|
|
178
|
+
});
|
|
157
179
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
});
|
|
175
|
-
});
|
|
180
|
+
withQuiet(
|
|
181
|
+
exportCmd
|
|
182
|
+
.command('email-templates')
|
|
183
|
+
.option('--no-commit')
|
|
184
|
+
.option('-b, --bump <level>', 'Version bump level (patch, minor, major)')
|
|
185
|
+
.option('-e, --exclude <value...>', 'exclude templates matching id, name, or template_code')
|
|
186
|
+
.option('-m, --module <name>', 'module directory name (skip prompt)')
|
|
187
|
+
.option('-w, --workflow <value>', 'workflow name or id (skip prompt)')
|
|
188
|
+
.option(
|
|
189
|
+
'--auto-premigrate',
|
|
190
|
+
'Auto-generate pre-migrate script when XML ID renames are detected (no prompt)'
|
|
191
|
+
)
|
|
192
|
+
).action(async (opts) => {
|
|
193
|
+
if (resolveQuiet(opts)) setSilent(true);
|
|
194
|
+
await exportEmailTemplates(opts);
|
|
195
|
+
});
|
|
176
196
|
|
|
177
|
-
program.command('routine').action(() => {
|
|
178
|
-
routine()
|
|
179
|
-
throw err;
|
|
180
|
-
});
|
|
197
|
+
program.command('routine').action(async () => {
|
|
198
|
+
await routine();
|
|
181
199
|
});
|
|
182
200
|
|
|
183
|
-
program
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
201
|
+
program
|
|
202
|
+
.command('update [version]')
|
|
203
|
+
.description('Update the global prbot install (defaults to latest if no version given)')
|
|
204
|
+
.action(async (version) => {
|
|
205
|
+
const target = version ? `@waron97/prbot@${version}` : '@waron97/prbot';
|
|
206
|
+
log(version ? `Updating prbot to ${version}...` : 'Updating prbot to latest...');
|
|
207
|
+
await new Promise((resolve, reject) => {
|
|
208
|
+
execFile('npm', ['i', '-g', target], (err, stdout, stderr) => {
|
|
209
|
+
if (err) {
|
|
210
|
+
reject(new Error(stderr || err.message));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
log(stdout);
|
|
214
|
+
log('Done.');
|
|
215
|
+
resolve();
|
|
216
|
+
});
|
|
217
|
+
});
|
|
192
218
|
});
|
|
193
|
-
});
|
|
194
219
|
|
|
195
|
-
program.
|
|
220
|
+
program.parseAsync().catch(fail);
|
package/src/lib/auth.js
CHANGED
|
@@ -18,7 +18,25 @@ async function getToken() {
|
|
|
18
18
|
body: payload.toString(),
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
// Never log the response body: on a password-grant endpoint it can
|
|
23
|
+
// echo back the submitted credentials.
|
|
24
|
+
throw new Error(
|
|
25
|
+
`AUTH_FAILED: Keycloak token request failed with status ${response.status}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let json;
|
|
30
|
+
try {
|
|
31
|
+
json = await response.json();
|
|
32
|
+
} catch {
|
|
33
|
+
throw new Error('AUTH_FAILED: Keycloak response was not valid JSON');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!json || typeof json.access_token !== 'string' || !json.access_token) {
|
|
37
|
+
throw new Error('AUTH_FAILED: Keycloak response did not contain an access_token');
|
|
38
|
+
}
|
|
39
|
+
|
|
22
40
|
return json.access_token;
|
|
23
41
|
}
|
|
24
42
|
|
package/src/lib/logger.js
CHANGED
|
@@ -8,8 +8,19 @@ function isSilent() {
|
|
|
8
8
|
return silent;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
// Informational output — suppressed by --quiet/--silent.
|
|
11
12
|
function log(...args) {
|
|
12
13
|
if (!silent) console.log(...args);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
// Warnings and errors are never suppressed: --quiet reduces noise, it must
|
|
17
|
+
// never hide something the user needs to see to trust the exit code.
|
|
18
|
+
function warn(...args) {
|
|
19
|
+
console.warn(...args);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function error(...args) {
|
|
23
|
+
console.error(...args);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { setSilent, isSilent, log, warn, error };
|