@wpmoo/toolkit 0.9.5 → 0.9.7
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 +95 -443
- package/dist/cli.js +417 -35
- package/dist/cockpit/command-registry.js +5 -1
- package/dist/cockpit/daily-prompts.js +30 -11
- package/dist/cockpit/menu.js +4 -1
- package/dist/cockpit/module-action-menu.js +40 -0
- package/dist/cockpit/module-browser.js +117 -0
- package/dist/daily-actions.js +40 -3
- package/dist/databases.js +58 -0
- package/dist/help.js +4 -2
- package/dist/menu-navigation.js +2 -2
- package/dist/module-actions.js +50 -1
- package/dist/prompts/index.js +69 -18
- package/dist/system-prerequisites.js +189 -0
- package/dist/templates.js +44 -25
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
import { realpathSync } from 'node:fs';
|
|
3
3
|
import { rm, rename } from 'node:fs/promises';
|
|
4
4
|
import { basename, relative, resolve } from 'node:path';
|
|
5
|
+
import { emitKeypressEvents } from 'node:readline';
|
|
5
6
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
7
|
import { commandFromArgs, isHelpRequested, isVersionRequested, optionsFromArgs, parseArgs, stripInternalFlags, } from './args.js';
|
|
8
|
+
import { cockpitCommands } from './cockpit/command-registry.js';
|
|
7
9
|
import { collectDailyActionArgs } from './cockpit/daily-prompts.js';
|
|
10
|
+
import { selectModuleAction } from './cockpit/module-action-menu.js';
|
|
11
|
+
import { selectModuleFromBrowser } from './cockpit/module-browser.js';
|
|
8
12
|
import { selectCockpitTopLevelMenu } from './cockpit/menu.js';
|
|
9
13
|
import { confirmCockpitCommandRisk } from './cockpit/safety.js';
|
|
10
14
|
import { detectDevelopmentEnvironment } from './environment.js';
|
|
11
15
|
import { commandOdooVersion } from './environment-version.js';
|
|
12
16
|
import { defaultAgentSkillsTemplateUrl } from './external-templates.js';
|
|
13
|
-
import {
|
|
17
|
+
import { listEnvironmentDatabases, normalizeDatabaseListResult } from './databases.js';
|
|
18
|
+
import { isDailyActionCommand, runDailyAction, runDailyActionWithStyledOutput } from './daily-actions.js';
|
|
14
19
|
import { getDoctorReport, runDoctor } from './doctor.js';
|
|
15
20
|
import { getOriginUrl, realGit } from './git.js';
|
|
16
21
|
import { renderHelp } from './help.js';
|
|
17
22
|
import { runLocalCockpit } from './local-cockpit.js';
|
|
18
|
-
import { addModuleToSourceRepo,
|
|
23
|
+
import { addModuleToSourceRepo, removeModuleFromSourceRepo, } from './module-actions.js';
|
|
19
24
|
import { supportedOdooVersions } from './odoo-versions.js';
|
|
20
25
|
import { renderRepositorySetupNote } from './prompt-copy.js';
|
|
21
26
|
import { promptRepositoryUrl } from './prompt-repositories.js';
|
|
@@ -28,6 +33,7 @@ import { backupTargetPath, expectedTargetConfirmation, inspectEnvironmentTarget,
|
|
|
28
33
|
import { getGitHubPrerequisiteStatus, renderGitHubPrerequisiteGuidance, } from './github-prerequisites.js';
|
|
29
34
|
import { checkGitHubRepositories, createGitHubRepositories, manualCreateCommands, } from './repository-preflight.js';
|
|
30
35
|
import { scaffold } from './scaffold.js';
|
|
36
|
+
import { getSystemPrerequisiteStatus, renderSystemPrerequisiteGuidance, } from './system-prerequisites.js';
|
|
31
37
|
import { confirmPrompt, introPrompt, isPromptCancel, notePrompt, outroPrompt, selectPrompt, textPrompt } from './prompts/index.js';
|
|
32
38
|
import { renderBanner } from './templates.js';
|
|
33
39
|
import { checkForUpdate, isUpdateCheckSkipped, restartCli } from './update-check.js';
|
|
@@ -211,6 +217,29 @@ function clearCockpitScreen() {
|
|
|
211
217
|
process.stdout.write('\u001B[2J\u001B[H');
|
|
212
218
|
}
|
|
213
219
|
}
|
|
220
|
+
function clearPrerequisiteScreen() {
|
|
221
|
+
if (process.stdout.isTTY) {
|
|
222
|
+
process.stdout.write('\u001B[3J\u001B[2J\u001B[H');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const ANSI_ACTION = '\u001B[38;2;226;184;96m';
|
|
226
|
+
const ANSI_SUCCESS = '\u001B[32m';
|
|
227
|
+
const ANSI_DEFAULT_FOREGROUND = '\u001B[39m';
|
|
228
|
+
const ANSI_DIM_INFO = '\u001B[2m\u001B[38;2;120;157;181m';
|
|
229
|
+
const ANSI_RESET = '\u001B[0m';
|
|
230
|
+
function renderActionText(value) {
|
|
231
|
+
return ansi(value, ANSI_ACTION, ANSI_DEFAULT_FOREGROUND);
|
|
232
|
+
}
|
|
233
|
+
function renderCompletedText(action) {
|
|
234
|
+
if (!supportsAnsi()) {
|
|
235
|
+
return `✓ ${action} completed.`;
|
|
236
|
+
}
|
|
237
|
+
return `${ANSI_SUCCESS}✓${ANSI_DEFAULT_FOREGROUND} ${action} ${ANSI_SUCCESS}completed${ANSI_DEFAULT_FOREGROUND}.`;
|
|
238
|
+
}
|
|
239
|
+
function renderBackHelp() {
|
|
240
|
+
return ansi('Esc to go back', ANSI_DIM_INFO, ANSI_RESET);
|
|
241
|
+
}
|
|
242
|
+
const manualDatabaseValue = '__wpmoo_manual_database_entry__';
|
|
214
243
|
async function showStartup(argv, skipUpdateCheck, details) {
|
|
215
244
|
if (skipUpdateCheck) {
|
|
216
245
|
console.log(renderStartupBanner(details));
|
|
@@ -603,9 +632,6 @@ async function selectSourceRepo(target, cancelAction = 'exit') {
|
|
|
603
632
|
}
|
|
604
633
|
return { repoPath: String(selected), sourceType: 'private' };
|
|
605
634
|
}
|
|
606
|
-
function formatSourceRepoPromptPath(target, selected) {
|
|
607
|
-
return renderedSourceRepoPath(target, selected.sourceType, selected.repoPath);
|
|
608
|
-
}
|
|
609
635
|
function suggestedModuleName(repoPath) {
|
|
610
636
|
return 'odoo_sample_module';
|
|
611
637
|
}
|
|
@@ -801,25 +827,20 @@ function removeModuleOptionsFromArgs(argv) {
|
|
|
801
827
|
};
|
|
802
828
|
}
|
|
803
829
|
async function removeModuleOptionsFromPrompts(showIntro = true, cancelAction = 'exit') {
|
|
804
|
-
|
|
830
|
+
if (showIntro) {
|
|
831
|
+
introPrompt('Remove module');
|
|
832
|
+
}
|
|
805
833
|
const target = process.cwd();
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
if (modules.length === 0) {
|
|
834
|
+
const selectedModule = await selectModuleFromBrowser(target, { cancelAction });
|
|
835
|
+
if (!selectedModule) {
|
|
809
836
|
if (cancelAction === 'back') {
|
|
810
|
-
notePrompt(
|
|
837
|
+
notePrompt('No Odoo modules found.\nNext: choose "Add module" or "Add source repo" first.', 'Nothing to remove');
|
|
811
838
|
handleUnavailableMenuChoice(cancelAction);
|
|
812
839
|
}
|
|
813
|
-
throw new Error(
|
|
840
|
+
throw new Error('No Odoo modules found');
|
|
814
841
|
}
|
|
815
|
-
const moduleName = await selectPrompt({
|
|
816
|
-
message: menuPromptMessage('Module to remove', cancelAction),
|
|
817
|
-
options: modules.map((module) => ({ value: module, label: module })),
|
|
818
|
-
initialValue: modules[0],
|
|
819
|
-
});
|
|
820
|
-
handleCancel(moduleName, cancelAction);
|
|
821
842
|
const deleteFiles = await confirmPrompt({
|
|
822
|
-
message: menuPromptMessage('Delete module files too?
|
|
843
|
+
message: menuPromptMessage('Delete module files too?', cancelAction),
|
|
823
844
|
active: 'Y',
|
|
824
845
|
inactive: 'n',
|
|
825
846
|
initialValue: false,
|
|
@@ -827,9 +848,9 @@ async function removeModuleOptionsFromPrompts(showIntro = true, cancelAction = '
|
|
|
827
848
|
handleCancel(deleteFiles, cancelAction);
|
|
828
849
|
return {
|
|
829
850
|
target,
|
|
830
|
-
repoPath:
|
|
831
|
-
sourceType:
|
|
832
|
-
moduleName:
|
|
851
|
+
repoPath: selectedModule.repoPath,
|
|
852
|
+
sourceType: selectedModule.sourceType,
|
|
853
|
+
moduleName: selectedModule.moduleName,
|
|
833
854
|
deleteFiles: Boolean(deleteFiles),
|
|
834
855
|
stage: true,
|
|
835
856
|
};
|
|
@@ -912,6 +933,40 @@ async function ensureGitHubRepositories(options, interactive) {
|
|
|
912
933
|
handleCancel(visibility, 'exit');
|
|
913
934
|
await createGitHubRepositories(missing, visibility);
|
|
914
935
|
}
|
|
936
|
+
async function ensureSystemPrerequisites(interactive) {
|
|
937
|
+
while (true) {
|
|
938
|
+
const status = await getSystemPrerequisiteStatus();
|
|
939
|
+
if (status.ok) {
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
942
|
+
const guidance = renderSystemPrerequisiteGuidance(status);
|
|
943
|
+
if (!interactive) {
|
|
944
|
+
throw new Error(guidance);
|
|
945
|
+
}
|
|
946
|
+
clearPrerequisiteScreen();
|
|
947
|
+
console.log(renderStartupBanner());
|
|
948
|
+
console.log(renderVersionTag());
|
|
949
|
+
console.log();
|
|
950
|
+
console.log(guidance);
|
|
951
|
+
console.log();
|
|
952
|
+
const action = await selectPrompt({
|
|
953
|
+
message: 'If you have installed the prerequisites',
|
|
954
|
+
options: [
|
|
955
|
+
{
|
|
956
|
+
value: 'check-again',
|
|
957
|
+
label: `${renderActionText('Check again')}${dim(' (Enter to re-check again)')}`,
|
|
958
|
+
},
|
|
959
|
+
],
|
|
960
|
+
initialValue: 'check-again',
|
|
961
|
+
loop: false,
|
|
962
|
+
navigationHelp: 'exit',
|
|
963
|
+
});
|
|
964
|
+
handleCancel(action, 'exit');
|
|
965
|
+
if (action === 'check-again') {
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
915
970
|
async function ensureNonInteractiveCreateTarget(options) {
|
|
916
971
|
if (options.dryRun) {
|
|
917
972
|
return;
|
|
@@ -928,7 +983,7 @@ async function ensureNonInteractiveCreateTarget(options) {
|
|
|
928
983
|
}
|
|
929
984
|
throw new Error(renderForeignEnvironmentTargetWarning(state));
|
|
930
985
|
}
|
|
931
|
-
async function finishCreateFlow(result, cwd, interactive) {
|
|
986
|
+
async function finishCreateFlow(result, cwd, interactive, checkSystemPrerequisites = true) {
|
|
932
987
|
if (result.kind === 'cancelled') {
|
|
933
988
|
outroPrompt('Create flow cancelled.');
|
|
934
989
|
return;
|
|
@@ -942,6 +997,9 @@ async function finishCreateFlow(result, cwd, interactive) {
|
|
|
942
997
|
return;
|
|
943
998
|
}
|
|
944
999
|
const { options } = result;
|
|
1000
|
+
if (!options.dryRun && checkSystemPrerequisites && !(await ensureSystemPrerequisites(interactive))) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
945
1003
|
await ensureGitHubRepositories(options, interactive);
|
|
946
1004
|
const scaffoldResult = await scaffold(options);
|
|
947
1005
|
if (options.dryRun) {
|
|
@@ -968,18 +1026,328 @@ async function finishCreateFlow(result, cwd, interactive) {
|
|
|
968
1026
|
}
|
|
969
1027
|
outroPrompt(`Created Odoo dev overlay in ${options.target}. Review staged changes, then commit.`);
|
|
970
1028
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1029
|
+
function selectedModuleRemovalOptions(module, cwd, deleteFiles) {
|
|
1030
|
+
return {
|
|
1031
|
+
target: cwd,
|
|
1032
|
+
repoPath: module.repoPath,
|
|
1033
|
+
sourceType: module.sourceType,
|
|
1034
|
+
moduleName: module.moduleName,
|
|
1035
|
+
deleteFiles,
|
|
1036
|
+
stage: true,
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
function moduleDailyAction(action) {
|
|
1040
|
+
if (action === 'update')
|
|
1041
|
+
return 'update';
|
|
1042
|
+
if (action === 'test')
|
|
1043
|
+
return 'test';
|
|
1044
|
+
if (action === 'lint')
|
|
1045
|
+
return 'lint';
|
|
1046
|
+
return undefined;
|
|
1047
|
+
}
|
|
1048
|
+
function moduleDailyActionArgs(action, module) {
|
|
1049
|
+
if (action === 'update' || action === 'test') {
|
|
1050
|
+
return [module.moduleName];
|
|
974
1051
|
}
|
|
975
|
-
|
|
976
|
-
|
|
1052
|
+
return [];
|
|
1053
|
+
}
|
|
1054
|
+
function moduleActionTitle(action) {
|
|
1055
|
+
if (action === 'update')
|
|
1056
|
+
return 'Update module';
|
|
1057
|
+
if (action === 'test')
|
|
1058
|
+
return 'Test module';
|
|
1059
|
+
if (action === 'lint')
|
|
1060
|
+
return 'Run environment lint';
|
|
1061
|
+
if (action === 'delete')
|
|
1062
|
+
return 'Delete module';
|
|
1063
|
+
return 'Module action';
|
|
1064
|
+
}
|
|
1065
|
+
function moduleActionCompletedLabel(action) {
|
|
1066
|
+
if (action === 'update')
|
|
1067
|
+
return 'Update';
|
|
1068
|
+
if (action === 'test')
|
|
1069
|
+
return 'Test';
|
|
1070
|
+
if (action === 'lint')
|
|
1071
|
+
return 'Environment lint';
|
|
1072
|
+
return 'Action';
|
|
1073
|
+
}
|
|
1074
|
+
function commandActionTitle(command) {
|
|
1075
|
+
if (command === 'update')
|
|
1076
|
+
return 'Update module';
|
|
1077
|
+
if (command === 'test')
|
|
1078
|
+
return 'Test module';
|
|
1079
|
+
if (command === 'lint')
|
|
1080
|
+
return 'Run environment lint';
|
|
1081
|
+
if (command === 'pot')
|
|
1082
|
+
return 'Generate POT';
|
|
1083
|
+
return command;
|
|
1084
|
+
}
|
|
1085
|
+
function commandCompletedLabel(command) {
|
|
1086
|
+
if (command === 'install')
|
|
1087
|
+
return 'Install';
|
|
1088
|
+
if (command === 'update')
|
|
1089
|
+
return 'Update';
|
|
1090
|
+
if (command === 'test')
|
|
1091
|
+
return 'Test';
|
|
1092
|
+
if (command === 'lint')
|
|
1093
|
+
return 'Environment lint';
|
|
1094
|
+
if (command === 'pot')
|
|
1095
|
+
return 'Generate POT';
|
|
1096
|
+
return command;
|
|
1097
|
+
}
|
|
1098
|
+
function shouldReturnToDailySelection(command) {
|
|
1099
|
+
return ['install', 'update', 'test', 'pot'].includes(command);
|
|
1100
|
+
}
|
|
1101
|
+
function shouldUseModuleBrowserForDailySelection(command) {
|
|
1102
|
+
return ['update', 'test', 'lint', 'pot'].includes(command);
|
|
1103
|
+
}
|
|
1104
|
+
function dailyActionSelectedLabel(command, argv) {
|
|
1105
|
+
if (['install', 'update', 'test', 'pot'].includes(command)) {
|
|
1106
|
+
return argv[0];
|
|
1107
|
+
}
|
|
1108
|
+
return undefined;
|
|
1109
|
+
}
|
|
1110
|
+
async function selectDatabaseArg(cwd, message, fallback, options = {}) {
|
|
1111
|
+
const databaseResult = normalizeDatabaseListResult(await listEnvironmentDatabases(cwd, options));
|
|
1112
|
+
const databases = databaseResult.databases;
|
|
1113
|
+
if (databases.length > 0) {
|
|
1114
|
+
const selected = await selectPrompt({
|
|
1115
|
+
message: menuPromptMessage(message, 'back'),
|
|
1116
|
+
options: [
|
|
1117
|
+
...databases.map((database) => ({ value: database, label: database })),
|
|
1118
|
+
{ value: manualDatabaseValue, label: 'Manual entry' },
|
|
1119
|
+
],
|
|
1120
|
+
initialValue: databases.includes(fallback) ? fallback : databases[0],
|
|
1121
|
+
});
|
|
1122
|
+
handleCancel(selected, 'back');
|
|
1123
|
+
if (selected !== manualDatabaseValue) {
|
|
1124
|
+
return String(selected);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
return asString(await textPrompt({
|
|
1128
|
+
message: menuPromptMessage(databaseResult.ok ? message : `${message} (database list unavailable; enter manually)`, 'back'),
|
|
1129
|
+
defaultValue: fallback,
|
|
1130
|
+
placeholder: fallback,
|
|
1131
|
+
}), fallback, 'back');
|
|
1132
|
+
}
|
|
1133
|
+
async function collectCockpitModuleDailyActionArgs(command, module, cwd) {
|
|
1134
|
+
const moduleName = module.moduleName;
|
|
1135
|
+
if (command === 'update') {
|
|
1136
|
+
return [moduleName];
|
|
1137
|
+
}
|
|
1138
|
+
if (command === 'test') {
|
|
1139
|
+
return [moduleName];
|
|
1140
|
+
}
|
|
1141
|
+
if (command === 'lint') {
|
|
1142
|
+
return [];
|
|
1143
|
+
}
|
|
1144
|
+
if (command === 'pot') {
|
|
1145
|
+
return [moduleName];
|
|
1146
|
+
}
|
|
1147
|
+
throw new Error(`Unsupported module action command: ${command}`);
|
|
1148
|
+
}
|
|
1149
|
+
async function renderDailyActionResultPageHeader(title, selectedLabel, cwd) {
|
|
1150
|
+
await renderCockpitSubmenuPage(title, cwd);
|
|
1151
|
+
if (selectedLabel) {
|
|
1152
|
+
console.log(renderActionText(selectedLabel));
|
|
1153
|
+
console.log('');
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
async function renderCockpitSubmenuPage(title, cwd) {
|
|
1157
|
+
const status = await getEnvironmentStatus(cwd);
|
|
1158
|
+
const serviceStatus = await getServiceRuntimeStatus(cwd, status);
|
|
1159
|
+
clearCockpitScreen();
|
|
1160
|
+
console.log(renderBanner(renderCockpitStatusLines(status, serviceStatus, `Last: ${title}`), { version: startupVersionLine() }));
|
|
1161
|
+
console.log();
|
|
1162
|
+
introPrompt(title);
|
|
1163
|
+
}
|
|
1164
|
+
async function waitForModuleActionBack() {
|
|
1165
|
+
console.log(renderBackHelp());
|
|
1166
|
+
if (!process.stdin.isTTY) {
|
|
1167
|
+
return false;
|
|
1168
|
+
}
|
|
1169
|
+
await new Promise((resolve) => {
|
|
1170
|
+
emitKeypressEvents(process.stdin);
|
|
1171
|
+
const input = process.stdin;
|
|
1172
|
+
const wasRaw = input.isRaw;
|
|
1173
|
+
const listener = (_value, key) => {
|
|
1174
|
+
if (key.ctrl && key.name === 'c') {
|
|
1175
|
+
process.exit(1);
|
|
1176
|
+
}
|
|
1177
|
+
if (key.name === 'escape' || key.sequence === '\u001B') {
|
|
1178
|
+
cleanup();
|
|
1179
|
+
resolve();
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
const cleanup = () => {
|
|
1183
|
+
input.off('keypress', listener);
|
|
1184
|
+
if (typeof input.setRawMode === 'function') {
|
|
1185
|
+
input.setRawMode(Boolean(wasRaw));
|
|
1186
|
+
}
|
|
1187
|
+
input.pause();
|
|
1188
|
+
};
|
|
1189
|
+
if (typeof input.setRawMode === 'function') {
|
|
1190
|
+
input.setRawMode(true);
|
|
1191
|
+
}
|
|
1192
|
+
input.resume();
|
|
1193
|
+
input.on('keypress', listener);
|
|
1194
|
+
});
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
async function runDailyActionResultPage(command, argv, cwd, title = commandActionTitle(command), selectedLabel = dailyActionSelectedLabel(command, argv), completedLabel = commandCompletedLabel(command)) {
|
|
1198
|
+
await renderDailyActionResultPageHeader(title, selectedLabel, cwd);
|
|
1199
|
+
try {
|
|
1200
|
+
await runDailyActionWithStyledOutput(command, argv, cwd);
|
|
1201
|
+
notePrompt(renderCompletedText(completedLabel), 'Done');
|
|
1202
|
+
}
|
|
1203
|
+
catch (error) {
|
|
1204
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1205
|
+
notePrompt(message, 'Error');
|
|
1206
|
+
await waitForModuleActionBack();
|
|
1207
|
+
throw error;
|
|
1208
|
+
}
|
|
1209
|
+
return waitForModuleActionBack();
|
|
1210
|
+
}
|
|
1211
|
+
async function runSelectedModuleDailyAction(action, module, cwd) {
|
|
1212
|
+
const command = moduleDailyAction(action);
|
|
1213
|
+
if (!command) {
|
|
1214
|
+
return false;
|
|
1215
|
+
}
|
|
1216
|
+
return runDailyActionResultPage(command, moduleDailyActionArgs(action, module), cwd, moduleActionTitle(action), action === 'lint' ? undefined : module.moduleName, moduleActionCompletedLabel(action));
|
|
1217
|
+
}
|
|
1218
|
+
async function runSelectedModuleAction(action, module, cwd) {
|
|
1219
|
+
if (action === 'delete') {
|
|
1220
|
+
const deleteFiles = await confirmPrompt({
|
|
1221
|
+
message: menuPromptMessage('Delete module files too?', 'back'),
|
|
1222
|
+
active: 'Y',
|
|
1223
|
+
inactive: 'n',
|
|
1224
|
+
initialValue: false,
|
|
1225
|
+
});
|
|
1226
|
+
handleCancel(deleteFiles, 'back');
|
|
1227
|
+
const removeCommand = cockpitCommands.find((entry) => entry.id === 'remove-module');
|
|
1228
|
+
if (removeCommand && !(await confirmCockpitCommandRisk(removeCommand))) {
|
|
1229
|
+
notePrompt(`Module ${module.moduleName} was not removed.`, 'Action skipped');
|
|
1230
|
+
return false;
|
|
1231
|
+
}
|
|
1232
|
+
await removeModuleFromSourceRepo(selectedModuleRemovalOptions(module, cwd, Boolean(deleteFiles)));
|
|
1233
|
+
notePrompt(`Removed module ${module.moduleName} from source repo ${module.repoPath}.`, 'Done');
|
|
1234
|
+
return false;
|
|
1235
|
+
}
|
|
1236
|
+
return runSelectedModuleDailyAction(action, module, cwd);
|
|
1237
|
+
}
|
|
1238
|
+
async function runCockpitModuleDailyCommand(command, cwd) {
|
|
1239
|
+
if (command.target.kind !== 'daily') {
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
const dailyCommand = command.target.command;
|
|
1243
|
+
while (true) {
|
|
1244
|
+
let selectedModule;
|
|
1245
|
+
let argv;
|
|
1246
|
+
try {
|
|
1247
|
+
await renderCockpitSubmenuPage(command.label, cwd);
|
|
1248
|
+
const module = await selectModuleFromBrowser(cwd);
|
|
1249
|
+
if (!module) {
|
|
1250
|
+
notePrompt('No Odoo modules found.\nNext: choose "Add module" or "Add source repo" first.', command.label);
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
selectedModule = module;
|
|
1254
|
+
argv = await collectCockpitModuleDailyActionArgs(dailyCommand, selectedModule, cwd);
|
|
1255
|
+
}
|
|
1256
|
+
catch (error) {
|
|
1257
|
+
if (isMenuBackSignal(error)) {
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
throw error;
|
|
1261
|
+
}
|
|
977
1262
|
if (!(await confirmCockpitCommandRisk(command))) {
|
|
978
1263
|
notePrompt(`${command.slashAlias} was not run.`, 'Action skipped');
|
|
979
|
-
return
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
const returnedByBack = await runDailyActionResultPage(dailyCommand, argv, cwd, command.label, selectedModule.moduleName, commandCompletedLabel(dailyCommand));
|
|
1267
|
+
if (!returnedByBack) {
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
async function runCockpitDailyCommand(command, cwd) {
|
|
1273
|
+
if (command.target.kind !== 'daily') {
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
const dailyCommand = command.target.command;
|
|
1277
|
+
if (shouldUseModuleBrowserForDailySelection(dailyCommand)) {
|
|
1278
|
+
await runCockpitModuleDailyCommand(command, cwd);
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
if (!shouldReturnToDailySelection(dailyCommand)) {
|
|
1282
|
+
const argv = await collectDailyActionArgs(dailyCommand, cwd);
|
|
1283
|
+
if (!(await confirmCockpitCommandRisk(command))) {
|
|
1284
|
+
notePrompt(`${command.slashAlias} was not run.`, 'Action skipped');
|
|
1285
|
+
return;
|
|
980
1286
|
}
|
|
981
|
-
await runDailyAction(
|
|
1287
|
+
await runDailyAction(dailyCommand, argv, cwd);
|
|
982
1288
|
notePrompt(`${command.slashAlias} completed.`, 'Done');
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
while (true) {
|
|
1292
|
+
let argv;
|
|
1293
|
+
try {
|
|
1294
|
+
await renderCockpitSubmenuPage(command.label, cwd);
|
|
1295
|
+
argv = await collectDailyActionArgs(dailyCommand, cwd);
|
|
1296
|
+
}
|
|
1297
|
+
catch (error) {
|
|
1298
|
+
if (isMenuBackSignal(error)) {
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
throw error;
|
|
1302
|
+
}
|
|
1303
|
+
if (!(await confirmCockpitCommandRisk(command))) {
|
|
1304
|
+
notePrompt(`${command.slashAlias} was not run.`, 'Action skipped');
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
const returnedByBack = await runDailyActionResultPage(dailyCommand, argv, cwd, command.label);
|
|
1308
|
+
if (!returnedByBack) {
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
async function runListModulesCommand(cwd) {
|
|
1314
|
+
while (true) {
|
|
1315
|
+
await renderCockpitSubmenuPage('List modules', cwd);
|
|
1316
|
+
const selectedModule = await selectModuleFromBrowser(cwd);
|
|
1317
|
+
if (!selectedModule) {
|
|
1318
|
+
notePrompt('No Odoo modules found.\nNext: choose "Add module" or "Add source repo" first.', 'List modules');
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
while (true) {
|
|
1322
|
+
let moduleAction;
|
|
1323
|
+
try {
|
|
1324
|
+
await renderCockpitSubmenuPage('List modules', cwd);
|
|
1325
|
+
console.log(renderActionText(selectedModule.moduleName));
|
|
1326
|
+
console.log('');
|
|
1327
|
+
moduleAction = await selectModuleAction(selectedModule);
|
|
1328
|
+
}
|
|
1329
|
+
catch (error) {
|
|
1330
|
+
if (isMenuBackSignal(error)) {
|
|
1331
|
+
break;
|
|
1332
|
+
}
|
|
1333
|
+
throw error;
|
|
1334
|
+
}
|
|
1335
|
+
if (!moduleAction) {
|
|
1336
|
+
break;
|
|
1337
|
+
}
|
|
1338
|
+
const returnedByBack = await runSelectedModuleAction(moduleAction, selectedModule, cwd);
|
|
1339
|
+
if (!returnedByBack) {
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
async function runCockpitCommand(command, cwd) {
|
|
1346
|
+
if (command.id === 'exit') {
|
|
1347
|
+
return 'exit';
|
|
1348
|
+
}
|
|
1349
|
+
if (command.target.kind === 'daily') {
|
|
1350
|
+
await runCockpitDailyCommand(command, cwd);
|
|
983
1351
|
return 'continue';
|
|
984
1352
|
}
|
|
985
1353
|
if (command.id === 'status') {
|
|
@@ -990,6 +1358,10 @@ async function runCockpitCommand(command, cwd) {
|
|
|
990
1358
|
notePrompt(await runDoctor(cwd), 'Doctor');
|
|
991
1359
|
return 'continue';
|
|
992
1360
|
}
|
|
1361
|
+
if (command.id === 'list-modules') {
|
|
1362
|
+
await runListModulesCommand(cwd);
|
|
1363
|
+
return 'continue';
|
|
1364
|
+
}
|
|
993
1365
|
if (command.id === 'add-repo') {
|
|
994
1366
|
const options = await addRepoOptionsFromPrompts(false, 'back');
|
|
995
1367
|
await ensureAddRepoGitHubRepository(options, 'back');
|
|
@@ -1014,7 +1386,7 @@ async function runCockpitCommand(command, cwd) {
|
|
|
1014
1386
|
return 'continue';
|
|
1015
1387
|
}
|
|
1016
1388
|
if (command.id === 'remove-module') {
|
|
1017
|
-
const options = await removeModuleOptionsFromPrompts(
|
|
1389
|
+
const options = await removeModuleOptionsFromPrompts(true, 'back');
|
|
1018
1390
|
if (!(await confirmCockpitCommandRisk(command))) {
|
|
1019
1391
|
notePrompt(`Module ${options.moduleName} was not removed.`, 'Action skipped');
|
|
1020
1392
|
return 'continue';
|
|
@@ -1051,13 +1423,21 @@ export async function runCli(cliArgv = process.argv.slice(2), cwd = process.cwd(
|
|
|
1051
1423
|
const detection = await detectDevelopmentEnvironment(cwd);
|
|
1052
1424
|
if (!detection.isEnvironment) {
|
|
1053
1425
|
await showStartup(argv, skipUpdateCheck);
|
|
1054
|
-
|
|
1426
|
+
if (!(await ensureSystemPrerequisites(true))) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
await finishCreateFlow(await optionsFromPrompts(), cwd, true, false);
|
|
1055
1430
|
return;
|
|
1056
1431
|
}
|
|
1057
1432
|
let lastStatus = 'Last: Ready';
|
|
1058
1433
|
let status = await getEnvironmentStatus(cwd);
|
|
1059
1434
|
let serviceStatus = await getServiceRuntimeStatus(cwd, status);
|
|
1060
1435
|
await showStartup(argv, skipUpdateCheck, () => renderCockpitStatusLines(status, serviceStatus, lastStatus));
|
|
1436
|
+
const renderCockpitMenuShell = () => {
|
|
1437
|
+
clearCockpitScreen();
|
|
1438
|
+
console.log(renderBanner(renderCockpitStatusLines(status, serviceStatus, lastStatus), { version: startupVersionLine() }));
|
|
1439
|
+
console.log();
|
|
1440
|
+
};
|
|
1061
1441
|
while (true) {
|
|
1062
1442
|
try {
|
|
1063
1443
|
const command = await selectCockpitCommandFromMenu(serviceStatus);
|
|
@@ -1071,6 +1451,7 @@ export async function runCli(cliArgv = process.argv.slice(2), cwd = process.cwd(
|
|
|
1071
1451
|
}
|
|
1072
1452
|
catch (error) {
|
|
1073
1453
|
if (isMenuBackSignal(error)) {
|
|
1454
|
+
renderCockpitMenuShell();
|
|
1074
1455
|
continue;
|
|
1075
1456
|
}
|
|
1076
1457
|
commandFailed = true;
|
|
@@ -1084,9 +1465,7 @@ export async function runCli(cliArgv = process.argv.slice(2), cwd = process.cwd(
|
|
|
1084
1465
|
}
|
|
1085
1466
|
status = await getEnvironmentStatus(cwd);
|
|
1086
1467
|
serviceStatus = await getServiceRuntimeStatus(cwd, status);
|
|
1087
|
-
|
|
1088
|
-
console.log(renderBanner(renderCockpitStatusLines(status, serviceStatus, lastStatus), { version: startupVersionLine() }));
|
|
1089
|
-
console.log();
|
|
1468
|
+
renderCockpitMenuShell();
|
|
1090
1469
|
}
|
|
1091
1470
|
catch (error) {
|
|
1092
1471
|
if (isMenuBackSignal(error)) {
|
|
@@ -1214,7 +1593,10 @@ export async function runCli(cliArgv = process.argv.slice(2), cwd = process.cwd(
|
|
|
1214
1593
|
await finishCreateFlow({ kind: 'create', options }, cwd, false);
|
|
1215
1594
|
return;
|
|
1216
1595
|
}
|
|
1217
|
-
|
|
1596
|
+
if (!(await ensureSystemPrerequisites(true))) {
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
await finishCreateFlow(await optionsFromPrompts(), cwd, true, false);
|
|
1218
1600
|
}
|
|
1219
1601
|
export function isCliEntrypoint(metaUrl, argvPath = process.argv[1]) {
|
|
1220
1602
|
if (!argvPath)
|
|
@@ -34,10 +34,14 @@ export const cockpitCommands = [
|
|
|
34
34
|
dailyCommand('restart', 'services', 'Restart services', 'Restart the Odoo development services.', ['reload']),
|
|
35
35
|
dailyCommand('logs', 'services', 'View logs', 'Stream logs for an Odoo environment service.', ['log', 'tail']),
|
|
36
36
|
dailyCommand('shell', 'services', 'Open shell', 'Open a shell inside the Odoo service container.', ['bash', 'terminal']),
|
|
37
|
+
internalCommand('list-modules', 'modules', 'List modules', 'Browse detected Odoo modules by source category.', [
|
|
38
|
+
'modules list',
|
|
39
|
+
'browse modules',
|
|
40
|
+
]),
|
|
37
41
|
dailyCommand('install', 'modules', 'Install module', 'Install one or more Odoo modules into a database.', ['install module']),
|
|
38
42
|
dailyCommand('update', 'modules', 'Update module', 'Update one or more Odoo modules in a database.', ['upgrade']),
|
|
39
43
|
dailyCommand('test', 'modules', 'Run tests', 'Run Odoo tests for one or more modules.', ['tests', 'pytest']),
|
|
40
|
-
dailyCommand('lint', 'modules', 'Run lint', 'Run the configured
|
|
44
|
+
dailyCommand('lint', 'modules', 'Run environment lint', 'Run the configured environment lint checks.', ['check', 'quality']),
|
|
41
45
|
dailyCommand('pot', 'modules', 'Generate POT', 'Generate translation template files for a module.', ['translation', 'i18n']),
|
|
42
46
|
dailyCommand('psql', 'database', 'Open psql', 'Open a PostgreSQL prompt for an environment database.', ['postgres', 'sql']),
|
|
43
47
|
dailyCommand('snapshot', 'database', 'Create snapshot', 'Create a database snapshot.', ['backup', 'dump']),
|