From 90b5cdd542991b27fb0bdb0506f199b2c899fe3a Mon Sep 17 00:00:00 2001 From: lemusthelroy Date: Wed, 22 Jul 2026 17:03:40 +0100 Subject: [PATCH] fix: reconstruct build_data when missing on direct-zip deploys ZISI's in-memory FunctionResult returns bootstrapVersion at the top level, but only nests it into buildData when writing the manifest cache. On plain `netlify deploy` and `--skip-functions-cache`, the deploy create payload was sending `build_data: undefined`, causing netlify-server to store an empty bd on every function and route all function uploads through the legacy Lambda env-var path (subject to the 4KB cap). Reconstruct buildData from the top-level fields so bootstrap_version lands on the deploy record correctly. Also lands a unit test asserting build_data.bootstrapVersion is populated for v2 functions on the direct-zip path. RUN-3166 Co-Authored-By: Claude Opus 4.7 (1M context) --- src/utils/deploy/hash-fns.ts | 12 ++++++++++ tests/unit/utils/deploy/hash-fns.test.ts | 28 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/utils/deploy/hash-fns.ts b/src/utils/deploy/hash-fns.ts index c9edc6379a3..44aae22c14b 100644 --- a/src/utils/deploy/hash-fns.ts +++ b/src/utils/deploy/hash-fns.ts @@ -153,6 +153,18 @@ const hashFns = async ( statusCb, tmpDir, }) + + // ZISI's in-memory FunctionResult only nests bootstrap/runtime version into + // buildData when writing the manifest cache. Reconstruct it for direct-zip paths. + for (const func of functionZips) { + if (!func.buildData) { + func.buildData = { + bootstrapVersion: func.bootstrapVersion, + runtimeAPIVersion: func.runtimeAPIVersion, + } + } + } + const fileObjs = functionZips.map( ({ buildData, diff --git a/tests/unit/utils/deploy/hash-fns.test.ts b/tests/unit/utils/deploy/hash-fns.test.ts index d58a5bea0e3..7b10c21c640 100644 --- a/tests/unit/utils/deploy/hash-fns.test.ts +++ b/tests/unit/utils/deploy/hash-fns.test.ts @@ -46,3 +46,31 @@ test('Hashes files in a folder', async (t) => { }) }) }) + +test('Populates build_data.bootstrapVersion for v2 functions on direct-zip path', async (t) => { + await withSiteBuilder(t, async (builder) => { + await builder + .withNetlifyToml({ config: { functions: { directory: 'functions' } } }) + .withFunction({ + path: 'hello.js', + runtimeAPIVersion: 2, + handler: (_req: Request) => new Response('Hello'), + }) + .build() + + const { fnConfig } = await hashFns(new BaseCommand(), [path.join(builder.directory, 'functions')], { + tmpDir: temporaryDirectory(), + concurrentHash: DEFAULT_CONCURRENT_HASH, + statusCb() {}, + }) + + expect(fnConfig).toBeDefined() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- covered by expectation above + const helloConfig = fnConfig!.hello as { build_data?: { bootstrapVersion?: string; runtimeAPIVersion?: number } } + expect(helloConfig).toBeDefined() + expect(helloConfig.build_data).toBeDefined() + expect(helloConfig.build_data?.runtimeAPIVersion).toBe(2) + expect(helloConfig.build_data?.bootstrapVersion).toEqual(expect.any(String)) + expect(helloConfig.build_data?.bootstrapVersion).not.toBe('') + }) +})