From cda7269d64002809706d33041ec4f59b8e3c82e8 Mon Sep 17 00:00:00 2001 From: Mitch Lillie Date: Wed, 15 Apr 2026 10:52:59 -0700 Subject: [PATCH] Run web builds before extension builds during deploy Web builds (e.g. Vite) must complete before extension builds start. Extensions like the admin module copy output from web builds (e.g. dist/) into the bundle via include_assets. Running them in parallel causes a race condition where dist/ is empty or missing when the admin extension tries to copy it, resulting in: index_missing: index.html must be present in the bundle This splits the single renderConcurrent call into two sequential phases: web builds first, then extension builds. --- packages/app/src/cli/services/deploy/bundle.test.ts | 2 +- packages/app/src/cli/services/deploy/bundle.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/app/src/cli/services/deploy/bundle.test.ts b/packages/app/src/cli/services/deploy/bundle.test.ts index e30d3e091fc..78aa958d5fa 100644 --- a/packages/app/src/cli/services/deploy/bundle.test.ts +++ b/packages/app/src/cli/services/deploy/bundle.test.ts @@ -256,7 +256,7 @@ describe('bundleAndBuildExtensions', () => { }) }) - test('runs web build command concurrently with extensions when build command is defined', async () => { + test('runs web build before extensions when build command is defined', async () => { await file.inTemporaryDirectory(async (tmpDir: string) => { // Given const bundlePath = joinPath(tmpDir, 'bundle.zip') diff --git a/packages/app/src/cli/services/deploy/bundle.ts b/packages/app/src/cli/services/deploy/bundle.ts index 5811ff368a9..c071d12e1b0 100644 --- a/packages/app/src/cli/services/deploy/bundle.ts +++ b/packages/app/src/cli/services/deploy/bundle.ts @@ -68,8 +68,16 @@ export async function bundleAndBuildExtensions(options: BundleOptions) { }, })) + // Web builds must complete before extension builds start. Extensions like + // the admin module copy output from web builds (e.g. dist/) into the bundle + // via include_assets. Running them in parallel causes a race condition where + // dist/ is empty or missing when the admin extension tries to copy it. + if (webBuildProcesses.length > 0) { + await renderConcurrent({processes: webBuildProcesses, showTimestamps: false}) + } + await renderConcurrent({ - processes: [webBuildProcesses, extensionBuildProcesses].flat(), + processes: extensionBuildProcesses, showTimestamps: false, })