@wpmoo/odoo 0.8.35 → 0.8.36
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 +19 -2
- package/dist/args.js +1 -0
- package/dist/cli.js +9 -0
- package/dist/daily-actions.js +51 -5
- package/dist/doctor.js +168 -0
- package/dist/help.js +12 -1
- package/dist/safe-reset.js +21 -1
- package/dist/templates.js +63 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -171,6 +171,12 @@ npx @wpmoo/odoo remove-module \
|
|
|
171
171
|
--module odoo_sample_module_base
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
+
Check that a generated environment is structurally ready:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx @wpmoo/odoo doctor
|
|
178
|
+
```
|
|
179
|
+
|
|
174
180
|
Refresh generated environment files without deleting module source code:
|
|
175
181
|
|
|
176
182
|
```bash
|
|
@@ -189,14 +195,25 @@ npx @wpmoo/odoo psql devel
|
|
|
189
195
|
npx @wpmoo/odoo install sale devel
|
|
190
196
|
npx @wpmoo/odoo update sale devel
|
|
191
197
|
npx @wpmoo/odoo test sale --db devel --mode update --tags /sale
|
|
198
|
+
npx @wpmoo/odoo resetdb devel sale
|
|
199
|
+
npx @wpmoo/odoo snapshot devel before-update
|
|
200
|
+
npx @wpmoo/odoo restore-snapshot before-update devel
|
|
201
|
+
npx @wpmoo/odoo lint
|
|
202
|
+
npx @wpmoo/odoo pot sale devel i18n/sale.pot
|
|
192
203
|
```
|
|
193
204
|
|
|
205
|
+
The doctor command must be run from a generated environment root containing
|
|
206
|
+
`.wpmoo/odoo.json`. It checks metadata, selected compose files, daily scripts,
|
|
207
|
+
source repo paths, `.env` ports, and Docker CLI access.
|
|
208
|
+
|
|
194
209
|
Daily actions require `.wpmoo/odoo.json` in the current directory and delegate to
|
|
195
210
|
fixed scripts under `./scripts`; they do not search parent directories or accept
|
|
196
211
|
arbitrary script names.
|
|
197
212
|
|
|
198
|
-
Generated environments also include a local `./moo` shortcut for
|
|
199
|
-
daily commands such as `./moo start`, `./moo restart`, and `./moo stop`.
|
|
213
|
+
Generated environments also include a local `./moo` shortcut for local compose
|
|
214
|
+
daily commands such as `./moo start`, `./moo restart`, and `./moo stop`. The
|
|
215
|
+
shortcut supports the same daily action arguments as `npx @wpmoo/odoo`. It also
|
|
216
|
+
falls back to `npx @wpmoo/odoo@latest doctor` for `./moo doctor`.
|
|
200
217
|
|
|
201
218
|
## Defaults
|
|
202
219
|
|
package/dist/args.js
CHANGED
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { detectDevelopmentEnvironment } from './environment.js';
|
|
|
6
6
|
import { commandOdooVersion } from './environment-version.js';
|
|
7
7
|
import { defaultAgentSkillsTemplateUrl } from './external-templates.js';
|
|
8
8
|
import { isDailyActionCommand, runDailyAction } from './daily-actions.js';
|
|
9
|
+
import { runDoctor } from './doctor.js';
|
|
9
10
|
import { getOriginUrl, realGit } from './git.js';
|
|
10
11
|
import { renderHelp } from './help.js';
|
|
11
12
|
import { addModuleToSourceRepo, listModulesInSourceRepo, removeModuleFromSourceRepo, } from './module-actions.js';
|
|
@@ -676,6 +677,14 @@ async function main() {
|
|
|
676
677
|
outro(`Safe reset refreshed generated environment files in ${options.target}.`);
|
|
677
678
|
return;
|
|
678
679
|
}
|
|
680
|
+
if (route.command === 'doctor') {
|
|
681
|
+
if (route.argv.length > 0) {
|
|
682
|
+
throw new Error('Usage: wpmoo doctor');
|
|
683
|
+
}
|
|
684
|
+
console.log(renderBanner());
|
|
685
|
+
console.log(await runDoctor(process.cwd()));
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
679
688
|
if (isDailyActionCommand(route.command)) {
|
|
680
689
|
console.log(renderBanner());
|
|
681
690
|
await runDailyAction(route.command, route.argv);
|
package/dist/daily-actions.js
CHANGED
|
@@ -2,9 +2,24 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { access } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { markerPath } from './environment.js';
|
|
5
|
-
export const dailyActionCommands = [
|
|
5
|
+
export const dailyActionCommands = [
|
|
6
|
+
'start',
|
|
7
|
+
'stop',
|
|
8
|
+
'logs',
|
|
9
|
+
'restart',
|
|
10
|
+
'shell',
|
|
11
|
+
'psql',
|
|
12
|
+
'install',
|
|
13
|
+
'update',
|
|
14
|
+
'test',
|
|
15
|
+
'resetdb',
|
|
16
|
+
'snapshot',
|
|
17
|
+
'restore-snapshot',
|
|
18
|
+
'lint',
|
|
19
|
+
'pot',
|
|
20
|
+
];
|
|
6
21
|
const dailyActionCommandSet = new Set(dailyActionCommands);
|
|
7
|
-
const
|
|
22
|
+
export const dailyActionScripts = {
|
|
8
23
|
start: 'up.sh',
|
|
9
24
|
stop: 'down.sh',
|
|
10
25
|
logs: 'logs.sh',
|
|
@@ -14,6 +29,11 @@ const scripts = {
|
|
|
14
29
|
install: 'install.sh',
|
|
15
30
|
update: 'update.sh',
|
|
16
31
|
test: 'test.sh',
|
|
32
|
+
resetdb: 'resetdb.sh',
|
|
33
|
+
snapshot: 'snapshot.sh',
|
|
34
|
+
'restore-snapshot': 'restore-snapshot.sh',
|
|
35
|
+
lint: 'lint.sh',
|
|
36
|
+
pot: 'pot.sh',
|
|
17
37
|
};
|
|
18
38
|
export function isDailyActionCommand(command) {
|
|
19
39
|
return dailyActionCommandSet.has(command);
|
|
@@ -35,7 +55,17 @@ function usage(command) {
|
|
|
35
55
|
return 'Usage: wpmoo install <module[,module]> [db]';
|
|
36
56
|
if (command === 'update')
|
|
37
57
|
return 'Usage: wpmoo update <module[,module]> [db]';
|
|
38
|
-
|
|
58
|
+
if (command === 'test')
|
|
59
|
+
return 'Usage: wpmoo test <module[,module]> [--db <db>] [--mode init|update] [--tags <tags>]';
|
|
60
|
+
if (command === 'resetdb')
|
|
61
|
+
return 'Usage: wpmoo resetdb [db] [module[,module]]';
|
|
62
|
+
if (command === 'snapshot')
|
|
63
|
+
return 'Usage: wpmoo snapshot [db] [snapshot-name]';
|
|
64
|
+
if (command === 'restore-snapshot')
|
|
65
|
+
return 'Usage: wpmoo restore-snapshot <snapshot-name> [db]';
|
|
66
|
+
if (command === 'lint')
|
|
67
|
+
return 'Usage: wpmoo lint';
|
|
68
|
+
return 'Usage: wpmoo pot <module[,module]> [db] [output]';
|
|
39
69
|
}
|
|
40
70
|
function ensureNoArgs(command, argv) {
|
|
41
71
|
if (argv.length > 0)
|
|
@@ -53,6 +83,12 @@ function moduleArgs(command, argv) {
|
|
|
53
83
|
throw new Error(usage(command));
|
|
54
84
|
return db ? [modules, db] : [modules];
|
|
55
85
|
}
|
|
86
|
+
function positionalArgs(command, argv, min, max) {
|
|
87
|
+
if (argv.length < min || argv.length > max || argv.some((arg) => arg.startsWith('-'))) {
|
|
88
|
+
throw new Error(usage(command));
|
|
89
|
+
}
|
|
90
|
+
return argv;
|
|
91
|
+
}
|
|
56
92
|
function testArgs(argv) {
|
|
57
93
|
const [modules, ...rest] = argv;
|
|
58
94
|
if (!modules || modules.startsWith('-'))
|
|
@@ -86,7 +122,17 @@ function scriptArgs(command, argv) {
|
|
|
86
122
|
return optionalSingleArg(command, argv, 'postgres');
|
|
87
123
|
if (command === 'install' || command === 'update')
|
|
88
124
|
return moduleArgs(command, argv);
|
|
89
|
-
|
|
125
|
+
if (command === 'test')
|
|
126
|
+
return testArgs(argv);
|
|
127
|
+
if (command === 'resetdb')
|
|
128
|
+
return positionalArgs(command, argv, 0, 2);
|
|
129
|
+
if (command === 'snapshot')
|
|
130
|
+
return positionalArgs(command, argv, 0, 2);
|
|
131
|
+
if (command === 'restore-snapshot')
|
|
132
|
+
return positionalArgs(command, argv, 1, 2);
|
|
133
|
+
if (command === 'lint')
|
|
134
|
+
return ensureNoArgs(command, argv);
|
|
135
|
+
return positionalArgs(command, argv, 1, 3);
|
|
90
136
|
}
|
|
91
137
|
async function assertEnvironmentRoot(cwd) {
|
|
92
138
|
try {
|
|
@@ -108,7 +154,7 @@ async function assertScriptExists(cwd, script) {
|
|
|
108
154
|
}
|
|
109
155
|
export async function dailyActionPlan(command, argv, cwd = process.cwd()) {
|
|
110
156
|
await assertEnvironmentRoot(cwd);
|
|
111
|
-
const scriptPath = await assertScriptExists(cwd,
|
|
157
|
+
const scriptPath = await assertScriptExists(cwd, dailyActionScripts[command]);
|
|
112
158
|
return {
|
|
113
159
|
cwd,
|
|
114
160
|
scriptPath,
|
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { access, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import { dailyActionScripts } from './daily-actions.js';
|
|
5
|
+
import { defaultOdooVersion, markerPath } from './environment.js';
|
|
6
|
+
const realCommandRunner = async (command, args, options) => {
|
|
7
|
+
const result = await execa(command, args, { cwd: options.cwd });
|
|
8
|
+
return { stdout: result.stdout, stderr: result.stderr };
|
|
9
|
+
};
|
|
10
|
+
async function exists(path) {
|
|
11
|
+
try {
|
|
12
|
+
await access(path);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function errorMessage(error) {
|
|
20
|
+
return error instanceof Error ? error.message : String(error);
|
|
21
|
+
}
|
|
22
|
+
function isRecord(value) {
|
|
23
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
24
|
+
}
|
|
25
|
+
function sourceReposFromMetadata(metadata) {
|
|
26
|
+
const sourceRepos = metadata.sourceRepos;
|
|
27
|
+
if (!Array.isArray(sourceRepos))
|
|
28
|
+
return [];
|
|
29
|
+
return sourceRepos.map((repo, index) => {
|
|
30
|
+
if (!isRecord(repo) || typeof repo.path !== 'string' || !repo.path.trim()) {
|
|
31
|
+
throw new Error(`Invalid sourceRepos entry in .wpmoo/odoo.json at index ${index}`);
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
url: typeof repo.url === 'string' ? repo.url : '',
|
|
35
|
+
path: repo.path.trim(),
|
|
36
|
+
addons: Array.isArray(repo.addons) ? repo.addons.filter((addon) => typeof addon === 'string') : [],
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async function readMetadata(target) {
|
|
41
|
+
let content;
|
|
42
|
+
try {
|
|
43
|
+
content = await readFile(join(target, markerPath), 'utf8');
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw new Error(`Missing metadata file: ${markerPath}`);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(content);
|
|
50
|
+
if (!isRecord(parsed)) {
|
|
51
|
+
throw new Error('metadata is not an object');
|
|
52
|
+
}
|
|
53
|
+
return parsed;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new Error(`Invalid metadata JSON in ${markerPath}: ${errorMessage(error)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function metadataString(metadata, key) {
|
|
60
|
+
const value = metadata[key];
|
|
61
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
62
|
+
}
|
|
63
|
+
function parseEnv(content) {
|
|
64
|
+
const values = new Map();
|
|
65
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
66
|
+
const line = rawLine.trim();
|
|
67
|
+
if (!line || line.startsWith('#'))
|
|
68
|
+
continue;
|
|
69
|
+
const separator = line.indexOf('=');
|
|
70
|
+
if (separator === -1)
|
|
71
|
+
continue;
|
|
72
|
+
const key = line.slice(0, separator).trim();
|
|
73
|
+
let value = line.slice(separator + 1).trim();
|
|
74
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
75
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
76
|
+
value = value.slice(1, -1);
|
|
77
|
+
}
|
|
78
|
+
values.set(key, value);
|
|
79
|
+
}
|
|
80
|
+
return values;
|
|
81
|
+
}
|
|
82
|
+
async function readEnv(target) {
|
|
83
|
+
const path = join(target, '.env');
|
|
84
|
+
if (!(await exists(path)))
|
|
85
|
+
return undefined;
|
|
86
|
+
return parseEnv(await readFile(path, 'utf8'));
|
|
87
|
+
}
|
|
88
|
+
function validatePort(name, env, errors) {
|
|
89
|
+
const value = env.get(name)?.trim() ?? '';
|
|
90
|
+
if (!/^\d+$/.test(value)) {
|
|
91
|
+
errors.push(`Invalid ${name} in .env: expected a non-empty numeric value`);
|
|
92
|
+
}
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
function renderFailure(errors) {
|
|
96
|
+
return ['WPMoo doctor failed:', ...errors.map((error) => `- ${error}`)].join('\n');
|
|
97
|
+
}
|
|
98
|
+
export async function runDoctor(target = process.cwd(), runner = realCommandRunner) {
|
|
99
|
+
const lines = ['WPMoo doctor'];
|
|
100
|
+
const errors = [];
|
|
101
|
+
const metadata = await readMetadata(target);
|
|
102
|
+
lines.push(`OK metadata ${markerPath}`);
|
|
103
|
+
const engine = metadataString(metadata, 'engine') ?? 'compose';
|
|
104
|
+
if (engine !== 'compose') {
|
|
105
|
+
errors.push(`Unsupported environment engine: ${engine}`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
lines.push('OK engine compose');
|
|
109
|
+
}
|
|
110
|
+
const odooVersion = metadataString(metadata, 'odooVersion') ?? defaultOdooVersion;
|
|
111
|
+
lines.push(`OK Odoo version ${odooVersion}`);
|
|
112
|
+
const env = await readEnv(target);
|
|
113
|
+
const composeVersions = new Set([odooVersion]);
|
|
114
|
+
const envOdooVersion = env?.get('ODOO_VERSION')?.trim();
|
|
115
|
+
if (envOdooVersion) {
|
|
116
|
+
composeVersions.add(envOdooVersion);
|
|
117
|
+
}
|
|
118
|
+
for (const version of composeVersions) {
|
|
119
|
+
const composeFile = `docker-compose_${version}.yml`;
|
|
120
|
+
if (await exists(join(target, composeFile))) {
|
|
121
|
+
lines.push(`OK compose ${composeFile}`);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
errors.push(`Missing compose file: ${composeFile}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const scriptNames = Object.values(dailyActionScripts);
|
|
128
|
+
const scriptErrorCount = errors.length;
|
|
129
|
+
for (const script of scriptNames) {
|
|
130
|
+
const relativePath = `scripts/${script}`;
|
|
131
|
+
if (!(await exists(join(target, relativePath)))) {
|
|
132
|
+
errors.push(`Missing daily action script: ${relativePath}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (errors.length === scriptErrorCount) {
|
|
136
|
+
lines.push(`OK scripts ${scriptNames.length} checked`);
|
|
137
|
+
}
|
|
138
|
+
const sourceRepos = sourceReposFromMetadata(metadata);
|
|
139
|
+
for (const repo of sourceRepos) {
|
|
140
|
+
const relativePath = `odoo/custom/src/private/${repo.path}`;
|
|
141
|
+
if (!(await exists(join(target, relativePath)))) {
|
|
142
|
+
errors.push(`Missing source repo path: ${relativePath}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
lines.push(`OK source repos ${sourceRepos.length} checked`);
|
|
146
|
+
if (env) {
|
|
147
|
+
const httpPort = validatePort('HTTP_PORT', env, errors);
|
|
148
|
+
const geventPort = validatePort('GEVENT_PORT', env, errors);
|
|
149
|
+
if (httpPort && geventPort && httpPort === geventPort) {
|
|
150
|
+
errors.push('HTTP_PORT and GEVENT_PORT in .env must not be equal');
|
|
151
|
+
}
|
|
152
|
+
if (/^\d+$/.test(httpPort) && /^\d+$/.test(geventPort) && httpPort !== geventPort) {
|
|
153
|
+
lines.push(`OK .env ports HTTP_PORT=${httpPort} GEVENT_PORT=${geventPort}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
await runner('docker', ['version'], { cwd: target });
|
|
158
|
+
lines.push('OK docker CLI');
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
errors.push(`Docker CLI check failed: ${errorMessage(error)}`);
|
|
162
|
+
}
|
|
163
|
+
if (errors.length > 0) {
|
|
164
|
+
throw new Error(renderFailure(errors));
|
|
165
|
+
}
|
|
166
|
+
lines.push('Doctor checks passed.');
|
|
167
|
+
return lines.join('\n');
|
|
168
|
+
}
|
package/dist/help.js
CHANGED
|
@@ -11,6 +11,7 @@ Usage:
|
|
|
11
11
|
npx @wpmoo/odoo add-module --repo <source-repo> --module <module-name>
|
|
12
12
|
npx @wpmoo/odoo remove-module --repo <source-repo> --module <module-name>
|
|
13
13
|
npx @wpmoo/odoo reset
|
|
14
|
+
npx @wpmoo/odoo doctor
|
|
14
15
|
npx @wpmoo/odoo start
|
|
15
16
|
npx @wpmoo/odoo stop
|
|
16
17
|
npx @wpmoo/odoo logs [service]
|
|
@@ -20,6 +21,11 @@ Usage:
|
|
|
20
21
|
npx @wpmoo/odoo install <module[,module]> [db]
|
|
21
22
|
npx @wpmoo/odoo update <module[,module]> [db]
|
|
22
23
|
npx @wpmoo/odoo test <module[,module]> [--db <db>] [--mode init|update] [--tags <tags>]
|
|
24
|
+
npx @wpmoo/odoo resetdb [db] [module[,module]]
|
|
25
|
+
npx @wpmoo/odoo snapshot [db] [snapshot-name]
|
|
26
|
+
npx @wpmoo/odoo restore-snapshot <snapshot-name> [db]
|
|
27
|
+
npx @wpmoo/odoo lint
|
|
28
|
+
npx @wpmoo/odoo pot <module[,module]> [db] [output]
|
|
23
29
|
|
|
24
30
|
Options:
|
|
25
31
|
--product <slug> Product slug, for example my_odoo_module.
|
|
@@ -57,7 +63,12 @@ Options:
|
|
|
57
63
|
Daily actions:
|
|
58
64
|
Daily actions must be run from a generated environment root containing .wpmoo/odoo.json.
|
|
59
65
|
They delegate to the fixed scripts copied from the compose resource under ./scripts.
|
|
60
|
-
Generated environments also include ./moo for
|
|
66
|
+
Generated environments also include ./moo for local compose commands such as ./moo start.
|
|
67
|
+
Use ./moo or npx @wpmoo/odoo with the same daily action arguments.
|
|
68
|
+
|
|
69
|
+
Doctor:
|
|
70
|
+
Run npx @wpmoo/odoo doctor from a generated environment root to check metadata,
|
|
71
|
+
compose files, daily scripts, source repo paths, .env ports, and Docker CLI access.
|
|
61
72
|
|
|
62
73
|
Example:
|
|
63
74
|
npx @wpmoo/odoo create \\
|
package/dist/safe-reset.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { basename, join } from 'node:path';
|
|
3
3
|
import { readEnvironmentMetadata } from './environment.js';
|
|
4
|
+
import { applyExternalAsset, writeTextFile } from './external-assets.js';
|
|
5
|
+
import { plannedExternalAssetOptions, renderComposeEnvExample } from './external-templates.js';
|
|
4
6
|
import { realGit, stageAll } from './git.js';
|
|
5
7
|
import { isValidPathSegment, validateAddonName, validateRepoPath } from './path-validation.js';
|
|
6
8
|
import { listModuleRepos, readAddonsYaml } from './repo-actions.js';
|
|
@@ -16,10 +18,12 @@ export function renderSafeResetPreview(target, stage) {
|
|
|
16
18
|
'- .wpmoo/odoo.json',
|
|
17
19
|
'- moo',
|
|
18
20
|
'- .gitignore',
|
|
21
|
+
'- .env.example',
|
|
19
22
|
'- README.md',
|
|
20
23
|
'- AGENTS.md',
|
|
21
24
|
'- docs/appstore-release.md',
|
|
22
|
-
'-
|
|
25
|
+
'- External compose template assets',
|
|
26
|
+
'- External agent skill assets when configured',
|
|
23
27
|
'',
|
|
24
28
|
'Will not touch:',
|
|
25
29
|
'- source repo folders under odoo/custom/src/private',
|
|
@@ -32,6 +36,17 @@ export function renderSafeResetPreview(target, stage) {
|
|
|
32
36
|
function titleFromTarget(target) {
|
|
33
37
|
return basename(target).replace(/_dev$/, '') || 'odoo_sample_module';
|
|
34
38
|
}
|
|
39
|
+
function safeResetExternalAssetOptions(options) {
|
|
40
|
+
return plannedExternalAssetOptions(options).map((assetOptions) => ({
|
|
41
|
+
...assetOptions,
|
|
42
|
+
exclude: [
|
|
43
|
+
...(assetOptions.exclude ?? []),
|
|
44
|
+
'.env',
|
|
45
|
+
'.gitmodules',
|
|
46
|
+
'odoo/custom/src/private',
|
|
47
|
+
],
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
35
50
|
function parseAddonsForRepo(addonsYaml, repoPath) {
|
|
36
51
|
const safeRepoPath = validateRepoPath(repoPath);
|
|
37
52
|
const lines = addonsYaml.split('\n');
|
|
@@ -112,6 +127,7 @@ async function inferOptions(target) {
|
|
|
112
127
|
export async function safeResetEnvironment(options, git = realGit) {
|
|
113
128
|
const scaffoldOptions = await inferOptions(options.target);
|
|
114
129
|
const files = generatedFiles(scaffoldOptions);
|
|
130
|
+
const externalAssets = safeResetExternalAssetOptions(scaffoldOptions);
|
|
115
131
|
for (const file of files) {
|
|
116
132
|
if (file.path === 'odoo/custom/src/addons.yaml') {
|
|
117
133
|
continue;
|
|
@@ -123,6 +139,10 @@ export async function safeResetEnvironment(options, git = realGit) {
|
|
|
123
139
|
await chmod(destination, file.mode);
|
|
124
140
|
}
|
|
125
141
|
}
|
|
142
|
+
for (const assetOptions of externalAssets) {
|
|
143
|
+
await applyExternalAsset(assetOptions, git);
|
|
144
|
+
}
|
|
145
|
+
await writeTextFile(join(options.target, '.env.example'), renderComposeEnvExample(scaffoldOptions));
|
|
126
146
|
if (options.stage) {
|
|
127
147
|
await stageAll(git, options.target);
|
|
128
148
|
}
|
package/dist/templates.js
CHANGED
|
@@ -130,6 +130,12 @@ cp .env.example .env
|
|
|
130
130
|
./moo logs
|
|
131
131
|
./moo shell
|
|
132
132
|
./moo stop
|
|
133
|
+
./moo doctor
|
|
134
|
+
./moo resetdb devel sale
|
|
135
|
+
./moo snapshot devel before-update
|
|
136
|
+
./moo restore-snapshot before-update devel
|
|
137
|
+
./moo lint
|
|
138
|
+
./moo pot sale devel i18n/sale.pot
|
|
133
139
|
\`\`\`
|
|
134
140
|
|
|
135
141
|
Run tests for one planned product addon:
|
|
@@ -238,6 +244,11 @@ usage() {
|
|
|
238
244
|
"install") echo "Usage: ./moo install <module[,module]> [db]" ;;
|
|
239
245
|
"update") echo "Usage: ./moo update <module[,module]> [db]" ;;
|
|
240
246
|
"test") echo "Usage: ./moo test <module[,module]> [--db <db>] [--mode init|update] [--tags <tags>]" ;;
|
|
247
|
+
"resetdb") echo "Usage: ./moo resetdb [db] [module[,module]]" ;;
|
|
248
|
+
"snapshot") echo "Usage: ./moo snapshot [db] [snapshot-name]" ;;
|
|
249
|
+
"restore-snapshot") echo "Usage: ./moo restore-snapshot <snapshot-name> [db]" ;;
|
|
250
|
+
"lint") echo "Usage: ./moo lint" ;;
|
|
251
|
+
"pot") echo "Usage: ./moo pot <module[,module]> [db] [output]" ;;
|
|
241
252
|
esac
|
|
242
253
|
}
|
|
243
254
|
|
|
@@ -272,6 +283,21 @@ require_module_args() {
|
|
|
272
283
|
fi
|
|
273
284
|
}
|
|
274
285
|
|
|
286
|
+
positional_args() {
|
|
287
|
+
local command="$1"
|
|
288
|
+
local min="$2"
|
|
289
|
+
local max="$3"
|
|
290
|
+
shift 3
|
|
291
|
+
if [[ "$#" -lt "$min" || "$#" -gt "$max" ]]; then
|
|
292
|
+
fail_usage "$command"
|
|
293
|
+
fi
|
|
294
|
+
for arg in "$@"; do
|
|
295
|
+
if [[ "$arg" == -* ]]; then
|
|
296
|
+
fail_usage "$command"
|
|
297
|
+
fi
|
|
298
|
+
done
|
|
299
|
+
}
|
|
300
|
+
|
|
275
301
|
validate_test_args() {
|
|
276
302
|
if [[ "$#" -lt 1 || "\${1:-}" == -* ]]; then
|
|
277
303
|
fail_usage "test"
|
|
@@ -363,6 +389,31 @@ case "$command" in
|
|
|
363
389
|
validate_test_args "$@"
|
|
364
390
|
run_script ./scripts/test.sh "$@"
|
|
365
391
|
;;
|
|
392
|
+
"resetdb")
|
|
393
|
+
shift
|
|
394
|
+
positional_args "$command" 0 2 "$@"
|
|
395
|
+
run_script ./scripts/resetdb.sh "$@"
|
|
396
|
+
;;
|
|
397
|
+
"snapshot")
|
|
398
|
+
shift
|
|
399
|
+
positional_args "$command" 0 2 "$@"
|
|
400
|
+
run_script ./scripts/snapshot.sh "$@"
|
|
401
|
+
;;
|
|
402
|
+
"restore-snapshot")
|
|
403
|
+
shift
|
|
404
|
+
positional_args "$command" 1 2 "$@"
|
|
405
|
+
run_script ./scripts/restore-snapshot.sh "$@"
|
|
406
|
+
;;
|
|
407
|
+
"lint")
|
|
408
|
+
shift
|
|
409
|
+
require_no_args "$command" "$@"
|
|
410
|
+
run_script ./scripts/lint.sh
|
|
411
|
+
;;
|
|
412
|
+
"pot")
|
|
413
|
+
shift
|
|
414
|
+
positional_args "$command" 1 3 "$@"
|
|
415
|
+
run_script ./scripts/pot.sh "$@"
|
|
416
|
+
;;
|
|
366
417
|
*)
|
|
367
418
|
exec npx --yes @wpmoo/odoo@latest "$@"
|
|
368
419
|
;;
|
|
@@ -439,6 +490,7 @@ root:
|
|
|
439
490
|
./moo start
|
|
440
491
|
./moo stop
|
|
441
492
|
./moo restart
|
|
493
|
+
./moo doctor
|
|
442
494
|
./moo add-module
|
|
443
495
|
\`\`\`
|
|
444
496
|
|
|
@@ -514,7 +566,17 @@ Use the environment's addon test/update command:
|
|
|
514
566
|
${verificationCommand(options)}
|
|
515
567
|
\`\`\`
|
|
516
568
|
|
|
517
|
-
|
|
569
|
+
Useful maintenance commands:
|
|
570
|
+
|
|
571
|
+
\`\`\`bash
|
|
572
|
+
./moo lint
|
|
573
|
+
./moo resetdb [db] [module[,module]]
|
|
574
|
+
./moo snapshot [db] [snapshot-name]
|
|
575
|
+
./moo restore-snapshot <snapshot-name> [db]
|
|
576
|
+
./moo pot <module[,module]> [db] [output]
|
|
577
|
+
\`\`\`
|
|
578
|
+
|
|
579
|
+
Only report completion after the relevant update/test/lint command exits cleanly.
|
|
518
580
|
`;
|
|
519
581
|
}
|
|
520
582
|
export function renderAppstoreRelease(options) {
|