@wpmoo/toolkit 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/LICENSE +22 -0
  2. package/README.md +519 -0
  3. package/dist/addons-yaml.js +59 -0
  4. package/dist/args.js +259 -0
  5. package/dist/cli.js +1039 -0
  6. package/dist/cockpit/command-palette.js +23 -0
  7. package/dist/cockpit/command-registry.js +91 -0
  8. package/dist/cockpit/daily-prompts.js +177 -0
  9. package/dist/cockpit/menu.js +99 -0
  10. package/dist/cockpit/safety.js +22 -0
  11. package/dist/compose-layout.js +118 -0
  12. package/dist/daily-actions.js +190 -0
  13. package/dist/doctor.js +519 -0
  14. package/dist/environment-context.js +10 -0
  15. package/dist/environment-version.js +5 -0
  16. package/dist/environment.js +136 -0
  17. package/dist/external-assets.js +153 -0
  18. package/dist/external-templates.js +86 -0
  19. package/dist/git.js +98 -0
  20. package/dist/github.js +87 -0
  21. package/dist/help.js +157 -0
  22. package/dist/menu-navigation.js +67 -0
  23. package/dist/module-actions.js +114 -0
  24. package/dist/odoo-versions.js +1 -0
  25. package/dist/path-validation.js +50 -0
  26. package/dist/prompt-copy.js +8 -0
  27. package/dist/prompt-repositories.js +34 -0
  28. package/dist/prompts/index.js +174 -0
  29. package/dist/repo-actions.js +158 -0
  30. package/dist/repo-url.js +27 -0
  31. package/dist/repository-preflight.js +46 -0
  32. package/dist/safe-reset.js +217 -0
  33. package/dist/scaffold.js +161 -0
  34. package/dist/source-actions.js +65 -0
  35. package/dist/source-manifest.js +338 -0
  36. package/dist/status.js +239 -0
  37. package/dist/templates.js +758 -0
  38. package/dist/types.js +1 -0
  39. package/dist/update-check.js +106 -0
  40. package/dist/version.js +19 -0
  41. package/docs/assets/patreon-donate.png +0 -0
  42. package/docs/assets/wpmoo-banner.png +0 -0
  43. package/docs/external-resources.md +136 -0
  44. package/docs/generated-environment-verification.md +140 -0
  45. package/docs/handoff.md +29 -0
  46. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WPMoo.org
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,519 @@
1
+ ![WPMoo Toolkit for Odoo development workflows](docs/assets/wpmoo-banner.png)
2
+
3
+ [![CI](https://img.shields.io/github/actions/workflow/status/wpmoo-org/wpmoo-toolkit/ci.yml?branch=main&label=CI&style=flat-square)](https://github.com/wpmoo-org/wpmoo-toolkit/actions/workflows/ci.yml) [![GitHub](https://img.shields.io/badge/GitHub-181717?logo=github&style=flat-square)](https://github.com/wpmoo-org/wpmoo-toolkit) [![npm](https://img.shields.io/npm/v/@wpmoo/toolkit?label=npm&logo=npm&style=flat-square&color=blue)](https://www.npmjs.com/package/@wpmoo/toolkit) [![coverage](https://img.shields.io/codecov/c/github/wpmoo-org/wpmoo-toolkit?branch=main&label=coverage&logo=codecov&style=flat-square&color=blue)](https://codecov.io/gh/wpmoo-org/wpmoo-toolkit) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](LICENSE) [![WPMoo Toolkit](https://img.shields.io/badge/WPMoo-Tool-714B67?style=flat-square)](https://github.com/wpmoo-org/wpmoo-toolkit) [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-FFDD00?logo=buymeacoffee&logoColor=000000&style=flat-square)](https://www.buymeacoffee.com/cangir) [![Patreon](https://img.shields.io/badge/Patreon-Support-F96854?logo=patreon&logoColor=white&style=flat-square)](https://www.patreon.com/wpmoo)
4
+
5
+ # WPMoo Toolkit
6
+
7
+ WPMoo Toolkit is a development-first CLI for creating and operating Docker Compose based environments for Odoo, with source repositories managed as Git submodules.
8
+
9
+ It gives Odoo teams a repeatable environment layout, a guided cockpit for daily work, direct commands for automation, and recovery tools that refresh generated files without touching product source code.
10
+
11
+ WPMoo Toolkit is an independent project and is not affiliated with, endorsed by, or sponsored by Odoo S.A. Odoo is a trademark of Odoo S.A.
12
+
13
+ ## Development Status
14
+
15
+ > [!IMPORTANT]
16
+ > **Pre-1.0 active development:** WPMoo Toolkit has not reached `1.0.0` yet. Until the `1.0.0` release, use it as a preview tool for evaluation, local trials, and feedback rather than a dependency for critical production workflows. Setup conventions and command behavior may still change between pre-1.0 releases.
17
+
18
+ ## Why WPMoo Toolkit
19
+
20
+ - Create a local Odoo development environment from a dev repository and one or more source repositories.
21
+ - Keep product source repositories under `odoo/custom/src/private`, `odoo/custom/src/oca`, or `odoo/custom/src/external` as Git submodules pinned to the selected Odoo branch.
22
+ - Copy Docker Compose resources from the standalone `wpmoo-org/odoo-docker-compose` resource instead of embedding large runtime assets in the TypeScript package.
23
+ - Optionally copy project-local Agent Skills from `wpmoo-org/odoo-skills` into generated environments.
24
+ - Use either a guided terminal cockpit or direct CLI commands for the same lifecycle tasks.
25
+
26
+ ## Requirements
27
+
28
+ - Node.js `>=20.17`
29
+ - Git
30
+ - Docker and Docker Compose for generated environment runtime commands
31
+ - GitHub CLI (`gh`) is optional. Use it for repository discovery, repository creation, and deeper diagnostics.
32
+
33
+ The wizard currently offers Odoo `19.0`, `18.0`, `17.0`, and `16.0`. Generated
34
+ environments now use the compact compose layout (`compose.yaml` with
35
+ `compose/<env>.yaml` overlays). Legacy root-level
36
+ `docker-compose_<version>.yml` layouts are still supported for compatibility.
37
+
38
+ Set up GitHub CLI only when you want WPMoo to discover your personal account and organizations or create missing repositories from the interactive wizard:
39
+
40
+ ```bash
41
+ brew install gh
42
+ gh auth login
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ Run the guided wizard from a workspace directory:
48
+
49
+ ```bash
50
+ npx @wpmoo/toolkit
51
+ ```
52
+
53
+ Short alias:
54
+
55
+ ```bash
56
+ npx wpmoo
57
+ ```
58
+
59
+ Legacy package paths `npx @wpmoo/odoo` and `npx @wpmoo/odoo-dev` remain
60
+ available as compatibility redirects to `@wpmoo/toolkit`.
61
+
62
+ If the current directory is not already a WPMoo environment, the CLI opens the create flow. It asks for the product slug, Odoo version, and environment folder. Choose any environment folder; the default is `./<product>_dev`.
63
+
64
+ After folder selection, connect Git/GitHub to use repository URLs. Choose local-only setup to skip Git/GitHub connection and source repo prompts. Add source repositories later from the cockpit (`Repositories` -> `add-repo`) or `npx @wpmoo/toolkit add-repo`.
65
+
66
+ For non-interactive usage with repository URLs:
67
+
68
+ Direct `create` commands keep the existing repo URL options; use `--target <path>` to choose a custom folder.
69
+
70
+ ```bash
71
+ npx @wpmoo/toolkit create \
72
+ --product odoo_sample_module \
73
+ --odoo-version 19.0 \
74
+ --dev-repo-url https://github.com/example-org/odoo_sample_module_dev.git \
75
+ --source-repo-url https://github.com/example-org/odoo_sample_module.git \
76
+ --init-empty-repos
77
+ ```
78
+
79
+ Add multiple source repositories by repeating `--source-repo-url`:
80
+
81
+ ```bash
82
+ npx @wpmoo/toolkit create \
83
+ --product odoo_sample_module \
84
+ --dev-repo-url https://github.com/example-org/odoo_sample_module_dev.git \
85
+ --source-repo-url https://github.com/example-org/odoo_sample_module.git \
86
+ --source-addons odoo_sample_module,odoo_sample_module_portal \
87
+ --source-repo-url git@github.com:example-org/odoo_sample_module_reports.git \
88
+ --source-path odoo_sample_module_reports \
89
+ --source-addons odoo_sample_module_reports
90
+ ```
91
+
92
+ Preview planned files and commands without writing:
93
+
94
+ ```bash
95
+ npx @wpmoo/toolkit create \
96
+ --product odoo_sample_module \
97
+ --dev-repo-url https://github.com/example-org/odoo_sample_module_dev.git \
98
+ --source-repo-url https://github.com/example-org/odoo_sample_module.git \
99
+ --dry-run
100
+ ```
101
+
102
+ ## The Cockpit
103
+
104
+ Run the package with no command inside a generated environment:
105
+
106
+ ```bash
107
+ npx @wpmoo/toolkit
108
+ ```
109
+
110
+ The cockpit starts with a fast environment status summary, then opens a compact menu designed for repeated local work:
111
+
112
+ ```text
113
+ Command palette /
114
+ Services
115
+ Modules
116
+ Database
117
+ Diagnostics
118
+ Repositories
119
+ Maintenance
120
+ Exit
121
+ ```
122
+
123
+ The UI is intentionally practical rather than decorative:
124
+
125
+ - `Command palette /` searches slash commands such as `/test`, `/logs`, `/doctor`, and `/safe-reset`.
126
+ - Category menus group related tasks for scanability: services, modules, database, diagnostics, repositories, and maintenance.
127
+ - `Esc` returns from category menus to the top-level cockpit.
128
+ - Empty states explain the next action, such as adding a source repo before selecting a module.
129
+ - Risky commands such as stopping services, resetting databases, restoring snapshots, removing repos, removing modules, and safe reset ask for explicit confirmation.
130
+ - Guided prompts collect common arguments for daily actions, including module names, database names, test modes, tags, snapshot names, and POT output paths.
131
+
132
+ ## Cockpit Command Map
133
+
134
+ | Category | Commands |
135
+ | --- | --- |
136
+ | Services | `start`, `stop`, `restart`, `logs`, `shell` |
137
+ | Modules | `install`, `update`, `test`, `lint`, `pot`, `add-module`, `remove-module` |
138
+ | Database | `psql`, `snapshot`, `restore-snapshot`, `resetdb` |
139
+ | Diagnostics | `status`, `doctor` |
140
+ | Repositories | `add-repo`, `remove-repo` |
141
+ | Maintenance | `safe-reset` |
142
+
143
+ Every cockpit action maps to a direct command, or to an equivalent management command such as `/safe-reset` mapping to `reset`, for scripting and repeatable terminal workflows.
144
+
145
+ ## Direct Commands
146
+
147
+ ```bash
148
+ npx @wpmoo/toolkit --help
149
+ npx @wpmoo/toolkit --version
150
+
151
+ npx @wpmoo/toolkit status
152
+ npx @wpmoo/toolkit status --json
153
+ npx @wpmoo/toolkit doctor
154
+ npx @wpmoo/toolkit doctor --json
155
+ npx @wpmoo/toolkit doctor --fix
156
+ npx @wpmoo/toolkit source list --json
157
+ npx @wpmoo/toolkit add-repo --repo-url https://github.com/example-org/odoo_sample_module_reports.git
158
+ npx @wpmoo/toolkit remove-repo --repo odoo_sample_module_reports
159
+ npx @wpmoo/toolkit add-module --repo odoo_sample_module --module odoo_sample_module_base --source-type private
160
+ npx @wpmoo/toolkit remove-module --repo odoo_sample_module --module odoo_sample_module_base --source-type private
161
+ npx @wpmoo/toolkit reset --dry-run
162
+ npx @wpmoo/toolkit reset
163
+
164
+ npx @wpmoo/toolkit start
165
+ npx @wpmoo/toolkit stop
166
+ npx @wpmoo/toolkit restart
167
+ npx @wpmoo/toolkit logs odoo
168
+ npx @wpmoo/toolkit shell
169
+ npx @wpmoo/toolkit psql postgres
170
+
171
+ npx @wpmoo/toolkit install sale devel
172
+ npx @wpmoo/toolkit update sale devel
173
+ npx @wpmoo/toolkit test sale --db devel --mode update --tags /sale
174
+ npx @wpmoo/toolkit lint
175
+ npx @wpmoo/toolkit pot sale devel i18n/sale.pot
176
+
177
+ npx @wpmoo/toolkit resetdb devel sale
178
+ npx @wpmoo/toolkit snapshot devel before-update
179
+ npx @wpmoo/toolkit restore-snapshot --dry-run before-update devel
180
+ npx @wpmoo/toolkit restore-snapshot before-update devel
181
+ ```
182
+
183
+ Daily action commands must be run from a generated environment root containing `.wpmoo/odoo.json`. They delegate to fixed scripts under `./scripts`; they do not search parent directories or run arbitrary script names.
184
+
185
+ ## Generated Environment Layout
186
+
187
+ A generated environment is a separate Git repository, usually named `<product>_dev`, but the wizard and `--target` can use any folder. Product source code stays in child source repositories.
188
+
189
+ ```text
190
+ odoo_sample_module_dev/
191
+ |-- .wpmoo/
192
+ | `-- odoo.json
193
+ |-- .env.example
194
+ |-- AGENTS.md
195
+ |-- README.md
196
+ |-- compose.yaml
197
+ |-- compose/
198
+ | |-- dev.yaml
199
+ | |-- stage.yaml
200
+ | `-- prod.yaml
201
+ |-- config/
202
+ | `-- odoo/
203
+ | `-- odoo.conf
204
+ |-- docs/
205
+ | |-- appstore-release.md
206
+ | `-- compose.md
207
+ |-- resources/
208
+ | `-- odoo/
209
+ | `-- entrypoint.sh
210
+ |-- moo
211
+ |-- odoo/
212
+ | `-- custom/
213
+ | `-- src/
214
+ | |-- private/
215
+ | |-- oca/
216
+ | `-- external/
217
+ `-- scripts/
218
+ ```
219
+
220
+ Development uses `compose.yaml` plus `compose/dev.yaml` by default. Set
221
+ `WPMOO_ENV=stage` or `WPMOO_ENV=prod` only after providing production-grade
222
+ secrets and volumes.
223
+
224
+ The metadata file `.wpmoo/odoo.json` records the product slug, selected Odoo version, dev repo URL, source repos, engine, external resource refs, ports, and template configuration. Status, doctor, daily actions, and safe reset use that metadata instead of guessing from the filesystem.
225
+
226
+ ## Daily `./moo` Commands
227
+
228
+ Generated environments include a local `./moo` dispatcher. It is the shortest path for everyday Compose and Odoo work:
229
+
230
+ ```bash
231
+ cp .env.example .env
232
+
233
+ ./moo start
234
+ ./moo logs odoo
235
+ ./moo shell
236
+ ./moo psql postgres
237
+ ./moo restart
238
+ ./moo stop
239
+
240
+ ./moo install sale devel
241
+ ./moo update sale devel
242
+ ./moo test sale --db devel --mode update --tags /sale
243
+ ./moo lint
244
+ ./moo pot sale devel i18n/sale.pot
245
+
246
+ ./moo snapshot devel before-update
247
+ ./moo restore-snapshot --dry-run before-update devel
248
+ ./moo restore-snapshot before-update devel
249
+ ./moo resetdb devel sale
250
+ ```
251
+
252
+ `restore-snapshot --dry-run` validates the selected snapshot and prints the
253
+ restore plan without changing the database or filestore. Generated environments
254
+ also support `WPMOO_SNAPSHOT_RETENTION_COUNT` for pruning old snapshot files.
255
+ When `WPMOO_ENV=stage` or `WPMOO_ENV=prod`, destructive database actions such
256
+ as `resetdb` and real `restore-snapshot` require `WPMOO_ALLOW_DESTRUCTIVE=1`.
257
+
258
+ Use `npx @wpmoo/toolkit ...` for package/operator commands such as `create`, `add-repo`, `remove-repo`, `add-module`, `remove-module`, `status`, `doctor`, and `reset`. Use `./moo ...` inside a generated environment for local daily Compose commands.
259
+
260
+ ## Repository and Module Management
261
+
262
+ Add a source repository after local-only setup from the cockpit or direct command:
263
+
264
+ ```bash
265
+ npx @wpmoo/toolkit add-repo \
266
+ --repo-url https://github.com/example-org/odoo_sample_module_reports.git \
267
+ --init-empty-repos
268
+ ```
269
+
270
+ Pin source repositories to dedicated source directories:
271
+
272
+ ```bash
273
+ npx @wpmoo/toolkit add-repo \
274
+ --repo-url https://github.com/OCA/sale-workflow.git \
275
+ --source-type oca
276
+
277
+ npx @wpmoo/toolkit add-repo \
278
+ --repo-url https://github.com/example-org/odoo_external_tool.git \
279
+ --source-type external
280
+ ```
281
+
282
+ GitHub CLI is optional for repository setup. When it is available and authenticated, the interactive flow can:
283
+
284
+ - detect the owner or organization from the current environment;
285
+ - suggest repository URLs;
286
+ - check whether the repository is accessible;
287
+ - create inaccessible repositories after confirmation;
288
+ - initialize empty repositories with the selected Odoo branch.
289
+
290
+ Add a minimal Odoo module skeleton to a source repository:
291
+
292
+ For module actions, `--source-type` selects the source directory (`private`, `oca`, or `external`). Default is `private`.
293
+
294
+ ```bash
295
+ npx @wpmoo/toolkit add-module \
296
+ --repo odoo_sample_module \
297
+ --module odoo_sample_module_base \
298
+ --source-type oca
299
+ ```
300
+
301
+ Remove a module registration while keeping files:
302
+
303
+ ```bash
304
+ npx @wpmoo/toolkit remove-module \
305
+ --repo odoo_sample_module \
306
+ --module odoo_sample_module_base \
307
+ --source-type oca
308
+ ```
309
+
310
+ Delete module files as well:
311
+
312
+ ```bash
313
+ npx @wpmoo/toolkit remove-module \
314
+ --repo odoo_sample_module \
315
+ --module odoo_sample_module_base \
316
+ --delete-files
317
+ ```
318
+
319
+ Remove a source repository submodule:
320
+
321
+ ```bash
322
+ npx @wpmoo/toolkit remove-repo --repo odoo_sample_module_reports
323
+ ```
324
+
325
+ WPMoo refuses to remove a source repo submodule when that submodule has uncommitted changes.
326
+
327
+ Generated environments also keep a deterministic source manifest at
328
+ `odoo/custom/manifests/sources.yaml`. It mirrors source submodules from
329
+ `.wpmoo/odoo.json` and `.gitmodules`, including source type, path, URL, branch,
330
+ and addon boundaries.
331
+
332
+ Inspect configured sources:
333
+
334
+ ```bash
335
+ npx @wpmoo/toolkit source list
336
+ npx @wpmoo/toolkit source list --json
337
+ ```
338
+
339
+ Regenerate the manifest and metadata from the current metadata/gitmodule state:
340
+
341
+ ```bash
342
+ npx @wpmoo/toolkit source sync
343
+ npx @wpmoo/toolkit source sync --json
344
+ ```
345
+
346
+ `source add` and `source remove` are direct aliases for the same repository
347
+ operations:
348
+
349
+ ```bash
350
+ npx @wpmoo/toolkit source add \
351
+ --repo-url https://github.com/OCA/server-tools.git \
352
+ --source-type oca
353
+
354
+ npx @wpmoo/toolkit source remove --repo server-tools --source-type oca
355
+ ```
356
+
357
+ ## Status, Doctor, and Recovery
358
+
359
+ `status` is fast and offline. It reads local metadata and files only:
360
+
361
+ ```bash
362
+ npx @wpmoo/toolkit status
363
+ npx @wpmoo/toolkit status --json
364
+ ```
365
+
366
+ It reports whether the environment is detected, which Odoo version is selected, how many source repos are configured, how many module candidates are present, which core files are missing, and the recommended next action.
367
+
368
+ For automation and VS Code cockpit integration, all of these commands also support
369
+ `--json`:
370
+
371
+ ```bash
372
+ npx @wpmoo/toolkit status --json
373
+ npx @wpmoo/toolkit source list --json
374
+ npx @wpmoo/toolkit source sync --json
375
+ npx @wpmoo/toolkit doctor --json
376
+ ```
377
+
378
+ JSON output is optional; human-readable output remains the default.
379
+
380
+ `doctor` performs deeper checks:
381
+
382
+ ```bash
383
+ npx @wpmoo/toolkit doctor
384
+ ```
385
+
386
+ It validates metadata, engine support, selected compose files, source repo paths,
387
+ source manifest consistency, daily scripts, `.env` settings, Docker CLI access,
388
+ Docker Compose access, GitHub CLI authentication when available, and PostgreSQL
389
+ 18 compatibility in compose mount targets (for mounts to
390
+ `/var/lib/postgresql/data` or `/var/lib/postgresql/18/docker`).
391
+
392
+ Use `doctor --fix` for safe file-level repairs. It can normalize PostgreSQL 18
393
+ mount targets and regenerate `odoo/custom/manifests/sources.yaml` from
394
+ metadata plus `.gitmodules`, then it runs doctor again and reports any remaining
395
+ manual issues.
396
+
397
+ Safe reset refreshes generated environment files without deleting product source code:
398
+
399
+ ```bash
400
+ npx @wpmoo/toolkit reset --dry-run
401
+ npx @wpmoo/toolkit reset
402
+ ```
403
+
404
+ Safe reset updates generated files such as `.wpmoo/odoo.json`, `moo`,
405
+ `.gitignore`, `.env.example`, generated docs, compose assets, and optional
406
+ Agent Skills. Compose overlays like `compose.yaml` and `compose/dev.yaml` are
407
+ also refreshed from the current compose template source.
408
+
409
+ Use `reset --dry-run` first when you want a deterministic preview of refreshed
410
+ files and cleanup warnings without writing to the environment.
411
+
412
+ It does not touch source repo folders under
413
+ `odoo/custom/src/private`, module source code, Git history, remotes, or
414
+ branches. It also preserves local runtime artifacts and custom source layout
415
+ content:
416
+
417
+ - `.env`, `data`, and `backups`
418
+ - `odoo/custom/src/oca`, `odoo/custom/src/external`, `odoo/custom/patches`,
419
+ `odoo/custom/manifests`, and their existing contents
420
+
421
+ Legacy compose template paths from older scaffolds can remain
422
+ (`docs/assets/`, `test/`, `.github/`) until you remove them manually.
423
+
424
+ Recommended recovery pattern:
425
+
426
+ ```bash
427
+ ./moo snapshot devel before-reset
428
+ npx @wpmoo/toolkit reset --dry-run
429
+ npx @wpmoo/toolkit reset
430
+ npx @wpmoo/toolkit doctor --fix
431
+ ./moo restore-snapshot --dry-run before-reset devel
432
+ ./moo restore-snapshot before-reset devel
433
+ ```
434
+
435
+ ## External Resources
436
+
437
+ WPMoo Toolkit keeps the package small by copying external resources into generated environments:
438
+
439
+ ```text
440
+ gh:wpmoo-org/odoo-docker-compose
441
+ gh:wpmoo-org/odoo-skills
442
+ ```
443
+
444
+ Use the default resources:
445
+
446
+ ```bash
447
+ npx @wpmoo/toolkit create \
448
+ --product odoo_sample_module \
449
+ --source-repo-url https://github.com/example-org/odoo_sample_module.git \
450
+ --agent-skills-template
451
+ ```
452
+
453
+ Pin external resource refs:
454
+
455
+ ```bash
456
+ npx @wpmoo/toolkit create \
457
+ --product odoo_sample_module \
458
+ --source-repo-url https://github.com/example-org/odoo_sample_module.git \
459
+ --compose-template-ref v0.1.0 \
460
+ --agent-skills-template \
461
+ --agent-skills-template-ref v0.1.0
462
+ ```
463
+
464
+ Use local resource clones while developing the resource packages:
465
+
466
+ ```bash
467
+ git clone https://github.com/wpmoo-org/odoo-docker-compose ../odoo-docker-compose
468
+ git clone https://github.com/wpmoo-org/odoo-skills ../odoo-skills
469
+
470
+ npx @wpmoo/toolkit create \
471
+ --engine compose \
472
+ --compose-template-url ../odoo-docker-compose \
473
+ --agent-skills-template \
474
+ --agent-skills-template-url ../odoo-skills \
475
+ --product odoo_sample_module \
476
+ --source-repo-url https://github.com/example-org/odoo_sample_module.git
477
+ ```
478
+
479
+ More detail: [External Resources](docs/external-resources.md).
480
+
481
+ ## Verification
482
+
483
+ Run local package checks from the repository root:
484
+
485
+ ```bash
486
+ npm run typecheck
487
+ npm test
488
+ npm run test:coverage
489
+ npm run build
490
+ ```
491
+
492
+ Generated environment behavior is covered by the operator-facing matrix in [Generated Environment Verification](docs/generated-environment-verification.md).
493
+
494
+ ## Release
495
+
496
+ The normal release path uses the repository helper and GitHub Actions trusted publishing:
497
+
498
+ ```bash
499
+ npm run release:check
500
+ npm run typecheck
501
+ npm test
502
+ npm run build
503
+ VERSION="$(node -p "require('./package.json').version")"
504
+ git tag -a "v$VERSION" -m "Release v$VERSION"
505
+ git push origin "v$VERSION"
506
+ ```
507
+
508
+ If `npm run release:check` bumps `package.json` and `package-lock.json`, commit and push that version bump first, then rerun the release check before tagging. Publishing is handled by the `Publish` workflow after the tag is pushed.
509
+
510
+ ## Sponsoring
511
+
512
+ Support ongoing WPMoo development through recurring or one-time sponsorship:
513
+
514
+ <a href="https://www.buymeacoffee.com/cangir">
515
+ <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me a Coffee" width="250">
516
+ </a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
517
+ <a href="https://www.patreon.com/wpmoo">
518
+ <img src="docs/assets/patreon-donate.png" alt="Support WPMoo on Patreon" width="250">
519
+ </a>
@@ -0,0 +1,59 @@
1
+ function escapeRegExp(value) {
2
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
3
+ }
4
+ function ensureFinalNewline(value) {
5
+ return value.endsWith('\n') ? value : `${value}\n`;
6
+ }
7
+ function yamlList(items) {
8
+ return items.map((item) => ` - ${item}`).join('\n');
9
+ }
10
+ function renderSourceRepoBlock(repo) {
11
+ return `private/${repo.path}:\n${yamlList(repo.addons)}\n`;
12
+ }
13
+ export function addSourceRepoToAddonsYaml(content, repo) {
14
+ const blockPattern = new RegExp(`^private/${escapeRegExp(repo.path)}:\\s*$`, 'm');
15
+ if (blockPattern.test(content)) {
16
+ return content;
17
+ }
18
+ const base = ensureFinalNewline(content.trimEnd());
19
+ return `${base}\n${renderSourceRepoBlock(repo)}`;
20
+ }
21
+ export function removeSourceRepoFromAddonsYaml(content, repoPath) {
22
+ const blockPattern = new RegExp(`(^|\\n)private/${escapeRegExp(repoPath)}:\\n(?:[ \\t].*(?:\\n|$))*`, 'g');
23
+ const updated = content
24
+ .replace(blockPattern, (match, prefix) => (prefix === '\n' && match.endsWith('\n') ? '\n' : ''))
25
+ .replace(/\n{3,}/g, '\n\n');
26
+ return ensureFinalNewline(updated.trimEnd());
27
+ }
28
+ function sourceRepoBlockPattern(repoPath) {
29
+ return new RegExp(`(^private/${escapeRegExp(repoPath)}:\\n)((?:[ \\t].*(?:\\n|$))*)`, 'm');
30
+ }
31
+ function parseYamlListItems(blockBody) {
32
+ return blockBody
33
+ .split('\n')
34
+ .map((line) => line.trim().match(/^-\s+(.+)$/)?.[1]?.trim())
35
+ .filter((item) => Boolean(item));
36
+ }
37
+ export function addModuleToSourceRepoInAddonsYaml(content, repoPath, moduleName) {
38
+ const blockPattern = sourceRepoBlockPattern(repoPath);
39
+ const match = content.match(blockPattern);
40
+ if (!match) {
41
+ return addSourceRepoToAddonsYaml(content, { path: repoPath, addons: [moduleName] });
42
+ }
43
+ const addons = parseYamlListItems(match[2]);
44
+ if (addons.includes(moduleName)) {
45
+ return content;
46
+ }
47
+ const replacement = `${match[1]}${yamlList([...addons, moduleName])}\n`;
48
+ return content.replace(blockPattern, replacement);
49
+ }
50
+ export function removeModuleFromSourceRepoInAddonsYaml(content, repoPath, moduleName) {
51
+ const blockPattern = sourceRepoBlockPattern(repoPath);
52
+ const match = content.match(blockPattern);
53
+ if (!match) {
54
+ return content;
55
+ }
56
+ const addons = parseYamlListItems(match[2]).filter((addon) => addon !== moduleName);
57
+ const replacement = `${match[1]}${addons.length ? `${yamlList(addons)}\n` : ''}`;
58
+ return ensureFinalNewline(content.replace(blockPattern, replacement).trimEnd());
59
+ }