@waron97/prbot 2.1.0 → 2.3.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/.claude/settings.local.json +9 -3
- package/.prettierrc.mjs +11 -0
- package/README.md +30 -30
- package/eslint.config.mjs +16 -0
- package/index.js +327 -344
- package/package.json +33 -32
- package/src/commands/autopr.js +238 -263
- package/src/commands/changelog.js +154 -150
- package/src/commands/commit.js +146 -0
- package/src/commands/export.js +7 -0
- package/src/commands/exportPb.js +156 -0
- package/src/commands/init.js +144 -136
- package/src/commands/pr.js +81 -107
- package/src/commands/ver.js +54 -64
- package/src/config.js +4 -4
- package/src/index.js +112 -78
- package/src/lib/addons.js +4 -4
- package/src/lib/auth.js +25 -0
- package/src/lib/git.js +6 -6
|
@@ -1,189 +1,193 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { resolveAddonsPath } from
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import search from '@inquirer/search';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { resolveAddonsPath } from '../lib/addons.js';
|
|
6
6
|
|
|
7
7
|
function buildRefString(tridents, jiras, prNumber) {
|
|
8
|
-
|
|
8
|
+
const refs = [];
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
if (tridents && tridents.length > 0) {
|
|
11
|
+
refs.push(`Trident ${tridents.map((t) => `#${t}`).join(', #')}`);
|
|
12
|
+
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if (jiras && jiras.length > 0) {
|
|
15
|
+
refs.push(`JIRA ${jiras.join(', ')}`);
|
|
16
|
+
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (prNumber) {
|
|
19
|
+
refs.push(`PR sorgenia_addons #${prNumber}ADO`);
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
return refs.length > 0 ? `(${refs.join(', ')})` : '';
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function appendPrToLine(line, prNumber) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
const parenMatch = line.match(/\(([^)]*)\)\s*$/);
|
|
27
|
+
if (parenMatch) {
|
|
28
|
+
const inner = parenMatch[1];
|
|
29
|
+
const suffix = inner.includes('PR sorgenia_addons')
|
|
30
|
+
? `, #${prNumber}ADO`
|
|
31
|
+
: `, PR sorgenia_addons #${prNumber}ADO`;
|
|
32
|
+
return line.replace(/\(([^)]*)\)\s*$/, `(${inner}${suffix})`);
|
|
33
|
+
}
|
|
34
|
+
return `${line.trimEnd()} (PR sorgenia_addons #${prNumber}ADO)`;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function findDuplicateLine(content, tridents, jiras) {
|
|
38
|
-
|
|
38
|
+
const lines = content.split('\n');
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
for (let i = 0; i < lines.length; i++) {
|
|
41
|
+
const line = lines[i];
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
if (tridents && tridents.length > 0) {
|
|
44
|
+
for (const t of tridents) {
|
|
45
|
+
if (line.includes(`Trident #${t}`)) {
|
|
46
|
+
return { lineNumber: i, line };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
47
49
|
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
if (jiras && jiras.length > 0) {
|
|
52
|
+
for (const j of jiras) {
|
|
53
|
+
if (line.includes(j)) {
|
|
54
|
+
return { lineNumber: i, line };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
55
57
|
}
|
|
56
|
-
}
|
|
57
58
|
}
|
|
58
|
-
}
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
return null;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
function extractSections(content) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
const lines = content.split('\n');
|
|
65
|
+
const sections = [];
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < lines.length; i++) {
|
|
68
|
+
if (lines[i].startsWith('### ')) {
|
|
69
|
+
sections.push({
|
|
70
|
+
heading: lines[i],
|
|
71
|
+
startLine: i,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
73
74
|
}
|
|
74
|
-
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
return sections;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function findSectionEndLine(lines, sectionStartLine) {
|
|
80
|
-
|
|
81
|
-
(l, i) => i > sectionStartLine && l.startsWith("### "),
|
|
82
|
-
);
|
|
80
|
+
const nextSectionLine = lines.findIndex((l, i) => i > sectionStartLine && l.startsWith('### '));
|
|
83
81
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
if (nextSectionLine === -1) {
|
|
83
|
+
return lines.length - 1;
|
|
84
|
+
}
|
|
87
85
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
let endLine = nextSectionLine - 1;
|
|
87
|
+
while (endLine > sectionStartLine && lines[endLine].trim() === '') {
|
|
88
|
+
endLine--;
|
|
89
|
+
}
|
|
92
90
|
|
|
93
|
-
|
|
91
|
+
return endLine;
|
|
94
92
|
}
|
|
95
93
|
|
|
96
94
|
function detectIndentation(lines, sectionStartLine, sectionEndLine) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
for (let i = sectionStartLine + 1; i <= sectionEndLine; i++) {
|
|
96
|
+
const line = lines[i];
|
|
97
|
+
const match = line.match(/^(\s*)-\s/);
|
|
98
|
+
if (match) {
|
|
99
|
+
return match[1];
|
|
100
|
+
}
|
|
102
101
|
}
|
|
103
|
-
|
|
104
|
-
return " ";
|
|
102
|
+
return ' ';
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
async function changelog(prNumber, options) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
await fs.writeFile(changelogPath, lines.join("\n"));
|
|
186
|
-
console.log("Changelog entry added");
|
|
106
|
+
const tridents = options.trident || [];
|
|
107
|
+
const jiras = options.jira || [];
|
|
108
|
+
let message = options.message;
|
|
109
|
+
|
|
110
|
+
let ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
111
|
+
const changelogPath = `${ADDONS_PATH}/CHANGELOG.md`;
|
|
112
|
+
|
|
113
|
+
const content = readFileSync(changelogPath, 'utf-8');
|
|
114
|
+
|
|
115
|
+
const duplicate = findDuplicateLine(content, tridents, jiras);
|
|
116
|
+
let appendMode = false;
|
|
117
|
+
if (duplicate) {
|
|
118
|
+
console.log(`\nExisting entry (line ${duplicate.lineNumber + 1}):\n ${duplicate.line}`);
|
|
119
|
+
const { confirm } = await inquirer.prompt([
|
|
120
|
+
{
|
|
121
|
+
type: 'confirm',
|
|
122
|
+
name: 'confirm',
|
|
123
|
+
message: 'Append PR ref to existing entry?',
|
|
124
|
+
default: true,
|
|
125
|
+
},
|
|
126
|
+
]);
|
|
127
|
+
appendMode = confirm;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (appendMode) {
|
|
131
|
+
const lines = content.split('\n');
|
|
132
|
+
lines[duplicate.lineNumber] = appendPrToLine(lines[duplicate.lineNumber], prNumber);
|
|
133
|
+
await fs.writeFile(changelogPath, lines.join('\n'));
|
|
134
|
+
console.log('Updated existing line');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!message) {
|
|
139
|
+
const answer = await inquirer.prompt([
|
|
140
|
+
{
|
|
141
|
+
type: 'input',
|
|
142
|
+
name: 'message',
|
|
143
|
+
message: 'Changelog entry message:',
|
|
144
|
+
},
|
|
145
|
+
]);
|
|
146
|
+
message = answer.message;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const sections = extractSections(content);
|
|
150
|
+
const sectionChoices = sections.map((s) => ({
|
|
151
|
+
name: s.heading.replace(/^## /, ''),
|
|
152
|
+
value: s.heading,
|
|
153
|
+
}));
|
|
154
|
+
|
|
155
|
+
const selectedSection = await search({
|
|
156
|
+
message: 'Select section to add entry:',
|
|
157
|
+
source: async (input) => {
|
|
158
|
+
if (!input) {
|
|
159
|
+
return sectionChoices;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const filtered = sectionChoices.filter((choice) =>
|
|
163
|
+
choice.name.toLowerCase().includes(input.toLowerCase())
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return filtered;
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const selectedSectionObj = sections.find((s) => s.heading === selectedSection);
|
|
171
|
+
|
|
172
|
+
const lines = content.split('\n');
|
|
173
|
+
const endLine = findSectionEndLine(lines, selectedSectionObj.startLine);
|
|
174
|
+
const indent = detectIndentation(lines, selectedSectionObj.startLine, endLine);
|
|
175
|
+
|
|
176
|
+
const refString = buildRefString(tridents, jiras, prNumber);
|
|
177
|
+
const newEntry = `${indent}- ${message}${refString ? ' ' + refString : ''}`;
|
|
178
|
+
|
|
179
|
+
lines.splice(endLine + 1, 0, newEntry);
|
|
180
|
+
|
|
181
|
+
await fs.writeFile(changelogPath, lines.join('\n'));
|
|
182
|
+
console.log('Changelog entry added');
|
|
187
183
|
}
|
|
188
184
|
|
|
189
|
-
export {
|
|
185
|
+
export {
|
|
186
|
+
changelog,
|
|
187
|
+
extractSections,
|
|
188
|
+
findSectionEndLine,
|
|
189
|
+
detectIndentation,
|
|
190
|
+
buildRefString,
|
|
191
|
+
findDuplicateLine,
|
|
192
|
+
appendPrToLine,
|
|
193
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import searchList from 'inquirer-search-list';
|
|
4
|
+
import { resolveAddonsPath } from '../lib/addons.js';
|
|
5
|
+
import { execGit } from '../lib/git.js';
|
|
6
|
+
|
|
7
|
+
inquirer.registerPrompt('search-list', searchList);
|
|
8
|
+
|
|
9
|
+
const commitOperations = [
|
|
10
|
+
{
|
|
11
|
+
name: `✅ ADD ${chalk.gray('- Adding something that was previously missing')}`,
|
|
12
|
+
value: '[ADD]',
|
|
13
|
+
},
|
|
14
|
+
{ name: `📖 DOC ${chalk.gray('- Documentation changes')}`, value: '[DOC]' },
|
|
15
|
+
{ name: `🩹 FIX ${chalk.gray('- Bugfix or hotfix')}`, value: '[FIX]' },
|
|
16
|
+
{ name: `🛠️ IMP ${chalk.gray('- Improvement of existing code')}`, value: '[IMP]' },
|
|
17
|
+
{ name: `💬 I18N ${chalk.gray('- Translation changes')}`, value: '[I18N]' },
|
|
18
|
+
{ name: `📦 MIG ${chalk.gray('- Module migration')}`, value: '[MIG]' },
|
|
19
|
+
{ name: `🔄 REF ${chalk.gray('- Code refactoring')}`, value: '[REF]' },
|
|
20
|
+
{ name: `🎉 REL ${chalk.gray('- Release commit')}`, value: '[REL]' },
|
|
21
|
+
{ name: `🗑️ REM ${chalk.gray('- Removing unnecessary files')}`, value: '[REM]' },
|
|
22
|
+
{ name: `✏️ REN ${chalk.gray('- Renaming files/variables/models/etc.')}`, value: '[REN]' },
|
|
23
|
+
{ name: `🔙 REV ${chalk.gray('- Revert of an existing commit')}`, value: '[REV]' },
|
|
24
|
+
{ name: `🧳 SUB ${chalk.gray('- Submodule adding/updating')}`, value: '[SUB]' },
|
|
25
|
+
{ name: `#️⃣ VER ${chalk.gray('- Versioning')}`, value: '[VER]' },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const commitTypes = [
|
|
29
|
+
{ name: '🔧 Workflow', value: 'workflow' },
|
|
30
|
+
{ name: '📋 Module', value: 'module' },
|
|
31
|
+
{ name: '👤 Wizard', value: 'wizard' },
|
|
32
|
+
{ name: '⚙️ Symphony Process', value: 'symphony' },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
async function commit() {
|
|
36
|
+
const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
37
|
+
|
|
38
|
+
const unstagedChanges = await execGit(['diff', '--name-only'], ADDONS_PATH);
|
|
39
|
+
const stagedChanges = await execGit(['diff', '--cached', '--name-only'], ADDONS_PATH);
|
|
40
|
+
|
|
41
|
+
let stageFilesAnswers = [];
|
|
42
|
+
|
|
43
|
+
if (!stagedChanges.trim()) {
|
|
44
|
+
console.log(chalk.yellow('No staged changes found.'));
|
|
45
|
+
console.log(chalk.yellow('Unstaged changes:'));
|
|
46
|
+
console.log(unstagedChanges);
|
|
47
|
+
|
|
48
|
+
stageFilesAnswers = await inquirer.prompt([
|
|
49
|
+
{
|
|
50
|
+
message: 'Select unstaged files to stage for commit:',
|
|
51
|
+
type: 'checkbox',
|
|
52
|
+
name: 'filesToStage',
|
|
53
|
+
choices: unstagedChanges
|
|
54
|
+
.trim()
|
|
55
|
+
.split('\n')
|
|
56
|
+
.map((file) => file),
|
|
57
|
+
},
|
|
58
|
+
]);
|
|
59
|
+
} else {
|
|
60
|
+
console.log(chalk.green('Staged changes:'));
|
|
61
|
+
console.log(stagedChanges);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const answers = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
type: 'search-list',
|
|
67
|
+
message: 'Select an operation:',
|
|
68
|
+
name: 'commitOperation',
|
|
69
|
+
choices: commitOperations,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'search-list',
|
|
73
|
+
message: 'Select what has been changed:',
|
|
74
|
+
name: 'commitType',
|
|
75
|
+
choices: commitTypes,
|
|
76
|
+
when(answers) {
|
|
77
|
+
return answers.commitOperation !== '[DOC]';
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'input',
|
|
82
|
+
message(answers) {
|
|
83
|
+
return `Please enter the name of the ${answers.commitType}:`;
|
|
84
|
+
},
|
|
85
|
+
name: 'commitModule',
|
|
86
|
+
default: 'config_wf_',
|
|
87
|
+
when(answers) {
|
|
88
|
+
return answers.commitType === 'workflow' || answers.commitType === 'module';
|
|
89
|
+
},
|
|
90
|
+
filter(input) {
|
|
91
|
+
let value = input.trim();
|
|
92
|
+
|
|
93
|
+
if (!value.startsWith('[')) {
|
|
94
|
+
value = `[${value}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!value.endsWith(']')) {
|
|
98
|
+
value = `${value}]`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return value;
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: 'input',
|
|
106
|
+
message: 'Please enter your message:',
|
|
107
|
+
name: 'commitMessage',
|
|
108
|
+
filter: (input) => input.trim(),
|
|
109
|
+
when(answers) {
|
|
110
|
+
return answers.commitOperation !== '[DOC]';
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
if (answers.commitOperation === '[DOC]') {
|
|
116
|
+
answers.commitModule = '[CHANGELOG]';
|
|
117
|
+
answers.commitMessage = 'Changelog';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (answers.commitType === 'wizard') {
|
|
121
|
+
answers.commitModule = '[.cloudbuild/pb/B2WA/processes/all]';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (answers.commitType === 'symphony') {
|
|
125
|
+
answers.commitModule = '[.cloudbuild/symphony/B2WA/processes/all]';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const commitMessage = `${answers.commitOperation}${answers.commitModule} ${answers.commitMessage}`;
|
|
129
|
+
|
|
130
|
+
const finalPrompt = await inquirer.prompt([
|
|
131
|
+
{
|
|
132
|
+
type: 'confirm',
|
|
133
|
+
message: `${chalk.red(commitMessage)}\nDo you want to proceed with this commit message?`,
|
|
134
|
+
name: 'confirmCommit',
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
if (finalPrompt.confirmCommit) {
|
|
139
|
+
if (stageFilesAnswers.filesToStage && stageFilesAnswers.filesToStage.length > 0) {
|
|
140
|
+
await execGit(['add', ...stageFilesAnswers.filesToStage], ADDONS_PATH);
|
|
141
|
+
}
|
|
142
|
+
await execGit(['commit', '-m', commitMessage], ADDONS_PATH);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { commit };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fetch from 'node-fetch';
|
|
4
|
+
import search from '@inquirer/search';
|
|
5
|
+
import { getToken } from '../lib/auth.js';
|
|
6
|
+
import { execGit } from '../lib/git.js';
|
|
7
|
+
import { resolveAddonsPath } from '../lib/addons.js';
|
|
8
|
+
|
|
9
|
+
async function getProcessList(token) {
|
|
10
|
+
const url = `${process.env.IMPORTEXPORT_URL}/object/process_builder?addLanguageParam=true`;
|
|
11
|
+
const response = await fetch(url, {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
Authorization: `Bearer ${token}`,
|
|
16
|
+
},
|
|
17
|
+
body: JSON.stringify({ page: 1, size: 999999, filters: [] }),
|
|
18
|
+
});
|
|
19
|
+
if (!response.ok) throw new Error(await response.text());
|
|
20
|
+
const json = await response.json();
|
|
21
|
+
return json.data;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function initiateExport(guid, token) {
|
|
25
|
+
const url = `${process.env.IMPORTEXPORT_URL}/symphony/export`;
|
|
26
|
+
const response = await fetch(url, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
Authorization: `Bearer ${token}`,
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify([{ object_guid: guid, object_type: 'process_builder' }]),
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) throw new Error(await response.text());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function pollExportResult(guid, requestTime, token) {
|
|
38
|
+
const url = `${process.env.IMPORTEXPORT_URL}/export/info/processKey=ExportElement&subProcess=true&status=FAILED,COMPLETED&referenceId=process_builder`;
|
|
39
|
+
// Server createDate is offset -1hr from system time; subtract 1hr+5s buffer
|
|
40
|
+
const cutoff = requestTime - 3_605_000;
|
|
41
|
+
|
|
42
|
+
while (true) {
|
|
43
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
44
|
+
|
|
45
|
+
const response = await fetch(url, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: {
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
Authorization: `Bearer ${token}`,
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify({ page: 1, size: 7, sorters: [] }),
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) throw new Error(await response.text());
|
|
54
|
+
|
|
55
|
+
const { data } = await response.json();
|
|
56
|
+
const match = data.find(
|
|
57
|
+
(item) =>
|
|
58
|
+
item.customResponse?.guid === guid &&
|
|
59
|
+
new Date(item.createdDate).getTime() >= cutoff
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (!match) continue;
|
|
63
|
+
if (match.status === 'FAILED') throw new Error(`Export failed for guid ${guid}`);
|
|
64
|
+
return match.requestId;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function downloadZip(requestId, token) {
|
|
69
|
+
const url = `${process.env.IMPORTEXPORT_URL}/export/${requestId}`;
|
|
70
|
+
const response = await fetch(url, {
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
Authorization: `Bearer ${token}`,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
if (!response.ok) throw new Error(await response.text());
|
|
77
|
+
return Buffer.from(await response.arrayBuffer());
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function findExistingZip(baseDir, filename) {
|
|
81
|
+
async function walk(dir) {
|
|
82
|
+
let entries;
|
|
83
|
+
try {
|
|
84
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
for (const entry of entries) {
|
|
89
|
+
const full = path.join(dir, entry.name);
|
|
90
|
+
if (entry.isDirectory()) {
|
|
91
|
+
const found = await walk(full);
|
|
92
|
+
if (found) return found;
|
|
93
|
+
} else if (entry.name === filename) {
|
|
94
|
+
return full;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return walk(baseDir);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function exportPb(opts) {
|
|
103
|
+
const token = await getToken();
|
|
104
|
+
|
|
105
|
+
const processes = await getProcessList(token);
|
|
106
|
+
const choices = processes.map((p) => ({
|
|
107
|
+
name: `${p.process_name} (${p.document_id})`,
|
|
108
|
+
value: { guid: p.guid, document_id: p.document_id },
|
|
109
|
+
}));
|
|
110
|
+
|
|
111
|
+
const selected = await search({
|
|
112
|
+
message: 'Select PB process to export:',
|
|
113
|
+
source: async (input) => {
|
|
114
|
+
if (!input) return choices;
|
|
115
|
+
const q = input.toLowerCase();
|
|
116
|
+
return choices.filter((c) => c.name.toLowerCase().includes(q));
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const { guid, document_id } = selected;
|
|
121
|
+
const filename = `${document_id}.zip`;
|
|
122
|
+
|
|
123
|
+
console.log(`Initiating export for ${document_id}...`);
|
|
124
|
+
const requestTime = Date.now();
|
|
125
|
+
await initiateExport(guid, token);
|
|
126
|
+
|
|
127
|
+
console.log('Waiting for export to complete...');
|
|
128
|
+
const requestId = await pollExportResult(guid, requestTime, token);
|
|
129
|
+
|
|
130
|
+
console.log(`Downloading ${filename}...`);
|
|
131
|
+
const zipBuffer = await downloadZip(requestId, token);
|
|
132
|
+
|
|
133
|
+
const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
134
|
+
const processesDir = path.join(ADDONS_PATH, '.cloudbuild', 'pb', 'B2WA', 'processes');
|
|
135
|
+
|
|
136
|
+
const existing = await findExistingZip(processesDir, filename);
|
|
137
|
+
let savePath;
|
|
138
|
+
if (existing) {
|
|
139
|
+
savePath = existing;
|
|
140
|
+
await fs.writeFile(savePath, zipBuffer);
|
|
141
|
+
console.log(`Updated existing file at ${savePath}`);
|
|
142
|
+
} else {
|
|
143
|
+
savePath = path.join(processesDir, 'all', filename);
|
|
144
|
+
await fs.mkdir(path.dirname(savePath), { recursive: true });
|
|
145
|
+
await fs.writeFile(savePath, zipBuffer);
|
|
146
|
+
console.log(`Created new file at ${savePath}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (opts.commit !== false) {
|
|
150
|
+
await execGit(['add', savePath], ADDONS_PATH);
|
|
151
|
+
await execGit(['commit', '-m', '[IMP][.cloudbuild] Update wizard'], ADDONS_PATH);
|
|
152
|
+
console.log('Committed.');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { exportPb };
|