@wpmoo/toolkit 0.9.31 → 0.9.33

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
@@ -143,6 +143,9 @@ Every cockpit action maps to a direct command, so the same workflow can be used
143
143
 
144
144
  When an environment has many module candidates, module selection switches to
145
145
  search so names, repositories, and source categories can be filtered quickly.
146
+ Runtime actions that need running services or a ready database are disabled in
147
+ the cockpit until the environment is ready; file-only actions such as module
148
+ scaffolding, source repository changes, and safe reset remain available.
146
149
 
147
150
  ```bash
148
151
  ./moo start
@@ -181,6 +184,9 @@ npx @wpmoo/toolkit doctor --json --postgres
181
184
  ```
182
185
 
183
186
  JSON output is optional; human-readable output remains the default.
187
+ `doctor --json --fix` is intentionally unsupported because `doctor --fix` may
188
+ write safe file-level repairs. Run `doctor --fix` first, then `doctor --json`
189
+ to inspect the post-fix state.
184
190
  Human `doctor` output is grouped into stable sections (`Generated files`,
185
191
  `Compose`, `Source repositories`, `PostgreSQL`, and `Host tools`) so terminal
186
192
  operators can see which lifecycle layer needs attention first.
@@ -212,6 +218,10 @@ JSON variant exposes a versioned PostgreSQL diagnostics contract.
212
218
  `doctor --json --postgres` keeps the JSON contract stable by versioning the
213
219
  `postgres` payload; individual fields are optional so automation can safely handle
214
220
  environments where PostgreSQL does not expose a metric.
221
+ Optional PostgreSQL probe permission failures appear under
222
+ `postgres.optionalProbeFailures` when available; they do not make
223
+ `postgres.available` false and can be ignored by automation that only needs core
224
+ diagnostics.
215
225
  All `doctor --json` reports also include optional `sections` entries that group
216
226
  checks, warnings, and errors without changing the legacy flat arrays.
217
227
 
@@ -244,7 +254,9 @@ warning while keeping the scoped package release valid.
244
254
  rejects it, the release still succeeds as long as the three required scoped
245
255
  packages are valid.
246
256
  - **Smoke expectation**: run `npm run smoke:published -- "$VERSION"` after the
247
- release tag workflow completes.
257
+ release tag workflow completes. The script prints `Smoke step:` progress lines
258
+ before each published CLI check so slow registry-backed `npx` resolution is
259
+ visible.
248
260
  - **Deterministic smoke target**: pin the target package explicitly so smoke checks
249
261
  are reproducible across reruns:
250
262
 
@@ -21,6 +21,16 @@ const topLevelCategoryOrder = [
21
21
  const topLevelCommands = topLevelCategoryOrder.flatMap((category) => cockpitCommands.filter((command) => command.category === category && command.id !== 'exit'));
22
22
  const topLevelCommandLabelWidth = Math.max(...topLevelCommands.map((command) => command.label.length));
23
23
  const moduleDependentCommandIds = new Set(['list-modules', 'install', 'update', 'test', 'pot', 'remove-module']);
24
+ const runningServicesRequiredCommandIds = new Set([
25
+ 'install',
26
+ 'update',
27
+ 'test',
28
+ 'pot',
29
+ 'psql',
30
+ 'snapshot',
31
+ 'restore-snapshot',
32
+ 'resetdb',
33
+ ]);
24
34
  function rgb(red, green, blue, value) {
25
35
  return `\u001B[38;2;${red};${green};${blue}m${value}\u001B[39m`;
26
36
  }
@@ -39,6 +49,7 @@ const disabledReasonNextStep = {
39
49
  'Services stopped.': 'Next: choose "Start services" first.',
40
50
  'Already running.': 'Next: choose "Stop services" or "Restart services".',
41
51
  'Docker not running.': 'Next: start Docker, then choose "Start services".',
52
+ 'Database not ready.': 'Next: wait for the database or choose "Restart services".',
42
53
  'No source repos found.': 'Next: choose "Add source repo" first.',
43
54
  };
44
55
  function disabledError(reason) {
@@ -50,15 +61,22 @@ function disabledError(reason) {
50
61
  return nextStep ? `${base}\nReason: ${reason}\n${nextStep}` : `${base}\nReason: ${reason}`;
51
62
  }
52
63
  function serviceDisabledReason(command, serviceStatus) {
53
- if (command.category !== 'services' || !serviceStatus)
64
+ if (!serviceStatus)
65
+ return undefined;
66
+ const requiresRunningServices = command.category === 'services' || runningServicesRequiredCommandIds.has(command.id);
67
+ if (!requiresRunningServices)
54
68
  return undefined;
55
69
  if (serviceStatus.kind === 'docker-not-running')
56
70
  return 'Docker not running.';
57
71
  if (serviceStatus.kind === 'running' && command.id === 'start')
58
72
  return 'Already running.';
59
- if (serviceStatus.kind === 'stopped' && ['stop', 'restart', 'logs', 'shell'].includes(command.id)) {
73
+ if (serviceStatus.kind === 'stopped' &&
74
+ (['stop', 'restart', 'logs', 'shell'].includes(command.id) || runningServicesRequiredCommandIds.has(command.id))) {
60
75
  return 'Services stopped.';
61
76
  }
77
+ if (serviceStatus.kind === 'services-running' && runningServicesRequiredCommandIds.has(command.id)) {
78
+ return 'Database not ready.';
79
+ }
62
80
  return undefined;
63
81
  }
64
82
  function moduleDisabledReason(command, moduleCount) {
@@ -71,10 +89,10 @@ function snapshotDisabledReason(command, snapshotCount) {
71
89
  return snapshotCount === 0 && command.id === 'restore-snapshot' ? 'No snapshots found.' : undefined;
72
90
  }
73
91
  function disabledReason(command, serviceStatus, moduleCount, sourceRepoCount, snapshotCount) {
74
- return (serviceDisabledReason(command, serviceStatus) ??
75
- moduleDisabledReason(command, moduleCount) ??
92
+ return (moduleDisabledReason(command, moduleCount) ??
76
93
  sourceRepoDisabledReason(command, sourceRepoCount) ??
77
- snapshotDisabledReason(command, snapshotCount));
94
+ snapshotDisabledReason(command, snapshotCount) ??
95
+ serviceDisabledReason(command, serviceStatus));
78
96
  }
79
97
  function commandDisabledValue(reason) {
80
98
  if (!reason) {
package/dist/doctor.js CHANGED
@@ -115,6 +115,13 @@ function postgresDiagnosticsTimeoutMs(options) {
115
115
  ? options.postgresTimeoutMs
116
116
  : defaultPostgresDiagnosticsTimeoutMs;
117
117
  }
118
+ function optionalPostgresProbeFailureWarning(error) {
119
+ const message = commandErrorText(error).trim();
120
+ if (!/(?:permission denied|insufficient privilege|must be superuser|not permitted|not allowed)/iu.test(message)) {
121
+ return undefined;
122
+ }
123
+ return message.split(/\r?\n/u).find((line) => line.trim())?.trim();
124
+ }
118
125
  async function readPostgresDiagnosticQuery(target, runner, query, timeoutMs) {
119
126
  const queryLiteral = JSON.stringify(query);
120
127
  const command = [
@@ -127,16 +134,21 @@ async function readPostgresDiagnosticQuery(target, runner, query, timeoutMs) {
127
134
  }
128
135
  async function readPostgresDiagnostics(target, runner, timeoutMs) {
129
136
  const diagnostics = await readPostgresDiagnosticQuery(target, runner, POSTGRES_DIAGNOSTICS_QUERY, timeoutMs);
137
+ const optionalProbeFailures = [];
130
138
  for (const probe of POSTGRES_DIAGNOSTICS_OPTIONAL_QUERIES) {
131
139
  try {
132
140
  Object.assign(diagnostics, await readPostgresDiagnosticQuery(target, runner, probe.query, timeoutMs));
133
141
  }
134
- catch {
142
+ catch (error) {
143
+ const warning = optionalPostgresProbeFailureWarning(error);
144
+ if (warning) {
145
+ optionalProbeFailures.push({ id: probe.id, warning });
146
+ }
135
147
  // Optional probes use PostgreSQL functions that can require elevated roles.
136
148
  // Their failure must not hide the core read-only health report.
137
149
  }
138
150
  }
139
- return diagnostics;
151
+ return { diagnostics, optionalProbeFailures };
140
152
  }
141
153
  function stripInlineComment(line) {
142
154
  const hashIndex = line.indexOf('#');
@@ -628,7 +640,7 @@ export async function getDoctorReport(target = process.cwd(), runnerOrOptions =
628
640
  }
629
641
  if (actualOptions.postgres) {
630
642
  try {
631
- const postgresDiagnostics = await readPostgresDiagnostics(target, actualRunner, postgresDiagnosticsTimeoutMs(actualOptions));
643
+ const { diagnostics: postgresDiagnostics, optionalProbeFailures } = await readPostgresDiagnostics(target, actualRunner, postgresDiagnosticsTimeoutMs(actualOptions));
632
644
  const missingKeys = missingPostgresDiagnosticKeys(postgresDiagnostics);
633
645
  const malformedKeys = malformedPostgresDiagnosticKeys(postgresDiagnostics);
634
646
  if (missingKeys.length === 0 && malformedKeys.length === 0) {
@@ -641,6 +653,7 @@ export async function getDoctorReport(target = process.cwd(), runnerOrOptions =
641
653
  contractVersion: POSTGRES_DIAGNOSTICS_CONTRACT_VERSION,
642
654
  available: true,
643
655
  diagnostics: structuredPostgresDiagnostics(postgresDiagnostics),
656
+ ...(optionalProbeFailures.length > 0 ? { optionalProbeFailures } : {}),
644
657
  };
645
658
  warnings.push(...postgresPostgresWarnings(postgresDiagnostics));
646
659
  }
@@ -654,6 +667,7 @@ export async function getDoctorReport(target = process.cwd(), runnerOrOptions =
654
667
  contractVersion: POSTGRES_DIAGNOSTICS_CONTRACT_VERSION,
655
668
  available: false,
656
669
  diagnostics: structuredPostgresDiagnostics(postgresDiagnostics),
670
+ ...(optionalProbeFailures.length > 0 ? { optionalProbeFailures } : {}),
657
671
  warning,
658
672
  };
659
673
  }
package/dist/help.js CHANGED
@@ -161,6 +161,7 @@ Machine-readable JSON output:
161
161
  npx @wpmoo/toolkit source sync --json
162
162
  npx @wpmoo/toolkit doctor --json
163
163
  doctor --json --postgres includes a structured postgres object for automation.
164
+ doctor --json is read-only; run doctor --fix first, then doctor --json for post-fix state.
164
165
  Incomplete or malformed PostgreSQL metric rows are reported as unavailable diagnostics.
165
166
 
166
167
  JSON compatibility policy:
@@ -29,6 +29,12 @@ unscoped `npx wpmoo` short alias is optional and best-effort; scripts should use
29
29
 
30
30
  Run these from the generated environment root:
31
31
 
32
+ The cockpit disables runtime actions such as `psql`, `snapshot`, `resetdb`,
33
+ `restore-snapshot`, `install`, `update`, `test`, and `pot` while services are
34
+ stopped or the database is not ready. File-only cockpit actions remain
35
+ selectable so source repositories, module scaffolds, and safe reset can still be
36
+ managed.
37
+
32
38
  | Command | Cockpit Equivalent | Guarded In Stage/Prod | Notes |
33
39
  | --- | --- | --- | --- |
34
40
  | `./moo start` | Services -> Start services | No | Starts local Odoo services. Disabled in cockpit when already running. |
@@ -66,9 +72,15 @@ Current JSON contract notes:
66
72
  - `status --json` uses `schemaVersion: 1`.
67
73
  - `source list --json` and `source sync --json` use `schemaVersion: 1`.
68
74
  - `doctor --json` uses `schemaVersion: 1`.
75
+ - `doctor --json --fix` is intentionally unsupported because `doctor --fix`
76
+ may mutate files. Run `doctor --fix` first, then `doctor --json` to inspect
77
+ post-fix state.
69
78
  - `doctor --json --postgres` adds `postgres.contractVersion` and a PostgreSQL
70
79
  diagnostics object with its own `schemaVersion`.
71
80
  - PostgreSQL fields are optional when a metric is unavailable.
81
+ - Optional privileged PostgreSQL probe failures may appear under
82
+ `postgres.optionalProbeFailures`; core diagnostics remain available when those
83
+ optional probes fail with permission errors.
72
84
  - Automation should ignore unknown JSON fields.
73
85
  - Minor and patch releases may add optional fields without a breaking release.
74
86
  - Removing, renaming, or changing the meaning of a documented field requires a
package/docs/handoff.md CHANGED
@@ -70,6 +70,9 @@ pre-existing global `NPM_CONFIG_CACHE` state unless you intentionally reuse it.
70
70
 
71
71
  The smoke script checks `--version`, top-level `--help`, and critical command
72
72
  help output before optional generated-environment acceptance smoke.
73
+ Long registry-backed smoke runs print `Smoke step:` progress lines before each
74
+ published CLI check, so a slow `npx` or `npm exec` resolution is visible in the
75
+ terminal instead of appearing stuck.
73
76
 
74
77
  For a 1.0.0 tag, run generated-environment acceptance smoke with
75
78
  WPMOO_SMOKE_ENVIRONMENT=1. Treat the release as final only after that smoke
@@ -11,6 +11,8 @@ Symptoms:
11
11
  - The create wizard stops before writing files.
12
12
  - `./moo start`, `./moo status`, or `./moo doctor` reports Docker or Docker
13
13
  Compose as unavailable.
14
+ - The cockpit disables database or module runtime actions because Docker,
15
+ services, or the database are not ready.
14
16
 
15
17
  Check:
16
18
 
@@ -30,6 +32,9 @@ The create wizard intentionally stops before setup questions when required
30
32
  runtime tools are missing. That protects the workspace from half-created
31
33
  environments.
32
34
 
35
+ If Docker is running but the cockpit says the database is not ready, wait for
36
+ PostgreSQL readiness or choose `Restart services`.
37
+
33
38
  ## No Modules Found
34
39
 
35
40
  Symptoms:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wpmoo/toolkit",
3
- "version": "0.9.31",
3
+ "version": "0.9.33",
4
4
  "description": "WPMoo Toolkit for development, staging, and production lifecycle workflows.",
5
5
  "type": "module",
6
6
  "repository": {