@waron97/prbot 3.3.0 → 3.5.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/README.md +53 -37
- 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 +64 -25
- package/src/agrippa/commands/init.js +14 -13
- package/src/agrippa/commands/initPhase.js +10 -11
- package/src/agrippa/commands/pb.js +29 -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 +25 -13
- package/src/agrippa/lib/pbEdit.js +49 -2
- 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 +91 -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
|
@@ -4,6 +4,7 @@ import search from '@inquirer/search';
|
|
|
4
4
|
import inquirer from 'inquirer';
|
|
5
5
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
6
6
|
import { fuzzyMatch } from '../lib/fuzzy.js';
|
|
7
|
+
import { log } from '../lib/logger.js';
|
|
7
8
|
|
|
8
9
|
function buildRefString(tridents, jiras, prNumber) {
|
|
9
10
|
const refs = [];
|
|
@@ -116,7 +117,7 @@ async function changelog(prNumber, options) {
|
|
|
116
117
|
const duplicate = findDuplicateLine(content, tridents, jiras);
|
|
117
118
|
let appendMode = false;
|
|
118
119
|
if (duplicate) {
|
|
119
|
-
|
|
120
|
+
log(`\nExisting entry (line ${duplicate.lineNumber + 1}):\n ${duplicate.line}`);
|
|
120
121
|
const { confirm } = await inquirer.prompt([
|
|
121
122
|
{
|
|
122
123
|
type: 'confirm',
|
|
@@ -132,7 +133,7 @@ async function changelog(prNumber, options) {
|
|
|
132
133
|
const lines = content.split('\n');
|
|
133
134
|
lines[duplicate.lineNumber] = appendPrToLine(lines[duplicate.lineNumber], prNumber);
|
|
134
135
|
await fs.writeFile(changelogPath, lines.join('\n'));
|
|
135
|
-
|
|
136
|
+
log('Updated existing line');
|
|
136
137
|
return;
|
|
137
138
|
}
|
|
138
139
|
|
|
@@ -176,7 +177,7 @@ async function changelog(prNumber, options) {
|
|
|
176
177
|
lines.splice(endLine + 1, 0, newEntry);
|
|
177
178
|
|
|
178
179
|
await fs.writeFile(changelogPath, lines.join('\n'));
|
|
179
|
-
|
|
180
|
+
log('Changelog entry added');
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
function findLineByPrNumber(content, prNumber) {
|
package/src/commands/commit.js
CHANGED
|
@@ -3,6 +3,7 @@ import inquirer from 'inquirer';
|
|
|
3
3
|
import searchList from 'inquirer-search-list';
|
|
4
4
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
5
5
|
import { execGit } from '../lib/git.js';
|
|
6
|
+
import { log } from '../lib/logger.js';
|
|
6
7
|
|
|
7
8
|
inquirer.registerPrompt('search-list', searchList);
|
|
8
9
|
|
|
@@ -50,7 +51,7 @@ function validateSameModule(files) {
|
|
|
50
51
|
const allSameModule = modules.every((module) => `[${module}]` === currentModule);
|
|
51
52
|
|
|
52
53
|
if (!allSameModule) {
|
|
53
|
-
|
|
54
|
+
log(chalk.red('Selected files are not of the same module'));
|
|
54
55
|
return null;
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -59,8 +60,8 @@ function validateSameModule(files) {
|
|
|
59
60
|
|
|
60
61
|
async function getFilesToCommit(stagedChanges, unstagedChanges) {
|
|
61
62
|
if (stagedChanges.trim()) {
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
log(chalk.green('Staged changes:'));
|
|
64
|
+
log(stagedChanges);
|
|
64
65
|
|
|
65
66
|
return {
|
|
66
67
|
filesToCheck: stagedChanges.trim().split('\n'),
|
|
@@ -71,9 +72,9 @@ async function getFilesToCommit(stagedChanges, unstagedChanges) {
|
|
|
71
72
|
const unstagedFiles = unstagedChanges.trim().split('\n');
|
|
72
73
|
|
|
73
74
|
while (true) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
log(chalk.yellow('No staged changes found.'));
|
|
76
|
+
log(chalk.yellow('Unstaged changes:'));
|
|
77
|
+
log(unstagedChanges);
|
|
77
78
|
|
|
78
79
|
const answers = await inquirer.prompt([
|
|
79
80
|
{
|
|
@@ -104,7 +105,7 @@ async function commit() {
|
|
|
104
105
|
const stagedChanges = await execGit(['diff', '--cached', '--name-only'], ADDONS_PATH);
|
|
105
106
|
|
|
106
107
|
if (!unstagedChanges.trim() && !stagedChanges.trim()) {
|
|
107
|
-
|
|
108
|
+
log(chalk.red('No changes found to commit.'));
|
|
108
109
|
return;
|
|
109
110
|
}
|
|
110
111
|
|
|
@@ -115,7 +116,7 @@ async function commit() {
|
|
|
115
116
|
} = await getFilesToCommit(stagedChanges, unstagedChanges);
|
|
116
117
|
|
|
117
118
|
if (filesToCheck.length === 0) {
|
|
118
|
-
|
|
119
|
+
log(chalk.red('No files selected.'));
|
|
119
120
|
return;
|
|
120
121
|
}
|
|
121
122
|
|
|
@@ -167,13 +168,13 @@ async function commit() {
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
await execGit(['commit', '-m', commitMessage], ADDONS_PATH);
|
|
170
|
-
|
|
171
|
+
log(chalk.green('Commit created successfully!'));
|
|
171
172
|
|
|
172
173
|
const remainingUnstaged = await execGit(['diff', '--name-only'], ADDONS_PATH);
|
|
173
174
|
const remainingStaged = await execGit(['diff', '--cached', '--name-only'], ADDONS_PATH);
|
|
174
175
|
|
|
175
176
|
if (!remainingUnstaged.trim() && !remainingStaged.trim()) {
|
|
176
|
-
|
|
177
|
+
log(chalk.green('No more changes to commit.'));
|
|
177
178
|
break;
|
|
178
179
|
}
|
|
179
180
|
|
package/src/commands/export.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { log } from '../lib/logger.js';
|
|
1
2
|
import { exportEmailTemplates } from './exportEmailTemplates.js';
|
|
2
3
|
import { exportImperex } from './exportImperex.js';
|
|
3
4
|
import { exportLrp } from './exportLrp.js';
|
|
@@ -5,7 +6,7 @@ import { exportPb } from './exportPb.js';
|
|
|
5
6
|
import { exportWorkflow } from './exportWorkflow.js';
|
|
6
7
|
|
|
7
8
|
function exportRip() {
|
|
8
|
-
|
|
9
|
+
log('Not implemented yet.');
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export { exportPb, exportRip, exportImperex, exportEmailTemplates, exportWorkflow, exportLrp };
|
package/src/commands/exportPb.js
CHANGED
|
@@ -36,13 +36,30 @@ async function initiateExport(guid, token) {
|
|
|
36
36
|
if (!response.ok) throw new Error(await response.text());
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// No job/request id is returned by initiateExport, so the poll still
|
|
40
|
+
// correlates by GUID + a corrected date window over the last few results —
|
|
41
|
+
// a heuristic, not a stable identifier (see REL-ADP-PB-001; a real fix
|
|
42
|
+
// depends on the ImportExport API exposing a request id, EXT-002). What we
|
|
43
|
+
// can and do own locally is not hanging forever: an overall deadline.
|
|
44
|
+
const PB_EXPORT_POLL_INTERVAL_MS = 3_000;
|
|
45
|
+
const PB_EXPORT_POLL_TIMEOUT_MS = 120_000;
|
|
46
|
+
|
|
39
47
|
async function pollExportResult(guid, requestTime, token) {
|
|
40
48
|
const url = `${process.env.IMPORTEXPORT_URL}/export/info/processKey=ExportElement&subProcess=true&status=FAILED,COMPLETED&referenceId=process_builder`;
|
|
41
49
|
// Server createDate is offset -1hr from system time; subtract 1hr+5s buffer
|
|
42
50
|
const cutoff = requestTime - 3_605_000;
|
|
51
|
+
const deadline = requestTime + PB_EXPORT_POLL_TIMEOUT_MS;
|
|
43
52
|
|
|
44
53
|
while (true) {
|
|
45
|
-
|
|
54
|
+
if (Date.now() > deadline) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`REMOTE_TIMEOUT: Process Builder export for guid ${guid} did not complete within ${
|
|
57
|
+
PB_EXPORT_POLL_TIMEOUT_MS / 1000
|
|
58
|
+
}s`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
await new Promise((r) => setTimeout(r, PB_EXPORT_POLL_INTERVAL_MS));
|
|
46
63
|
|
|
47
64
|
const response = await fetch(url, {
|
|
48
65
|
method: 'POST',
|
|
@@ -153,4 +170,4 @@ async function exportPb(opts) {
|
|
|
153
170
|
}
|
|
154
171
|
}
|
|
155
172
|
|
|
156
|
-
export { exportPb };
|
|
173
|
+
export { exportPb, pollExportResult };
|
package/src/commands/init.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import { CONFIG_DIR, CONFIG_FILE } from '../config.js';
|
|
4
|
+
import { log } from '../lib/logger.js';
|
|
4
5
|
|
|
5
6
|
async function init() {
|
|
6
7
|
if (!existsSync(CONFIG_DIR)) {
|
|
@@ -142,7 +143,7 @@ async function init() {
|
|
|
142
143
|
.map(([k, v]) => `${k}=${v}`)
|
|
143
144
|
.join('\n') + '\n'
|
|
144
145
|
);
|
|
145
|
-
|
|
146
|
+
log(`Config written to ${CONFIG_FILE}`);
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
export { init };
|
package/src/commands/routine.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import { select } from '@inquirer/prompts';
|
|
4
4
|
import { parse } from 'yaml';
|
|
5
5
|
import { CONFIG_DIR } from '../config.js';
|
|
6
|
-
import { setSilent } from '../lib/logger.js';
|
|
6
|
+
import { log, setSilent } from '../lib/logger.js';
|
|
7
7
|
import { exportEmailTemplates } from './exportEmailTemplates.js';
|
|
8
8
|
import { exportImperex } from './exportImperex.js';
|
|
9
9
|
import { exportLrp } from './exportLrp.js';
|
|
@@ -52,39 +52,50 @@ function isNothingToCommit(err) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
async function runRoutine(routine) {
|
|
55
|
-
|
|
55
|
+
log(`Running Routine: ${routine.name}`);
|
|
56
|
+
|
|
57
|
+
const failures = [];
|
|
56
58
|
|
|
57
59
|
for (const step of routine.steps) {
|
|
58
60
|
const label = `[${step.name}]`;
|
|
59
61
|
const fn = COMMAND_MAP[step.command];
|
|
60
62
|
if (!fn) {
|
|
61
|
-
|
|
63
|
+
log(`${label} Unknown command: ${step.command}`);
|
|
64
|
+
failures.push(`${step.name}: unknown command "${step.command}"`);
|
|
62
65
|
continue;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
log(`${label} Job started`);
|
|
66
69
|
setSilent(true);
|
|
67
70
|
|
|
68
71
|
try {
|
|
69
72
|
await fn(stepOpts(step));
|
|
70
|
-
|
|
73
|
+
log(`${label} Done (committed)`);
|
|
71
74
|
} catch (err) {
|
|
72
75
|
if (isNothingToCommit(err)) {
|
|
73
|
-
|
|
76
|
+
log(`${label} Done (nothing to commit)`);
|
|
74
77
|
} else {
|
|
75
|
-
|
|
78
|
+
log(`${label} Failed: ${err.message}`);
|
|
79
|
+
failures.push(`${step.name}: ${err.message}`);
|
|
76
80
|
}
|
|
77
81
|
} finally {
|
|
78
82
|
setSilent(false);
|
|
79
83
|
}
|
|
80
84
|
}
|
|
85
|
+
|
|
86
|
+
if (failures.length) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Routine "${routine.name}" completed with ${failures.length} failed step(s):\n` +
|
|
89
|
+
failures.map((f) => ` - ${f}`).join('\n')
|
|
90
|
+
);
|
|
91
|
+
}
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
async function routine() {
|
|
84
95
|
const routines = loadRoutines();
|
|
85
96
|
|
|
86
97
|
if (!routines.length) {
|
|
87
|
-
|
|
98
|
+
log(
|
|
88
99
|
'No routines defined. Create ~/.config/prbot/routines.yaml or add routines: to agrippa.yaml.'
|
|
89
100
|
);
|
|
90
101
|
return;
|
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 };
|