diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0b793a9c1c..3460350c77 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,7 +4,7 @@ name: release # Trigger: GitHub Actions UI → "Run workflow" (workflow_dispatch). # # Flow (one click → everything below): -# 1. prepare — bump version, regen CHANGELOG, open + merge release PR, push tag vX.Y.Z +# 1. prepare — bump version, regen CHANGELOG, commit to main, push tag vX.Y.Z # 2. build-web — build Next.js standalone bundle # 3. build-cli — build + esbuild-bundle CLI # 4. build-bundles — assemble bin/ + web/ + cli/ tarballs for 5 platforms @@ -33,7 +33,7 @@ on: - minor - major dry_run: - description: "Dry run (no PR, tag, npm publish, or tap update)" + description: "Dry run (no commit, tag, npm publish, or tap update)" type: boolean default: false diff --git a/apps/cli/src/runtime.test.ts b/apps/cli/src/runtime.test.ts index 158c181404..e76e39779d 100644 --- a/apps/cli/src/runtime.test.ts +++ b/apps/cli/src/runtime.test.ts @@ -1,4 +1,5 @@ import fs from "node:fs"; +import Module from "node:module"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; @@ -116,6 +117,43 @@ describe("resolveRuntime", () => { }); }); + describe("installed npm runtime package (step 2)", () => { + // Step 2 normally relies on require.resolve('@kdlbs/runtime-darwin-arm64') + // succeeding because the npm package is installed alongside the kandev + // package. In this test environment that package isn't present, so we + // intercept Node's module resolver to point at a fake bundle inside tmpDir. + function mockResolve(target: string): void { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const ModuleAny = Module as unknown as { _resolveFilename: any }; + vi.spyOn(ModuleAny, "_resolveFilename").mockImplementationOnce(() => target); + } + + it("resolves from the installed npm runtime package", () => { + const pkgRoot = path.join(tmpDir, "npm-runtime"); + createFakeBundle(pkgRoot); + // require.resolve('/package.json') normally returns the path to the + // package.json inside node_modules. Point it at our fake bundle instead. + const pkgJsonPath = path.join(pkgRoot, "package.json"); + fs.writeFileSync(pkgJsonPath, "{}"); + mockResolve(pkgJsonPath); + + const result = resolveRuntime(); + expect(result.source).toBe("npm"); + expect(result.bundleDir).toBe(pkgRoot); + }); + + it("surfaces validateBundle errors when the npm package is installed but corrupt", () => { + const pkgRoot = path.join(tmpDir, "broken-npm-runtime"); + fs.mkdirSync(pkgRoot); + // package.json present (so require.resolve succeeds), but no bin/ or web/ + const pkgJsonPath = path.join(pkgRoot, "package.json"); + fs.writeFileSync(pkgJsonPath, "{}"); + mockResolve(pkgJsonPath); + + expect(() => resolveRuntime()).toThrow(/Backend binary not found/); + }); + }); + describe("cache fallback (step 3 — runtimeVersion only)", () => { it("resolves from cache when runtimeVersion is given and cache exists", () => { const tag = "v0.16.0";