From 6779ef06ba5380fe72602d46bcf6fe68c8366477 Mon Sep 17 00:00:00 2001 From: jake champion Date: Mon, 20 Oct 2025 13:25:58 +0100 Subject: [PATCH] fix: add dry-run feature flag for edge-function tarball generation adding a new feature flag for a dry-run mode for tarball bundling of edge-functions. this will allow us to ensure that the new bunding system functionality is not throwing errors before we look to turn it on --- packages/edge-bundler/node/bridge.ts | 13 +++- packages/edge-bundler/node/bundler.test.ts | 76 +++++++++++++++++++++ packages/edge-bundler/node/bundler.ts | 43 ++++++++---- packages/edge-bundler/node/feature_flags.ts | 1 + 4 files changed, 116 insertions(+), 17 deletions(-) diff --git a/packages/edge-bundler/node/bridge.ts b/packages/edge-bundler/node/bridge.ts index 85fb386d1f..08b55933de 100644 --- a/packages/edge-bundler/node/bridge.ts +++ b/packages/edge-bundler/node/bridge.ts @@ -69,9 +69,16 @@ export class DenoBridge { this.onAfterDownload = options.onAfterDownload this.onBeforeDownload = options.onBeforeDownload this.useGlobal = options.useGlobal ?? true - this.versionRange = - options.versionRange ?? - (options.featureFlags?.edge_bundler_generate_tarball ? NEXT_DENO_VERSION_RANGE : DENO_VERSION_RANGE) + if (options.versionRange) { + this.versionRange = options.versionRange + } else if ( + options.featureFlags?.edge_bundler_dry_run_generate_tarball || + options.featureFlags?.edge_bundler_generate_tarball + ) { + this.versionRange = NEXT_DENO_VERSION_RANGE + } else { + this.versionRange = DENO_VERSION_RANGE + } } private async downloadBinary() { diff --git a/packages/edge-bundler/node/bundler.test.ts b/packages/edge-bundler/node/bundler.test.ts index 355a4cfcd4..171057d7bf 100644 --- a/packages/edge-bundler/node/bundler.test.ts +++ b/packages/edge-bundler/node/bundler.test.ts @@ -832,6 +832,82 @@ describe.skipIf(lt(denoVersion, '2.4.3'))( await cleanup() await rm(vendorDirectory.path, { force: true, recursive: true }) }) + + describe('Dry-run tarball generation flag enabled', () => { + test('Logs success message when tarball generation succeeded', async () => { + const systemLogger = vi.fn() + const { basePath, cleanup, distPath } = await useFixture('imports_node_builtin', { copyDirectory: true }) + const declarations: Declaration[] = [ + { + function: 'func1', + path: '/func1', + }, + ] + + await bundle([join(basePath, 'netlify/edge-functions')], distPath, declarations, { + basePath, + configPath: join(basePath, '.netlify/edge-functions/config.json'), + featureFlags: { + edge_bundler_dry_run_generate_tarball: true, + edge_bundler_generate_tarball: false, + }, + systemLogger, + }) + + expect(systemLogger).toHaveBeenCalledWith('Dry run: Tarball bundle generated successfully.') + + const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8') + const manifest = JSON.parse(manifestFile) + + expect(manifest.bundles.length).toBe(1) + expect(manifest.bundles[0].format).toBe('eszip2') + + await cleanup() + }) + + test('Logs error message when tarball generation failed and does not fail the overall build', async () => { + const systemLogger = vi.fn() + vi.resetModules() + vi.doMock('./formats/tarball.js', () => ({ + bundle: vi.fn().mockRejectedValue(new Error('Simulated tarball bundling failure')), + })) + + const { bundle: bundleUnderTest } = await import('./bundler.js') + + const { basePath, cleanup, distPath } = await useFixture('imports_node_builtin', { copyDirectory: true }) + const sourceDirectory = join(basePath, 'functions') + const declarations: Declaration[] = [ + { + function: 'func1', + path: '/func1', + }, + ] + + await expect( + bundleUnderTest([sourceDirectory], distPath, declarations, { + basePath, + configPath: join(sourceDirectory, 'config.json'), + featureFlags: { + edge_bundler_dry_run_generate_tarball: true, + edge_bundler_generate_tarball: false, + }, + systemLogger, + }), + ).resolves.toBeDefined() + + expect(systemLogger).toHaveBeenCalledWith( + `Dry run: Tarball bundle generation failed: Simulated tarball bundling failure`, + ) + + const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8') + const manifest = JSON.parse(manifestFile) + expect(manifest.bundles.length).toBe(1) + expect(manifest.bundles[0].format).toBe('eszip2') + + await cleanup() + vi.resetModules() + }) + }) }, 10_000, ) diff --git a/packages/edge-bundler/node/bundler.ts b/packages/edge-bundler/node/bundler.ts index 20e309ec37..1eac79ded5 100644 --- a/packages/edge-bundler/node/bundler.ts +++ b/packages/edge-bundler/node/bundler.ts @@ -118,20 +118,35 @@ export const bundle = async ( const bundles: Bundle[] = [] - if (featureFlags.edge_bundler_generate_tarball) { - bundles.push( - await bundleTarball({ - basePath, - buildID, - debug, - deno, - distDirectory, - functions, - featureFlags, - importMap: importMap.clone(), - vendorDirectory: vendor?.directory, - }), - ) + if (featureFlags.edge_bundler_generate_tarball || featureFlags.edge_bundler_dry_run_generate_tarball) { + const tarballPromise = bundleTarball({ + basePath, + buildID, + debug, + deno, + distDirectory, + functions, + featureFlags, + importMap: importMap.clone(), + vendorDirectory: vendor?.directory, + }) + + if (featureFlags.edge_bundler_dry_run_generate_tarball) { + try { + await tarballPromise + logger.system('Dry run: Tarball bundle generated successfully.') + } catch (error: unknown) { + if (error instanceof Error) { + logger.system(`Dry run: Tarball bundle generation failed: ${error.message}`) + } else { + logger.system(`Dry run: Tarball bundle generation failed: ${String(error)}`) + } + } + } + + if (featureFlags.edge_bundler_generate_tarball) { + bundles.push(await tarballPromise) + } } if (vendor) { diff --git a/packages/edge-bundler/node/feature_flags.ts b/packages/edge-bundler/node/feature_flags.ts index eb27ce1528..a5cd183c9a 100644 --- a/packages/edge-bundler/node/feature_flags.ts +++ b/packages/edge-bundler/node/feature_flags.ts @@ -1,5 +1,6 @@ const defaultFlags = { edge_bundler_generate_tarball: false, + edge_bundler_dry_run_generate_tarball: false, } type FeatureFlag = keyof typeof defaultFlags