@wpmoo/odoo 0.8.33 → 0.8.35

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 CHANGED
@@ -177,6 +177,27 @@ Refresh generated environment files without deleting module source code:
177
177
  npx @wpmoo/odoo reset
178
178
  ```
179
179
 
180
+ Run daily local development actions from a generated environment root:
181
+
182
+ ```bash
183
+ npx @wpmoo/odoo start
184
+ npx @wpmoo/odoo logs odoo
185
+ npx @wpmoo/odoo restart
186
+ npx @wpmoo/odoo stop
187
+ npx @wpmoo/odoo shell
188
+ npx @wpmoo/odoo psql devel
189
+ npx @wpmoo/odoo install sale devel
190
+ npx @wpmoo/odoo update sale devel
191
+ npx @wpmoo/odoo test sale --db devel --mode update --tags /sale
192
+ ```
193
+
194
+ Daily actions require `.wpmoo/odoo.json` in the current directory and delegate to
195
+ fixed scripts under `./scripts`; they do not search parent directories or accept
196
+ arbitrary script names.
197
+
198
+ Generated environments also include a local `./moo` shortcut for Doodba-style
199
+ daily commands such as `./moo start`, `./moo restart`, and `./moo stop`.
200
+
180
201
  ## Defaults
181
202
 
182
203
  Each source repo can contain one or many Odoo modules. For example:
@@ -235,4 +256,4 @@ If this project helps you, you can support the work here:
235
256
 
236
257
  <a href="https://www.buymeacoffee.com/cangir">
237
258
  <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me a Coffee" width="250">
238
- </a>
259
+ </a>
package/dist/args.js CHANGED
@@ -2,9 +2,18 @@ import { basename, resolve } from 'node:path';
2
2
  import { supportedOdooVersions } from './odoo-versions.js';
3
3
  import { defaultAgentSkillsTemplateUrl, defaultComposeTemplateUrl } from './external-templates.js';
4
4
  import { defaultCommunityAddons, defaultProAddons } from './templates.js';
5
+ import { dailyActionCommands } from './daily-actions.js';
5
6
  import { validateAddonName, validateRepoPath } from './path-validation.js';
6
7
  import { inferGitHubOwner, inferRepoPath, normalizeRepositoryUrl } from './repo-url.js';
7
- const commandNames = new Set(['create', 'add-repo', 'remove-repo', 'add-module', 'remove-module', 'reset']);
8
+ const commandNames = new Set([
9
+ 'create',
10
+ 'add-repo',
11
+ 'remove-repo',
12
+ 'add-module',
13
+ 'remove-module',
14
+ 'reset',
15
+ ...dailyActionCommands,
16
+ ]);
8
17
  const internalFlags = new Set(['--no-update-check']);
9
18
  export function isUpdateCheckFlag(arg) {
10
19
  return internalFlags.has(arg);
package/dist/cli.js CHANGED
@@ -5,6 +5,7 @@ import { commandFromArgs, defaultTargetForProduct, isHelpRequested, isVersionReq
5
5
  import { detectDevelopmentEnvironment } from './environment.js';
6
6
  import { commandOdooVersion } from './environment-version.js';
7
7
  import { defaultAgentSkillsTemplateUrl } from './external-templates.js';
8
+ import { isDailyActionCommand, runDailyAction } from './daily-actions.js';
8
9
  import { getOriginUrl, realGit } from './git.js';
9
10
  import { renderHelp } from './help.js';
10
11
  import { addModuleToSourceRepo, listModulesInSourceRepo, removeModuleFromSourceRepo, } from './module-actions.js';
@@ -675,6 +676,11 @@ async function main() {
675
676
  outro(`Safe reset refreshed generated environment files in ${options.target}.`);
676
677
  return;
677
678
  }
679
+ if (isDailyActionCommand(route.command)) {
680
+ console.log(renderBanner());
681
+ await runDailyAction(route.command, route.argv);
682
+ return;
683
+ }
678
684
  const options = optionsFromArgs(route.argv);
679
685
  if (options) {
680
686
  console.log(renderBanner());
@@ -0,0 +1,133 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { access } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { markerPath } from './environment.js';
5
+ export const dailyActionCommands = ['start', 'stop', 'logs', 'restart', 'shell', 'psql', 'install', 'update', 'test'];
6
+ const dailyActionCommandSet = new Set(dailyActionCommands);
7
+ const scripts = {
8
+ start: 'up.sh',
9
+ stop: 'down.sh',
10
+ logs: 'logs.sh',
11
+ restart: 'restart.sh',
12
+ shell: 'shell.sh',
13
+ psql: 'psql.sh',
14
+ install: 'install.sh',
15
+ update: 'update.sh',
16
+ test: 'test.sh',
17
+ };
18
+ export function isDailyActionCommand(command) {
19
+ return dailyActionCommandSet.has(command);
20
+ }
21
+ function usage(command) {
22
+ if (command === 'start')
23
+ return 'Usage: wpmoo start';
24
+ if (command === 'stop')
25
+ return 'Usage: wpmoo stop';
26
+ if (command === 'logs')
27
+ return 'Usage: wpmoo logs [service]';
28
+ if (command === 'restart')
29
+ return 'Usage: wpmoo restart';
30
+ if (command === 'shell')
31
+ return 'Usage: wpmoo shell';
32
+ if (command === 'psql')
33
+ return 'Usage: wpmoo psql [db]';
34
+ if (command === 'install')
35
+ return 'Usage: wpmoo install <module[,module]> [db]';
36
+ if (command === 'update')
37
+ return 'Usage: wpmoo update <module[,module]> [db]';
38
+ return 'Usage: wpmoo test <module[,module]> [--db <db>] [--mode init|update] [--tags <tags>]';
39
+ }
40
+ function ensureNoArgs(command, argv) {
41
+ if (argv.length > 0)
42
+ throw new Error(usage(command));
43
+ return [];
44
+ }
45
+ function optionalSingleArg(command, argv, fallback) {
46
+ if (argv.length > 1)
47
+ throw new Error(usage(command));
48
+ return [argv[0] ?? fallback];
49
+ }
50
+ function moduleArgs(command, argv) {
51
+ const [modules, db, ...rest] = argv;
52
+ if (!modules || modules.startsWith('-') || rest.length > 0)
53
+ throw new Error(usage(command));
54
+ return db ? [modules, db] : [modules];
55
+ }
56
+ function testArgs(argv) {
57
+ const [modules, ...rest] = argv;
58
+ if (!modules || modules.startsWith('-'))
59
+ throw new Error(usage('test'));
60
+ for (let index = 0; index < rest.length; index += 1) {
61
+ const option = rest[index];
62
+ if (!['--db', '--mode', '--tags'].includes(option))
63
+ throw new Error(`Unknown option for wpmoo test: ${option}`);
64
+ const value = rest[index + 1];
65
+ if (!value || value.startsWith('--'))
66
+ throw new Error(`Missing value for ${option}`);
67
+ if (option === '--mode' && value !== 'init' && value !== 'update') {
68
+ throw new Error('Invalid value for --mode: expected init or update');
69
+ }
70
+ index += 1;
71
+ }
72
+ return argv;
73
+ }
74
+ function scriptArgs(command, argv) {
75
+ if (command === 'start')
76
+ return ensureNoArgs(command, argv);
77
+ if (command === 'stop')
78
+ return ensureNoArgs(command, argv);
79
+ if (command === 'logs')
80
+ return optionalSingleArg(command, argv, 'odoo');
81
+ if (command === 'restart')
82
+ return ensureNoArgs(command, argv);
83
+ if (command === 'shell')
84
+ return ensureNoArgs(command, argv);
85
+ if (command === 'psql')
86
+ return optionalSingleArg(command, argv, 'postgres');
87
+ if (command === 'install' || command === 'update')
88
+ return moduleArgs(command, argv);
89
+ return testArgs(argv);
90
+ }
91
+ async function assertEnvironmentRoot(cwd) {
92
+ try {
93
+ await access(join(cwd, markerPath));
94
+ }
95
+ catch {
96
+ throw new Error('Daily actions must be run from a WPMoo Odoo environment root containing .wpmoo/odoo.json.');
97
+ }
98
+ }
99
+ async function assertScriptExists(cwd, script) {
100
+ const scriptPath = join(cwd, 'scripts', script);
101
+ try {
102
+ await access(scriptPath);
103
+ }
104
+ catch {
105
+ throw new Error(`Missing daily action script: scripts/${script}`);
106
+ }
107
+ return scriptPath;
108
+ }
109
+ export async function dailyActionPlan(command, argv, cwd = process.cwd()) {
110
+ await assertEnvironmentRoot(cwd);
111
+ const scriptPath = await assertScriptExists(cwd, scripts[command]);
112
+ return {
113
+ cwd,
114
+ scriptPath,
115
+ args: scriptArgs(command, argv),
116
+ };
117
+ }
118
+ async function spawnDailyAction(plan) {
119
+ const child = spawn(plan.scriptPath, plan.args, {
120
+ cwd: plan.cwd,
121
+ stdio: 'inherit',
122
+ });
123
+ const exitCode = await new Promise((resolve, reject) => {
124
+ child.on('error', reject);
125
+ child.on('close', resolve);
126
+ });
127
+ if (exitCode !== 0) {
128
+ throw new Error(`Daily action script exited with code ${exitCode ?? 'unknown'}: ${plan.scriptPath}`);
129
+ }
130
+ }
131
+ export async function runDailyAction(command, argv, cwd = process.cwd(), runner = spawnDailyAction) {
132
+ await runner(await dailyActionPlan(command, argv, cwd));
133
+ }
package/dist/help.js CHANGED
@@ -11,6 +11,15 @@ 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 start
15
+ npx @wpmoo/odoo stop
16
+ npx @wpmoo/odoo logs [service]
17
+ npx @wpmoo/odoo restart
18
+ npx @wpmoo/odoo shell
19
+ npx @wpmoo/odoo psql [db]
20
+ npx @wpmoo/odoo install <module[,module]> [db]
21
+ npx @wpmoo/odoo update <module[,module]> [db]
22
+ npx @wpmoo/odoo test <module[,module]> [--db <db>] [--mode init|update] [--tags <tags>]
14
23
 
15
24
  Options:
16
25
  --product <slug> Product slug, for example my_odoo_module.
@@ -45,6 +54,11 @@ Options:
45
54
  --version, -v Show the package version.
46
55
  --help, -h Show this help.
47
56
 
57
+ Daily actions:
58
+ Daily actions must be run from a generated environment root containing .wpmoo/odoo.json.
59
+ They delegate to the fixed scripts copied from the compose resource under ./scripts.
60
+ Generated environments also include ./moo for Doodba-style local commands such as ./moo start.
61
+
48
62
  Example:
49
63
  npx @wpmoo/odoo create \\
50
64
  --product odoo_sample_module \\
package/dist/templates.js CHANGED
@@ -22,6 +22,7 @@ function repositoryLayout(options) {
22
22
  ├── docker-compose_17.0.yml
23
23
  ├── docker-compose_18.0.yml
24
24
  ├── docker-compose_19.0.yml
25
+ ├── moo
25
26
  ├── scripts/
26
27
  ├── etc/
27
28
  ├── odoo/
@@ -100,7 +101,7 @@ function repoDuplicationNote() {
100
101
  }
101
102
  function verificationCommand(options) {
102
103
  const firstAddon = allAddons(options)[0] ?? options.product;
103
- return `./scripts/test.sh ${firstAddon}`;
104
+ return `./moo test ${firstAddon}`;
104
105
  }
105
106
  function environmentUsageDocs(options) {
106
107
  return `## Docker Compose Notes
@@ -125,16 +126,16 @@ Source repositories stay under \`odoo/custom/src/private\`. At container startup
125
126
 
126
127
  \`\`\`bash
127
128
  cp .env.example .env
128
- ./scripts/up.sh
129
- ./scripts/logs.sh
130
- ./scripts/shell.sh
131
- ./scripts/down.sh
129
+ ./moo start
130
+ ./moo logs
131
+ ./moo shell
132
+ ./moo stop
132
133
  \`\`\`
133
134
 
134
135
  Run tests for one planned product addon:
135
136
 
136
137
  \`\`\`bash
137
- ./scripts/test.sh ${allAddons(options)[0] ?? options.product}
138
+ ./moo test ${allAddons(options)[0] ?? options.product}
138
139
  \`\`\`
139
140
  `;
140
141
  }
@@ -226,7 +227,146 @@ set -euo pipefail
226
227
  script_dir="$(cd -- "$(dirname -- "\${BASH_SOURCE[0]}")" && pwd)"
227
228
  cd "$script_dir"
228
229
 
229
- exec npx --yes @wpmoo/odoo@latest "$@"
230
+ usage() {
231
+ case "$1" in
232
+ "start") echo "Usage: ./moo start" ;;
233
+ "stop") echo "Usage: ./moo stop" ;;
234
+ "logs") echo "Usage: ./moo logs [service]" ;;
235
+ "restart") echo "Usage: ./moo restart" ;;
236
+ "shell") echo "Usage: ./moo shell" ;;
237
+ "psql") echo "Usage: ./moo psql [db]" ;;
238
+ "install") echo "Usage: ./moo install <module[,module]> [db]" ;;
239
+ "update") echo "Usage: ./moo update <module[,module]> [db]" ;;
240
+ "test") echo "Usage: ./moo test <module[,module]> [--db <db>] [--mode init|update] [--tags <tags>]" ;;
241
+ esac
242
+ }
243
+
244
+ fail_usage() {
245
+ usage "$1" >&2
246
+ exit 2
247
+ }
248
+
249
+ require_no_args() {
250
+ local command="$1"
251
+ shift
252
+ if [[ "$#" -ne 0 ]]; then
253
+ fail_usage "$command"
254
+ fi
255
+ }
256
+
257
+ optional_single_arg() {
258
+ local command="$1"
259
+ local fallback="$2"
260
+ shift 2
261
+ if [[ "$#" -gt 1 ]]; then
262
+ fail_usage "$command"
263
+ fi
264
+ printf '%s\\n' "\${1:-$fallback}"
265
+ }
266
+
267
+ require_module_args() {
268
+ local command="$1"
269
+ shift
270
+ if [[ "$#" -lt 1 || "\${1:-}" == -* || "$#" -gt 2 ]]; then
271
+ fail_usage "$command"
272
+ fi
273
+ }
274
+
275
+ validate_test_args() {
276
+ if [[ "$#" -lt 1 || "\${1:-}" == -* ]]; then
277
+ fail_usage "test"
278
+ fi
279
+
280
+ shift
281
+ while [[ "$#" -gt 0 ]]; do
282
+ case "$1" in
283
+ "--db"|"--tags")
284
+ if [[ "$#" -lt 2 || "\${2:-}" == --* ]]; then
285
+ echo "Missing value for $1" >&2
286
+ exit 2
287
+ fi
288
+ shift 2
289
+ ;;
290
+ "--mode")
291
+ if [[ "$#" -lt 2 || "\${2:-}" == --* ]]; then
292
+ echo "Missing value for --mode" >&2
293
+ exit 2
294
+ fi
295
+ if [[ "$2" != "init" && "$2" != "update" ]]; then
296
+ echo "Invalid value for --mode: expected init or update" >&2
297
+ exit 2
298
+ fi
299
+ shift 2
300
+ ;;
301
+ *)
302
+ echo "Unknown option for ./moo test: $1" >&2
303
+ exit 2
304
+ ;;
305
+ esac
306
+ done
307
+ }
308
+
309
+ run_script() {
310
+ local script="$1"
311
+ shift
312
+ if [[ ! -x "$script" ]]; then
313
+ echo "Missing daily action script: \${script#./}" >&2
314
+ exit 1
315
+ fi
316
+ exec "$script" "$@"
317
+ }
318
+
319
+ command="\${1:-}"
320
+ case "$command" in
321
+ "start")
322
+ shift
323
+ require_no_args "$command" "$@"
324
+ run_script ./scripts/up.sh
325
+ ;;
326
+ "stop")
327
+ shift
328
+ require_no_args "$command" "$@"
329
+ run_script ./scripts/down.sh
330
+ ;;
331
+ "logs")
332
+ shift
333
+ service="$(optional_single_arg "$command" "odoo" "$@")"
334
+ run_script ./scripts/logs.sh "$service"
335
+ ;;
336
+ "restart")
337
+ shift
338
+ require_no_args "$command" "$@"
339
+ run_script ./scripts/restart.sh
340
+ ;;
341
+ "shell")
342
+ shift
343
+ require_no_args "$command" "$@"
344
+ run_script ./scripts/shell.sh
345
+ ;;
346
+ "psql")
347
+ shift
348
+ db="$(optional_single_arg "$command" "postgres" "$@")"
349
+ run_script ./scripts/psql.sh "$db"
350
+ ;;
351
+ "install")
352
+ shift
353
+ require_module_args "$command" "$@"
354
+ run_script ./scripts/install.sh "$@"
355
+ ;;
356
+ "update")
357
+ shift
358
+ require_module_args "$command" "$@"
359
+ run_script ./scripts/update.sh "$@"
360
+ ;;
361
+ "test")
362
+ shift
363
+ validate_test_args "$@"
364
+ run_script ./scripts/test.sh "$@"
365
+ ;;
366
+ *)
367
+ exec npx --yes @wpmoo/odoo@latest "$@"
368
+ ;;
369
+ esac
230
370
  `;
231
371
  }
232
372
  export function renderAddonsYaml(options) {
@@ -291,16 +431,19 @@ git submodule update --init --recursive
291
431
 
292
432
  ## WPMoo CLI Shortcut
293
433
 
294
- This environment includes a local \`moo\` delegation script. From the repository
434
+ This environment includes a local \`moo\` shortcut script. From the repository
295
435
  root:
296
436
 
297
437
  \`\`\`bash
298
438
  ./moo
439
+ ./moo start
440
+ ./moo stop
441
+ ./moo restart
299
442
  ./moo add-module
300
443
  \`\`\`
301
444
 
302
- If this repository root is on your \`PATH\`, you can run \`moo ...\` from
303
- anywhere and the script will delegate back to this environment.
445
+ Optionally, if this repository root is on your \`PATH\`, you can run \`moo ...\`
446
+ from anywhere and the script will return to this environment root first.
304
447
  ${optionalAgentSkillsReadme(options)}
305
448
  ## Source Repositories
306
449
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wpmoo/odoo",
3
- "version": "0.8.33",
3
+ "version": "0.8.35",
4
4
  "description": "WPMoo Odoo lifecycle tooling for development, staging, and production workflows.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -12,6 +12,19 @@
12
12
  "url": "https://github.com/wpmoo-org/wpmoo-odoo/issues"
13
13
  },
14
14
  "readmeFilename": "README.md",
15
+ "fundingUrl": "https://buymeacoffee.com/cangir",
16
+ "keywords": [
17
+ "odoo",
18
+ "odoo development",
19
+ "odoo cli",
20
+ "odoo lifecycle",
21
+ "odoo dev workflow",
22
+ "odoo staging workflow",
23
+ "odoo production workflow",
24
+ "odoo docker",
25
+ "odoo docker compose",
26
+ "odoo skills"
27
+ ],
15
28
  "bin": {
16
29
  "wpmoo": "dist/cli.js"
17
30
  },