|
| 1 | +#!/usr/bin/env node |
| 2 | +// Propaga la versione di package.json al PM privato (pm/board.json → meta.version) |
| 3 | +// e rigenera ROADMAP.md. Agganciato al lifecycle `npm version` (gira dopo il bump |
| 4 | +// di package.json, prima del commit). |
| 5 | +// |
| 6 | +// npm version patch|minor|major → bump + sync automatico del PM |
| 7 | +// node scripts/sync-pm-version.mjs → sync manuale (usa la versione corrente) |
| 8 | +// |
| 9 | +// È una comodità per lo sviluppo locale: il repo pubblico e pm/ sono due git |
| 10 | +// separati, qui li teniamo allineati. Se pm/ non esiste (CI, clone fresco) lo |
| 11 | +// script non fa nulla ed esce con successo — non deve mai rompere `npm version`. |
| 12 | + |
| 13 | +import { readFile, writeFile, access } from 'node:fs/promises'; |
| 14 | +import { fileURLToPath } from 'node:url'; |
| 15 | +import { dirname, join } from 'node:path'; |
| 16 | + |
| 17 | +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); |
| 18 | +const PM_DIR = join(ROOT, 'pm'); |
| 19 | +const BOARD = join(PM_DIR, 'board.json'); |
| 20 | +const ROADMAP = join(PM_DIR, 'ROADMAP.md'); |
| 21 | +const PORT = Number(process.env.PM_PORT) || 4178; |
| 22 | + |
| 23 | +const log = (msg) => console.log(`[sync-pm] ${msg}`); |
| 24 | + |
| 25 | +async function exists(p) { |
| 26 | + try { |
| 27 | + await access(p); |
| 28 | + return true; |
| 29 | + } catch { |
| 30 | + return false; |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +async function getVersion() { |
| 35 | + // npm espone la nuova versione qui durante il lifecycle `version`. |
| 36 | + if (process.env.npm_package_version) return process.env.npm_package_version; |
| 37 | + const pkg = JSON.parse(await readFile(join(ROOT, 'package.json'), 'utf-8')); |
| 38 | + return pkg.version; |
| 39 | +} |
| 40 | + |
| 41 | +// Via preferita: il portale è già attivo → PUT, così è il server a rigenerare |
| 42 | +// ROADMAP.md (singola sorgente del generatore). Ritorna true se ha aggiornato. |
| 43 | +async function syncViaPortal(version) { |
| 44 | + let board; |
| 45 | + try { |
| 46 | + const res = await fetch(`http://localhost:${PORT}/api/board`, { cache: 'no-store' }); |
| 47 | + if (!res.ok) return false; |
| 48 | + board = await res.json(); |
| 49 | + } catch { |
| 50 | + return false; // portale spento: si passa al fallback su filesystem |
| 51 | + } |
| 52 | + board.meta = board.meta ?? {}; |
| 53 | + if (board.meta.version === version) { |
| 54 | + log(`portale già a ${version}, niente da fare.`); |
| 55 | + return true; |
| 56 | + } |
| 57 | + board.meta.version = version; |
| 58 | + const put = await fetch(`http://localhost:${PORT}/api/board`, { |
| 59 | + method: 'PUT', |
| 60 | + headers: { 'Content-Type': 'application/json' }, |
| 61 | + body: JSON.stringify(board), |
| 62 | + }); |
| 63 | + if (!put.ok) return false; |
| 64 | + log(`board.json → meta.version ${version} (via portale, ROADMAP rigenerato).`); |
| 65 | + return true; |
| 66 | +} |
| 67 | + |
| 68 | +// Fallback: portale spento → scrivo board.json e rigenero ROADMAP.md con lo |
| 69 | +// stesso generatore del server (modulo condiviso pm/roadmap.mjs). |
| 70 | +async function syncViaFilesystem(version) { |
| 71 | + const board = JSON.parse(await readFile(BOARD, 'utf-8')); |
| 72 | + board.meta = board.meta ?? {}; |
| 73 | + if (board.meta.version === version) { |
| 74 | + log(`board.json già a ${version}, niente da fare.`); |
| 75 | + return; |
| 76 | + } |
| 77 | + board.meta.version = version; |
| 78 | + board.meta.updatedAt = new Date().toISOString().slice(0, 10); |
| 79 | + const { renderRoadmap } = await import(join(PM_DIR, 'roadmap.mjs')); |
| 80 | + await writeFile(BOARD, JSON.stringify(board, null, 2) + '\n', 'utf-8'); |
| 81 | + await writeFile(ROADMAP, renderRoadmap(board) + '\n', 'utf-8'); |
| 82 | + log(`board.json → meta.version ${version} (via filesystem, ROADMAP rigenerato).`); |
| 83 | +} |
| 84 | + |
| 85 | +async function main() { |
| 86 | + if (!(await exists(BOARD))) { |
| 87 | + log('pm/board.json assente — skip (probabile CI o clone senza PM).'); |
| 88 | + return; |
| 89 | + } |
| 90 | + const version = await getVersion(); |
| 91 | + if (await syncViaPortal(version)) return; |
| 92 | + await syncViaFilesystem(version); |
| 93 | +} |
| 94 | + |
| 95 | +main().catch((err) => { |
| 96 | + // Non bloccare mai `npm version`: avvisa e prosegui. |
| 97 | + console.warn(`[sync-pm] sync non riuscito (ignorato): ${err.message}`); |
| 98 | +}); |
0 commit comments