@waron97/prbot 3.1.0 → 3.1.2
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/agrippa_typings/__builtins__.pyi +2 -0
- package/agrippa_typings/odoo_environment.pyi +3 -7
- package/agrippa_typings/odoo_records.pyi +22 -34
- package/package.json +1 -1
- package/src/commands/autopr.js +77 -0
- package/src/commands/changelog.js +50 -0
- package/src/commands/exportEmailTemplates.js +94 -12
- package/src/commands/exportImperex.js +5 -4
- package/src/commands/exportLrp.js +26 -57
- package/src/commands/exportPb.js +7 -6
- package/src/commands/exportWorkflow.js +101 -10
- package/src/commands/pr.js +7 -16
- package/src/commands/routine.js +99 -0
- package/src/commands/ver.js +6 -15
- package/src/index.js +28 -5
- package/src/lib/git.js +6 -2
- package/src/lib/logger.js +15 -0
- package/src/lib/premigrate.js +215 -0
|
@@ -5,10 +5,7 @@ import search from '@inquirer/search';
|
|
|
5
5
|
import { getToken } from '../lib/auth.js';
|
|
6
6
|
import { execGit } from '../lib/git.js';
|
|
7
7
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
8
|
-
import {
|
|
9
|
-
import { CONFIG_DIR } from '../config.js';
|
|
10
|
-
|
|
11
|
-
const CACHE_FILE = path.join(CONFIG_DIR, 'lrp_processes_cache.json');
|
|
8
|
+
import { log } from '../lib/logger.js';
|
|
12
9
|
|
|
13
10
|
function getSymphonyBase() {
|
|
14
11
|
const url = process.env.IMPORTEXPORT_URL;
|
|
@@ -17,13 +14,16 @@ function getSymphonyBase() {
|
|
|
17
14
|
return `${parsed.protocol}//${parsed.host}`;
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
async function
|
|
17
|
+
async function fetchProcesses(token, nameFilter, signal) {
|
|
21
18
|
const base = getSymphonyBase();
|
|
22
|
-
const
|
|
19
|
+
const size = nameFilter ? 20 : 12;
|
|
20
|
+
const params = encodeURIComponent(
|
|
21
|
+
JSON.stringify({ page: 1, size, sorters: [], filters: [] })
|
|
22
|
+
);
|
|
23
23
|
const otherfilters = encodeURIComponent(
|
|
24
24
|
JSON.stringify([
|
|
25
25
|
{ field: 'id', type: '=', value: null },
|
|
26
|
-
{ field: 'name', type: 'like', value: null },
|
|
26
|
+
{ field: 'name', type: 'like', value: nameFilter ?? null },
|
|
27
27
|
{ field: 'tenantId', type: '=', value: null },
|
|
28
28
|
{ field: 'latestVersion', type: '=', value: true },
|
|
29
29
|
])
|
|
@@ -53,24 +53,6 @@ async function fetchProcessList(token, signal) {
|
|
|
53
53
|
return items;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
async function loadCache() {
|
|
57
|
-
try {
|
|
58
|
-
const data = await fs.readFile(CACHE_FILE, 'utf-8');
|
|
59
|
-
return JSON.parse(data);
|
|
60
|
-
} catch {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function saveCache(items) {
|
|
66
|
-
try {
|
|
67
|
-
await fs.mkdir(path.dirname(CACHE_FILE), { recursive: true });
|
|
68
|
-
await fs.writeFile(CACHE_FILE, JSON.stringify(items));
|
|
69
|
-
} catch {
|
|
70
|
-
// non-fatal
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
56
|
async function fetchProcessDetail(id, token) {
|
|
75
57
|
const base = getSymphonyBase();
|
|
76
58
|
const url = `${base}/symphony/restInfo/ajax/tabulator/id/${id}?connector=SymphBpmnFileTabCon&modelroot=/management/development/edit`;
|
|
@@ -118,41 +100,30 @@ async function findExistingFile(baseDir, filename) {
|
|
|
118
100
|
async function exportLrp(opts) {
|
|
119
101
|
const token = await getToken();
|
|
120
102
|
|
|
121
|
-
|
|
122
|
-
|
|
103
|
+
log('Fetching processes...');
|
|
104
|
+
const initialItems = await fetchProcesses(token, null);
|
|
105
|
+
const initialChoices = initialItems.map((p) => ({ name: p.name, value: p.id }));
|
|
123
106
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const bgController = new AbortController();
|
|
127
|
-
|
|
128
|
-
if (cached) {
|
|
129
|
-
choices = cached.map((p) => ({ name: p.name, value: p.id }));
|
|
130
|
-
console.log(`Loaded ${choices.length} processes from cache. Fetching fresh list in background (~1 min) — results update as you type.`);
|
|
131
|
-
fetchProcessList(token, bgController.signal)
|
|
132
|
-
.then(async (fresh) => {
|
|
133
|
-
await saveCache(fresh);
|
|
134
|
-
const updated = fresh.map((p) => ({ name: p.name, value: p.id }));
|
|
135
|
-
choices.length = 0;
|
|
136
|
-
choices.push(...updated);
|
|
137
|
-
})
|
|
138
|
-
.catch(() => {});
|
|
139
|
-
} else {
|
|
140
|
-
console.log('No cache found. Fetching process list (~1 min)...');
|
|
141
|
-
const items = await fetchProcessList(token);
|
|
142
|
-
await saveCache(items);
|
|
143
|
-
choices = items.map((p) => ({ name: p.name, value: p.id }));
|
|
144
|
-
console.log(`Loaded ${choices.length} processes.`);
|
|
145
|
-
}
|
|
107
|
+
let searchController = null;
|
|
146
108
|
|
|
147
109
|
const selectedId = await search({
|
|
148
110
|
message: 'Select LRP process to export:',
|
|
149
111
|
source: async (input) => {
|
|
150
|
-
if (!input) return
|
|
151
|
-
|
|
112
|
+
if (!input) return initialChoices;
|
|
113
|
+
|
|
114
|
+
if (searchController) searchController.abort();
|
|
115
|
+
searchController = new AbortController();
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const items = await fetchProcesses(token, input, searchController.signal);
|
|
119
|
+
return items.map((p) => ({ name: p.name, value: p.id }));
|
|
120
|
+
} catch {
|
|
121
|
+
return initialChoices;
|
|
122
|
+
}
|
|
152
123
|
},
|
|
153
124
|
});
|
|
154
125
|
|
|
155
|
-
|
|
126
|
+
log('Fetching process detail...');
|
|
156
127
|
const jsText = await fetchProcessDetail(selectedId, token);
|
|
157
128
|
const { xml, filename } = extractBpmnData(jsText);
|
|
158
129
|
const bpmnFilename = `${filename.replace(/^B2WA_/, '')}.bpmn20.xml`;
|
|
@@ -165,21 +136,19 @@ async function exportLrp(opts) {
|
|
|
165
136
|
if (existing) {
|
|
166
137
|
savePath = existing;
|
|
167
138
|
await fs.writeFile(savePath, xml, 'utf-8');
|
|
168
|
-
|
|
139
|
+
log(`Updated: ${savePath}`);
|
|
169
140
|
} else {
|
|
170
141
|
savePath = path.join(processesDir, 'all', bpmnFilename);
|
|
171
142
|
await fs.mkdir(path.dirname(savePath), { recursive: true });
|
|
172
143
|
await fs.writeFile(savePath, xml, 'utf-8');
|
|
173
|
-
|
|
144
|
+
log(`Created: ${savePath}`);
|
|
174
145
|
}
|
|
175
146
|
|
|
176
147
|
if (opts.commit !== false) {
|
|
177
148
|
await execGit(['add', savePath], ADDONS_PATH);
|
|
178
149
|
await execGit(['commit', '-m', '[IMP][.cloudbuild] Update long running process'], ADDONS_PATH);
|
|
179
|
-
|
|
150
|
+
log('Committed.');
|
|
180
151
|
}
|
|
181
|
-
|
|
182
|
-
bgController.abort();
|
|
183
152
|
}
|
|
184
153
|
|
|
185
154
|
export { exportLrp };
|
package/src/commands/exportPb.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getToken } from '../lib/auth.js';
|
|
|
6
6
|
import { execGit } from '../lib/git.js';
|
|
7
7
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
8
8
|
import { fuzzyMatch } from '../lib/fuzzy.js';
|
|
9
|
+
import { log } from '../lib/logger.js';
|
|
9
10
|
|
|
10
11
|
async function getProcessList(token) {
|
|
11
12
|
const url = `${process.env.IMPORTEXPORT_URL}/object/process_builder?addLanguageParam=true`;
|
|
@@ -120,14 +121,14 @@ async function exportPb(opts) {
|
|
|
120
121
|
const { guid, document_id } = selected;
|
|
121
122
|
const filename = `${document_id}.zip`;
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
log(`Initiating export for ${document_id}...`);
|
|
124
125
|
const requestTime = Date.now();
|
|
125
126
|
await initiateExport(guid, token);
|
|
126
127
|
|
|
127
|
-
|
|
128
|
+
log('Waiting for export to complete...');
|
|
128
129
|
const requestId = await pollExportResult(guid, requestTime, token);
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
log(`Downloading ${filename}...`);
|
|
131
132
|
const zipBuffer = await downloadZip(requestId, token);
|
|
132
133
|
|
|
133
134
|
const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
@@ -138,18 +139,18 @@ async function exportPb(opts) {
|
|
|
138
139
|
if (existing) {
|
|
139
140
|
savePath = existing;
|
|
140
141
|
await fs.writeFile(savePath, zipBuffer);
|
|
141
|
-
|
|
142
|
+
log(`Updated existing file at ${savePath}`);
|
|
142
143
|
} else {
|
|
143
144
|
savePath = path.join(processesDir, 'all', filename);
|
|
144
145
|
await fs.mkdir(path.dirname(savePath), { recursive: true });
|
|
145
146
|
await fs.writeFile(savePath, zipBuffer);
|
|
146
|
-
|
|
147
|
+
log(`Created new file at ${savePath}`);
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
if (opts.commit !== false) {
|
|
150
151
|
await execGit(['add', savePath], ADDONS_PATH);
|
|
151
152
|
await execGit(['commit', '-m', '[IMP][.cloudbuild] Update wizard'], ADDONS_PATH);
|
|
152
|
-
|
|
153
|
+
log('Committed.');
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import search from '@inquirer/search';
|
|
4
|
-
import { select } from '@inquirer/prompts';
|
|
4
|
+
import { select, confirm } from '@inquirer/prompts';
|
|
5
5
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
6
6
|
import { fuzzyMatch } from '../lib/fuzzy.js';
|
|
7
|
+
import { execGit } from '../lib/git.js';
|
|
8
|
+
import { log, isSilent } from '../lib/logger.js';
|
|
7
9
|
import { runPr } from './pr.js';
|
|
8
10
|
import { verbot } from './ver.js';
|
|
11
|
+
import {
|
|
12
|
+
readWorkflowMappings,
|
|
13
|
+
detectRenames,
|
|
14
|
+
computeMigrationVersion,
|
|
15
|
+
generatePreMigrateScript,
|
|
16
|
+
} from '../lib/premigrate.js';
|
|
9
17
|
|
|
10
18
|
async function getModuleChoices() {
|
|
11
19
|
const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
@@ -16,17 +24,46 @@ async function getModuleChoices() {
|
|
|
16
24
|
.map((e) => ({ name: e.name, value: e.name }));
|
|
17
25
|
}
|
|
18
26
|
|
|
27
|
+
async function resolveManifestPath(module, ADDONS_PATH) {
|
|
28
|
+
for (const candidate of [
|
|
29
|
+
path.join(ADDONS_PATH, module, '__manifest__.py'),
|
|
30
|
+
path.join(ADDONS_PATH, 'config', module, '__manifest__.py'),
|
|
31
|
+
]) {
|
|
32
|
+
try {
|
|
33
|
+
await fs.access(candidate);
|
|
34
|
+
return candidate;
|
|
35
|
+
} catch {
|
|
36
|
+
// try next
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
19
42
|
async function exportWorkflow(opts) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
let module;
|
|
44
|
+
if (opts.module) {
|
|
45
|
+
module = opts.module;
|
|
46
|
+
} else {
|
|
47
|
+
const moduleChoices = await getModuleChoices();
|
|
48
|
+
module = await search({
|
|
49
|
+
message: 'Select module:',
|
|
50
|
+
source: async (input) => {
|
|
51
|
+
if (!input) return moduleChoices;
|
|
52
|
+
return moduleChoices.filter((c) => fuzzyMatch(c.name, input));
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
28
56
|
|
|
29
|
-
|
|
57
|
+
const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
58
|
+
const dataDir = path.join(ADDONS_PATH, 'config', module, 'data');
|
|
59
|
+
|
|
60
|
+
const oldMappings = await readWorkflowMappings(dataDir);
|
|
61
|
+
|
|
62
|
+
await runPr(module, { ...opts, commit: false });
|
|
63
|
+
|
|
64
|
+
const newMappings = await readWorkflowMappings(dataDir);
|
|
65
|
+
const renames = detectRenames(oldMappings, newMappings);
|
|
66
|
+
const hasRenames = renames.stateCodes.length > 0 || renames.phaseCodes.length > 0;
|
|
30
67
|
|
|
31
68
|
let bumpLevel = opts.bump;
|
|
32
69
|
if (!bumpLevel) {
|
|
@@ -41,6 +78,60 @@ async function exportWorkflow(opts) {
|
|
|
41
78
|
});
|
|
42
79
|
}
|
|
43
80
|
|
|
81
|
+
let preMigratePath = null;
|
|
82
|
+
|
|
83
|
+
if (hasRenames) {
|
|
84
|
+
if (renames.stateCodes.length > 0) {
|
|
85
|
+
log(`Renamed state_codes (${renames.stateCodes.length}): ${renames.stateCodes.join(', ')}`);
|
|
86
|
+
}
|
|
87
|
+
if (renames.phaseCodes.length > 0) {
|
|
88
|
+
log(`Renamed phase_codes (${renames.phaseCodes.length}): ${renames.phaseCodes.join(', ')}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let shouldGenerate = opts.autoPremigrate;
|
|
92
|
+
if (!shouldGenerate && !isSilent()) {
|
|
93
|
+
shouldGenerate = await confirm({
|
|
94
|
+
message: `Detected ${renames.stateCodes.length} renamed state_code(s) and ${renames.phaseCodes.length} renamed phase_code(s). Generate pre-migrate script?`,
|
|
95
|
+
default: true,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (shouldGenerate) {
|
|
100
|
+
const manifestPath = await resolveManifestPath(module, ADDONS_PATH);
|
|
101
|
+
if (!manifestPath) {
|
|
102
|
+
log(`Warning: __manifest__.py not found for ${module}, skipping pre-migrate generation`);
|
|
103
|
+
} else {
|
|
104
|
+
const version = await computeMigrationVersion(manifestPath, bumpLevel);
|
|
105
|
+
const migrationDir = path.join(ADDONS_PATH, 'config', module, 'migrations', version);
|
|
106
|
+
preMigratePath = path.join(migrationDir, 'pre-migrate.py');
|
|
107
|
+
await fs.mkdir(migrationDir, { recursive: true });
|
|
108
|
+
await fs.writeFile(preMigratePath, generatePreMigrateScript(renames.stateCodes, renames.phaseCodes));
|
|
109
|
+
log(`Wrote pre-migrate: ${preMigratePath}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (opts.commit === false) {
|
|
115
|
+
if (bumpLevel && bumpLevel !== 'none') {
|
|
116
|
+
await verbot(module, bumpLevel, { ...opts, commit: false });
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const filesToAdd = [
|
|
122
|
+
path.join(dataDir, 'workflow_missing_relations.xml'),
|
|
123
|
+
path.join(dataDir, 'workflow_configuration.xml'),
|
|
124
|
+
];
|
|
125
|
+
if (preMigratePath) filesToAdd.push(preMigratePath);
|
|
126
|
+
|
|
127
|
+
for (const filePath of filesToAdd) {
|
|
128
|
+
await execGit(['add', filePath], ADDONS_PATH);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const commitMessage = `[IMP][${module}] Update workflow`;
|
|
132
|
+
await execGit(['commit', '-m', commitMessage], ADDONS_PATH);
|
|
133
|
+
log(`Committed with message: ${commitMessage}`);
|
|
134
|
+
|
|
44
135
|
if (bumpLevel && bumpLevel !== 'none') {
|
|
45
136
|
await verbot(module, bumpLevel, opts);
|
|
46
137
|
}
|
package/src/commands/pr.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { execFile } from 'child_process';
|
|
2
1
|
import fs from 'fs/promises';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import fetch from 'node-fetch';
|
|
5
4
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
6
5
|
import { getToken } from '../lib/auth.js';
|
|
6
|
+
import { execGit } from '../lib/git.js';
|
|
7
|
+
import { log } from '../lib/logger.js';
|
|
7
8
|
|
|
8
9
|
async function getFiles(module_name, token) {
|
|
9
10
|
const url = `${process.env.RIP_URL}/ir.model/xml_prbot`;
|
|
@@ -66,7 +67,7 @@ async function runPr(module_name, opts = {}) {
|
|
|
66
67
|
|
|
67
68
|
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
68
69
|
await fs.writeFile(destPath, content);
|
|
69
|
-
|
|
70
|
+
log(`Processed: ${file.name} -> ${destPath}`);
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
if (opts.commit === false) return;
|
|
@@ -78,23 +79,13 @@ async function runPr(module_name, opts = {}) {
|
|
|
78
79
|
];
|
|
79
80
|
|
|
80
81
|
for (const filePath of filesToAdd) {
|
|
81
|
-
await
|
|
82
|
-
execFile('git', ['add', filePath], { cwd: ADDONS_PATH }, (error) => {
|
|
83
|
-
if (error) reject(error);
|
|
84
|
-
else resolve();
|
|
85
|
-
});
|
|
86
|
-
});
|
|
82
|
+
await execGit(['add', filePath], ADDONS_PATH);
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
const commitMessage = `[IMP][${module_name}] Update workflow`;
|
|
90
|
-
await
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
else resolve();
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
console.log(`Committed with message: ${commitMessage}`);
|
|
86
|
+
await execGit(['commit', '-m', commitMessage], ADDONS_PATH);
|
|
87
|
+
|
|
88
|
+
log(`Committed with message: ${commitMessage}`);
|
|
98
89
|
}
|
|
99
90
|
|
|
100
91
|
async function main(module_name) {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { parse } from 'yaml';
|
|
4
|
+
import { select } from '@inquirer/prompts';
|
|
5
|
+
import { CONFIG_DIR } from '../config.js';
|
|
6
|
+
import { setSilent } from '../lib/logger.js';
|
|
7
|
+
import { exportWorkflow } from './exportWorkflow.js';
|
|
8
|
+
import { exportPb } from './exportPb.js';
|
|
9
|
+
import { exportImperex } from './exportImperex.js';
|
|
10
|
+
import { exportEmailTemplates } from './exportEmailTemplates.js';
|
|
11
|
+
import { exportLrp } from './exportLrp.js';
|
|
12
|
+
|
|
13
|
+
const ROUTINES_FILE = path.join(CONFIG_DIR, 'routines.yaml');
|
|
14
|
+
const WORKSPACE_FILE = 'agrippa.yaml';
|
|
15
|
+
|
|
16
|
+
const COMMAND_MAP = {
|
|
17
|
+
'export workflow': exportWorkflow,
|
|
18
|
+
'export pb': exportPb,
|
|
19
|
+
'export imperex': exportImperex,
|
|
20
|
+
'export lrp': exportLrp,
|
|
21
|
+
'export email-templates': exportEmailTemplates,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function loadRoutines() {
|
|
25
|
+
const routines = [];
|
|
26
|
+
|
|
27
|
+
if (existsSync(ROUTINES_FILE)) {
|
|
28
|
+
const config = parse(readFileSync(ROUTINES_FILE, 'utf-8'));
|
|
29
|
+
if (config?.routines) routines.push(...config.routines);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (existsSync(WORKSPACE_FILE)) {
|
|
33
|
+
const config = parse(readFileSync(WORKSPACE_FILE, 'utf-8'));
|
|
34
|
+
if (config?.routines) routines.push(...config.routines);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return routines;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stepOpts(step) {
|
|
41
|
+
const opts = { bump: step.bump ?? 'none' };
|
|
42
|
+
if (step.module) opts.module = step.module;
|
|
43
|
+
if (step.workflow) opts.workflow = step.workflow;
|
|
44
|
+
if (step.exclude) opts.exclude = step.exclude;
|
|
45
|
+
if (step.no_commit) opts.commit = false;
|
|
46
|
+
if (step.auto_premigrate) opts.autoPremigrate = true;
|
|
47
|
+
return opts;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isNothingToCommit(err) {
|
|
51
|
+
return err?.gitOutput?.includes('nothing to commit');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function runRoutine(routine) {
|
|
55
|
+
console.log(`Running Routine: ${routine.name}`);
|
|
56
|
+
|
|
57
|
+
for (const step of routine.steps) {
|
|
58
|
+
const label = `[${step.name}]`;
|
|
59
|
+
const fn = COMMAND_MAP[step.command];
|
|
60
|
+
if (!fn) {
|
|
61
|
+
console.log(`${label} Unknown command: ${step.command}`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(`${label} Job started`);
|
|
66
|
+
setSilent(true);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
await fn(stepOpts(step));
|
|
70
|
+
console.log(`${label} Done (committed)`);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
if (isNothingToCommit(err)) {
|
|
73
|
+
console.log(`${label} Done (nothing to commit)`);
|
|
74
|
+
} else {
|
|
75
|
+
console.log(`${label} Failed: ${err.message}`);
|
|
76
|
+
}
|
|
77
|
+
} finally {
|
|
78
|
+
setSilent(false);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function routine() {
|
|
84
|
+
const routines = loadRoutines();
|
|
85
|
+
|
|
86
|
+
if (!routines.length) {
|
|
87
|
+
console.log('No routines defined. Create ~/.config/prbot/routines.yaml or add routines: to agrippa.yaml.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const selected = await select({
|
|
92
|
+
message: 'Select routine:',
|
|
93
|
+
choices: routines.map((r) => ({ name: r.name, value: r })),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await runRoutine(selected);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { routine };
|
package/src/commands/ver.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { execFile } from 'child_process';
|
|
2
1
|
import fs from 'fs/promises';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
4
|
+
import { execGit } from '../lib/git.js';
|
|
5
|
+
import { log } from '../lib/logger.js';
|
|
5
6
|
|
|
6
7
|
async function verbot(module_name, level, opts = {}) {
|
|
7
8
|
if (!['major', 'minor', 'patch'].includes(level)) {
|
|
@@ -50,26 +51,16 @@ async function verbot(module_name, level, opts = {}) {
|
|
|
50
51
|
);
|
|
51
52
|
|
|
52
53
|
await fs.writeFile(manifestPath, newContent);
|
|
53
|
-
|
|
54
|
+
log(`Updated version: ${currentVersion} -> ${newVersion}`);
|
|
54
55
|
|
|
55
56
|
if (opts.commit === false) return;
|
|
56
57
|
|
|
57
|
-
await
|
|
58
|
-
execFile('git', ['add', manifestPath], { cwd: ADDONS_PATH }, (error) => {
|
|
59
|
-
if (error) reject(error);
|
|
60
|
-
else resolve();
|
|
61
|
-
});
|
|
62
|
-
});
|
|
58
|
+
await execGit(['add', manifestPath], ADDONS_PATH);
|
|
63
59
|
|
|
64
60
|
const commitMessage = `[VER][${module_name}] Bump`;
|
|
65
|
-
await
|
|
66
|
-
execFile('git', ['commit', '-m', commitMessage], { cwd: ADDONS_PATH }, (error) => {
|
|
67
|
-
if (error) reject(error);
|
|
68
|
-
else resolve();
|
|
69
|
-
});
|
|
70
|
-
});
|
|
61
|
+
await execGit(['commit', '-m', commitMessage], ADDONS_PATH);
|
|
71
62
|
|
|
72
|
-
|
|
63
|
+
log(`Committed with message: ${commitMessage}`);
|
|
73
64
|
}
|
|
74
65
|
|
|
75
66
|
export { verbot };
|
package/src/index.js
CHANGED
|
@@ -6,11 +6,13 @@ import { autopr } from './commands/autopr.js';
|
|
|
6
6
|
import { changelog } from './commands/changelog.js';
|
|
7
7
|
import { commit } from './commands/commit.js';
|
|
8
8
|
import { exportPb, exportRip, exportImperex, exportEmailTemplates, exportWorkflow, exportLrp } from './commands/export.js';
|
|
9
|
+
import { routine } from './commands/routine.js';
|
|
9
10
|
import { init } from './commands/init.js';
|
|
10
11
|
import { main as prMain } from './commands/pr.js';
|
|
11
12
|
import { verbot } from './commands/ver.js';
|
|
12
13
|
import { CONFIG_FILE } from './config.js';
|
|
13
14
|
import { checkForUpdate, currentVersion } from './lib/updateCheck.js';
|
|
15
|
+
import { setSilent } from './lib/logger.js';
|
|
14
16
|
|
|
15
17
|
configDotenv({ path: CONFIG_FILE, quiet: true });
|
|
16
18
|
|
|
@@ -75,6 +77,7 @@ program
|
|
|
75
77
|
.option('-m, --message <text>', 'Changelog entry message')
|
|
76
78
|
.option('-b, --branch <name>', 'Branch name (default: autopr_<taskId>)')
|
|
77
79
|
.option('-n, --name <text>', 'PR title (default: task name from Odoo)')
|
|
80
|
+
.option('--amend', 'Amend existing PR on current branch with new trident/jira refs')
|
|
78
81
|
.action((opts) => {
|
|
79
82
|
autopr(opts).catch((err) => {
|
|
80
83
|
throw err;
|
|
@@ -93,9 +96,13 @@ exportCmd
|
|
|
93
96
|
.command('workflow')
|
|
94
97
|
.option('--no-commit')
|
|
95
98
|
.option('-b, --bump <level>', 'Version bump level (patch, minor, major)')
|
|
99
|
+
.option('-m, --module <id>', 'Module/workflow ID to export (skips interactive selection)')
|
|
100
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
101
|
+
.option('--auto-premigrate', 'Auto-generate pre-migrate script when XML ID renames are detected (no prompt)')
|
|
96
102
|
.action((opts) => {
|
|
103
|
+
if (opts.silent) setSilent(true);
|
|
97
104
|
exportWorkflow(opts).catch((err) => {
|
|
98
|
-
throw err;
|
|
105
|
+
if (!opts.silent) throw err;
|
|
99
106
|
});
|
|
100
107
|
});
|
|
101
108
|
|
|
@@ -104,42 +111,58 @@ exportCmd.command('rip').action(() => exportRip());
|
|
|
104
111
|
exportCmd
|
|
105
112
|
.command('pb')
|
|
106
113
|
.option('--no-commit')
|
|
114
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
107
115
|
.action((opts) => {
|
|
116
|
+
if (opts.silent) setSilent(true);
|
|
108
117
|
exportPb(opts).catch((err) => {
|
|
109
|
-
throw err;
|
|
118
|
+
if (!opts.silent) throw err;
|
|
110
119
|
});
|
|
111
120
|
});
|
|
112
121
|
|
|
113
122
|
exportCmd
|
|
114
123
|
.command('imperex')
|
|
115
124
|
.option('--no-commit')
|
|
125
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
116
126
|
.action((opts) => {
|
|
127
|
+
if (opts.silent) setSilent(true);
|
|
117
128
|
exportImperex(opts).catch((err) => {
|
|
118
|
-
throw err;
|
|
129
|
+
if (!opts.silent) throw err;
|
|
119
130
|
});
|
|
120
131
|
});
|
|
121
132
|
|
|
122
133
|
exportCmd
|
|
123
134
|
.command('lrp')
|
|
124
135
|
.option('--no-commit')
|
|
136
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
125
137
|
.action((opts) => {
|
|
138
|
+
if (opts.silent) setSilent(true);
|
|
126
139
|
exportLrp(opts).catch((err) => {
|
|
127
|
-
throw err;
|
|
140
|
+
if (!opts.silent) throw err;
|
|
128
141
|
});
|
|
129
142
|
});
|
|
130
143
|
|
|
131
144
|
exportCmd
|
|
132
145
|
.command('email-templates')
|
|
133
146
|
.option('--no-commit')
|
|
147
|
+
.option('-b, --bump <level>', 'Version bump level (patch, minor, major)')
|
|
134
148
|
.option('-e, --exclude <value...>', 'exclude templates matching id, name, or template_code')
|
|
135
149
|
.option('-m, --module <name>', 'module directory name (skip prompt)')
|
|
136
150
|
.option('-w, --workflow <value>', 'workflow name or id (skip prompt)')
|
|
151
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
152
|
+
.option('--auto-premigrate', 'Auto-generate pre-migrate script when XML ID renames are detected (no prompt)')
|
|
137
153
|
.action((opts) => {
|
|
154
|
+
if (opts.silent) setSilent(true);
|
|
138
155
|
exportEmailTemplates(opts).catch((err) => {
|
|
139
|
-
throw err;
|
|
156
|
+
if (!opts.silent) throw err;
|
|
140
157
|
});
|
|
141
158
|
});
|
|
142
159
|
|
|
160
|
+
program.command('routine').action(() => {
|
|
161
|
+
routine().catch((err) => {
|
|
162
|
+
throw err;
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
143
166
|
program.command('update').action(() => {
|
|
144
167
|
console.log('Updating prbot...');
|
|
145
168
|
execFile('npm', ['i', '-g', '@waron97/prbot'], (error, stdout, stderr) => {
|
package/src/lib/git.js
CHANGED
|
@@ -3,8 +3,12 @@ import { execFile } from 'child_process';
|
|
|
3
3
|
function execGit(args, cwd) {
|
|
4
4
|
return new Promise((resolve, reject) => {
|
|
5
5
|
execFile('git', args, { cwd }, (error, stdout) => {
|
|
6
|
-
if (error)
|
|
7
|
-
|
|
6
|
+
if (error) {
|
|
7
|
+
error.gitOutput = stdout;
|
|
8
|
+
reject(error);
|
|
9
|
+
} else {
|
|
10
|
+
resolve(stdout);
|
|
11
|
+
}
|
|
8
12
|
});
|
|
9
13
|
});
|
|
10
14
|
}
|