Skip to content

Commit 13dd70d

Browse files
marcofarinaclaude
andcommitted
feat(tooling): sync versione verso il PM su npm version
Aggancia uno script al lifecycle "version" di npm: al bump propaga package.json → pm/board.json (meta.version) e rigenera ROADMAP.md, preferendo il portale se attivo, altrimenti scrivendo su filesystem. No-op sicuro se pm/ è assente (CI, clone fresco). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c8fcdaf commit 13dd70d

2 files changed

Lines changed: 100 additions & 1 deletion

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"typecheck": "tsc",
1616
"lint": "eslint src --ext .js,.jsx,.ts,.tsx",
1717
"lint:fix": "eslint src --ext .js,.jsx,.ts,.tsx --fix",
18-
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,css,md,mdx}\""
18+
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,css,md,mdx}\"",
19+
"version": "node scripts/sync-pm-version.mjs"
1920
},
2021
"dependencies": {
2122
"@codemirror/commands": "^6.10.3",

scripts/sync-pm-version.mjs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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

Comments
 (0)