Skip to content

Commit a4430fa

Browse files
author
Nicolas Dorseuil
committed
Merge branch 'main' into cloudflare/rnd-7231
2 parents 86222c7 + a1da4f0 commit a4430fa

File tree

17 files changed

+952
-73
lines changed

17 files changed

+952
-73
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: Gradual Deploy to Cloudflare
2+
description: Use gradual deployment to deploy to Cloudflare. This action will upload the middleware and server versions to Cloudflare and kept them bound together
3+
inputs:
4+
apiToken:
5+
description: 'Cloudflare API token'
6+
required: true
7+
accountId:
8+
description: 'Cloudflare account ID'
9+
required: true
10+
environment:
11+
description: 'Cloudflare environment to deploy to (staging, production, preview)'
12+
required: true
13+
middlewareVersionId:
14+
description: 'Middleware version ID to deploy'
15+
required: true
16+
serverVersionId:
17+
description: 'Server version ID to deploy'
18+
required: true
19+
outputs:
20+
deployment-url:
21+
description: "Deployment URL"
22+
value: ${{ steps.deploy_middleware.outputs.deployment-url }}
23+
runs:
24+
using: 'composite'
25+
steps:
26+
- id: wrangler_status
27+
name: Check wrangler deployment status
28+
uses: cloudflare/[email protected]
29+
with:
30+
apiToken: ${{ inputs.apiToken }}
31+
accountId: ${{ inputs.accountId }}
32+
workingDirectory: ./
33+
wranglerVersion: '4.10.0'
34+
environment: ${{ inputs.environment }}
35+
command: deployments status --config ./packages/gitbook-v2/openNext/customWorkers/defaultWrangler.jsonc
36+
37+
# This step is used to get the version ID that is currently deployed to Cloudflare.
38+
- id: extract_current_version
39+
name: Extract current version
40+
shell: bash
41+
run: |
42+
version_id=$(echo "${{ steps.wrangler_status.outputs.command-output }}" | grep -A 3 "(100%)" | grep -oP '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
43+
echo "version_id=$version_id" >> $GITHUB_OUTPUT
44+
45+
- id: deploy_server
46+
name: Deploy server to Cloudflare at 0%
47+
uses: cloudflare/[email protected]
48+
with:
49+
apiToken: ${{ inputs.apiToken }}
50+
accountId: ${{ inputs.accountId }}
51+
workingDirectory: ./
52+
wranglerVersion: '4.10.0'
53+
environment: ${{ inputs.environment }}
54+
command: versions deploy ${{ steps.extract_current_version.outputs.version_id }}@100% ${{ inputs.serverVersionId }}@0% -y --config ./packages/gitbook-v2/openNext/customWorkers/defaultWrangler.jsonc
55+
56+
# Since we use version overrides headers, we can directly deploy the middleware to 100%.
57+
- id: deploy_middleware
58+
name: Deploy middleware to Cloudflare at 100%
59+
uses: cloudflare/[email protected]
60+
with:
61+
apiToken: ${{ inputs.apiToken }}
62+
accountId: ${{ inputs.accountId }}
63+
workingDirectory: ./
64+
wranglerVersion: '4.10.0'
65+
environment: ${{ inputs.environment }}
66+
command: versions deploy ${{ inputs.middlewareVersionId }}@100% -y --config ./packages/gitbook-v2/openNext/customWorkers/middlewareWrangler.jsonc
67+
68+
- name: Deploy server to Cloudflare at 100%
69+
uses: cloudflare/[email protected]
70+
with:
71+
apiToken: ${{ inputs.apiToken }}
72+
accountId: ${{ inputs.accountId }}
73+
workingDirectory: ./
74+
wranglerVersion: '4.10.0'
75+
environment: ${{ inputs.environment }}
76+
command: versions deploy ${{ inputs.serverVersionId }}@100% -y --config ./packages/gitbook-v2/openNext/customWorkers/defaultWrangler.jsonc
77+
78+
- name: Outputs
79+
shell: bash
80+
env:
81+
DEPLOYMENT_URL: ${{ steps.deploy_middleware.outputs.deployment-url }}
82+
run: |
83+
echo "URL: ${{ steps.deploy_middleware.outputs.deployment-url }}"

.github/composite/deploy-cloudflare/action.yaml

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ inputs:
2828
outputs:
2929
deployment-url:
3030
description: "Deployment URL"
31-
value: ${{ steps.deploy.outputs.deployment-url }}
31+
value: ${{ steps.upload_middleware.outputs.deployment-url }}
3232
runs:
3333
using: 'composite'
3434
steps:
@@ -63,19 +63,76 @@ runs:
6363
env:
6464
GITBOOK_RUNTIME: cloudflare
6565
shell: bash
66-
- id: deploy
67-
name: Deploy to Cloudflare
66+
67+
- name: Upload the DO worker
6868
uses: cloudflare/[email protected]
6969
with:
7070
apiToken: ${{ inputs.apiToken }}
7171
accountId: ${{ inputs.accountId }}
7272
workingDirectory: ./
7373
wranglerVersion: '4.10.0'
7474
environment: ${{ inputs.environment }}
75-
command: ${{ inputs.deploy == 'true' && 'deploy' || format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook-v2/wrangler.jsonc
75+
command: deploy --config ./packages/gitbook-v2/openNext/customWorkers/doWrangler.jsonc
76+
77+
- id: upload_server
78+
name: Upload server to Cloudflare
79+
uses: cloudflare/[email protected]
80+
with:
81+
apiToken: ${{ inputs.apiToken }}
82+
accountId: ${{ inputs.accountId }}
83+
workingDirectory: ./
84+
wranglerVersion: '4.10.0'
85+
environment: ${{ inputs.environment }}
86+
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook-v2/openNext/customWorkers/defaultWrangler.jsonc
87+
88+
- name: Extract server version worker ID
89+
shell: bash
90+
id: extract_server_version_id
91+
run: |
92+
version_id=$(echo '${{ steps.upload_server.outputs.command-output }}' | grep "Worker Version ID" | awk '{print $4}')
93+
echo "version_id=$version_id" >> $GITHUB_OUTPUT
94+
95+
- name: Run updateWrangler scripts
96+
shell: bash
97+
run: |
98+
bun run ./packages/gitbook-v2/openNext/customWorkers/script/updateWrangler.ts ${{ steps.extract_server_version_id.outputs.version_id }}
99+
100+
- id: upload_middleware
101+
name: Upload middleware to Cloudflare
102+
uses: cloudflare/[email protected]
103+
with:
104+
apiToken: ${{ inputs.apiToken }}
105+
accountId: ${{ inputs.accountId }}
106+
workingDirectory: ./
107+
wranglerVersion: '4.10.0'
108+
environment: ${{ inputs.environment }}
109+
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook-v2/openNext/customWorkers/middlewareWrangler.jsonc
110+
111+
- name: Extract middleware version worker ID
112+
shell: bash
113+
id: extract_middleware_version_id
114+
run: |
115+
version_id=$(echo '${{ steps.upload_middleware.outputs.command-output }}' | grep "Worker Version ID" | awk '{print $4}')
116+
echo "version_id=$version_id" >> $GITHUB_OUTPUT
117+
118+
- name: Deploy server and middleware to Cloudflare
119+
if: ${{ inputs.deploy == 'true' }}
120+
uses: ./.github/actions/gradual-deploy-cloudflare
121+
with:
122+
apiToken: ${{ inputs.apiToken }}
123+
accountId: ${{ inputs.accountId }}
124+
opServiceAccount: ${{ inputs.opServiceAccount }}
125+
opItem: ${{ inputs.opItem }}
126+
environment: ${{ inputs.environment }}
127+
serverVersionId: ${{ steps.extract_server_version_id.outputs.version_id }}
128+
middlewareVersionId: ${{ steps.extract_middleware_version_id.outputs.version_id }}
129+
deploy: ${{ inputs.deploy }}
130+
131+
76132
- name: Outputs
77133
shell: bash
78134
env:
79-
DEPLOYMENT_URL: ${{ steps.deploy.outputs.deployment-url }}
135+
DEPLOYMENT_URL: ${{ steps.upload_middleware.outputs.deployment-url }}
80136
run: |
81-
echo "URL: ${{ steps.deploy.outputs.deployment-url }}"
137+
echo "URL: ${{ steps.upload_middleware.outputs.deployment-url }}"
138+
echo "Output server: ${{ steps.upload_server.outputs.command-output }}"
Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
1-
import { defineCloudflareConfig } from '@opennextjs/cloudflare';
2-
import doShardedTagCache from '@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache';
3-
import {
4-
softTagFilter,
5-
withFilter,
6-
} from '@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter';
1+
import type { OpenNextConfig } from '@opennextjs/cloudflare';
72

8-
export default defineCloudflareConfig({
9-
incrementalCache: () => import('./openNext/incrementalCache').then((m) => m.default),
10-
tagCache: withFilter({
11-
tagCache: doShardedTagCache({
12-
baseShardSize: 12,
13-
regionalCache: true,
14-
shardReplication: {
15-
numberOfSoftReplicas: 2,
16-
numberOfHardReplicas: 1,
17-
},
18-
}),
19-
// We don't use `revalidatePath`, so we filter out soft tags
20-
filterFn: softTagFilter,
21-
}),
22-
queue: () => import('./openNext/queue').then((m) => m.default),
23-
24-
// Performance improvements as we don't use PPR
25-
enableCacheInterception: true,
26-
});
3+
export default {
4+
default: {
5+
override: {
6+
wrapper: 'cloudflare-node',
7+
converter: 'edge',
8+
proxyExternalRequest: 'fetch',
9+
queue: () => import('./openNext/queue/server').then((m) => m.default),
10+
incrementalCache: () => import('./openNext/incrementalCache').then((m) => m.default),
11+
tagCache: () => import('./openNext/tagCache/middleware').then((m) => m.default),
12+
},
13+
},
14+
middleware: {
15+
external: true,
16+
override: {
17+
wrapper: 'cloudflare-edge',
18+
converter: 'edge',
19+
proxyExternalRequest: 'fetch',
20+
queue: () => import('./openNext/queue/middleware').then((m) => m.default),
21+
incrementalCache: () => import('./openNext/incrementalCache').then((m) => m.default),
22+
tagCache: () => import('./openNext/tagCache/middleware').then((m) => m.default),
23+
},
24+
},
25+
dangerous: {
26+
enableCacheInterception: true,
27+
},
28+
edgeExternals: ['node:crypto'],
29+
} satisfies OpenNextConfig;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { runWithCloudflareRequestContext } from '../../.open-next/cloudflare/init.js';
2+
3+
import { DurableObject } from 'cloudflare:workers';
4+
5+
// Only needed to run locally, in prod we'll use the one from do.js
6+
export class R2WriteBuffer extends DurableObject {
7+
writePromise;
8+
9+
async write(cacheKey, value) {
10+
// We are already writing to this key
11+
if (this.writePromise) {
12+
return;
13+
}
14+
15+
this.writePromise = this.env.NEXT_INC_CACHE_R2_BUCKET.put(cacheKey, value);
16+
this.ctx.waitUntil(
17+
this.writePromise.finally(() => {
18+
this.writePromise = undefined;
19+
})
20+
);
21+
}
22+
}
23+
24+
export default {
25+
async fetch(request, env, ctx) {
26+
return runWithCloudflareRequestContext(request, env, ctx, async () => {
27+
// We can't move the handler import to the top level, otherwise the runtime will not be properly initialized
28+
const { handler } = await import(
29+
'../../.open-next/server-functions/default/handler.mjs'
30+
);
31+
32+
// - `Request`s are handled by the Next server
33+
return handler(request, env, ctx);
34+
});
35+
},
36+
};

0 commit comments

Comments
 (0)