From adc1610d00cd8a6c554ce3614a49b374a27c2b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Wed, 5 Aug 2026 02:04:13 +0000 Subject: [PATCH] fix(cli): upgrade project wrapper pins --- .../cli/src/commands/upgrade.project.test.ts | 13 ++++++++ packages/cli/src/commands/upgrade.ts | 25 ++++++++++++--- packages/cli/src/utils/projectPin.ts | 32 ++++++++++++++++--- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/commands/upgrade.project.test.ts b/packages/cli/src/commands/upgrade.project.test.ts index 2fbb94e39f..cbc360ebc0 100644 --- a/packages/cli/src/commands/upgrade.project.test.ts +++ b/packages/cli/src/commands/upgrade.project.test.ts @@ -78,6 +78,19 @@ describe("upgradeProjectPins", () => { expect(pkg.scripts.render).toBe("npx --yes hyperframes@0.7.55 render"); }); + it("rewrites project-root shell wrapper pins alongside package scripts", async () => { + const d = project({ render: "npx --yes hyperframes@0.7.48 render" }); + const fullCpu = join(d, "hyperframes-full-cpu.sh"); + const publish = join(d, "publish-wrapper.sh"); + writeFileSync(fullCpu, "npx --yes hyperframes@0.7.48 render --workers 2\n"); + writeFileSync(publish, "./publish.sh 0.7.48 hyperframes@0.7.48\n"); + + await upgradeProjectPins(d, { json: false, check: false }); + + expect(readFileSync(fullCpu, "utf8")).toContain("hyperframes@0.7.55"); + expect(readFileSync(publish, "utf8")).toContain("hyperframes@0.7.55"); + }); + it("--check reports without writing", async () => { const d = project({ render: "npx --yes hyperframes@0.7.48 render" }); const before = readFileSync(join(d, "package.json"), "utf-8"); diff --git a/packages/cli/src/commands/upgrade.ts b/packages/cli/src/commands/upgrade.ts index ebd49b7f95..d38a993029 100644 --- a/packages/cli/src/commands/upgrade.ts +++ b/packages/cli/src/commands/upgrade.ts @@ -3,10 +3,10 @@ import { defineCommand } from "citty"; import type { Example } from "./_examples.js"; import * as clack from "@clack/prompts"; import { execFileSync } from "node:child_process"; -import { existsSync, readFileSync, writeFileSync, renameSync } from "node:fs"; +import { existsSync, readFileSync, writeFileSync, renameSync, readdirSync } from "node:fs"; import { resolve } from "node:path"; import { c } from "../ui/colors.js"; -import { rewriteProjectPinnedScripts } from "../utils/projectPin.js"; +import { rewritePinnedHyperframesText, rewriteProjectPinnedScripts } from "../utils/projectPin.js"; export const examples: Example[] = [ ["Check for updates interactively", "hyperframes upgrade"], @@ -197,13 +197,28 @@ export async function upgradeProjectPins( const { latest } = await checkForUpdate(true); if (!isSafeVersion(latest)) return { changed: false, from: [], to: latest, path: pkgPath }; const rewrite = rewriteProjectPinnedScripts(scripts, latest); + const wrapperRewrites = readdirSync(dir, { withFileTypes: true }) + .filter((entry) => entry.isFile() && entry.name.endsWith(".sh")) + .map((entry) => { + const path = resolve(dir, entry.name); + return { path, rewrite: rewritePinnedHyperframesText(readFileSync(path, "utf-8"), latest) }; + }); if (rewrite.changed && !opts.check) { raw.scripts = rewrite.scripts; const tmp = `${pkgPath}.tmp`; writeFileSync(tmp, `${JSON.stringify(raw, null, 2)}\n`, "utf-8"); renameSync(tmp, pkgPath); } - return { changed: rewrite.changed, from: rewrite.fromVersions, to: latest, path: pkgPath }; + if (!opts.check) { + for (const wrapper of wrapperRewrites) { + if (wrapper.rewrite.changed) writeFileSync(wrapper.path, wrapper.rewrite.text, "utf-8"); + } + } + const from = new Set(rewrite.fromVersions); + for (const wrapper of wrapperRewrites) { + for (const version of wrapper.rewrite.fromVersions) from.add(version); + } + return { changed: from.size > 0, from: [...from].sort(), to: latest, path: pkgPath }; } function printProjectPinResult( @@ -215,12 +230,12 @@ function printProjectPinResult( return; } if (!res.changed) { - console.log(` ${c.success("◇")} Project scripts already on hyperframes@${res.to}`); + console.log(` ${c.success("◇")} Project pins already on hyperframes@${res.to}`); return; } const verb = checkOnly ? "would bump" : "bumped"; console.log( - ` ${c.success("◇")} ${verb} project scripts ${res.from.join(", ")} → ${c.accent(res.to)}`, + ` ${c.success("◇")} ${verb} project pins ${res.from.join(", ")} → ${c.accent(res.to)}`, ); if (checkOnly) console.log(` ${c.dim("Run `npx hyperframes@latest upgrade --project` to apply.")}`); diff --git a/packages/cli/src/utils/projectPin.ts b/packages/cli/src/utils/projectPin.ts index 6e7575b4c7..fcaeb6a7a2 100644 --- a/packages/cli/src/utils/projectPin.ts +++ b/packages/cli/src/utils/projectPin.ts @@ -12,6 +12,31 @@ export interface PinRewriteResult { fromVersions: string[]; } +export interface TextPinRewriteResult { + changed: boolean; + text: string; + fromVersions: string[]; +} + +export function rewritePinnedHyperframesText( + text: string, + targetVersion: string, +): TextPinRewriteResult { + if (!isSafeVersion(targetVersion)) { + return { changed: false, text, fromVersions: [] }; + } + const fromVersions = new Set(); + const next = text.replace(HYPERFRAMES_PIN_RE, (_full, version: string) => { + if (version !== targetVersion) fromVersions.add(version); + return `hyperframes@${targetVersion}`; + }); + return { + changed: fromVersions.size > 0, + text: next, + fromVersions: [...fromVersions].sort(), + }; +} + export function readPinnedHyperframesVersions(scripts: Record): string[] { const found = new Set(); for (const cmd of Object.values(scripts ?? {})) { @@ -31,10 +56,9 @@ export function rewriteProjectPinnedScripts( const fromVersions = new Set(); const next: Record = {}; for (const [name, cmd] of Object.entries(scripts ?? {})) { - next[name] = cmd.replace(HYPERFRAMES_PIN_RE, (_full, version: string) => { - if (version !== targetVersion) fromVersions.add(version); - return `hyperframes@${targetVersion}`; - }); + const rewrite = rewritePinnedHyperframesText(cmd, targetVersion); + next[name] = rewrite.text; + for (const version of rewrite.fromVersions) fromVersions.add(version); } return { changed: [...fromVersions].length > 0,