From 358c608100c9d5f6225575d0b15a6131fc425374 Mon Sep 17 00:00:00 2001 From: Maciej Krajowski-Kukiel Date: Thu, 20 Aug 2026 16:20:02 +0200 Subject: [PATCH 1/2] release script --- RELEASE.md | 108 ++++++++++++ scripts/release.mjs | 420 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 528 insertions(+) create mode 100644 RELEASE.md create mode 100644 scripts/release.mjs diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..acefdcc2 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,108 @@ +# Releasing modules + +Modules are released to the Partner Portal Marketplace with the interactive +release tool: + +```bash +node scripts/release.mjs +``` + +## Prerequisites + +- Node.js >= 20.12 (the script has no npm dependencies) +- `pos-cli` available in `PATH` +- A Partner Portal account with permission to publish the modules +- A reasonably clean working tree — version bumps and lock-file updates are + committed together at the end, so unrelated local changes will muddy that + commit + +## Walkthrough + +1. **Pick modules.** The checklist shows every `pos-module-*` directory that + contains a `pos-module.json`, with its current version. Modules are listed + in **release order** (see below) — the loop releases them top to bottom. + + | Key | Action | + |-----|--------| + | `↑` / `↓` (or `k` / `j`) | move | + | `space` | select / deselect | + | `←` / `→` | cycle bump type: `patch` → `minor` → `major` → `push only` | + | `a` | toggle all | + | `enter` | continue | + | `q` / `esc` / `ctrl-c` | quit | + + `push only` publishes the **current** version without bumping — useful for + retrying a release whose push failed after the version was already bumped. + +2. **Credentials.** You are asked for your Partner Portal email (prefilled + from `POS_PORTAL_EMAIL` or `git config user.email`) and password (hidden). + Setting the standard pos-cli env vars skips the prompts: + + ```bash + export POS_PORTAL_EMAIL=you@example.com + export POS_PORTAL_PASSWORD=... + ``` + + The password is only ever passed to `pos-cli modules push` via the + `POS_PORTAL_PASSWORD` environment variable, never on a command line. + +3. **Confirm.** The script shows the exact list, order, and target versions, + and asks before doing anything. + +4. **Release loop.** For each selected module, in order: + + ``` + pos-cli modules update # skipped if the module has no dependencies + pos-cli modules version --no-git # skipped for "push only" + pos-cli modules push --email + ``` + + - `modules update` refreshes `pos-module.lock.json` so the published + archive — and the commit CI checks — reference the parents released + earlier in the same run. + - A failed step marks the module as failed and **skips its remaining + steps**, but the loop continues with the next module. + +5. **Commit.** After the summary, the script offers a single combined git + commit of every released module's `pos-module.json`, + `pos-module.lock.json`, and `template-values.json` + (e.g. `Release common-styling@1.38.9, core@2.1.11`). Push it so CI picks + up the lock-file changes. Tags are intentionally **not** created — + per-module version tags like `2.1.11` would collide in the shared + monorepo history. + +## Release order + +Order is a topological sort of the `dependencies` declared in each module's +`pos-module.json`, seeded so the foundation modules come first: + +``` +common-styling -> core -> user +captchas -> captchas-{hcaptcha,recaptcha,recaptcha3,turnstile} +push-notifications -> chat +payments -> payments-{example-gateway,stripe} +...then the remaining independent modules +``` + +A parent is always published before its dependents, and `pos-cli modules push` +waits until publishing completes, so each dependent's `modules update` already +sees the parent's fresh version. When releasing a family (payments, captchas, +oauth, …), select the parent and its children in the same run and the ordering +is handled for you. + +## Gotchas + +- **Major bumps don't propagate automatically.** If you major-bump a parent + (e.g. core `2.x` → `3.0.0`), dependents declaring `"core": "^2.1.9"` will + correctly keep resolving to `2.x`. Verify compatibility, update the range in + each dependent's `pos-module.json` by hand, then release the dependents. +- **Version bumps are file-only** (`--no-git`). Until you accept the commit + offer (or commit manually), the bump exists only in your working tree. +- **A failed module doesn't stop the run.** Check the summary: if a parent + failed but its dependents succeeded, the dependents were published with + lock files pointing at the parent's *previous* version. To fix it, release + the parent, then give each affected dependent a fresh patch release + (`push only` won't work here — the marketplace already has that version). +- The version shown in the TUI comes from `pos-module.json`; + `modules//template-values.json` is kept in sync by + `pos-cli modules version`. diff --git a/scripts/release.mjs b/scripts/release.mjs new file mode 100644 index 00000000..5ecdcd1c --- /dev/null +++ b/scripts/release.mjs @@ -0,0 +1,420 @@ +#!/usr/bin/env node +// Interactive release tool for pos-module-* packages. +// +// Lists every pos-module-* directory containing a pos-module.json, lets you +// pick which ones to release and with what semver bump (patch/minor/major, +// or "push only" to publish the current version), then asks for your Partner +// Portal email + password and releases each selected module via: +// +// pos-cli modules update (refresh pos-module.lock.json) +// pos-cli modules version --no-git (skipped for "push only") +// pos-cli modules push --email (password via POS_PORTAL_PASSWORD) +// +// Modules are listed and released in dependency order (topological sort of +// the "dependencies" in each pos-module.json, seeded with common-styling, +// core, user first), so a parent module is always published before its +// dependents and each dependent's lock file picks up the fresh version. +// +// Bumps use --no-git because modules share this monorepo's git history and +// pos-cli's per-module tags (e.g. "2.1.11") would collide between modules. +// After a successful run the script offers a single combined git commit. +// +// Requires Node.js >= 20.12 (uses node:util styleText). Usage: +// node scripts/release.mjs + +import { readFile, readdir } from 'node:fs/promises'; +import { existsSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { spawnSync, execSync } from 'node:child_process'; +import { styleText } from 'node:util'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const BUMPS = ['patch', 'minor', 'major', 'push']; + +const KEY = { + ctrlC: '\x03', + escape: '\x1b', + backspace: '\x7f', + up: '\x1b[A', + down: '\x1b[B', + right: '\x1b[C', + left: '\x1b[D', +}; + +const ansi = { + clearLine: '\x1b[2K', + cursorUp: (n) => `\x1b[${n}A`, + hideCursor: '\x1b[?25l', + showCursor: '\x1b[?25h', +}; + +const bumpLabel = { + patch: styleText('green', 'patch'), + minor: styleText('yellow', 'minor'), + major: styleText('red', 'major'), + push: styleText('cyan', 'push only'), +}; + +const semverInc = (version, bump) => { + const match = version.match(/^(\d+)\.(\d+)\.(\d+)/); + if (!match) return null; + const [major, minor, patch] = match.slice(1).map(Number); + switch (bump) { + case 'major': + return `${major + 1}.0.0`; + case 'minor': + return `${major}.${minor + 1}.0`; + case 'patch': + return `${major}.${minor}.${patch + 1}`; + default: + return version; + } +}; + +const readManifest = async (dir) => { + try { + return JSON.parse(await readFile(path.join(dir, 'pos-module.json'), 'utf8')); + } catch { + return null; + } +}; + +const discoverModules = async () => { + const entries = (await readdir(ROOT)).filter((name) => name.startsWith('pos-module-')).sort(); + const modules = await Promise.all( + entries.map(async (name) => { + const dir = path.join(ROOT, name); + const manifest = await readManifest(dir); + if (!manifest) return null; + return { + name, + dir, + machineName: manifest.machine_name, + version: manifest.version, + dependencyNames: Object.keys(manifest.dependencies ?? {}), + selected: false, + bump: 'patch', + }; + }) + ); + return sortByReleaseOrder(modules.filter(Boolean)); +}; + +// Topological sort by declared dependencies, so parents are released before +// their dependents. Seeded with the foundation modules first so the overall +// order starts: common-styling, core, user, then everything else. +const FOUNDATION_ORDER = ['common-styling', 'core', 'user']; + +const sortByReleaseOrder = (modules) => { + const seeded = modules.toSorted((a, b) => { + const rank = (m) => { + const i = FOUNDATION_ORDER.indexOf(m.machineName); + return i === -1 ? FOUNDATION_ORDER.length : i; + }; + return rank(a) - rank(b) || a.name.localeCompare(b.name); + }); + + const byMachineName = new Map(seeded.map((m) => [m.machineName, m])); + const sorted = []; + const done = new Set(); + const visit = (m, chain) => { + if (done.has(m.machineName) || chain.has(m.machineName)) return; + chain.add(m.machineName); + for (const dep of m.dependencyNames) { + const parent = byMachineName.get(dep); + if (parent) visit(parent, chain); + } + chain.delete(m.machineName); + done.add(m.machineName); + sorted.push(m); + }; + for (const m of seeded) visit(m, new Set()); + return sorted; +}; + +// --- low-level input helpers ------------------------------------------------- + +// Splits a raw stdin chunk into individual keys — escape sequences (e.g. arrow +// keys) and plain characters can arrive batched in a single chunk. +const tokenizeKeys = (data) => { + const keys = []; + let i = 0; + while (i < data.length) { + if (data[i] === KEY.escape && data[i + 1] === '[') { + let end = i + 2; + while (end < data.length && !/[@-~]/.test(data[end])) end += 1; + keys.push(data.slice(i, end + 1)); + i = end + 1; + } else { + keys.push(data[i]); + i += 1; + } + } + return keys; +}; + +const readKeys = (onKey) => { + let stopped = false; + process.stdin.setRawMode(true); + process.stdin.resume(); + const handler = (data) => { + for (const key of tokenizeKeys(data.toString('utf8'))) { + if (stopped) break; + onKey(key); + } + }; + process.stdin.on('data', handler); + return () => { + stopped = true; + process.stdin.off('data', handler); + process.stdin.setRawMode(false); + process.stdin.pause(); + }; +}; + +const abort = () => { + process.stdout.write(`${ansi.showCursor}\naborted\n`); + process.exit(130); +}; + +const promptText = (question, defaultValue = '') => + new Promise((resolve) => { + const suffix = defaultValue ? styleText('dim', ` [${defaultValue}]`) : ''; + process.stdout.write(`${question}${suffix}: `); + let buffer = ''; + const stop = readKeys((key) => { + if (key === KEY.ctrlC) abort(); + if (key === '\r' || key === '\n') { + process.stdout.write('\n'); + stop(); + resolve(buffer.trim() || defaultValue); + } else if (key === KEY.backspace || key === '\b') { + if (buffer.length) { + buffer = buffer.slice(0, -1); + process.stdout.write('\b \b'); + } + } else if (key >= ' ') { + buffer += key; + process.stdout.write(key); + } + }); + }); + +const promptHidden = (question) => + new Promise((resolve) => { + process.stdout.write(`${question}: `); + let buffer = ''; + const stop = readKeys((key) => { + if (key === KEY.ctrlC) abort(); + if (key === '\r' || key === '\n') { + process.stdout.write('\n'); + stop(); + resolve(buffer); + } else if (key === KEY.backspace || key === '\b') { + if (buffer.length) { + buffer = buffer.slice(0, -1); + process.stdout.write('\b \b'); + } + } else if (key >= ' ') { + buffer += key; + process.stdout.write('*'); + } + }); + }); + +const promptYesNo = async (question) => /^y(es)?$/i.test(await promptText(`${question} (y/N)`)); + +// --- checklist TUI ----------------------------------------------------------- + +const selectModules = (modules) => + new Promise((resolve) => { + let cursor = 0; + let linesDrawn = 0; + const nameWidth = Math.max(...modules.map((m) => m.name.length)) + 2; + + const row = (m, active) => { + const pointer = active ? styleText('cyan', '❯') : ' '; + const box = m.selected ? styleText('green', '[x]') : styleText('dim', '[ ]'); + const name = m.name.padEnd(nameWidth); + let versionInfo = styleText('dim', m.version); + if (m.selected) { + versionInfo = + m.bump === 'push' + ? `${m.version} ${bumpLabel.push}` + : `${m.version} ${styleText('dim', '→')} ${styleText('bold', semverInc(m.version, m.bump))} ${bumpLabel[m.bump]}`; + } + return ` ${pointer} ${box} ${name} ${versionInfo}`; + }; + + const render = () => { + if (linesDrawn) process.stdout.write(ansi.cursorUp(linesDrawn)); + const lines = [ + styleText('bold', 'platformOS module release') + styleText('dim', ' (listed in release order — dependencies first)'), + styleText('dim', ' ↑/↓ move · space select · ←/→ bump type · a toggle all · enter continue · q quit'), + '', + ...modules.map((m, i) => row(m, i === cursor)), + '', + ]; + process.stdout.write(lines.map((line) => `${ansi.clearLine}${line}`).join('\n') + '\n'); + linesDrawn = lines.length; + }; + + process.stdout.write(ansi.hideCursor); + render(); + + const stop = readKeys((key) => { + if (key === KEY.ctrlC || key === 'q' || key === KEY.escape) { + stop(); + abort(); + } else if (key === KEY.up || key === 'k') { + cursor = (cursor - 1 + modules.length) % modules.length; + } else if (key === KEY.down || key === 'j') { + cursor = (cursor + 1) % modules.length; + } else if (key === ' ') { + modules[cursor].selected = !modules[cursor].selected; + } else if (key === KEY.right || key === KEY.left) { + const module = modules[cursor]; + module.selected = true; + const delta = key === KEY.right ? 1 : -1; + module.bump = BUMPS.at((BUMPS.indexOf(module.bump) + delta) % BUMPS.length); + } else if (key === 'a') { + const allSelected = modules.every((m) => m.selected); + for (const m of modules) m.selected = !allSelected; + } else if (key === '\r' || key === '\n') { + if (!modules.some((m) => m.selected)) return; + stop(); + process.stdout.write(ansi.showCursor); + resolve(modules.filter((m) => m.selected)); + return; + } + render(); + }); + }); + +// --- release steps ----------------------------------------------------------- + +const releaseModule = async (module, email, password) => { + process.stdout.write(`\n${styleText('bold', `── ${module.name} ──`)}\n`); + + // Refresh dependencies + pos-module.lock.json so the published archive (and + // the eventual git commit CI checks) references the just-released parents. + if (module.dependencyNames.length) { + const update = spawnSync('pos-cli', ['modules', 'update'], { cwd: module.dir, stdio: 'inherit' }); + if (update.status !== 0) return { ...module, ok: false, stage: 'dependency update' }; + } + + if (module.bump !== 'push') { + const bump = spawnSync('pos-cli', ['modules', 'version', module.bump, '--no-git'], { + cwd: module.dir, + stdio: 'inherit', + }); + if (bump.status !== 0) return { ...module, ok: false, stage: 'version bump' }; + module.newVersion = (await readManifest(module.dir))?.version ?? semverInc(module.version, module.bump); + console.log(styleText('green', `version bumped to ${module.newVersion}`)); + } else { + module.newVersion = module.version; + } + + const push = spawnSync('pos-cli', ['modules', 'push', '--email', email], { + cwd: module.dir, + stdio: 'inherit', + env: { ...process.env, POS_PORTAL_PASSWORD: password }, + }); + if (push.status !== 0) return { ...module, ok: false, stage: 'push' }; + return { ...module, ok: true }; +}; + +const offerGitCommit = async (results) => { + const released = results.filter((r) => r.ok); + if (!released.length) return; + try { + execSync('git rev-parse --git-dir', { cwd: ROOT, stdio: 'pipe' }); + } catch { + return; + } + + // Version bumps touch pos-module.json + template-values.json; the update + // step touches pos-module.lock.json (even for "push only" releases). + const files = released + .flatMap((r) => [ + path.join(r.name, 'pos-module.json'), + path.join(r.name, 'pos-module.lock.json'), + path.join(r.name, 'modules', r.machineName, 'template-values.json'), + ]) + .filter((f) => existsSync(path.join(ROOT, f))); + const quoted = files.map((f) => `'${f}'`).join(' '); + + const dirty = execSync(`git status --porcelain -- ${quoted}`, { cwd: ROOT, stdio: 'pipe' }) + .toString() + .trim(); + if (!dirty) return; + + console.log(); + if (!(await promptYesNo('Commit version bumps and lock files to git?'))) return; + + const message = `Release ${released.map((r) => `${r.machineName}@${r.newVersion}`).join(', ')}`; + try { + execSync(`git add ${quoted}`, { cwd: ROOT, stdio: 'inherit' }); + execSync(`git commit -m '${message}'`, { cwd: ROOT, stdio: 'inherit' }); + } catch { + console.log(styleText('red', 'git commit failed — commit manually.')); + } +}; + +// --- main -------------------------------------------------------------------- + +if (!process.stdin.isTTY || !process.stdout.isTTY) { + console.error('This script is interactive and requires a TTY.'); + process.exit(1); +} +if (spawnSync('pos-cli', ['-V'], { stdio: 'pipe' }).error) { + console.error('pos-cli not found in PATH.'); + process.exit(1); +} + +const modules = await discoverModules(); +if (!modules.length) { + console.error(`No pos-module-* directories with pos-module.json found in ${ROOT}`); + process.exit(1); +} + +const selected = await selectModules(modules); + +const defaultEmail = + process.env.POS_PORTAL_EMAIL || + execSync('git config user.email', { cwd: ROOT, stdio: 'pipe' }).toString().trim(); +const email = await promptText('Partner Portal email', defaultEmail); +if (!email) { + console.error(styleText('red', 'Email is required.')); + process.exit(1); +} +const password = process.env.POS_PORTAL_PASSWORD || (await promptHidden('Partner Portal password')); +if (!password) { + console.error(styleText('red', 'Password is required.')); + process.exit(1); +} + +console.log(`\nReleasing ${selected.length} module(s) in this order as ${styleText('bold', email)}:`); +for (const m of selected) { + const target = m.bump === 'push' ? m.version : semverInc(m.version, m.bump); + console.log(` • ${m.name} ${m.version} → ${styleText('bold', target)} (${bumpLabel[m.bump]})`); +} +if (!(await promptYesNo('\nProceed?'))) abort(); + +const results = []; +for (const m of selected) { + results.push(await releaseModule(m, email, password)); +} + +console.log(`\n${styleText('bold', 'Summary')}`); +for (const r of results) { + console.log( + r.ok + ? ` ${styleText('green', '✔')} ${r.name} ${styleText('dim', 'released as')} ${styleText('bold', r.newVersion)}` + : ` ${styleText('red', '✖')} ${r.name} ${styleText('red', `failed at ${r.stage}`)}` + ); +} + +await offerGitCommit(results); +process.exit(results.every((r) => r.ok) ? 0 : 1); From 2e28811699b40d67859bdb3a981f8cafb80e406f Mon Sep 17 00:00:00 2001 From: Maciej Krajowski-Kukiel Date: Thu, 20 Aug 2026 16:34:55 +0200 Subject: [PATCH 2/2] bump modules versions and release with a script --- CLAUDE.md | 39 +++-- RELEASE.md | 38 ++-- .../captchas_hcaptcha/template-values.json | 9 - pos-module-captchas-hcaptcha/pos-module.json | 4 +- .../pos-module.lock.json | 4 +- .../captchas_recaptcha/template-values.json | 9 - pos-module-captchas-recaptcha/pos-module.json | 4 +- .../pos-module.lock.json | 4 +- .../captchas_recaptcha3/template-values.json | 9 - .../pos-module.json | 4 +- .../pos-module.lock.json | 4 +- .../captchas_turnstile/template-values.json | 9 - pos-module-captchas-turnstile/pos-module.json | 4 +- .../pos-module.lock.json | 4 +- .../modules/captchas/template-values.json | 7 - pos-module-captchas/pos-module.json | 4 +- pos-module-captchas/pos-module.lock.json | 2 +- .../modules/chat/template-values.json | 12 -- pos-module-chat/pos-module.json | 4 +- pos-module-chat/pos-module.lock.json | 8 +- pos-module-core/README.md | 2 +- pos-module-core/modules/core/package.json | 2 +- .../modules/core/template-values.json | 7 - pos-module-core/pos-module.json | 3 +- .../data_export_api/template-values.json | 9 - pos-module-data-export-api/package.json | 2 +- pos-module-data-export-api/pos-module.json | 4 +- .../pos-module.lock.json | 2 +- .../oauth_facebook/template-values.json | 9 - pos-module-oauth-facebook/pos-module.json | 2 +- .../pos-module.lock.json | 2 +- .../modules/oauth_github/template-values.json | 9 - pos-module-oauth-github/pos-module.json | 2 +- pos-module-oauth-github/pos-module.lock.json | 2 +- .../modules/oauth_google/template-values.json | 9 - pos-module-oauth-google/pos-module.json | 2 +- pos-module-oauth-google/pos-module.lock.json | 2 +- pos-module-openai/package.json | 2 +- pos-module-openai/pos-module.json | 2 +- pos-module-openai/pos-module.lock.json | 2 +- pos-module-openai/template-values.json | 9 - .../payments_example_gateway/package.json | 2 +- .../template-values.json | 10 -- .../pos-module.json | 4 +- .../pos-module.lock.json | 10 +- .../modules/payments_stripe/package.json | 2 +- .../payments_stripe/template-values.json | 14 -- pos-module-payments-stripe/pos-module.json | 2 +- .../pos-module.lock.json | 10 +- .../modules/payments/package.json | 2 +- .../modules/payments/template-values.json | 9 - pos-module-payments/pos-module.json | 2 +- pos-module-payments/pos-module.lock.json | 8 +- .../push_notifications/template-values.json | 11 -- pos-module-push-notifications/pos-module.json | 2 +- .../pos-module.lock.json | 6 +- .../modules/reports/template-values.json | 11 -- pos-module-reports/pos-module.json | 2 +- pos-module-reports/pos-module.lock.json | 6 +- pos-module-tests/CLAUDE.md | 2 +- .../modules/tests/template-values.json | 7 - pos-module-tests/pos-module.json | 7 +- .../modules/user_invites/template-values.json | 11 -- pos-module-user-invites/pos-module.json | 4 +- pos-module-user-invites/pos-module.lock.json | 6 +- pos-module-user/CLAUDE.md | 2 +- pos-module-user/modules/user/package.json | 2 +- .../modules/user/template-values.json | 10 -- pos-module-user/pos-module.json | 2 +- pos-module-user/pos-module.lock.json | 4 +- scripts/release.mjs | 162 ++++++++++++++++-- 71 files changed, 281 insertions(+), 318 deletions(-) delete mode 100644 pos-module-captchas-hcaptcha/modules/captchas_hcaptcha/template-values.json delete mode 100644 pos-module-captchas-recaptcha/modules/captchas_recaptcha/template-values.json delete mode 100644 pos-module-captchas-recaptcha3/modules/captchas_recaptcha3/template-values.json delete mode 100644 pos-module-captchas-turnstile/modules/captchas_turnstile/template-values.json delete mode 100644 pos-module-captchas/modules/captchas/template-values.json delete mode 100644 pos-module-chat/modules/chat/template-values.json delete mode 100644 pos-module-core/modules/core/template-values.json delete mode 100644 pos-module-data-export-api/modules/data_export_api/template-values.json delete mode 100644 pos-module-oauth-facebook/modules/oauth_facebook/template-values.json delete mode 100644 pos-module-oauth-github/modules/oauth_github/template-values.json delete mode 100644 pos-module-oauth-google/modules/oauth_google/template-values.json delete mode 100644 pos-module-openai/template-values.json delete mode 100644 pos-module-payments-example-gateway/modules/payments_example_gateway/template-values.json delete mode 100644 pos-module-payments-stripe/modules/payments_stripe/template-values.json delete mode 100644 pos-module-payments/modules/payments/template-values.json delete mode 100644 pos-module-push-notifications/modules/push_notifications/template-values.json delete mode 100644 pos-module-reports/modules/reports/template-values.json delete mode 100644 pos-module-tests/modules/tests/template-values.json delete mode 100644 pos-module-user-invites/modules/user_invites/template-values.json delete mode 100644 pos-module-user/modules/user/template-values.json diff --git a/CLAUDE.md b/CLAUDE.md index f7891f32..de9ca796 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -69,7 +69,7 @@ pos-cli test run [test-name] Each module is **independently distributable** but shares development infrastructure: -- **Independent**: Own git history, versioning (template-values.json), marketplace distribution +- **Independent**: Own git history, versioning (pos-module.json), marketplace distribution - **Hierarchical**: Complex modules contain `modules/` subdirectory with nested dependency modules - **Composable**: Modules integrate via hooks, commands, events without modifying source code @@ -79,25 +79,26 @@ Each module is **independently distributable** but shares development infrastruc pos-module-/ ├── modules/ │ └── / # The actual module (distributed part) -│ ├── public/ -│ │ ├── lib/ -│ │ │ ├── commands/ # Business logic (Build/Check/Execute pattern) -│ │ │ ├── queries/ # Data access wrappers -│ │ │ ├── hooks/ # Integration hooks (hook_*.liquid) -│ │ │ ├── consumers/ # Event consumers -│ │ │ ├── validations/ # Input validators -│ │ │ └── helpers/ # Utility functions -│ │ ├── graphql/ # GraphQL queries/mutations -│ │ ├── views/ -│ │ │ ├── pages/ # Endpoints -│ │ │ ├── partials/ # Reusable components -│ │ │ └── layouts/ # Page templates -│ │ ├── assets/ # CSS, JS, images -│ │ ├── schema/ # Database schema -│ │ ├── translations/ # i18n -│ │ └── api_calls/ # External API templates -│ └── template-values.json # Module metadata & dependencies +│ └── public/ +│ ├── lib/ +│ │ ├── commands/ # Business logic (Build/Check/Execute pattern) +│ │ ├── queries/ # Data access wrappers +│ │ ├── hooks/ # Integration hooks (hook_*.liquid) +│ │ ├── consumers/ # Event consumers +│ │ ├── validations/ # Input validators +│ │ └── helpers/ # Utility functions +│ ├── graphql/ # GraphQL queries/mutations +│ ├── views/ +│ │ ├── pages/ # Endpoints +│ │ ├── partials/ # Reusable components +│ │ └── layouts/ # Page templates +│ ├── assets/ # CSS, JS, images +│ ├── schema/ # Database schema +│ ├── translations/ # i18n +│ └── api_calls/ # External API templates ├── app/ # Example application (NOT distributed) +├── pos-module.json # Module manifest: metadata & dependencies +├── pos-module.lock.json # Resolved dependency versions ├── package.json # npm scripts for development └── README.md # Module documentation ``` diff --git a/RELEASE.md b/RELEASE.md index acefdcc2..1ba9d9ab 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -52,21 +52,36 @@ node scripts/release.mjs 4. **Release loop.** For each selected module, in order: ``` - pos-cli modules update # skipped if the module has no dependencies + pos-cli modules update --dev # skipped if the module has no dependencies pos-cli modules version --no-git # skipped for "push only" pos-cli modules push --email ``` + - Before `modules update`, any declared range that cannot reach a parent + released earlier in this run is bumped in `pos-module.json` (e.g. + `^0.0.13` → `^0.0.14` — npm caret semantics pin `^0.0.x` to that exact + patch, so the range would otherwise never resolve to the new version). + Major jumps are never auto-bumped. - `modules update` refreshes `pos-module.lock.json` so the published archive — and the commit CI checks — reference the parents released earlier in the same run. - A failed step marks the module as failed and **skips its remaining steps**, but the loop continues with the next module. -5. **Commit.** After the summary, the script offers a single combined git - commit of every released module's `pos-module.json`, - `pos-module.lock.json`, and `template-values.json` - (e.g. `Release common-styling@1.38.9, core@2.1.11`). Push it so CI picks +5. **Dependent sync.** After the loop, every module in the repo that was + *not* released this run but declares a just-released module in its + `dependencies` or `devDependencies` gets the same treatment: stale ranges + bumped (same-major releases only) and `pos-cli modules update ` run + to refresh its lock file. This matters especially for devDependencies — + they are not part of the release order, so releasing e.g. `oauth_github` + alone would otherwise leave `pos-module-user`'s lock pinned to the old + version, and CI (which installs from the frozen lock) would keep checking + against it. + +6. **Commit.** After the summary, the script offers a single combined git + commit of the `pos-module.json` and `pos-module.lock.json` of every + released module and every synced dependent + (e.g. `Release oauth_github@0.0.14; sync user`). Push it so CI picks up the lock-file changes. Tags are intentionally **not** created — per-module version tags like `2.1.11` would collide in the shared monorepo history. @@ -94,8 +109,10 @@ is handled for you. - **Major bumps don't propagate automatically.** If you major-bump a parent (e.g. core `2.x` → `3.0.0`), dependents declaring `"core": "^2.1.9"` will - correctly keep resolving to `2.x`. Verify compatibility, update the range in - each dependent's `pos-module.json` by hand, then release the dependents. + correctly keep resolving to `2.x` — the range auto-bump and dependent sync + deliberately skip major jumps and print a warning instead. Verify + compatibility, update the range in each dependent's `pos-module.json` by + hand, then release the dependents. - **Version bumps are file-only** (`--no-git`). Until you accept the commit offer (or commit manually), the bump exists only in your working tree. - **A failed module doesn't stop the run.** Check the summary: if a parent @@ -103,6 +120,7 @@ is handled for you. lock files pointing at the parent's *previous* version. To fix it, release the parent, then give each affected dependent a fresh patch release (`push only` won't work here — the marketplace already has that version). -- The version shown in the TUI comes from `pos-module.json`; - `modules//template-values.json` is kept in sync by - `pos-cli modules version`. +- `pos-module.json` is the single source of truth for module metadata. + The legacy `modules//template-values.json` files were removed + (`pos-cli modules migrate`); copies of them that appear under `modules/` + are vendored dependency downloads — gitignored, never edited by hand. diff --git a/pos-module-captchas-hcaptcha/modules/captchas_hcaptcha/template-values.json b/pos-module-captchas-hcaptcha/modules/captchas_hcaptcha/template-values.json deleted file mode 100644 index 27cf398d..00000000 --- a/pos-module-captchas-hcaptcha/modules/captchas_hcaptcha/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pOS Captchas hCaptcha", - "machine_name": "captchas_hcaptcha", - "type": "module", - "version": "1.1.0", - "dependencies": { - "captchas": "^1.0.0" - } -} diff --git a/pos-module-captchas-hcaptcha/pos-module.json b/pos-module-captchas-hcaptcha/pos-module.json index 8587c30e..8f1f3650 100644 --- a/pos-module-captchas-hcaptcha/pos-module.json +++ b/pos-module-captchas-hcaptcha/pos-module.json @@ -1,11 +1,11 @@ { "name": "pOS Captchas hCaptcha", "machine_name": "captchas_hcaptcha", - "version": "1.1.0", + "version": "1.1.1", "dependencies": { "captchas": "^1.1.0" }, "devDependencies": { "tests": "^1.3.4" } -} +} \ No newline at end of file diff --git a/pos-module-captchas-hcaptcha/pos-module.lock.json b/pos-module-captchas-hcaptcha/pos-module.lock.json index 3e3e3d37..2d9fc2f6 100644 --- a/pos-module-captchas-hcaptcha/pos-module.lock.json +++ b/pos-module-captchas-hcaptcha/pos-module.lock.json @@ -1,9 +1,9 @@ { "dependencies": { - "captchas": "1.1.0" + "captchas": "1.1.1" }, "devDependencies": { - "tests": "1.3.4" + "tests": "1.3.5" }, "registries": { "tests": "https://partners.platformos.com", diff --git a/pos-module-captchas-recaptcha/modules/captchas_recaptcha/template-values.json b/pos-module-captchas-recaptcha/modules/captchas_recaptcha/template-values.json deleted file mode 100644 index ed80769f..00000000 --- a/pos-module-captchas-recaptcha/modules/captchas_recaptcha/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pOS Captchas reCAPTCHA v2", - "machine_name": "captchas_recaptcha", - "type": "module", - "version": "1.1.0", - "dependencies": { - "captchas": "^1.0.0" - } -} diff --git a/pos-module-captchas-recaptcha/pos-module.json b/pos-module-captchas-recaptcha/pos-module.json index 2dc472c3..c3f11061 100644 --- a/pos-module-captchas-recaptcha/pos-module.json +++ b/pos-module-captchas-recaptcha/pos-module.json @@ -1,11 +1,11 @@ { "name": "pOS Captchas reCAPTCHA v2", "machine_name": "captchas_recaptcha", - "version": "1.1.0", + "version": "1.1.1", "dependencies": { "captchas": "^1.1.0" }, "devDependencies": { "tests": "^1.3.4" } -} +} \ No newline at end of file diff --git a/pos-module-captchas-recaptcha/pos-module.lock.json b/pos-module-captchas-recaptcha/pos-module.lock.json index 3e3e3d37..2d9fc2f6 100644 --- a/pos-module-captchas-recaptcha/pos-module.lock.json +++ b/pos-module-captchas-recaptcha/pos-module.lock.json @@ -1,9 +1,9 @@ { "dependencies": { - "captchas": "1.1.0" + "captchas": "1.1.1" }, "devDependencies": { - "tests": "1.3.4" + "tests": "1.3.5" }, "registries": { "tests": "https://partners.platformos.com", diff --git a/pos-module-captchas-recaptcha3/modules/captchas_recaptcha3/template-values.json b/pos-module-captchas-recaptcha3/modules/captchas_recaptcha3/template-values.json deleted file mode 100644 index 55085978..00000000 --- a/pos-module-captchas-recaptcha3/modules/captchas_recaptcha3/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pOS Captchas reCAPTCHA v3", - "machine_name": "captchas_recaptcha3", - "type": "module", - "version": "1.1.0", - "dependencies": { - "captchas": "^1.0.0" - } -} diff --git a/pos-module-captchas-recaptcha3/pos-module.json b/pos-module-captchas-recaptcha3/pos-module.json index 589c5daa..e5be1023 100644 --- a/pos-module-captchas-recaptcha3/pos-module.json +++ b/pos-module-captchas-recaptcha3/pos-module.json @@ -1,11 +1,11 @@ { "name": "pOS Captchas reCAPTCHA v3", "machine_name": "captchas_recaptcha3", - "version": "1.1.0", + "version": "1.1.1", "dependencies": { "captchas": "^1.1.0" }, "devDependencies": { "tests": "^1.3.4" } -} +} \ No newline at end of file diff --git a/pos-module-captchas-recaptcha3/pos-module.lock.json b/pos-module-captchas-recaptcha3/pos-module.lock.json index 3e3e3d37..2d9fc2f6 100644 --- a/pos-module-captchas-recaptcha3/pos-module.lock.json +++ b/pos-module-captchas-recaptcha3/pos-module.lock.json @@ -1,9 +1,9 @@ { "dependencies": { - "captchas": "1.1.0" + "captchas": "1.1.1" }, "devDependencies": { - "tests": "1.3.4" + "tests": "1.3.5" }, "registries": { "tests": "https://partners.platformos.com", diff --git a/pos-module-captchas-turnstile/modules/captchas_turnstile/template-values.json b/pos-module-captchas-turnstile/modules/captchas_turnstile/template-values.json deleted file mode 100644 index 46d0c312..00000000 --- a/pos-module-captchas-turnstile/modules/captchas_turnstile/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pOS Captchas Turnstile", - "machine_name": "captchas_turnstile", - "type": "module", - "version": "1.1.0", - "dependencies": { - "captchas": "^1.0.0" - } -} diff --git a/pos-module-captchas-turnstile/pos-module.json b/pos-module-captchas-turnstile/pos-module.json index 7eebac45..e5dfc8d9 100644 --- a/pos-module-captchas-turnstile/pos-module.json +++ b/pos-module-captchas-turnstile/pos-module.json @@ -1,11 +1,11 @@ { "name": "pOS Captchas Turnstile", "machine_name": "captchas_turnstile", - "version": "1.1.0", + "version": "1.1.1", "dependencies": { "captchas": "^1.1.0" }, "devDependencies": { "tests": "^1.3.4" } -} +} \ No newline at end of file diff --git a/pos-module-captchas-turnstile/pos-module.lock.json b/pos-module-captchas-turnstile/pos-module.lock.json index 3e3e3d37..2d9fc2f6 100644 --- a/pos-module-captchas-turnstile/pos-module.lock.json +++ b/pos-module-captchas-turnstile/pos-module.lock.json @@ -1,9 +1,9 @@ { "dependencies": { - "captchas": "1.1.0" + "captchas": "1.1.1" }, "devDependencies": { - "tests": "1.3.4" + "tests": "1.3.5" }, "registries": { "tests": "https://partners.platformos.com", diff --git a/pos-module-captchas/modules/captchas/template-values.json b/pos-module-captchas/modules/captchas/template-values.json deleted file mode 100644 index 7d2f76cf..00000000 --- a/pos-module-captchas/modules/captchas/template-values.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "Pos Module Captchas", - "machine_name": "captchas", - "type": "module", - "version": "1.1.0", - "dependencies": {} -} diff --git a/pos-module-captchas/pos-module.json b/pos-module-captchas/pos-module.json index 3ecb527a..e2121725 100644 --- a/pos-module-captchas/pos-module.json +++ b/pos-module-captchas/pos-module.json @@ -1,9 +1,9 @@ { "name": "Pos Module Captchas", "machine_name": "captchas", - "version": "1.1.0", + "version": "1.1.1", "dependencies": {}, "devDependencies": { "tests": "^1.3.4" } -} +} \ No newline at end of file diff --git a/pos-module-captchas/pos-module.lock.json b/pos-module-captchas/pos-module.lock.json index 7d8962ec..3cb7eb13 100644 --- a/pos-module-captchas/pos-module.lock.json +++ b/pos-module-captchas/pos-module.lock.json @@ -1,7 +1,7 @@ { "dependencies": {}, "devDependencies": { - "tests": "1.3.4" + "tests": "1.3.5" }, "registries": { "tests": "https://partners.platformos.com" diff --git a/pos-module-chat/modules/chat/template-values.json b/pos-module-chat/modules/chat/template-values.json deleted file mode 100644 index 487fec2a..00000000 --- a/pos-module-chat/modules/chat/template-values.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "Pos Module Chat", - "machine_name": "chat", - "type": "module", - "version": "2.1.6", - "dependencies": { - "core": "^2.1.9", - "user": "^5.3.0", - "common-styling": "^1.38.7", - "push_notifications": "^2.0.1" - } -} diff --git a/pos-module-chat/pos-module.json b/pos-module-chat/pos-module.json index 1f170408..171a813a 100644 --- a/pos-module-chat/pos-module.json +++ b/pos-module-chat/pos-module.json @@ -1,11 +1,11 @@ { "machine_name": "chat", "name": "Pos Module Chat", - "version": "2.1.6", + "version": "2.1.8", "dependencies": { "core": "^2.1.9", "user": "^5.3.0", "common-styling": "^1.38.7", "push_notifications": "^2.0.1" } -} +} \ No newline at end of file diff --git a/pos-module-chat/pos-module.lock.json b/pos-module-chat/pos-module.lock.json index 1a76f732..914ef830 100644 --- a/pos-module-chat/pos-module.lock.json +++ b/pos-module-chat/pos-module.lock.json @@ -1,9 +1,9 @@ { "dependencies": { - "core": "2.1.9", - "user": "5.3.0", - "common-styling": "1.38.7", - "push_notifications": "2.0.1" + "core": "2.1.10", + "user": "5.3.1", + "common-styling": "1.38.8", + "push_notifications": "2.0.2" }, "devDependencies": {}, "registries": { diff --git a/pos-module-core/README.md b/pos-module-core/README.md index 89692c7e..b4e7a6cd 100644 --- a/pos-module-core/README.md +++ b/pos-module-core/README.md @@ -446,7 +446,7 @@ You can store small data in a session. A session is connected with the current b ## Module registry -Module information is automatically registered based on the module's `template-values.json` file. +Module information is automatically registered based on the module's `pos-module.json` manifest. It is possible to list the registered modules with diff --git a/pos-module-core/modules/core/package.json b/pos-module-core/modules/core/package.json index 49515a04..05a3e982 100644 --- a/pos-module-core/modules/core/package.json +++ b/pos-module-core/modules/core/package.json @@ -4,7 +4,7 @@ "description": "Module description", "type": "module", "scripts": { - "version": "(cd ../../ && pos-cli modules version core -p) && git add template-values.json && auto-changelog -p && git add CHANGELOG.md" + "version": "(cd ../.. && pos-cli modules version --no-git && git add pos-module.json) && auto-changelog -p && git add CHANGELOG.md" }, "repository": { "type": "git", diff --git a/pos-module-core/modules/core/template-values.json b/pos-module-core/modules/core/template-values.json deleted file mode 100644 index 6acd8c93..00000000 --- a/pos-module-core/modules/core/template-values.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "Pos Module Core", - "machine_name": "core", - "type": "module", - "version": "2.1.9", - "dependencies": {} -} diff --git a/pos-module-core/pos-module.json b/pos-module-core/pos-module.json index fefd5c02..99ec0398 100644 --- a/pos-module-core/pos-module.json +++ b/pos-module-core/pos-module.json @@ -1,5 +1,6 @@ { "machine_name": "core", "version": "2.1.10", - "name": "Pos Module Core" + "name": "Pos Module Core", + "dependencies": {} } diff --git a/pos-module-data-export-api/modules/data_export_api/template-values.json b/pos-module-data-export-api/modules/data_export_api/template-values.json deleted file mode 100644 index d63ea0db..00000000 --- a/pos-module-data-export-api/modules/data_export_api/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pos-module-data-export-api", - "machine_name": "data_export_api", - "type": "module", - "version": "0.2.1", - "dependencies": { - "core": "^2.1.9" - } -} diff --git a/pos-module-data-export-api/package.json b/pos-module-data-export-api/package.json index 49ee208b..73147d31 100644 --- a/pos-module-data-export-api/package.json +++ b/pos-module-data-export-api/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "API endpoints to trigger the data export and retrieve URL to the data", "scripts": { - "version": "(cd ../../ && pos-cli modules version pos-module-data-export-api -p) && git add template-values.json && auto-changelog -p && git add CHANGELOG.md", + "version": "pos-cli modules version --no-git && git add pos-module.json && auto-changelog -p && git add CHANGELOG.md", "api-tests": "playwright test tests --project=api-tests" }, "repository": { diff --git a/pos-module-data-export-api/pos-module.json b/pos-module-data-export-api/pos-module.json index 5e2dfb44..9b80ed0e 100644 --- a/pos-module-data-export-api/pos-module.json +++ b/pos-module-data-export-api/pos-module.json @@ -3,6 +3,6 @@ "core": "^2.1.9" }, "machine_name": "data_export_api", - "version": "0.2.1", + "version": "0.2.2", "name": "pos-module-data-export-api" -} +} \ No newline at end of file diff --git a/pos-module-data-export-api/pos-module.lock.json b/pos-module-data-export-api/pos-module.lock.json index 627bf307..d7481ef1 100644 --- a/pos-module-data-export-api/pos-module.lock.json +++ b/pos-module-data-export-api/pos-module.lock.json @@ -1,6 +1,6 @@ { "dependencies": { - "core": "2.1.9" + "core": "2.1.10" }, "devDependencies": {}, "registries": { diff --git a/pos-module-oauth-facebook/modules/oauth_facebook/template-values.json b/pos-module-oauth-facebook/modules/oauth_facebook/template-values.json deleted file mode 100644 index 932282ac..00000000 --- a/pos-module-oauth-facebook/modules/oauth_facebook/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pos-module-oauth-facebook", - "machine_name": "oauth_facebook", - "type": "module", - "version": "0.0.5", - "dependencies": { - "core": "^2.1.9" - } -} diff --git a/pos-module-oauth-facebook/pos-module.json b/pos-module-oauth-facebook/pos-module.json index a594dcb1..e562f456 100644 --- a/pos-module-oauth-facebook/pos-module.json +++ b/pos-module-oauth-facebook/pos-module.json @@ -1,6 +1,6 @@ { "machine_name": "oauth_facebook", - "version": "0.0.5", + "version": "0.0.6", "name": "pos-module-oauth-facebook", "dependencies": { "core": "^2.1.9" diff --git a/pos-module-oauth-facebook/pos-module.lock.json b/pos-module-oauth-facebook/pos-module.lock.json index 627bf307..d7481ef1 100644 --- a/pos-module-oauth-facebook/pos-module.lock.json +++ b/pos-module-oauth-facebook/pos-module.lock.json @@ -1,6 +1,6 @@ { "dependencies": { - "core": "2.1.9" + "core": "2.1.10" }, "devDependencies": {}, "registries": { diff --git a/pos-module-oauth-github/modules/oauth_github/template-values.json b/pos-module-oauth-github/modules/oauth_github/template-values.json deleted file mode 100644 index 18f03ce1..00000000 --- a/pos-module-oauth-github/modules/oauth_github/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pos-module-oauth-github", - "machine_name": "oauth_github", - "type": "module", - "version": "0.0.13", - "dependencies": { - "core": "^2.1.9" - } -} diff --git a/pos-module-oauth-github/pos-module.json b/pos-module-oauth-github/pos-module.json index 16dc09cd..d26794d8 100644 --- a/pos-module-oauth-github/pos-module.json +++ b/pos-module-oauth-github/pos-module.json @@ -1,6 +1,6 @@ { "machine_name": "oauth_github", - "version": "0.0.13", + "version": "0.0.14", "name": "pos-module-oauth-github", "dependencies": { "core": "^2.1.9" diff --git a/pos-module-oauth-github/pos-module.lock.json b/pos-module-oauth-github/pos-module.lock.json index 627bf307..d7481ef1 100644 --- a/pos-module-oauth-github/pos-module.lock.json +++ b/pos-module-oauth-github/pos-module.lock.json @@ -1,6 +1,6 @@ { "dependencies": { - "core": "2.1.9" + "core": "2.1.10" }, "devDependencies": {}, "registries": { diff --git a/pos-module-oauth-google/modules/oauth_google/template-values.json b/pos-module-oauth-google/modules/oauth_google/template-values.json deleted file mode 100644 index 53910480..00000000 --- a/pos-module-oauth-google/modules/oauth_google/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pos-module-oauth-google", - "machine_name": "oauth_google", - "type": "module", - "version": "0.0.6", - "dependencies": { - "core": "^2.1.9" - } -} diff --git a/pos-module-oauth-google/pos-module.json b/pos-module-oauth-google/pos-module.json index b5dc7f17..0c5dce30 100644 --- a/pos-module-oauth-google/pos-module.json +++ b/pos-module-oauth-google/pos-module.json @@ -1,6 +1,6 @@ { "machine_name": "oauth_google", - "version": "0.0.6", + "version": "0.0.7", "name": "pos-module-oauth-google", "dependencies": { "core": "^2.1.9" diff --git a/pos-module-oauth-google/pos-module.lock.json b/pos-module-oauth-google/pos-module.lock.json index 627bf307..d7481ef1 100644 --- a/pos-module-oauth-google/pos-module.lock.json +++ b/pos-module-oauth-google/pos-module.lock.json @@ -1,6 +1,6 @@ { "dependencies": { - "core": "2.1.9" + "core": "2.1.10" }, "devDependencies": {}, "registries": { diff --git a/pos-module-openai/package.json b/pos-module-openai/package.json index 52430797..78cab3a3 100644 --- a/pos-module-openai/package.json +++ b/pos-module-openai/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "description": "Integration between platformOS and openai embeddings", "scripts": { - "version": "(cd ../../ && pos-cli modules version pos-module-openai -p) && git add template-values.json && auto-changelog -p && git add CHANGELOG.md", + "version": "pos-cli modules version --no-git && git add pos-module.json && auto-changelog -p && git add CHANGELOG.md", "pw-tests": "playwright test --project=e2e" }, "repository": { diff --git a/pos-module-openai/pos-module.json b/pos-module-openai/pos-module.json index 6e2c9c4c..45eba8bd 100644 --- a/pos-module-openai/pos-module.json +++ b/pos-module-openai/pos-module.json @@ -3,6 +3,6 @@ "core": "^2.1.9" }, "machine_name": "openai", - "version": "1.3.1", + "version": "1.3.2", "name": "pos-module-openai" } \ No newline at end of file diff --git a/pos-module-openai/pos-module.lock.json b/pos-module-openai/pos-module.lock.json index 627bf307..d7481ef1 100644 --- a/pos-module-openai/pos-module.lock.json +++ b/pos-module-openai/pos-module.lock.json @@ -1,6 +1,6 @@ { "dependencies": { - "core": "2.1.9" + "core": "2.1.10" }, "devDependencies": {}, "registries": { diff --git a/pos-module-openai/template-values.json b/pos-module-openai/template-values.json deleted file mode 100644 index 6bfb73f2..00000000 --- a/pos-module-openai/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pos-module-openai", - "machine_name": "openai", - "type": "module", - "version": "1.3.1", - "dependencies": { - "core": "^2.1.9" - } -} diff --git a/pos-module-payments-example-gateway/modules/payments_example_gateway/package.json b/pos-module-payments-example-gateway/modules/payments_example_gateway/package.json index 271495e6..116b9507 100644 --- a/pos-module-payments-example-gateway/modules/payments_example_gateway/package.json +++ b/pos-module-payments-example-gateway/modules/payments_example_gateway/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "description": "Module description", "scripts": { - "version": "(cd ../../ && pos-cli modules version MODULE_NAME -p) && git add template-values.json && auto-changelog -p && git add CHANGELOG.md" + "version": "(cd ../.. && pos-cli modules version --no-git && git add pos-module.json) && auto-changelog -p && git add CHANGELOG.md" }, "repository": { "type": "git", diff --git a/pos-module-payments-example-gateway/modules/payments_example_gateway/template-values.json b/pos-module-payments-example-gateway/modules/payments_example_gateway/template-values.json deleted file mode 100644 index cd82568b..00000000 --- a/pos-module-payments-example-gateway/modules/payments_example_gateway/template-values.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "pOS Payments Example Gateway", - "machine_name": "payments_example_gateway", - "type": "module", - "version": "0.1.2", - "dependencies": { - "core": "^2.1.9", - "payments": "^0.3.1" - } -} diff --git a/pos-module-payments-example-gateway/pos-module.json b/pos-module-payments-example-gateway/pos-module.json index e01dc0f3..ba0d0396 100644 --- a/pos-module-payments-example-gateway/pos-module.json +++ b/pos-module-payments-example-gateway/pos-module.json @@ -1,6 +1,6 @@ { "machine_name": "payments_example_gateway", - "version": "0.1.2", + "version": "0.1.3", "name": "pOS Payments Example Gateway", "dependencies": { "core": "^2.1.9", @@ -9,4 +9,4 @@ "devDependencies": { "tests": "^1.3.4" } -} +} \ No newline at end of file diff --git a/pos-module-payments-example-gateway/pos-module.lock.json b/pos-module-payments-example-gateway/pos-module.lock.json index 3a367f82..04d32809 100644 --- a/pos-module-payments-example-gateway/pos-module.lock.json +++ b/pos-module-payments-example-gateway/pos-module.lock.json @@ -1,14 +1,14 @@ { "dependencies": { - "core": "2.1.9", - "payments": "0.3.2" + "core": "2.1.10", + "payments": "0.3.3" }, "devDependencies": { - "tests": "1.3.4" + "tests": "1.3.5" }, "registries": { + "tests": "https://partners.platformos.com", "core": "https://partners.platformos.com", - "payments": "https://partners.platformos.com", - "tests": "https://partners.platformos.com" + "payments": "https://partners.platformos.com" } } \ No newline at end of file diff --git a/pos-module-payments-stripe/modules/payments_stripe/package.json b/pos-module-payments-stripe/modules/payments_stripe/package.json index 271495e6..116b9507 100644 --- a/pos-module-payments-stripe/modules/payments_stripe/package.json +++ b/pos-module-payments-stripe/modules/payments_stripe/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "description": "Module description", "scripts": { - "version": "(cd ../../ && pos-cli modules version MODULE_NAME -p) && git add template-values.json && auto-changelog -p && git add CHANGELOG.md" + "version": "(cd ../.. && pos-cli modules version --no-git && git add pos-module.json) && auto-changelog -p && git add CHANGELOG.md" }, "repository": { "type": "git", diff --git a/pos-module-payments-stripe/modules/payments_stripe/template-values.json b/pos-module-payments-stripe/modules/payments_stripe/template-values.json deleted file mode 100644 index 143df293..00000000 --- a/pos-module-payments-stripe/modules/payments_stripe/template-values.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "pOS Payments Stripe", - "machine_name": "payments_stripe", - "type": "module", - "version": "1.3.1", - "dependencies": { - "core": "^2.1.9", - "payments": "^0.3.1", - "user": "^5.2.10" - }, - "devDependencies": { - "tests": "^1.3.4" - } -} diff --git a/pos-module-payments-stripe/pos-module.json b/pos-module-payments-stripe/pos-module.json index 245b70a7..55c4cd2c 100644 --- a/pos-module-payments-stripe/pos-module.json +++ b/pos-module-payments-stripe/pos-module.json @@ -8,6 +8,6 @@ "tests": "^1.3.4" }, "machine_name": "payments_stripe", - "version": "1.3.1", + "version": "1.3.2", "name": "pOS Payments Stripe" } \ No newline at end of file diff --git a/pos-module-payments-stripe/pos-module.lock.json b/pos-module-payments-stripe/pos-module.lock.json index ee367ff5..19fb572d 100644 --- a/pos-module-payments-stripe/pos-module.lock.json +++ b/pos-module-payments-stripe/pos-module.lock.json @@ -1,12 +1,12 @@ { "dependencies": { - "payments": "0.3.2", - "core": "2.1.9", - "user": "5.2.12", - "common-styling": "1.38.5" + "payments": "0.3.3", + "core": "2.1.10", + "user": "5.3.1", + "common-styling": "1.38.8" }, "devDependencies": { - "tests": "1.3.4" + "tests": "1.3.5" }, "registries": { "tests": "https://partners.platformos.com", diff --git a/pos-module-payments/modules/payments/package.json b/pos-module-payments/modules/payments/package.json index 271495e6..116b9507 100644 --- a/pos-module-payments/modules/payments/package.json +++ b/pos-module-payments/modules/payments/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "description": "Module description", "scripts": { - "version": "(cd ../../ && pos-cli modules version MODULE_NAME -p) && git add template-values.json && auto-changelog -p && git add CHANGELOG.md" + "version": "(cd ../.. && pos-cli modules version --no-git && git add pos-module.json) && auto-changelog -p && git add CHANGELOG.md" }, "repository": { "type": "git", diff --git a/pos-module-payments/modules/payments/template-values.json b/pos-module-payments/modules/payments/template-values.json deleted file mode 100644 index 05c2d683..00000000 --- a/pos-module-payments/modules/payments/template-values.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pOS Payments", - "machine_name": "payments", - "type": "module", - "version": "0.3.2", - "dependencies": { - "core": "^2.1.9" - } -} \ No newline at end of file diff --git a/pos-module-payments/pos-module.json b/pos-module-payments/pos-module.json index fb10d5ac..5dde088a 100644 --- a/pos-module-payments/pos-module.json +++ b/pos-module-payments/pos-module.json @@ -1,7 +1,7 @@ { "machine_name": "payments", "name": "pOS Payments", - "version": "0.3.2", + "version": "0.3.3", "dependencies": { "core": "^2.1.9" }, diff --git a/pos-module-payments/pos-module.lock.json b/pos-module-payments/pos-module.lock.json index 45a2cdc8..2922125a 100644 --- a/pos-module-payments/pos-module.lock.json +++ b/pos-module-payments/pos-module.lock.json @@ -1,12 +1,12 @@ { "dependencies": { - "core": "2.1.9" + "core": "2.1.10" }, "devDependencies": { - "tests": "1.3.4" + "tests": "1.3.5" }, "registries": { - "core": "https://partners.platformos.com", - "tests": "https://partners.platformos.com" + "tests": "https://partners.platformos.com", + "core": "https://partners.platformos.com" } } \ No newline at end of file diff --git a/pos-module-push-notifications/modules/push_notifications/template-values.json b/pos-module-push-notifications/modules/push_notifications/template-values.json deleted file mode 100644 index 248cee51..00000000 --- a/pos-module-push-notifications/modules/push_notifications/template-values.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "Push Notifications", - "machine_name": "push_notifications", - "type": "module", - "version": "2.0.1", - "dependencies": { - "core": "^2.1.9", - "common-styling": "^1.38.5", - "user": "^5.2.12" - } -} diff --git a/pos-module-push-notifications/pos-module.json b/pos-module-push-notifications/pos-module.json index f3692742..5c8b3b84 100644 --- a/pos-module-push-notifications/pos-module.json +++ b/pos-module-push-notifications/pos-module.json @@ -1,7 +1,7 @@ { "machine_name": "push_notifications", "name": "Push Notifications", - "version": "2.0.1", + "version": "2.0.2", "dependencies": { "core": "^2.1.9", "common-styling": "^1.38.5", diff --git a/pos-module-push-notifications/pos-module.lock.json b/pos-module-push-notifications/pos-module.lock.json index 438d5766..b744c412 100644 --- a/pos-module-push-notifications/pos-module.lock.json +++ b/pos-module-push-notifications/pos-module.lock.json @@ -1,8 +1,8 @@ { "dependencies": { - "core": "2.1.9", - "common-styling": "1.38.5", - "user": "5.2.12" + "core": "2.1.10", + "common-styling": "1.38.8", + "user": "5.3.1" }, "devDependencies": {}, "registries": { diff --git a/pos-module-reports/modules/reports/template-values.json b/pos-module-reports/modules/reports/template-values.json deleted file mode 100644 index c58b6a98..00000000 --- a/pos-module-reports/modules/reports/template-values.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "Pos Module Reports", - "machine_name": "reports", - "type": "module", - "version": "2.0.1", - "dependencies": { - "core": "^2.1.9", - "tests": "^1.3.4", - "user": "^5.2.10" - } -} diff --git a/pos-module-reports/pos-module.json b/pos-module-reports/pos-module.json index 2a5c0005..dd79708e 100644 --- a/pos-module-reports/pos-module.json +++ b/pos-module-reports/pos-module.json @@ -5,5 +5,5 @@ }, "machine_name": "reports", "name": "Pos Module Reports", - "version": "2.0.1" + "version": "2.0.2" } \ No newline at end of file diff --git a/pos-module-reports/pos-module.lock.json b/pos-module-reports/pos-module.lock.json index aca0099d..086bbd65 100644 --- a/pos-module-reports/pos-module.lock.json +++ b/pos-module-reports/pos-module.lock.json @@ -1,8 +1,8 @@ { "dependencies": { - "core": "2.1.9", - "user": "5.2.12", - "common-styling": "1.38.5" + "core": "2.1.10", + "user": "5.3.1", + "common-styling": "1.38.8" }, "devDependencies": {}, "registries": { diff --git a/pos-module-tests/CLAUDE.md b/pos-module-tests/CLAUDE.md index 04a03e6b..1b415efb 100644 --- a/pos-module-tests/CLAUDE.md +++ b/pos-module-tests/CLAUDE.md @@ -48,7 +48,7 @@ modules/tests/ │ ├── layouts/ # Test and mailer layouts │ ├── pages/ # Test runner endpoints (/_tests/*) │ └── partials/ # HTML/text formatters for test output -└── template-values.json # Module metadata +└── pos-module.json # Module manifest (at repo root) ``` ### Writing Tests diff --git a/pos-module-tests/modules/tests/template-values.json b/pos-module-tests/modules/tests/template-values.json deleted file mode 100644 index 3bf566af..00000000 --- a/pos-module-tests/modules/tests/template-values.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "Pos Module Tests", - "machine_name": "tests", - "type": "module", - "version": "1.3.4", - "dependencies": {} -} diff --git a/pos-module-tests/pos-module.json b/pos-module-tests/pos-module.json index 7f21007b..7749609c 100644 --- a/pos-module-tests/pos-module.json +++ b/pos-module-tests/pos-module.json @@ -1,5 +1,6 @@ { "machine_name": "tests", - "version": "1.3.4", - "name": "Pos Module Tests" -} \ No newline at end of file + "version": "1.3.5", + "name": "Pos Module Tests", + "dependencies": {} +} diff --git a/pos-module-user-invites/modules/user_invites/template-values.json b/pos-module-user-invites/modules/user_invites/template-values.json deleted file mode 100644 index 4d74c49f..00000000 --- a/pos-module-user-invites/modules/user_invites/template-values.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "Pos Module User Invites", - "machine_name": "user_invites", - "type": "module", - "version": "1.0.1", - "dependencies": { - "core": "^2.0.0", - "user": "^5.2.8", - "common-styling": "^1.37.26" - } -} diff --git a/pos-module-user-invites/pos-module.json b/pos-module-user-invites/pos-module.json index 0dec88c3..8f4b1074 100644 --- a/pos-module-user-invites/pos-module.json +++ b/pos-module-user-invites/pos-module.json @@ -1,10 +1,10 @@ { "machine_name": "user_invites", - "version": "1.0.1", + "version": "1.0.2", "name": "Pos Module User Invites", "dependencies": { "core": "^2.1.9", "user": "^5.2.12", "common-styling": "^1.38.5" } -} +} \ No newline at end of file diff --git a/pos-module-user-invites/pos-module.lock.json b/pos-module-user-invites/pos-module.lock.json index aca0099d..086bbd65 100644 --- a/pos-module-user-invites/pos-module.lock.json +++ b/pos-module-user-invites/pos-module.lock.json @@ -1,8 +1,8 @@ { "dependencies": { - "core": "2.1.9", - "user": "5.2.12", - "common-styling": "1.38.5" + "core": "2.1.10", + "user": "5.3.1", + "common-styling": "1.38.8" }, "devDependencies": {}, "registries": { diff --git a/pos-module-user/CLAUDE.md b/pos-module-user/CLAUDE.md index 13667240..427d1375 100644 --- a/pos-module-user/CLAUDE.md +++ b/pos-module-user/CLAUDE.md @@ -25,7 +25,7 @@ npm run version This command: 1. Prompts for version selection -2. Updates `modules/user/template-values.json` +2. Updates `pos-module.json` 3. Auto-generates CHANGELOG entries from git commits 4. Stages changes for commit diff --git a/pos-module-user/modules/user/package.json b/pos-module-user/modules/user/package.json index c3117b1c..cee0afd3 100644 --- a/pos-module-user/modules/user/package.json +++ b/pos-module-user/modules/user/package.json @@ -3,7 +3,7 @@ "version": "1.0.4", "description": "This module handles the user operations, assign users to roles and add permissions to roles.", "scripts": { - "version": "(cd ../../ && pos-cli modules version user -p) && git add template-values.json && auto-changelog -p && git add CHANGELOG.md" + "version": "(cd ../.. && pos-cli modules version --no-git && git add pos-module.json) && auto-changelog -p && git add CHANGELOG.md" }, "repository": { "type": "git", diff --git a/pos-module-user/modules/user/template-values.json b/pos-module-user/modules/user/template-values.json deleted file mode 100644 index 68984145..00000000 --- a/pos-module-user/modules/user/template-values.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "User", - "machine_name": "user", - "type": "module", - "version": "5.3.1", - "dependencies": { - "core": "^2.1.9", - "common-styling": "^1.11.0" - } -} diff --git a/pos-module-user/pos-module.json b/pos-module-user/pos-module.json index 1376a285..dbbea3d6 100644 --- a/pos-module-user/pos-module.json +++ b/pos-module-user/pos-module.json @@ -7,7 +7,7 @@ "common-styling": "^1.38.5" }, "devDependencies": { - "oauth_github": "^0.0.13", + "oauth_github": "^0.0.14", "tests": "^1.3.4" }, "postInstall": { diff --git a/pos-module-user/pos-module.lock.json b/pos-module-user/pos-module.lock.json index f7e34958..675bfde7 100644 --- a/pos-module-user/pos-module.lock.json +++ b/pos-module-user/pos-module.lock.json @@ -4,8 +4,8 @@ "common-styling": "1.38.8" }, "devDependencies": { - "oauth_github": "0.0.13", - "tests": "1.3.4" + "oauth_github": "0.0.14", + "tests": "1.3.5" }, "registries": { "oauth_github": "https://partners.platformos.com", diff --git a/scripts/release.mjs b/scripts/release.mjs index 5ecdcd1c..e5f0ff49 100644 --- a/scripts/release.mjs +++ b/scripts/release.mjs @@ -15,6 +15,13 @@ // core, user first), so a parent module is always published before its // dependents and each dependent's lock file picks up the fresh version. // +// After the release loop, every other module in the repo whose dependencies +// or devDependencies reference a just-released module gets its declared range +// bumped (same-major releases only — e.g. ^0.0.13 → ^0.0.14, since npm caret +// semantics pin ^0.0.x to that exact patch) and its pos-module.lock.json +// refreshed, so the committed locks — and CI's frozen installs — reference +// what was actually published. +// // Bumps use --no-git because modules share this monorepo's git history and // pos-cli's per-module tags (e.g. "2.1.11") would collide between modules. // After a successful run the script offers a single combined git commit. @@ -22,7 +29,7 @@ // Requires Node.js >= 20.12 (uses node:util styleText). Usage: // node scripts/release.mjs -import { readFile, readdir } from 'node:fs/promises'; +import { readFile, readdir, writeFile } from 'node:fs/promises'; import { existsSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -72,6 +79,36 @@ const semverInc = (version, bump) => { } }; +const parseVersion = (version) => { + const match = String(version).match(/^(\d+)\.(\d+)\.(\d+)/); + return match ? match.slice(1).map(Number) : null; +}; + +const compareVersions = (a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]; + +// Minimal matcher for the range shapes used in pos-module.json files +// (^x.y.z, ~x.y.z, >=x.y.z, exact). Follows npm semver semantics — notably +// ^0.0.x matches only that exact patch. Returns null for shapes it does not +// understand so callers can warn instead of guessing. +const rangeIncludes = (range, version) => { + const v = parseVersion(version); + const match = String(range).trim().match(/^(\^|~|>=)?\s*(\d+\.\d+\.\d+)$/); + if (!v || !match) return null; + const [, op, base] = match; + const b = parseVersion(base); + if (compareVersions(v, b) < 0) return false; + if (op === '>=') return true; + if (op === '~') return v[0] === b[0] && v[1] === b[1]; + if (op === '^') { + if (b[0] > 0) return v[0] === b[0]; + if (b[1] > 0) return v[0] === 0 && v[1] === b[1]; + return compareVersions(v, b) === 0; + } + return compareVersions(v, b) === 0; +}; + +const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const readManifest = async (dir) => { try { return JSON.parse(await readFile(path.join(dir, 'pos-module.json'), 'utf8')); @@ -80,6 +117,14 @@ const readManifest = async (dir) => { } }; +const readLock = async (dir) => { + try { + return JSON.parse(await readFile(path.join(dir, 'pos-module.lock.json'), 'utf8')); + } catch { + return null; + } +}; + const discoverModules = async () => { const entries = (await readdir(ROOT)).filter((name) => name.startsWith('pos-module-')).sort(); const modules = await Promise.all( @@ -92,7 +137,7 @@ const discoverModules = async () => { dir, machineName: manifest.machine_name, version: manifest.version, - dependencyNames: Object.keys(manifest.dependencies ?? {}), + dependencyNames: Object.keys({ ...manifest.dependencies, ...manifest.devDependencies }), selected: false, bump: 'patch', }; @@ -103,8 +148,9 @@ const discoverModules = async () => { // Topological sort by declared dependencies, so parents are released before // their dependents. Seeded with the foundation modules first so the overall -// order starts: common-styling, core, user, then everything else. -const FOUNDATION_ORDER = ['common-styling', 'core', 'user']; +// order starts: tests, common-styling, core, user, then everything else. +// tests goes first so dependents' lock refreshes pick up its fresh version. +const FOUNDATION_ORDER = ['tests', 'common-styling', 'core', 'user']; const sortByReleaseOrder = (modules) => { const seeded = modules.toSorted((a, b) => { @@ -294,13 +340,83 @@ const selectModules = (modules) => // --- release steps ----------------------------------------------------------- -const releaseModule = async (module, email, password) => { +// Rewrite declared ranges in pos-module.json that cannot resolve to a version +// released in this run. Without this, caret ranges anchored at 0.0.x keep +// dependents' lock files on the old version forever (^0.0.13 never matches +// 0.0.14). Major jumps are left alone: compatibility must be verified by hand +// (see RELEASE.md). +const fixDependencyRanges = async (module, released) => { + const manifest = await readManifest(module.dir); + if (!manifest) return; + const file = path.join(module.dir, 'pos-module.json'); + let raw = await readFile(file, 'utf8'); + let changed = false; + for (const section of ['dependencies', 'devDependencies']) { + for (const [dep, range] of Object.entries(manifest[section] ?? {})) { + const release = released.get(dep); + if (!release || rangeIncludes(range, release.newVersion) === true) continue; + const from = parseVersion(release.oldVersion); + const to = parseVersion(release.newVersion); + const shape = String(range).trim().match(/^([\^~]?)\d+\.\d+\.\d+$/); + if (!from || !to || from[0] !== to[0] || !shape) { + console.log(styleText('yellow', ` "${dep}": "${range}" does not cover ${release.newVersion} — update the range by hand`)); + continue; + } + const newRange = `${shape[1]}${release.newVersion}`; + raw = raw.replace(new RegExp(`("${escapeRegExp(dep)}"\\s*:\\s*)"${escapeRegExp(range)}"`), `$1"${newRange}"`); + console.log(` ${dep}: range ${range} ${styleText('dim', '→')} ${styleText('bold', newRange)}`); + changed = true; + } + } + if (changed) await writeFile(file, raw); +}; + +// After the release loop, other modules in the repo may reference the released +// modules without having been part of the run — devDependencies especially, +// since they are not part of the release order (e.g. user's oauth_github). +// Bump their stale ranges and refresh their lock files so the committed locks +// — and CI's frozen installs — reference what was actually published. +const syncDependents = async (allModules, released) => { + if (!released.size) return []; + const synced = []; + for (const module of allModules) { + if (released.has(module.machineName)) continue; + const manifest = await readManifest(module.dir); + if (!manifest) continue; + const lock = await readLock(module.dir); + const stale = []; + for (const [section, flags] of [['dependencies', []], ['devDependencies', ['--dev']]]) { + for (const [dep, range] of Object.entries(manifest[section] ?? {})) { + const release = released.get(dep); + if (!release) continue; + const locked = lock?.dependencies?.[dep] ?? lock?.devDependencies?.[dep]; + if (locked === release.newVersion && rangeIncludes(range, release.newVersion) === true) continue; + stale.push({ dep, flags }); + } + } + if (!stale.length) continue; + process.stdout.write(`\n${styleText('bold', `── ${module.name} ──`)} ${styleText('dim', '(dependent of a released module)')}\n`); + await fixDependencyRanges(module, released); + let ok = true; + for (const { dep, flags } of stale) { + const update = spawnSync('pos-cli', ['modules', 'update', dep, ...flags], { cwd: module.dir, stdio: 'inherit' }); + if (update.status !== 0) ok = false; + } + synced.push({ ...module, ok }); + } + return synced; +}; + +const releaseModule = async (module, email, password, released) => { process.stdout.write(`\n${styleText('bold', `── ${module.name} ──`)}\n`); // Refresh dependencies + pos-module.lock.json so the published archive (and // the eventual git commit CI checks) references the just-released parents. + // Ranges that cannot reach a parent released earlier in this run (^0.0.x) + // are bumped first so the update can pick it up. if (module.dependencyNames.length) { - const update = spawnSync('pos-cli', ['modules', 'update'], { cwd: module.dir, stdio: 'inherit' }); + await fixDependencyRanges(module, released); + const update = spawnSync('pos-cli', ['modules', 'update', '--dev'], { cwd: module.dir, stdio: 'inherit' }); if (update.status !== 0) return { ...module, ok: false, stage: 'dependency update' }; } @@ -325,7 +441,7 @@ const releaseModule = async (module, email, password) => { return { ...module, ok: true }; }; -const offerGitCommit = async (results) => { +const offerGitCommit = async (results, synced) => { const released = results.filter((r) => r.ok); if (!released.length) return; try { @@ -334,13 +450,13 @@ const offerGitCommit = async (results) => { return; } - // Version bumps touch pos-module.json + template-values.json; the update - // step touches pos-module.lock.json (even for "push only" releases). - const files = released + // Version bumps touch pos-module.json; the update step touches + // pos-module.lock.json (even for "push only" releases). The dependent sync + // can touch both files of modules that were not released themselves. + const files = [...released, ...synced] .flatMap((r) => [ path.join(r.name, 'pos-module.json'), path.join(r.name, 'pos-module.lock.json'), - path.join(r.name, 'modules', r.machineName, 'template-values.json'), ]) .filter((f) => existsSync(path.join(ROOT, f))); const quoted = files.map((f) => `'${f}'`).join(' '); @@ -353,7 +469,11 @@ const offerGitCommit = async (results) => { console.log(); if (!(await promptYesNo('Commit version bumps and lock files to git?'))) return; - const message = `Release ${released.map((r) => `${r.machineName}@${r.newVersion}`).join(', ')}`; + const dirtyDirs = new Set(dirty.split('\n').map((line) => line.slice(3).split('/')[0])); + const syncedNames = synced.filter((s) => dirtyDirs.has(s.name)).map((s) => s.machineName); + const message = + `Release ${released.map((r) => `${r.machineName}@${r.newVersion}`).join(', ')}` + + (syncedNames.length ? `; sync ${syncedNames.join(', ')}` : ''); try { execSync(`git add ${quoted}`, { cwd: ROOT, stdio: 'inherit' }); execSync(`git commit -m '${message}'`, { cwd: ROOT, stdio: 'inherit' }); @@ -403,10 +523,15 @@ for (const m of selected) { if (!(await promptYesNo('\nProceed?'))) abort(); const results = []; +const released = new Map(); for (const m of selected) { - results.push(await releaseModule(m, email, password)); + const result = await releaseModule(m, email, password, released); + results.push(result); + if (result.ok) released.set(result.machineName, { oldVersion: result.version, newVersion: result.newVersion }); } +const synced = await syncDependents(modules, released); + console.log(`\n${styleText('bold', 'Summary')}`); for (const r of results) { console.log( @@ -415,6 +540,13 @@ for (const r of results) { : ` ${styleText('red', '✖')} ${r.name} ${styleText('red', `failed at ${r.stage}`)}` ); } +for (const s of synced) { + console.log( + s.ok + ? ` ${styleText('green', '✔')} ${s.name} ${styleText('dim', 'dependency ranges/lock synced')}` + : ` ${styleText('red', '✖')} ${s.name} ${styleText('red', 'dependency sync failed — fix with pos-cli modules update')}` + ); +} -await offerGitCommit(results); -process.exit(results.every((r) => r.ok) ? 0 : 1); +await offerGitCommit(results, synced); +process.exit(results.every((r) => r.ok) && synced.every((s) => s.ok) ? 0 : 1);