diff --git a/.changeset/preset-next-bun-reconcile.md b/.changeset/preset-next-bun-reconcile.md new file mode 100644 index 0000000..10b2132 --- /dev/null +++ b/.changeset/preset-next-bun-reconcile.md @@ -0,0 +1,10 @@ +--- +"@codelitdev/platform-cli": patch +--- + +The preset and product template now pin `next` 16.4.3 and Bun 1.4.4, the patched releases, so `doctor` no longer fails products that already moved to them. `sync` also records a managed file that was reconciled by hand to the template instead of reporting it as a conflict. + +## Product changes + +- Use `next` 16.4.3 and Bun 1.4.4 (`packageManager`, CI `bun-version`, `@types/bun` 1.4.4). +- Run `bunx @codelitdev/platform-cli@ sync`. If it reports `platform-conformance.yml` as a conflict, make that file match this release's template, then run `sync` again. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0292186..c2e69b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: - name: Install Bun uses: oven-sh/setup-bun@v2 with: - bun-version: 1.4.1 + bun-version: 1.4.4 - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e36bf00..231be49 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: - name: Install Bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: - bun-version: 1.4.1 + bun-version: 1.4.4 - name: Cache Bun install uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 diff --git a/docs/architecture.md b/docs/architecture.md index dbccbc7..4d492f5 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -378,7 +378,7 @@ Products depend on individual packages; CI verifies that resolved versions match { "schemaVersion": 1, "runtime": { - "bun": "1.4.1" + "bun": "1.4.4" }, "packages": { "@codelitdev/platform": { diff --git a/package.json b/package.json index 83dd761..ce61a8f 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "Shared infrastructure packages and product bootstrap for CodeLit applications", "license": "UNLICENSED", "type": "module", - "packageManager": "bun@1.4.1", + "packageManager": "bun@1.4.4", "workspaces": [ "packages/*", "examples/*/apps/*", @@ -24,7 +24,7 @@ "@anatine/zod-openapi@1.14.2": "patches/@anatine__zod-openapi@1.14.2.patch" }, "engines": { - "bun": ">=1.4.1" + "bun": ">=1.4.4" }, "scripts": { "build": "bun run --workspaces --if-present build", diff --git a/packages/billing/scripts/packed-consumer.mjs b/packages/billing/scripts/packed-consumer.mjs index 0bcca77..2dd704c 100644 --- a/packages/billing/scripts/packed-consumer.mjs +++ b/packages/billing/scripts/packed-consumer.mjs @@ -35,7 +35,7 @@ writeFileSync( name: "consumer", type: "module", private: true, - packageManager: "bun@1.4.1", + packageManager: "bun@1.4.4", }, null, 2, diff --git a/packages/mcp-server-kit/scripts/packed-consumer.mjs b/packages/mcp-server-kit/scripts/packed-consumer.mjs index 40bbb84..582e2d4 100644 --- a/packages/mcp-server-kit/scripts/packed-consumer.mjs +++ b/packages/mcp-server-kit/scripts/packed-consumer.mjs @@ -46,7 +46,7 @@ writeFileSync( name: "consumer", type: "module", private: true, - packageManager: "bun@1.4.1", + packageManager: "bun@1.4.4", overrides: { "@codelitdev/platform": platformTarball, }, diff --git a/packages/observability/scripts/packed-consumer.mjs b/packages/observability/scripts/packed-consumer.mjs index 182e0a8..8b3c580 100644 --- a/packages/observability/scripts/packed-consumer.mjs +++ b/packages/observability/scripts/packed-consumer.mjs @@ -32,7 +32,7 @@ writeFileSync( name: "consumer", type: "module", private: true, - packageManager: "bun@1.4.1", + packageManager: "bun@1.4.4", }), ); execFileSync("bun", ["add", tarball, "--ignore-scripts"], { diff --git a/packages/platform-cli/README.md b/packages/platform-cli/README.md index c9c925b..ae61721 100644 --- a/packages/platform-cli/README.md +++ b/packages/platform-cli/README.md @@ -48,7 +48,9 @@ bunx @codelitdev/platform-cli@0.2.0 sync replaces a file only when its hash still matches the manifest, and creates a newly managed file only when it is absent or already identical. Any other difference is a conflict: nothing is written, and you reconcile the file by -hand. Writes require a clean Git worktree; `--dry-run` works in a dirty one. If +hand. To reconcile, make the file match this release's template, for example +by moving a product change into a product-owned file; the next `sync` records +it. Writes require a clean Git worktree; `--dry-run` works in a dirty one. If writing fails, the CLI restores the files it changed. When a release requires changes to product-owned files, its release notes say diff --git a/packages/platform-cli/scripts/packed-consumer.mjs b/packages/platform-cli/scripts/packed-consumer.mjs index a2e760a..ef3d6b2 100644 --- a/packages/platform-cli/scripts/packed-consumer.mjs +++ b/packages/platform-cli/scripts/packed-consumer.mjs @@ -50,7 +50,7 @@ writeFileSync( name: "consumer", type: "module", private: true, - packageManager: "bun@1.4.1", + packageManager: "bun@1.4.4", dependencies: { "@codelitdev/platform-cli": tarball, }, diff --git a/packages/platform-cli/src/cli.test.ts b/packages/platform-cli/src/cli.test.ts index c4f0ca0..fff92d3 100644 --- a/packages/platform-cli/src/cli.test.ts +++ b/packages/platform-cli/src/cli.test.ts @@ -138,6 +138,29 @@ describe("platform-cli", () => { expect(readManifest(target)).toEqual(manifestBefore); }); + it("records a drifted managed file reconciled by hand to the template", () => { + const target = createCommitted("cli-reconcile-"); + const file = path.join(target, WORKFLOW); + const desired = readFileSync(file, "utf8"); + writeFileSync(file, "name: edited by the product\n"); + const manifest = readManifest(target); + manifest.managedFiles[WORKFLOW] = sha256File(file); + writeManifest(target, manifest); + // An edit after the hash was recorded: drift. + writeFileSync(file, desired.replace("bun-version", "bun-version ")); + expect(syncProduct({ root: target, allowDirty: true }).conflicts).toEqual([ + WORKFLOW, + ]); + // The product resolves it by restoring the template's content. + writeFileSync(file, desired); + git(target, ["commit", "-am", "reconcile workflow"]); + const result = syncProduct({ root: target }); + expect(result.conflicts).toEqual([]); + expect(result.changed).toEqual([]); + expect(readManifest(target).managedFiles[WORKFLOW]).toBe(sha256File(file)); + expect(doctor(target).issues.filter((i) => i.startsWith("managed_"))).toEqual([]); + }); + it("adopts a missing managed file but not a different existing one", () => { const target = createCommitted("cli-adopt-"); const manifest = readManifest(target); diff --git a/packages/platform-cli/src/create.ts b/packages/platform-cli/src/create.ts index 7835e39..adc1c77 100644 --- a/packages/platform-cli/src/create.ts +++ b/packages/platform-cli/src/create.ts @@ -92,7 +92,7 @@ function validateGeneratedMetadata(root: string): void { ) as { packageManager?: string; }; - if (packageJson.packageManager !== "bun@1.4.1") { + if (packageJson.packageManager !== "bun@1.4.4") { throw new Error("create_metadata_invalid"); } } diff --git a/packages/platform-cli/src/preset.json b/packages/platform-cli/src/preset.json index 960191b..2710a3e 100644 --- a/packages/platform-cli/src/preset.json +++ b/packages/platform-cli/src/preset.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "runtime": { - "bun": "1.4.1" + "bun": "1.4.4" }, "packages": { "@codelitdev/platform": { @@ -59,11 +59,11 @@ "drizzle-orm": "0.45.2", "drizzle-kit": "0.31.10", "pino": "9.6.0", - "@types/bun": "1.4.1", + "@types/bun": "1.4.4", "@electric-sql/pglite": "0.5.8", "@modelcontextprotocol/sdk": "1.30.0", "dodopayments": "2.48.0", - "next": "16.3.6", + "next": "16.4.3", "react": "19.2.8", "react-dom": "19.2.8" } diff --git a/packages/platform-cli/src/preset.test.ts b/packages/platform-cli/src/preset.test.ts index dadb714..a3ae37c 100644 --- a/packages/platform-cli/src/preset.test.ts +++ b/packages/platform-cli/src/preset.test.ts @@ -5,7 +5,7 @@ describe("platform preset", () => { it("ships schemaVersion 1 with recommended/supported/minimumSecure", () => { const manifest = loadPresetManifest(); expect(manifest.schemaVersion).toBe(1); - expect(manifest.runtime.bun).toBe("1.4.1"); + expect(manifest.runtime.bun).toBe("1.4.4"); const platform = manifest.packages["@codelitdev/platform"]; expect(platform?.recommended).toBeTruthy(); expect(platform?.supported).toContain(">="); diff --git a/packages/platform-cli/src/sync.ts b/packages/platform-cli/src/sync.ts index b256d5d..0ff0858 100644 --- a/packages/platform-cli/src/sync.ts +++ b/packages/platform-cli/src/sync.ts @@ -36,17 +36,20 @@ export function syncProduct(input: { const managedFiles: Record = {}; // Compute and validate the complete plan before touching any file. A - // managed hash mismatch is a conflict, not a best-effort warning. A new - // managed file may be adopted only when it is absent or already identical. + // managed hash mismatch is a conflict, not a best-effort warning. A file + // that already matches the template is never a conflict: that is how a + // product reconciles a conflict by hand, and how a new managed file is + // adopted when it exists. for (const rel of MANAGED_FILES) { const file = path.join(root, rel); const desired = renderTemplateFile(rel, manifest.product); const recorded = manifest.managedFiles[rel]; const current = existsSync(file) ? readFileSync(file, "utf8") : undefined; const drifted = - recorded === undefined - ? current !== undefined && current !== desired - : current === undefined || sha256File(file) !== recorded; + current !== desired && + (recorded === undefined + ? current !== undefined + : current === undefined || sha256File(file) !== recorded); if (drifted) { conflicts.push(rel); continue; diff --git a/packages/platform-conformance/scripts/packed-consumer.mjs b/packages/platform-conformance/scripts/packed-consumer.mjs index fbdfc1f..f4ee2fa 100644 --- a/packages/platform-conformance/scripts/packed-consumer.mjs +++ b/packages/platform-conformance/scripts/packed-consumer.mjs @@ -39,7 +39,7 @@ writeFileSync( name: "consumer", type: "module", private: true, - packageManager: "bun@1.4.1", + packageManager: "bun@1.4.4", overrides: { "@codelitdev/platform": platformTarball, }, diff --git a/packages/platform/scripts/packed-consumer.mjs b/packages/platform/scripts/packed-consumer.mjs index dafafa5..71a4070 100644 --- a/packages/platform/scripts/packed-consumer.mjs +++ b/packages/platform/scripts/packed-consumer.mjs @@ -35,7 +35,7 @@ writeFileSync( name: "consumer", type: "module", private: true, - packageManager: "bun@1.4.1", + packageManager: "bun@1.4.4", }, null, 2, diff --git a/templates/saas-product/.github/workflows/code-quality.yml b/templates/saas-product/.github/workflows/code-quality.yml index 9479604..0639e69 100644 --- a/templates/saas-product/.github/workflows/code-quality.yml +++ b/templates/saas-product/.github/workflows/code-quality.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: oven-sh/setup-bun@v2 with: - bun-version: 1.4.1 + bun-version: 1.4.4 # `platform-cli create` intentionally does not resolve dependencies. # Bootstrap the product lockfile on its first run; all subsequent CI # installs are immutable and use the frozen lockfile below. diff --git a/templates/saas-product/.github/workflows/platform-conformance.yml b/templates/saas-product/.github/workflows/platform-conformance.yml index 216a4d0..0bb5b77 100644 --- a/templates/saas-product/.github/workflows/platform-conformance.yml +++ b/templates/saas-product/.github/workflows/platform-conformance.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: oven-sh/setup-bun@v2 with: - bun-version: 1.4.1 + bun-version: 1.4.4 # `platform-cli create` intentionally does not resolve dependencies. # Bootstrap the product lockfile on its first run; all subsequent CI # installs are immutable and use the frozen lockfile below. diff --git a/templates/saas-product/apps/api/package.json b/templates/saas-product/apps/api/package.json index 17c5c1e..3d020ad 100644 --- a/templates/saas-product/apps/api/package.json +++ b/templates/saas-product/apps/api/package.json @@ -51,6 +51,6 @@ "@types/swagger-ui-express": "^4.1.8", "drizzle-kit": "0.31.10", "typescript": "5.9.3", - "@types/bun": "1.4.1" + "@types/bun": "1.4.4" } } diff --git a/templates/saas-product/apps/web/package.json b/templates/saas-product/apps/web/package.json index 0da18f3..d230155 100644 --- a/templates/saas-product/apps/web/package.json +++ b/templates/saas-product/apps/web/package.json @@ -12,7 +12,7 @@ "dependencies": { "@codelitdev/design-system": "workspace:*", "@codelitdev/observability": "workspace:*", - "next": "16.3.6", + "next": "16.4.3", "posthog-js": "^1.427.2", "react": "19.2.8", "react-dom": "19.2.8" diff --git a/templates/saas-product/package.json b/templates/saas-product/package.json index dd4062a..5ccba4a 100644 --- a/templates/saas-product/package.json +++ b/templates/saas-product/package.json @@ -3,9 +3,9 @@ "private": true, "version": "0.0.0", "type": "module", - "packageManager": "bun@1.4.1", + "packageManager": "bun@1.4.4", "engines": { - "bun": ">=1.4.1" + "bun": ">=1.4.4" }, "workspaces": [ "apps/*", @@ -25,7 +25,7 @@ }, "devDependencies": { "@biomejs/biome": "2.5.12", - "@types/bun": "1.4.1" + "@types/bun": "1.4.4" }, "scripts": { "lint": "bun run --workspaces --if-present lint",