@wpmoo/odoo 0.8.36 → 0.8.38

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
@@ -266,6 +266,34 @@ tool-owned development guideline files.
266
266
  - Legacy `--org`, `--community-repo`, and `--pro-repo` flags are still accepted
267
267
  when no `--source-repo-url` flags are provided.
268
268
 
269
+ ## Release
270
+
271
+ For local release checks from the repository root, run:
272
+
273
+ ```bash
274
+ npm run release:check
275
+ ```
276
+
277
+ The script checks whether the current package version already exists on npm. If
278
+ it does, it runs a patch version bump without creating a git tag, then stops so
279
+ you can commit and push `package.json` and `package-lock.json` before publishing.
280
+ If the version is not published yet, it keeps the current version and runs
281
+ `npm test -- test/package.test.ts` and `npm pack --dry-run`. Actual publishing
282
+ is handled by the GitHub Actions workflow.
283
+
284
+ For GitHub Actions publishing, configure npm Trusted Publishing for
285
+ `wpmoo-org/wpmoo-odoo` with workflow filename `publish.yml`, then run the
286
+ `Publish` workflow manually from GitHub Actions or push a matching release tag:
287
+
288
+ ```bash
289
+ VERSION="$(node -p "require('./package.json').version")"
290
+ git tag -a "v$VERSION" -m "Release v$VERSION"
291
+ git push origin "v$VERSION"
292
+ ```
293
+
294
+ The workflow uses OIDC instead of an npm token, verifies typecheck/tests/build,
295
+ fails if a pushed tag does not match the package version, fails if the package
296
+ version already exists on npm, runs `npm pack --dry-run`, and publishes to npm.
269
297
 
270
298
  ## Support
271
299
 
package/dist/doctor.js CHANGED
@@ -19,6 +19,18 @@ async function exists(path) {
19
19
  function errorMessage(error) {
20
20
  return error instanceof Error ? error.message : String(error);
21
21
  }
22
+ function commandErrorText(error) {
23
+ const parts = [errorMessage(error)];
24
+ if (isRecord(error)) {
25
+ for (const key of ['stderr', 'stdout']) {
26
+ const value = error[key];
27
+ if (typeof value === 'string' && value.trim()) {
28
+ parts.push(value.trim());
29
+ }
30
+ }
31
+ }
32
+ return parts.join('\n');
33
+ }
22
34
  function isRecord(value) {
23
35
  return typeof value === 'object' && value !== null && !Array.isArray(value);
24
36
  }
@@ -95,9 +107,39 @@ function validatePort(name, env, errors) {
95
107
  function renderFailure(errors) {
96
108
  return ['WPMoo doctor failed:', ...errors.map((error) => `- ${error}`)].join('\n');
97
109
  }
110
+ function isNotGitCheckoutError(error) {
111
+ return commandErrorText(error).toLowerCase().includes('not a git repository');
112
+ }
113
+ function isSourceRepoSubmodule(path, sourceRepos) {
114
+ return sourceRepos.some((repo) => {
115
+ const sourcePath = `odoo/custom/src/private/${repo.path}`;
116
+ return path === sourcePath || path.startsWith(`${sourcePath}/`);
117
+ });
118
+ }
119
+ function sourceSubmoduleStatusErrors(output, sourceRepos) {
120
+ const errors = [];
121
+ for (const rawLine of output.split(/\r?\n/)) {
122
+ const line = rawLine.trimEnd();
123
+ if (!line)
124
+ continue;
125
+ const status = line[0];
126
+ const parts = line.slice(1).trim().split(/\s+/);
127
+ const path = parts[1];
128
+ if (!path || !isSourceRepoSubmodule(path, sourceRepos))
129
+ continue;
130
+ if (status === '-') {
131
+ errors.push(`Uninitialized Git submodule: ${path}`);
132
+ }
133
+ else if (status === 'U') {
134
+ errors.push(`Conflicted Git submodule: ${path}`);
135
+ }
136
+ }
137
+ return errors;
138
+ }
98
139
  export async function runDoctor(target = process.cwd(), runner = realCommandRunner) {
99
140
  const lines = ['WPMoo doctor'];
100
141
  const errors = [];
142
+ const warnings = [];
101
143
  const metadata = await readMetadata(target);
102
144
  lines.push(`OK metadata ${markerPath}`);
103
145
  const engine = metadataString(metadata, 'engine') ?? 'compose';
@@ -160,9 +202,42 @@ export async function runDoctor(target = process.cwd(), runner = realCommandRunn
160
202
  catch (error) {
161
203
  errors.push(`Docker CLI check failed: ${errorMessage(error)}`);
162
204
  }
205
+ try {
206
+ await runner('docker', ['compose', 'version'], { cwd: target });
207
+ lines.push('OK docker compose');
208
+ }
209
+ catch (error) {
210
+ errors.push(`Docker Compose check failed: ${errorMessage(error)}`);
211
+ }
212
+ if (sourceRepos.length > 0) {
213
+ try {
214
+ const result = await runner('git', ['submodule', 'status', '--recursive'], { cwd: target });
215
+ const submoduleErrors = sourceSubmoduleStatusErrors(result.stdout, sourceRepos);
216
+ errors.push(...submoduleErrors);
217
+ if (submoduleErrors.length === 0) {
218
+ lines.push(`OK git submodules ${sourceRepos.length} checked`);
219
+ }
220
+ }
221
+ catch (error) {
222
+ if (isNotGitCheckoutError(error)) {
223
+ lines.push('OK git submodules skipped (not a git checkout)');
224
+ }
225
+ else {
226
+ errors.push(`Git submodule status check failed: ${errorMessage(error)}`);
227
+ }
228
+ }
229
+ }
230
+ try {
231
+ await runner('gh', ['auth', 'status'], { cwd: target });
232
+ lines.push('OK GitHub CLI auth');
233
+ }
234
+ catch (error) {
235
+ warnings.push(`WARN GitHub CLI auth: ${errorMessage(error)}`);
236
+ }
163
237
  if (errors.length > 0) {
164
238
  throw new Error(renderFailure(errors));
165
239
  }
240
+ lines.push(...warnings);
166
241
  lines.push('Doctor checks passed.');
167
242
  return lines.join('\n');
168
243
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wpmoo/odoo",
3
- "version": "0.8.36",
3
+ "version": "0.8.38",
4
4
  "description": "WPMoo Odoo lifecycle tooling for development, staging, and production workflows.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -38,6 +38,7 @@
38
38
  "scripts": {
39
39
  "prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
40
40
  "build": "tsc -p tsconfig.build.json",
41
+ "release:check": "bash scripts/release-check.sh",
41
42
  "test": "vitest run",
42
43
  "typecheck": "tsc -p tsconfig.json --noEmit"
43
44
  },