@wpmoo/odoo 0.8.68 → 0.9.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.
Files changed (46) hide show
  1. package/bin/wpmoo.js +10 -0
  2. package/package.json +11 -44
  3. package/LICENSE +0 -22
  4. package/README.md +0 -510
  5. package/dist/addons-yaml.js +0 -59
  6. package/dist/args.js +0 -259
  7. package/dist/cli.js +0 -1002
  8. package/dist/cockpit/command-palette.js +0 -19
  9. package/dist/cockpit/command-registry.js +0 -91
  10. package/dist/cockpit/daily-prompts.js +0 -177
  11. package/dist/cockpit/menu.js +0 -88
  12. package/dist/cockpit/safety.js +0 -22
  13. package/dist/compose-layout.js +0 -118
  14. package/dist/daily-actions.js +0 -190
  15. package/dist/doctor.js +0 -519
  16. package/dist/environment-context.js +0 -10
  17. package/dist/environment-version.js +0 -5
  18. package/dist/environment.js +0 -136
  19. package/dist/external-assets.js +0 -153
  20. package/dist/external-templates.js +0 -86
  21. package/dist/git.js +0 -98
  22. package/dist/github.js +0 -87
  23. package/dist/help.js +0 -151
  24. package/dist/menu-navigation.js +0 -67
  25. package/dist/module-actions.js +0 -114
  26. package/dist/odoo-versions.js +0 -1
  27. package/dist/path-validation.js +0 -50
  28. package/dist/prompt-copy.js +0 -8
  29. package/dist/prompt-repositories.js +0 -34
  30. package/dist/repo-actions.js +0 -158
  31. package/dist/repo-url.js +0 -27
  32. package/dist/repository-preflight.js +0 -46
  33. package/dist/safe-reset.js +0 -217
  34. package/dist/scaffold.js +0 -161
  35. package/dist/source-actions.js +0 -65
  36. package/dist/source-manifest.js +0 -338
  37. package/dist/status.js +0 -239
  38. package/dist/templates.js +0 -754
  39. package/dist/types.js +0 -1
  40. package/dist/update-check.js +0 -106
  41. package/dist/version.js +0 -19
  42. package/docs/assets/patreon-donate.png +0 -0
  43. package/docs/assets/wpmoo-banner.png +0 -0
  44. package/docs/external-resources.md +0 -136
  45. package/docs/generated-environment-verification.md +0 -140
  46. package/docs/handoff.md +0 -29
package/dist/templates.js DELETED
@@ -1,754 +0,0 @@
1
- import { packageName, packageVersion } from './version.js';
2
- function fallbackPackageSpec() {
3
- return `${packageName()}@${packageVersion()}`;
4
- }
5
- export function defaultCommunityAddons(product) {
6
- return [product];
7
- }
8
- export function defaultProAddons(product) {
9
- return [`${product}_pro`];
10
- }
11
- function titleizeProduct(product) {
12
- return product
13
- .split(/[_-]+/)
14
- .filter(Boolean)
15
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
16
- .join(' ');
17
- }
18
- function yamlList(items) {
19
- return items.map((item) => ` - ${item}`).join('\n');
20
- }
21
- function allAddons(options) {
22
- return options.sourceRepos.flatMap((repo) => repo.addons);
23
- }
24
- function hasSourceRepos(options) {
25
- return options.sourceRepos.length > 0;
26
- }
27
- function sourceTypeOf(repo) {
28
- return repo.sourceType ?? 'private';
29
- }
30
- function sourceRepoRelativePath(repo) {
31
- return `odoo/custom/src/${sourceTypeOf(repo)}/${repo.path}`;
32
- }
33
- function repositoryLayout(options) {
34
- const sourceRepoRows = hasSourceRepos(options)
35
- ? options.sourceRepos
36
- .map((repo, index) => {
37
- const connector = index === options.sourceRepos.length - 1 ? '└──' : '├──';
38
- return `│ │ │ ${connector} ${repo.path}/ # Project-owned addon source repository`;
39
- })
40
- .join('\n')
41
- : '│ │ │ └── (add project-owned repos with ./moo add-repo)';
42
- return `${options.devRepo}/ # Development environment root
43
- ├── compose.yaml # Base Docker Compose file
44
- ├── compose/ # Compose overlays for each workflow
45
- │ ├── dev.yaml # Local development services
46
- │ ├── debug.yaml # Debug tooling and debug-friendly settings
47
- │ ├── test.yaml # Test runner services and test database setup
48
- │ ├── stage.yaml # Staging-like deployment overlay
49
- │ ├── prod.yaml # Production deployment overlay
50
- │ ├── proxy.yaml # Reverse proxy / edge routing overlay
51
- │ └── tools.yaml # Optional maintenance and helper tools
52
- ├── config/ # Runtime configuration mounted into containers
53
- │ ├── odoo/ # Odoo server configuration
54
- │ │ ├── odoo.conf # Main Odoo configuration file
55
- │ │ └── requirements.txt # Extra Python dependencies for the Odoo container
56
- │ └── logrotate/ # Log rotation configuration
57
- │ └── odoo # Logrotate rules for Odoo logs
58
- ├── resources/ # Container-side helper resources
59
- │ └── odoo/ # Resources specific to the Odoo service
60
- │ └── entrypoint.sh # Container startup script that discovers addons
61
- ├── moo # Local command hub shortcut
62
- ├── scripts/ # Shell scripts used by the local command hub
63
- ├── odoo/ # Odoo workspace data and custom source tree
64
- │ └── custom/ # Custom addon layer for this environment
65
- │ ├── src/ # Source repository checkout root
66
- │ │ ├── private/ # Project-owned/private addon repositories
67
- ${sourceRepoRows}
68
- │ │ ├── oca/ # OCA addon repositories
69
- │ │ └── external/ # Non-OCA third-party addon repositories
70
- │ ├── patches/ # Local patches for upstream repositories
71
- │ └── manifests/ # Source manifests, locks, and pinned revisions
72
- ├── docs/ # Project-specific documentation
73
- │ ├── appstore-release.md # Odoo App Store release checklist and notes
74
- │ └── compose.md # Compose layout and operations reference
75
- ├── .env.example # Template for local environment variables
76
- ├── README.md # This environment overview
77
- └── AGENTS.md # Agent instructions for this environment`;
78
- }
79
- function sourceRepoDocs(options) {
80
- if (!hasSourceRepos(options)) {
81
- return `This environment was scaffolded without source repository submodules.
82
- Add source repositories later from the cockpit or with \`npx @wpmoo/odoo add-repo\`.
83
- They can be organized under:
84
-
85
- \`odoo/custom/src/private\` for project-owned/private addon repositories,
86
- \`odoo/custom/src/oca\` for OCA repositories, and
87
- \`odoo/custom/src/external\` for non-OCA third-party repositories.
88
-
89
- Pinned external manifests and local patches should live under
90
- \`odoo/custom/manifests\` and \`odoo/custom/patches\` respectively.`;
91
- }
92
- return options.sourceRepos
93
- .map((repo) => `### ${repo.path}
94
-
95
- URL:
96
-
97
- \`\`\`text
98
- ${repo.url}
99
- \`\`\`
100
-
101
- Submodule path:
102
-
103
- \`\`\`text
104
- ${sourceRepoRelativePath(repo)}
105
- \`\`\`
106
-
107
- Source manifest entry:
108
-
109
- \`\`\`text
110
- odoo/custom/manifests/sources.yaml
111
- \`\`\`
112
-
113
- Expected addon layout:
114
-
115
- \`\`\`text
116
- ${repo.path}/
117
- ${repo.addons.map((addon) => `├── ${addon}/`).join('\n')}
118
- \`\`\``)
119
- .join('\n\n');
120
- }
121
- function cloneDocs(options) {
122
- if (!hasSourceRepos(options)) {
123
- return `## Local Folder
124
-
125
- This environment is ready in this folder:
126
-
127
- \`\`\`bash
128
- cd ${options.devRepo}
129
- \`\`\`
130
-
131
- If you later connect it to Git, commit the generated files after reviewing them.
132
- `;
133
- }
134
- return `## Clone
135
-
136
- Clone with submodules:
137
-
138
- \`\`\`bash
139
- git clone --recurse-submodules ${options.devRepoUrl}
140
- cd ${options.devRepo}
141
- \`\`\`
142
-
143
- If already cloned:
144
-
145
- \`\`\`bash
146
- git submodule update --init --recursive
147
- \`\`\`
148
- `;
149
- }
150
- function optionalAgentSkillsReadme(options) {
151
- if (!options.agentSkillsTemplateUrl)
152
- return '';
153
- return `
154
- ## Agent Skills
155
-
156
- This environment is configured to install project-local Agent Skills from:
157
-
158
- \`\`\`text
159
- ${options.agentSkillsTemplateUrl}${options.agentSkillsTemplateRef ? `#${options.agentSkillsTemplateRef}` : ''}
160
- \`\`\`
161
-
162
- After external resource installation, skills normally live under:
163
-
164
- \`\`\`text
165
- .agents/skills/
166
- \`\`\`
167
-
168
- Agents that support the Agent Skills standard can load them on demand.
169
- `;
170
- }
171
- function optionalAgentSkillsAgentsSection(options) {
172
- if (!options.agentSkillsTemplateUrl)
173
- return '';
174
- return `
175
- ## Active Agent Skills
176
-
177
- When using an agent that supports Agent Skills, prefer the project-local skills
178
- installed under \`.agents/skills/\`. They are sourced from:
179
-
180
- \`\`\`text
181
- ${options.agentSkillsTemplateUrl}${options.agentSkillsTemplateRef ? `#${options.agentSkillsTemplateRef}` : ''}
182
- \`\`\`
183
- `;
184
- }
185
- function environmentKind() {
186
- return 'Docker Compose';
187
- }
188
- function repoDuplicationNote() {
189
- return 'Keep source repositories under the relevant source directory (`private`, `oca`, or `external`); the Compose entrypoint exposes discovered addons through `/mnt/wpmoo-addons`.';
190
- }
191
- function verificationCommand(options) {
192
- const firstAddon = allAddons(options)[0] ?? options.product;
193
- return `./moo test ${firstAddon}`;
194
- }
195
- function environmentUsageDocs(options) {
196
- return `## Docker Compose Notes
197
-
198
- This environment uses the compact WPMoo Compose layout:
199
-
200
- \`\`\`text
201
- compose.yaml
202
- compose/dev.yaml
203
- compose/stage.yaml
204
- compose/prod.yaml
205
- config/odoo/odoo.conf
206
- resources/odoo/entrypoint.sh
207
- \`\`\`
208
-
209
- Development uses compose.yaml plus compose/dev.yaml by default.
210
- Set WPMOO_ENV=stage or WPMOO_ENV=prod only after providing production-grade secrets and volumes.
211
-
212
- If copied from the standalone resource, additional compose notes are in
213
- \`docs/compose.md\`.
214
-
215
- Source repositories stay under \`odoo/custom/src/{private,oca,external}\` when
216
- configured. At
217
- container startup, \`entrypoint.sh\` scans those repositories for addons and
218
- exposes them through \`/mnt/wpmoo-addons\`.
219
-
220
- ## Daily Command Hub (\`./moo\`)
221
-
222
- \`./moo\` routes day-to-day service and module workflows to local scripts in
223
- \`./scripts/\` (for example \`start\`, \`logs\`, \`update\`, \`test\`, \`snapshot\`).
224
- \`./moo status\` and \`./moo doctor\` are package fallback commands that run via
225
- \`npx --yes ${fallbackPackageSpec()} ...\`.
226
-
227
- ### Start And Inspect Services
228
-
229
- \`\`\`bash
230
- cp .env.example .env
231
- ./moo start
232
- ./moo logs odoo
233
- ./moo shell
234
- ./moo psql postgres
235
- ./moo stop
236
- \`\`\`
237
-
238
- ### Run, Update, And Test Modules
239
-
240
- \`\`\`bash
241
- ./moo install ${allAddons(options)[0] ?? options.product}
242
- ./moo update ${allAddons(options)[0] ?? options.product}
243
- ./moo test ${allAddons(options)[0] ?? options.product}
244
- \`\`\`
245
-
246
- ### Snapshot And Restore
247
-
248
- \`\`\`bash
249
- ./moo snapshot devel before-update
250
- ./moo restore-snapshot --dry-run before-update devel
251
- ./moo restore-snapshot before-update devel
252
- \`\`\`
253
-
254
- ### Lint
255
-
256
- \`\`\`bash
257
- ./moo lint
258
- \`\`\`
259
-
260
- ### Export Translations
261
-
262
- \`\`\`bash
263
- ./moo pot ${allAddons(options)[0] ?? options.product} devel i18n/${allAddons(options)[0] ?? options.product}.pot
264
- \`\`\`
265
-
266
- ### Recover / Reset
267
-
268
- \`\`\`bash
269
- ./moo doctor
270
- ./moo status
271
- ./moo resetdb devel ${allAddons(options)[0] ?? options.product}
272
- \`\`\`
273
- `;
274
- }
275
- const BANNER_GRADIENT_START = [31, 151, 231];
276
- const BANNER_GRADIENT_END = [209, 95, 127];
277
- const ANSI_BOLD = '\u001B[1m';
278
- const ANSI_RESET = '\u001B[0m';
279
- function gradientColor(column, width) {
280
- const ratio = width <= 1 ? 0 : column / (width - 1);
281
- const [startR, startG, startB] = BANNER_GRADIENT_START;
282
- const [endR, endG, endB] = BANNER_GRADIENT_END;
283
- const r = Math.round(startR + (endR - startR) * ratio);
284
- const g = Math.round(startG + (endG - startG) * ratio);
285
- const b = Math.round(startB + (endB - startB) * ratio);
286
- return `\u001B[38;2;${r};${g};${b}m`;
287
- }
288
- function applyBannerGradient(banner) {
289
- const lines = banner.split('\n');
290
- const width = Math.max(...lines.map((line) => line.length));
291
- return lines
292
- .map((line) => Array.from(line)
293
- .map((character, column) => `${gradientColor(column, width)}${character}`)
294
- .join(''))
295
- .join('\n');
296
- }
297
- export function renderBanner() {
298
- const banner = String.raw `
299
-
300
- ░██ ░██ ░█████████ ░███ ░███
301
- ░██ ░██ ░██ ░██ ░████ ░████
302
- ░██ ░██ ░██ ░██ ░██ ░██░██ ░██░██ ░███████ ░███████
303
- ░██ ░████ ░██ ░█████████ ░██ ░████ ░██ ░██ ░██ ░██ ░██
304
- ░██░██ ░██░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██
305
- ░████ ░████ ░██ ░██ ░██ ░██ ░██ ░██ ░██
306
- ░███ ░███ ░██ ░██ ░██ ░███████ ░███████
307
-
308
- ░░░░░░░░░ Workflow Platform - Micro Object Oriented ░░░░░░░░░
309
- `;
310
- return `${ANSI_BOLD}${applyBannerGradient(banner)}${ANSI_RESET}`;
311
- }
312
- export function renderGitignore() {
313
- return `# macOS/editor noise
314
- .DS_Store
315
- .idea/
316
- .vscode/*.log
317
-
318
- # Node/local package files
319
- node_modules/
320
- dist/
321
- coverage/
322
- *.log
323
-
324
- # Python/cache files
325
- __pycache__/
326
- *.py[cod]
327
- .pytest_cache/
328
- .mypy_cache/
329
- .ruff_cache/
330
-
331
- # Local environment files
332
- .env
333
- .env.*
334
- !.env.example
335
- *.local
336
-
337
- # Local generated files
338
- *.code-workspace
339
- addons/
340
- auto/
341
- backups/
342
- data/
343
- filestore/
344
- postgresql/
345
- sessions/
346
- odoo/custom/auto/
347
- odoo/custom/src/*/.git-aggregate-cache/
348
-
349
- # Backups and archives
350
- *.bak
351
- *.backup
352
- *.dump
353
- *.sql
354
- *.zip
355
- *.tar
356
- *.tar.gz
357
- `;
358
- }
359
- export function renderMooDelegationScript() {
360
- return `#!/usr/bin/env bash
361
- set -euo pipefail
362
-
363
- script_dir="$(cd -- "$(dirname -- "\${BASH_SOURCE[0]}")" && pwd)"
364
- cd "$script_dir"
365
-
366
- usage() {
367
- case "$1" in
368
- "start") echo "Usage: ./moo start" ;;
369
- "stop") echo "Usage: ./moo stop" ;;
370
- "logs") echo "Usage: ./moo logs [service]" ;;
371
- "restart") echo "Usage: ./moo restart" ;;
372
- "shell") echo "Usage: ./moo shell" ;;
373
- "psql") echo "Usage: ./moo psql [db]" ;;
374
- "install") echo "Usage: ./moo install <module[,module]> [db]" ;;
375
- "update") echo "Usage: ./moo update <module[,module]> [db]" ;;
376
- "test") echo "Usage: ./moo test <module[,module]> [--db <db>] [--mode init|update] [--tags <tags>]" ;;
377
- "resetdb") echo "Usage: ./moo resetdb [db] [module[,module]]" ;;
378
- "snapshot") echo "Usage: ./moo snapshot [db] [snapshot-name]" ;;
379
- "restore-snapshot") echo "Usage: ./moo restore-snapshot [--dry-run] <snapshot-name> [db]" ;;
380
- "lint") echo "Usage: ./moo lint" ;;
381
- "pot") echo "Usage: ./moo pot <module[,module]> [db] [output]" ;;
382
- esac
383
- }
384
-
385
- fail_usage() {
386
- usage "$1" >&2
387
- exit 2
388
- }
389
-
390
- require_no_args() {
391
- local command="$1"
392
- shift
393
- if [[ "$#" -ne 0 ]]; then
394
- fail_usage "$command"
395
- fi
396
- }
397
-
398
- optional_single_arg() {
399
- local command="$1"
400
- local fallback="$2"
401
- shift 2
402
- if [[ "$#" -gt 1 ]]; then
403
- fail_usage "$command"
404
- fi
405
- printf '%s\\n' "\${1:-$fallback}"
406
- }
407
-
408
- require_module_args() {
409
- local command="$1"
410
- shift
411
- if [[ "$#" -lt 1 || "\${1:-}" == -* || "$#" -gt 2 ]]; then
412
- fail_usage "$command"
413
- fi
414
- }
415
-
416
- positional_args() {
417
- local command="$1"
418
- local min="$2"
419
- local max="$3"
420
- shift 3
421
- if [[ "$#" -lt "$min" || "$#" -gt "$max" ]]; then
422
- fail_usage "$command"
423
- fi
424
- for arg in "$@"; do
425
- if [[ "$arg" == -* ]]; then
426
- fail_usage "$command"
427
- fi
428
- done
429
- }
430
-
431
- validate_test_args() {
432
- if [[ "$#" -lt 1 || "\${1:-}" == -* ]]; then
433
- fail_usage "test"
434
- fi
435
-
436
- shift
437
- while [[ "$#" -gt 0 ]]; do
438
- case "$1" in
439
- "--db"|"--tags")
440
- if [[ "$#" -lt 2 || "\${2:-}" == --* ]]; then
441
- echo "Missing value for $1" >&2
442
- exit 2
443
- fi
444
- shift 2
445
- ;;
446
- "--mode")
447
- if [[ "$#" -lt 2 || "\${2:-}" == --* ]]; then
448
- echo "Missing value for --mode" >&2
449
- exit 2
450
- fi
451
- if [[ "$2" != "init" && "$2" != "update" ]]; then
452
- echo "Invalid value for --mode: expected init or update" >&2
453
- exit 2
454
- fi
455
- shift 2
456
- ;;
457
- *)
458
- echo "Unknown option for ./moo test: $1" >&2
459
- exit 2
460
- ;;
461
- esac
462
- done
463
- }
464
-
465
- run_script() {
466
- local script="$1"
467
- shift
468
- if [[ ! -x "$script" ]]; then
469
- echo "Missing daily action script: \${script#./}" >&2
470
- exit 1
471
- fi
472
- exec "$script" "$@"
473
- }
474
-
475
- command="\${1:-}"
476
- case "$command" in
477
- "start")
478
- shift
479
- require_no_args "$command" "$@"
480
- run_script ./scripts/up.sh
481
- ;;
482
- "stop")
483
- shift
484
- require_no_args "$command" "$@"
485
- run_script ./scripts/down.sh
486
- ;;
487
- "logs")
488
- shift
489
- service="$(optional_single_arg "$command" "odoo" "$@")"
490
- run_script ./scripts/logs.sh "$service"
491
- ;;
492
- "restart")
493
- shift
494
- require_no_args "$command" "$@"
495
- run_script ./scripts/restart.sh
496
- ;;
497
- "shell")
498
- shift
499
- require_no_args "$command" "$@"
500
- run_script ./scripts/shell.sh
501
- ;;
502
- "psql")
503
- shift
504
- db="$(optional_single_arg "$command" "postgres" "$@")"
505
- run_script ./scripts/psql.sh "$db"
506
- ;;
507
- "install")
508
- shift
509
- require_module_args "$command" "$@"
510
- run_script ./scripts/install.sh "$@"
511
- ;;
512
- "update")
513
- shift
514
- require_module_args "$command" "$@"
515
- run_script ./scripts/update.sh "$@"
516
- ;;
517
- "test")
518
- shift
519
- validate_test_args "$@"
520
- run_script ./scripts/test.sh "$@"
521
- ;;
522
- "resetdb")
523
- shift
524
- positional_args "$command" 0 2 "$@"
525
- run_script ./scripts/resetdb.sh "$@"
526
- ;;
527
- "snapshot")
528
- shift
529
- positional_args "$command" 0 2 "$@"
530
- run_script ./scripts/snapshot.sh "$@"
531
- ;;
532
- "restore-snapshot")
533
- shift
534
- restore_args=()
535
- if [[ "\${1:-}" == "--dry-run" ]]; then
536
- restore_args+=("--dry-run")
537
- shift
538
- fi
539
- positional_args "$command" 1 2 "$@"
540
- restore_args+=("$@")
541
- run_script ./scripts/restore-snapshot.sh "\${restore_args[@]}"
542
- ;;
543
- "lint")
544
- shift
545
- require_no_args "$command" "$@"
546
- run_script ./scripts/lint.sh
547
- ;;
548
- "pot")
549
- shift
550
- positional_args "$command" 1 3 "$@"
551
- run_script ./scripts/pot.sh "$@"
552
- ;;
553
- *)
554
- exec npx --yes ${fallbackPackageSpec()} "$@"
555
- ;;
556
- esac
557
- `;
558
- }
559
- export function renderAddonsYaml(options) {
560
- return `# Addons activated from source submodules.
561
- #
562
- # Source repos are managed as Git submodules under odoo/custom/src/private.
563
- # Do not duplicate these same repos in repos.yaml.
564
-
565
- ${options.sourceRepos.map((repo) => `${sourceTypeOf(repo)}/${repo.path}:\n${yamlList(repo.addons)}`).join('\n\n')}
566
- `;
567
- }
568
- export function renderReposYaml(options) {
569
- return `# git-aggregator repositories.
570
- #
571
- # Project source repositories are intentionally not listed here because
572
- # they are pinned as Git submodules:
573
- #
574
- ${options.sourceRepos.map((repo) => `# - ${sourceTypeOf(repo)}/${repo.path}`).join('\n')}
575
- #
576
- # Keep this file for upstream/OCA repositories that should be aggregated.
577
-
578
- odoo:
579
- defaults:
580
- depth: $DEPTH_MERGE
581
- remotes:
582
- origin: https://github.com/OCA/OCB.git
583
- odoo: https://github.com/odoo/odoo.git
584
- target: origin $ODOO_VERSION
585
- merges:
586
- - origin $ODOO_VERSION
587
- `;
588
- }
589
- export function renderReadme(options) {
590
- const title = titleizeProduct(options.product);
591
- return `# ${title} Development Environment
592
-
593
- Private ${environmentKind()} development environment for the ${title} product.
594
-
595
- This folder owns the development environment only. Product source code lives
596
- in source repository submodules under \`odoo/custom/src/private\`,
597
- \`odoo/custom/src/oca\`, or \`odoo/custom/src/external\` when source
598
- repositories are connected.
599
-
600
- ## Repository Layout
601
-
602
- \`\`\`text
603
- ${repositoryLayout(options)}
604
- \`\`\`
605
-
606
- ${cloneDocs(options)}
607
-
608
- ## WPMoo CLI Shortcut
609
-
610
- This environment includes a local \`moo\` shortcut script. From the repository
611
- root:
612
-
613
- \`\`\`bash
614
- ./moo
615
- ./moo start
616
- ./moo stop
617
- ./moo restart
618
- ./moo doctor
619
- ./moo add-module
620
- \`\`\`
621
-
622
- Optionally, if this repository root is on your \`PATH\`, you can run \`moo ...\`
623
- from anywhere and the script will return to this environment root first.
624
- ${optionalAgentSkillsReadme(options)}
625
- ## Source Repositories
626
-
627
- ${sourceRepoDocs(options)}
628
-
629
- ${environmentUsageDocs(options)}
630
- ## Branching
631
-
632
- Use Odoo major-version branches in source repositories when you add them:
633
-
634
- \`\`\`text
635
- ${options.odooVersion}
636
- \`\`\`
637
-
638
- If this environment is connected to Git, the dev repository can stay on \`main\`
639
- and pin exact source commits through submodule references.
640
- `;
641
- }
642
- export function renderAgents(options) {
643
- const repoList = hasSourceRepos(options)
644
- ? options.sourceRepos.map((repo) => `- \`${repo.path}\`: \`${repo.url}\``).join('\n')
645
- : '- No source repositories are configured yet.';
646
- const sourceLayout = hasSourceRepos(options)
647
- ? `Product repositories are Git submodules. They are listed under the private
648
- source directory below for this environment:
649
-
650
- \`\`\`text
651
- ${options.sourceRepos.map(sourceRepoRelativePath).join('\n')}
652
- \`\`\`
653
-
654
- ${repoDuplicationNote()}`
655
- : 'No source repositories are configured yet. Use `./moo add-repo` or the cockpit Repositories menu before module-specific work.';
656
- const addonList = hasSourceRepos(options)
657
- ? options.sourceRepos
658
- .map((repo) => `\`${repo.path}\` addons:\n${repo.addons.map((addon) => `- \`${addon}\``).join('\n')}`)
659
- .join('\n\n')
660
- : 'No addon boundaries are known yet. Add source repositories before module-specific implementation.';
661
- return `# AGENTS.md
662
-
663
- ## Project
664
-
665
- Private ${environmentKind()} development environment for the ${titleizeProduct(options.product)} product.
666
-
667
- ## Repository Roles
668
-
669
- - \`${options.devRepo}\`: environment/config only, private.
670
- ${repoList}
671
-
672
- ## Source Layout
673
-
674
- ${sourceLayout}
675
- ${optionalAgentSkillsAgentsSection(options)}
676
- ## Addon Boundaries
677
-
678
- ${addonList}
679
-
680
- Public/community addons must not depend on private paid addons. Private paid
681
- addons may depend on public/community addons.
682
-
683
- ## Odoo 19 Rules
684
-
685
- - Use \`<list>\` instead of \`<tree>\`.
686
- - Use direct view expressions such as \`invisible="..."\` instead of \`attrs\`.
687
- - Use \`models.Constraint\` instead of \`_sql_constraints\`.
688
- - Use \`@api.ondelete(at_uninstall=False)\` for delete validation.
689
- - Avoid \`default_*\` field names in \`res.config.settings\`.
690
- - Keep core/community installable without any pro modules.
691
-
692
- ## Verification
693
-
694
- Use the environment's addon test/update command:
695
-
696
- \`\`\`bash
697
- ${verificationCommand(options)}
698
- \`\`\`
699
-
700
- Useful maintenance commands:
701
-
702
- \`\`\`bash
703
- ./moo lint
704
- ./moo resetdb [db] [module[,module]]
705
- ./moo snapshot [db] [snapshot-name]
706
- ./moo restore-snapshot [--dry-run] <snapshot-name> [db]
707
- ./moo pot <module[,module]> [db] [output]
708
- \`\`\`
709
-
710
- Daily script delegation vs package fallback:
711
- - \`./moo start\`, \`logs\`, \`install\`, \`update\`, \`test\`, \`snapshot\`, and related runtime tasks delegate to local \`./scripts/*.sh\`.
712
- - \`./moo status\` and \`./moo doctor\` are package fallback commands routed to \`npx --yes ${fallbackPackageSpec()} ...\`.
713
-
714
- Only report completion after the relevant update/test/lint command exits cleanly.
715
- `;
716
- }
717
- export function renderAppstoreRelease(options) {
718
- return `# Odoo Apps Release Notes
719
-
720
- Paid addons can live together in a private source repository during development.
721
- Each paid addon still needs its own App Store metadata.
722
-
723
- Per addon checklist:
724
-
725
- - \`__manifest__.py\` has correct \`name\`, \`summary\`, \`version\`, \`depends\`,
726
- \`license\`, \`price\`, \`currency\`, and \`support\`.
727
- - \`license\` is appropriate for paid distribution, typically \`OPL-1\`.
728
- - \`static/description/icon.png\` exists.
729
- - \`static/description/index.html\` exists.
730
- - Screenshots are stored under \`static/description/\`.
731
- - Community dependency versions are compatible with the target Odoo major
732
- version.
733
-
734
- Recommended release flow:
735
-
736
- 1. Develop in the relevant private source repository.
737
- 2. Update the addon manifest version.
738
- 3. Run update/test commands in this dev environment.
739
- 4. Tag the source commit.
740
- 5. Prepare an App Store publish package or publish mirror per paid addon.
741
- 6. Trigger Odoo Apps repository scan/update.
742
-
743
- If App Store scan coupling becomes a problem, create separate private publish
744
- mirror repositories for each paid addon. Keep development in
745
- \`odoo/custom/src/private\`; mirrors should be generated artifacts, not
746
- hand-edited source repositories.
747
- `;
748
- }
749
- export function renderPlaceholder(title, body) {
750
- return `# ${title}
751
-
752
- ${body}
753
- `;
754
- }