ferst-core 0.6.1 → 0.6.2
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/bin/ferst-update.mjs +62 -4
- package/package.json +1 -1
package/bin/ferst-update.mjs
CHANGED
|
@@ -46,6 +46,8 @@ export function isDirectInvocation(argv1) {
|
|
|
46
46
|
export const DEFAULT_UPSTREAM = 'https://github.com/MyAI4WebDev/ferst-client-template.git';
|
|
47
47
|
/** The ownership manifest a seeded client repo carries at its root. */
|
|
48
48
|
export const MANIFEST = 'ferst-template.json';
|
|
49
|
+
/** The per-site provenance/config file a client repo carries at its root (gitBase, etc.). */
|
|
50
|
+
export const SITE_META = 'ferst-site.json';
|
|
49
51
|
|
|
50
52
|
// ── pure helpers (unit-tested; no shell, no fs writes) ───────────────────────────
|
|
51
53
|
|
|
@@ -155,6 +157,33 @@ export function tail(text, n = 40) {
|
|
|
155
157
|
return lines.slice(-n).join('\n');
|
|
156
158
|
}
|
|
157
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Build the provenance "drift stamp" for a completed update: WHICH template commit and
|
|
162
|
+
* core version this repo landed on, and when. Pure — the write is the caller's job.
|
|
163
|
+
* @param {{ upstream?: string, commit?: string|null, coreVersion?: string|null, now?: Date|string }} p
|
|
164
|
+
* @returns {{ templateUpstream: string|null, templateCommit: string|null, coreVersion: string|null, at: string }}
|
|
165
|
+
*/
|
|
166
|
+
export function buildProvenance({ upstream, commit, coreVersion, now = new Date() } = {}) {
|
|
167
|
+
return {
|
|
168
|
+
templateUpstream: upstream ?? null,
|
|
169
|
+
templateCommit: commit ?? null,
|
|
170
|
+
coreVersion: coreVersion ?? null,
|
|
171
|
+
at: typeof now === 'string' ? now : now.toISOString(),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Merge a provenance stamp into an existing ferst-site.json object under `lastUpdate`,
|
|
177
|
+
* preserving every other field (gitBase, productionBranch, unknown keys) and overwriting
|
|
178
|
+
* any prior `lastUpdate`. Pass null (no ferst-site.json yet) → a fresh object with just
|
|
179
|
+
* the stamp. Pure — never mutates the input.
|
|
180
|
+
* @returns {object} the object to write back
|
|
181
|
+
*/
|
|
182
|
+
export function mergeSiteMeta(existing, provenance) {
|
|
183
|
+
const base = existing && typeof existing === 'object' && !Array.isArray(existing) ? existing : {};
|
|
184
|
+
return { ...base, lastUpdate: provenance };
|
|
185
|
+
}
|
|
186
|
+
|
|
158
187
|
// ── thin side-effecting shell (kept out of the pure helpers above) ────────────────
|
|
159
188
|
|
|
160
189
|
const exec = (cmd, cwd) => execSync(cmd, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
@@ -181,6 +210,16 @@ function readInstalledCoreVersion(cwd) {
|
|
|
181
210
|
}
|
|
182
211
|
}
|
|
183
212
|
|
|
213
|
+
/** The repo's ferst-site.json parsed to an object, or null when absent/unreadable/non-object. */
|
|
214
|
+
function readSiteMeta(cwd) {
|
|
215
|
+
try {
|
|
216
|
+
const parsed = JSON.parse(readFileSync(resolve(cwd, SITE_META), 'utf8'));
|
|
217
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
184
223
|
/** Ensure an `upstream` remote points at `url` (add, or set-url if it already exists). */
|
|
185
224
|
function ensureUpstream(url, cwd) {
|
|
186
225
|
const remotes = (tryGit('remote', cwd) || '').split(/\r?\n/).map((s) => s.trim());
|
|
@@ -240,6 +279,8 @@ function main() {
|
|
|
240
279
|
}
|
|
241
280
|
const branch = parseDefaultBranch(tryGit('ls-remote --symref upstream HEAD', cwd)) || 'main';
|
|
242
281
|
console.log(`ferst-update: upstream default branch = ${branch}`);
|
|
282
|
+
// The exact template commit this run syncs from (for the provenance stamp / #18 ledger).
|
|
283
|
+
const templateCommit = (tryGit(`rev-parse upstream/${branch}`, cwd) || '').trim() || null;
|
|
243
284
|
|
|
244
285
|
let scaffoldCount = 0; // files the scaffold sync touched (counted BEFORE the npm churn)
|
|
245
286
|
if (dryRun) {
|
|
@@ -263,12 +304,13 @@ function main() {
|
|
|
263
304
|
|
|
264
305
|
// 3. Engine (ferst-core) update.
|
|
265
306
|
let coreBump = null; // { before, after } once bumped
|
|
307
|
+
let dryLatest = null; // dry-run only: the version `@latest` WOULD install (for the stamp preview)
|
|
266
308
|
if (noCore) {
|
|
267
309
|
console.log('ferst-update: --no-core — skipping the ferst-core npm bump.');
|
|
268
310
|
} else if (dryRun) {
|
|
269
|
-
|
|
311
|
+
dryLatest = (tryExec('npm view ferst-core version', cwd) || '').trim() || null;
|
|
270
312
|
const current = readInstalledCoreVersion(cwd);
|
|
271
|
-
console.log(`ferst-update: ferst-core installed=${current ?? '(none)'} latest=${
|
|
313
|
+
console.log(`ferst-update: ferst-core installed=${current ?? '(none)'} latest=${dryLatest || '(unknown)'}`);
|
|
272
314
|
} else {
|
|
273
315
|
const before = readInstalledCoreVersion(cwd);
|
|
274
316
|
console.log('ferst-update: npm install ferst-core@latest --save ...');
|
|
@@ -302,6 +344,9 @@ function main() {
|
|
|
302
344
|
// 4. Verify — a failed @latest bump is a real incompatibility to surface, so on
|
|
303
345
|
// failure we print the tail and exit non-zero WITHOUT reverting.
|
|
304
346
|
if (dryRun) {
|
|
347
|
+
const wouldCommit = templateCommit ? templateCommit.slice(0, 7) : '(unknown)';
|
|
348
|
+
const wouldCore = (noCore ? readInstalledCoreVersion(cwd) : dryLatest) ?? '(unknown)';
|
|
349
|
+
console.log(`ferst-update: --dry-run — WOULD stamp ${SITE_META}: template ${wouldCommit}, core ${wouldCore} (no write).`);
|
|
305
350
|
console.log('ferst-update: --dry-run complete (no writes, no verify).');
|
|
306
351
|
return;
|
|
307
352
|
}
|
|
@@ -315,12 +360,25 @@ function main() {
|
|
|
315
360
|
}
|
|
316
361
|
}
|
|
317
362
|
|
|
318
|
-
// 5.
|
|
363
|
+
// 5. Provenance drift stamp — record WHICH template commit + core version this repo
|
|
364
|
+
// landed on (only after a fully-successful update), so the repo and the future fleet
|
|
365
|
+
// drift ledger (#18) always know its state. MERGE: preserve gitBase/productionBranch
|
|
366
|
+
// and any other existing ferst-site.json fields.
|
|
367
|
+
const coreVersion = coreBump?.after ?? readInstalledCoreVersion(cwd);
|
|
368
|
+
const provenance = buildProvenance({ upstream: upstream.url, commit: templateCommit, coreVersion });
|
|
369
|
+
writeFileSync(
|
|
370
|
+
resolve(cwd, SITE_META),
|
|
371
|
+
`${JSON.stringify(mergeSiteMeta(readSiteMeta(cwd), provenance), null, 2)}\n`,
|
|
372
|
+
);
|
|
373
|
+
const stampSha = templateCommit ? templateCommit.slice(0, 7) : '(unknown)';
|
|
374
|
+
|
|
375
|
+
// 6. Summary.
|
|
319
376
|
const coreLine = coreBump
|
|
320
377
|
? `ferst-core ${coreBump.before ?? '(none)'} → ${coreBump.after}`
|
|
321
378
|
: 'ferst-core unchanged (--no-core)';
|
|
322
379
|
console.log(
|
|
323
|
-
`ferst-update: DONE — scaffold files updated: ${scaffoldCount}; ${coreLine};
|
|
380
|
+
`ferst-update: DONE — scaffold files updated: ${scaffoldCount}; ${coreLine}; ` +
|
|
381
|
+
`stamped ${SITE_META}: template ${stampSha}, core ${coreVersion ?? '(unknown)'}; build + check:thin green.`,
|
|
324
382
|
);
|
|
325
383
|
}
|
|
326
384
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ferst-core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ferst Core — the shared, brand-agnostic client-site system: Astro components, layouts, content schemas, the layered config resolver, and a neutral styling architecture that every client overrides.",
|
|
6
6
|
"license": "BUSL-1.1",
|