From 6733ac3fa0cb80108bdaf52b8a2edb0a1415ee19 Mon Sep 17 00:00:00 2001 From: Nicolas Dorseuil Date: Thu, 23 Jul 2026 18:31:25 +0200 Subject: [PATCH] Enhance deployment process by extracting deployment ID and updating cache key logic; add tests for GitbookIncrementalCache --- .../composite/deploy-cloudflare/action.yaml | 15 +++++- packages/gitbook/next.config.mjs | 6 +-- packages/gitbook/openNext/customWorkers/do.ts | 2 +- .../openNext/customWorkers/doWrangler.jsonc | 1 + .../incrementalCache/cacheWorkerClient.ts | 2 +- .../incrementalCache/incrementalCache.test.ts | 51 +++++++++++++++++++ .../incrementalCache/incrementalCache.ts | 3 +- 7 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 packages/gitbook/openNext/incrementalCache/incrementalCache.test.ts diff --git a/.github/composite/deploy-cloudflare/action.yaml b/.github/composite/deploy-cloudflare/action.yaml index 77bc5689d..9f71f5112 100644 --- a/.github/composite/deploy-cloudflare/action.yaml +++ b/.github/composite/deploy-cloudflare/action.yaml @@ -67,10 +67,23 @@ runs: run: bun run turbo build:cloudflare env: GITBOOK_RUNTIME: cloudflare + VERCEL_TARGET_ENV: ${{ inputs.environment }} GITBOOK_BLOCK_SEARCH_INDEXATION: ${{ inputs.environment == 'preview' && 'true' || '' }} GITBOOK_ALLOW_CUSTOMIZATION_OVERRIDE: ${{ inputs.environment == 'preview' && 'true' || '' }} shell: bash + - id: extract_deployment_id + name: Extract Next deployment ID + shell: bash + run: | + deployment_id=$(bun -e ' + const buildOutput = await Bun.file("packages/gitbook/.open-next/server-functions/default/packages/gitbook/.next/required-server-files.json").json(); + const deploymentId = buildOutput.config?.deploymentId; + if (!deploymentId) throw new Error("Next build did not emit a deployment ID"); + console.log(deploymentId); + ') + echo "deployment_id=$deployment_id" >> $GITHUB_OUTPUT + - name: Upload the DO worker uses: cloudflare/wrangler-action@v3.14.0 with: @@ -79,7 +92,7 @@ runs: workingDirectory: ./ wranglerVersion: '4.43.0' environment: ${{ inputs.environment }} - command: deploy --config ./packages/gitbook/openNext/customWorkers/doWrangler.jsonc + command: ${{ format('deploy --var OPEN_NEXT_BUILD_ID:{0} --config ./packages/gitbook/openNext/customWorkers/doWrangler.jsonc', steps.extract_deployment_id.outputs.deployment_id) }} - id: upload_server name: Upload server to Cloudflare diff --git a/packages/gitbook/next.config.mjs b/packages/gitbook/next.config.mjs index 756cefb58..e31d24281 100644 --- a/packages/gitbook/next.config.mjs +++ b/packages/gitbook/next.config.mjs @@ -21,11 +21,7 @@ const allowedDevOrigins = ] : undefined; -// We don't use the deployment ID yet on 2c, we need to remove it because of https://github.com/opennextjs/opennextjs-aws/issues/1136 -let deploymentId = - process.env.GITBOOK_RUNTIME === 'cloudflare' - ? undefined - : process.env.GITBOOK_HEAD_SHA || process.env.GITHUB_SHA || Date.now().toString(); // Needed because we use a custom deployment method i.e. https://vercel.com/docs/skew-protection#custom-deployment-id +let deploymentId = process.env.GITBOOK_HEAD_SHA || process.env.GITHUB_SHA || Date.now().toString(); // Needed because we use a custom deployment method i.e. https://vercel.com/docs/skew-protection#custom-deployment-id const { VERCEL_TARGET_ENV } = process.env; diff --git a/packages/gitbook/openNext/customWorkers/do.ts b/packages/gitbook/openNext/customWorkers/do.ts index 1325d542b..ba2389128 100644 --- a/packages/gitbook/openNext/customWorkers/do.ts +++ b/packages/gitbook/openNext/customWorkers/do.ts @@ -23,7 +23,7 @@ globalThis.openNextConfig = { dangerous: { enableCacheInterception: true, }, -} +}; const cacheEntryTypes = new Set(['cache', 'fetch', 'composable']); diff --git a/packages/gitbook/openNext/customWorkers/doWrangler.jsonc b/packages/gitbook/openNext/customWorkers/doWrangler.jsonc index acab26a53..dc446fd7b 100644 --- a/packages/gitbook/openNext/customWorkers/doWrangler.jsonc +++ b/packages/gitbook/openNext/customWorkers/doWrangler.jsonc @@ -21,6 +21,7 @@ "dev": { "vars": { "STAGE": "dev", + "OPEN_NEXT_BUILD_ID": "local", "NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE": "true" }, "r2_buckets": [ diff --git a/packages/gitbook/openNext/incrementalCache/cacheWorkerClient.ts b/packages/gitbook/openNext/incrementalCache/cacheWorkerClient.ts index a30472335..44f011dd1 100644 --- a/packages/gitbook/openNext/incrementalCache/cacheWorkerClient.ts +++ b/packages/gitbook/openNext/incrementalCache/cacheWorkerClient.ts @@ -75,4 +75,4 @@ export class GitbookIncrementalCache implements IncrementalCache { } } -export default new GitbookIncrementalCache(); \ No newline at end of file +export default new GitbookIncrementalCache(); diff --git a/packages/gitbook/openNext/incrementalCache/incrementalCache.test.ts b/packages/gitbook/openNext/incrementalCache/incrementalCache.test.ts new file mode 100644 index 000000000..7c58f68ab --- /dev/null +++ b/packages/gitbook/openNext/incrementalCache/incrementalCache.test.ts @@ -0,0 +1,51 @@ +import { afterEach, describe, expect, it } from 'bun:test'; +import { createHash } from 'node:crypto'; + +import { DEFAULT_PREFIX, GitbookIncrementalCache } from './incrementalCache'; + +const environmentKeys = ['OPEN_NEXT_BUILD_ID', 'DEPLOYMENT_ID', 'NEXT_BUILD_ID'] as const; +const originalEnvironment = Object.fromEntries( + environmentKeys.map((key) => [key, process.env[key]]) +); + +const hash = (key: string) => createHash('sha256').update(key).digest('hex'); + +afterEach(() => { + for (const key of environmentKeys) { + const value = originalEnvironment[key]; + if (value === undefined) { + delete process.env[key]; + } else { + process.env[key] = value; + } + } +}); + +describe('GitbookIncrementalCache cache keys', () => { + it('uses the deployment-aware OpenNext build ID', () => { + process.env.OPEN_NEXT_BUILD_ID = 'deployment-id'; + process.env.NEXT_BUILD_ID = 'legacy-build-id'; + + expect(new GitbookIncrementalCache().getR2Key('entry')).toBe( + `${DEFAULT_PREFIX}/deployment-id/${hash('entry')}.cache` + ); + }); + + it('uses the Cloudflare deployment ID when the worker build ID is unavailable', () => { + delete process.env.OPEN_NEXT_BUILD_ID; + process.env.DEPLOYMENT_ID = 'runtime-deployment-id'; + + expect(new GitbookIncrementalCache().getR2Key('entry')).toBe( + `${DEFAULT_PREFIX}/runtime-deployment-id/${hash('entry')}.cache` + ); + }); + + it('normalizes composable cache keys before applying the deployment namespace', () => { + process.env.OPEN_NEXT_BUILD_ID = 'deployment-id'; + const key = JSON.stringify(['next-build-id', 'cache-key']); + + expect(new GitbookIncrementalCache().getR2Key(key, 'composable')).toBe( + `${DEFAULT_PREFIX}/dataCache/${hash(JSON.stringify(['cache-key']))}.composable` + ); + }); +}); diff --git a/packages/gitbook/openNext/incrementalCache/incrementalCache.ts b/packages/gitbook/openNext/incrementalCache/incrementalCache.ts index 550c355ea..27f71e9fa 100644 --- a/packages/gitbook/openNext/incrementalCache/incrementalCache.ts +++ b/packages/gitbook/openNext/incrementalCache/incrementalCache.ts @@ -128,7 +128,8 @@ export class GitbookIncrementalCache implements IncrementalCache { } const hash = createHash('sha256').update(key).digest('hex'); - return `${DEFAULT_PREFIX}/${cacheType === 'cache' ? process.env?.NEXT_BUILD_ID : 'dataCache'}/${hash}.${cacheType}`.replace( + const buildId = process.env.OPEN_NEXT_BUILD_ID ?? process.env.DEPLOYMENT_ID; + return `${DEFAULT_PREFIX}/${cacheType === 'cache' ? buildId : 'dataCache'}/${hash}.${cacheType}`.replace( /\/+/g, '/' );