diff --git a/platform/frontend/next.config.ts b/platform/frontend/next.config.ts index cfc8cfd74f..4bcb892ef0 100644 --- a/platform/frontend/next.config.ts +++ b/platform/frontend/next.config.ts @@ -14,6 +14,16 @@ const nextConfig: NextConfig = { NEXT_PUBLIC_APP_VERSION: platformPkg.version, }, output: "standalone", + // Version skew protection during rolling deployments. + // https://nextjs.org/docs/app/api-reference/config/next-config-js/deploymentId + // VERSION is set as a build arg by CI and baked into the + // client bundle here. On client navigation, a mismatch between + // the client's deployment id and the server's response header triggers a + // hard reload, fetching fresh assets that match the server build. + // Next.js restricts the id to [a-zA-Z0-9_-], so non-conforming characters + // (e.g. the dots in `v1.2.41`) are replaced with hyphens. + // https://nextjs.org/docs/messages/deploymentid-invalid-characters + deploymentId: process.env.VERSION?.replace(/[^a-zA-Z0-9_-]/g, "-"), transpilePackages: ["@shared"], // Disable dev indicators so they don't show up in docs automated screenshots devIndicators: false, diff --git a/platform/frontend/src/next-config.test.ts b/platform/frontend/src/next-config.test.ts index 0024ae147c..d3dfd40929 100644 --- a/platform/frontend/src/next-config.test.ts +++ b/platform/frontend/src/next-config.test.ts @@ -6,7 +6,17 @@ vi.mock("@sentry/nextjs", () => ({ describe("next config rewrites", () => { beforeEach(() => { + vi.resetModules(); delete process.env.ARCHESTRA_INTERNAL_API_BASE_URL; + delete process.env.VERSION; + }); + + it("uses sanitized VERSION as the deployment id", async () => { + process.env.VERSION = "v1.2.41+build.5"; + + const { default: nextConfig } = await import("../next.config"); + + expect(nextConfig.deploymentId).toBe("v1-2-41-build-5"); }); it("proxies well-known oauth discovery routes to the backend by default", async () => { diff --git a/platform/turbo.json b/platform/turbo.json index ffe620f383..be51db5f54 100644 --- a/platform/turbo.json +++ b/platform/turbo.json @@ -5,7 +5,8 @@ "tasks": { "build": { "dependsOn": ["^build"], - "outputs": [".next/**", "!.next/cache/**", "dist/**"] + "outputs": [".next/**", "!.next/cache/**", "dist/**"], + "env": ["VERSION"] }, "dev": { "persistent": true,