Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/scripts/wrangler-deploy.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { spawnSync } from "node:child_process";

const maxAttempts = 12;

for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const result = spawnSync("vp", ["exec", "wrangler", "deploy", ...process.argv.slice(2)], {
encoding: "utf8",
env: { ...process.env, NO_COLOR: "1" },
});
process.stdout.write(result.stdout ?? "");
process.stderr.write(result.stderr ?? "");

if (result.status === 0) process.exit(0);
if (!`${result.stdout}${result.stderr}`.includes("[code: 10007]") || attempt === maxAttempts) {
process.exit(result.status ?? 1);
}

console.log("Worker provisioning has not propagated; retrying in 10 seconds...");
await new Promise((resolve) => setTimeout(resolve, 10_000));
}
28 changes: 9 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,6 @@ jobs:
runs-on: ubuntu-latest
env:
CNA_CLOUDFLARE_WORKER: create-next-app-cloudflare
# Public test namespace already used by examples/response-store-demo.
CNA_CLOUDFLARE_KV_NAMESPACE_ID: b38f84f642aa4bd4ba17a37bb516f0b5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down Expand Up @@ -337,7 +335,7 @@ jobs:
working-directory: ${{ runner.temp }}/cna-cloudflare
run: >-
vp exec vinext init --skip-check --platform=cloudflare
--data-cache=kv
--cdn-cache=response-store
--image-optimization=cloudflare-images

- name: Configure generated Cloudflare deployment
Expand All @@ -349,11 +347,6 @@ jobs:
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
config.name = process.env.CNA_CLOUDFLARE_WORKER;
config.preview_urls = true;
for (const namespace of config.kv_namespaces ?? []) {
if (namespace.binding === "VINEXT_KV_CACHE") {
namespace.id = process.env.CNA_CLOUDFLARE_KV_NAMESPACE_ID;
}
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
'

Expand All @@ -363,15 +356,15 @@ jobs:

- name: Verify generated Cloudflare build output
working-directory: ${{ runner.temp }}/cna-cloudflare
run: test -f dist/server/wrangler.json
run: |
test -f dist/server/wrangler.json
test -f wrangler.response-store.jsonc

create-vinext-app-cloudflare:
name: create-vinext-app (Cloudflare build)
runs-on: ubuntu-latest
env:
CVA_CLOUDFLARE_WORKER: create-vinext-app-cloudflare
# Public test namespace already used by examples/response-store-demo.
CVA_CLOUDFLARE_KV_NAMESPACE_ID: b38f84f642aa4bd4ba17a37bb516f0b5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down Expand Up @@ -403,8 +396,7 @@ jobs:
--skip-install
--disable-git
--platform=cloudflare
--cdn-cache=data-cache
--data-cache=kv
--cdn-cache=response-store
--image-optimization=cloudflare-images

- name: Pin pnpm in scaffolded project to vinext's version
Expand Down Expand Up @@ -445,11 +437,6 @@ jobs:
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
config.name = process.env.CVA_CLOUDFLARE_WORKER;
config.preview_urls = true;
for (const namespace of config.kv_namespaces ?? []) {
if (namespace.binding === "VINEXT_KV_CACHE") {
namespace.id = process.env.CVA_CLOUDFLARE_KV_NAMESPACE_ID;
}
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
'

Expand All @@ -459,7 +446,9 @@ jobs:

- name: Verify generated Cloudflare build output
working-directory: ${{ runner.temp }}/cva-cloudflare
run: test -f dist/server/wrangler.json
run: |
test -f dist/server/wrangler.json
test -f wrangler.response-store.jsonc

e2e:
name: E2E (${{ matrix.label }})
Expand Down Expand Up @@ -689,6 +678,7 @@ jobs:
create-next-app-cloudflare,
create-vinext-app-cloudflare,
e2e,
response-store-e2e,
]
runs-on: ubuntu-latest
steps:
Expand Down
21 changes: 11 additions & 10 deletions .github/workflows/create-vinext-app-cloudflare-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ jobs:
runs-on: ubuntu-latest
env:
CVA_CLOUDFLARE_WORKER: create-vinext-app-cloudflare
# Public test namespace already used by examples/response-store-demo.
CVA_CLOUDFLARE_KV_NAMESPACE_ID: b38f84f642aa4bd4ba17a37bb516f0b5
CVA_CLOUDFLARE_EXPECTED_TEXT: vinext + Cloudflare Workers
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -56,8 +54,7 @@ jobs:
--skip-install
--disable-git
--platform=cloudflare
--cdn-cache=data-cache
--data-cache=kv
--cdn-cache=response-store
--image-optimization=cloudflare-images

- name: Pin pnpm in scaffolded project to vinext's version
Expand Down Expand Up @@ -98,11 +95,6 @@ jobs:
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
config.name = process.env.CVA_CLOUDFLARE_WORKER;
config.preview_urls = true;
for (const namespace of config.kv_namespaces ?? []) {
if (namespace.binding === "VINEXT_KV_CACHE") {
namespace.id = process.env.CVA_CLOUDFLARE_KV_NAMESPACE_ID;
}
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
'

Expand All @@ -112,7 +104,16 @@ jobs:

- name: Verify generated Cloudflare build output
working-directory: ${{ runner.temp }}/cva-cloudflare
run: test -f dist/server/wrangler.json
run: |
test -f dist/server/wrangler.json
test -f wrangler.response-store.jsonc

- name: Deploy generated Workers Response Store
working-directory: ${{ runner.temp }}/cva-cloudflare
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: node "$GITHUB_WORKSPACE/.github/scripts/wrangler-deploy.mjs" --config wrangler.response-store.jsonc

- name: Deploy generated Cloudflare project
working-directory: ${{ runner.temp }}/cva-cloudflare
Expand Down
95 changes: 53 additions & 42 deletions .github/workflows/deploy-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: Deploy Examples
on:
push:
branches: [main]
pull_request: # run on PRs against any base branch, not just main
pull_request:
types: [opened, synchronize, reopened, closed]

concurrency:
group: deploy-${{ github.event_name }}-${{ github.ref }}
Expand All @@ -14,7 +15,7 @@ permissions: {}
jobs:
response_store:
# Skip entirely for fork PRs — they don't have access to deploy secrets.
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
if: github.event_name == 'push' || (github.event.action != 'closed' && github.event.pull_request.head.repo.full_name == github.repository)
name: Deploy and test Workers Response Store
runs-on: ubuntu-latest
permissions:
Expand Down Expand Up @@ -48,14 +49,6 @@ jobs:
echo "inline_name=${prefix}-rs-inline"
} >> "$GITHUB_OUTPUT"

- name: Deploy cache Worker
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: packages/workers-response-store
command: deploy --config example/service-binding/wrangler.cache.jsonc --name ${{ steps.names.outputs.service_name }}

- name: Configure live client Worker
working-directory: packages/workers-response-store
env:
Expand All @@ -74,21 +67,17 @@ jobs:
fs.writeFileSync(".wrangler-e2e.user.jsonc", config);
'

- name: Deploy live client Worker
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: packages/workers-response-store
command: deploy --config .wrangler-e2e.user.jsonc

- name: Deploy live self-contained Worker
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: packages/workers-response-store
command: deploy --config wrangler.jsonc --name ${{ steps.names.outputs.inline_name }}
- name: Deploy live Response Store Workers
working-directory: packages/workers-response-store
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
SERVICE_NAME: ${{ steps.names.outputs.service_name }}
INLINE_NAME: ${{ steps.names.outputs.inline_name }}
run: |
node "$GITHUB_WORKSPACE/.github/scripts/wrangler-deploy.mjs" --config example/service-binding/wrangler.cache.jsonc --name "$SERVICE_NAME"
node "$GITHUB_WORKSPACE/.github/scripts/wrangler-deploy.mjs" --config .wrangler-e2e.user.jsonc
node "$GITHUB_WORKSPACE/.github/scripts/wrangler-deploy.mjs" --config wrangler.jsonc --name "$INLINE_NAME"

- name: Run live Response Store E2E
working-directory: packages/workers-response-store
Expand Down Expand Up @@ -135,7 +124,7 @@ jobs:

deploy:
# Skip entirely for fork PRs — they don't have access to deploy secrets.
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
if: github.event_name == 'push' || (github.event.action != 'closed' && github.event.pull_request.head.repo.full_name == github.repository)
name: Deploy ${{ matrix.example.name }}
needs: response_store
runs-on: ubuntu-latest
Expand Down Expand Up @@ -199,29 +188,46 @@ jobs:
- name: Build vinext plugin
run: vp run build

- name: Build example
if: matrix.example.static_export != true
run: vp build
working-directory: ${{ matrix.example.working_directory }}

- name: Bind Response Store demo to this run's cache Worker
if: matrix.example.name == 'response-store-demo'
- name: Configure Response Store demo preview
if: matrix.example.name == 'response-store-demo' && github.event_name == 'pull_request'
working-directory: ${{ matrix.example.working_directory }}
env:
RESPONSE_STORE_SERVICE: ${{ needs.response_store.outputs.service_name }}
SERVICE_NAME: pr-${{ github.event.pull_request.number }}-response-store-demo-response-store
run: |
# Node reads the value from process.env.
# shellcheck disable=SC2016
node -e '
const fs = require("node:fs");
const path = "dist/server/wrangler.json";
const config = JSON.parse(fs.readFileSync(path, "utf8"));
const binding = config.services?.find(({ binding }) => binding === "RESPONSE_STORE");
if (!binding) throw new Error("Missing RESPONSE_STORE service binding");
binding.service = process.env.RESPONSE_STORE_SERVICE;
fs.writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`);
const appPath = "wrangler.jsonc";
const servicePath = "wrangler.response-store.jsonc";
const app = fs.readFileSync(appPath, "utf8").replace(
`"service": "response-store-demo-response-store"`,
`"service": "${process.env.SERVICE_NAME}"`,
);
const service = fs.readFileSync(servicePath, "utf8").replace(
`"name": "response-store-demo-response-store"`,
`"name": "${process.env.SERVICE_NAME}"`,
);
if (!app.includes(process.env.SERVICE_NAME) || !service.includes(process.env.SERVICE_NAME)) {
throw new Error("Could not configure the Response Store preview Worker name");
}
fs.writeFileSync(appPath, app);
fs.writeFileSync(servicePath, service);
'

- name: Deploy configured Response Store service
if: matrix.example.name == 'response-store-demo' || (matrix.example.name == 'web' && github.event_name == 'push' && github.ref == 'refs/heads/main')
working-directory: ${{ matrix.example.working_directory }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: node "$GITHUB_WORKSPACE/.github/scripts/wrangler-deploy.mjs" --config wrangler.response-store.jsonc

- name: Build example
if: matrix.example.static_export != true
run: vp build
working-directory: ${{ matrix.example.working_directory }}

- name: Build static export
if: matrix.example.static_export == true
run: vp exec vinext build
Expand Down Expand Up @@ -348,7 +354,7 @@ jobs:
tests/e2e/cloudflare-workers/optimistic-search-navigation.spec.ts

cleanup-response-store:
name: Clean up Workers Response Store service
name: Clean up Workers Response Store services
if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
needs: [response_store, deploy, smoke-test, deployed-e2e]
runs-on: ubuntu-latest
Expand All @@ -361,13 +367,18 @@ jobs:
- uses: ./.github/actions/setup

- name: Delete cache Worker
continue-on-error: true
working-directory: packages/workers-response-store
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
SERVICE_NAME: ${{ needs.response_store.outputs.service_name }}
DEMO_SERVICE_NAME: pr-${{ github.event.pull_request.number }}-response-store-demo-response-store
PR_ACTION: ${{ github.event.action }}
run: |
if [[ -n "$SERVICE_NAME" ]]; then
if [[ "$PR_ACTION" == "closed" ]]; then
vp exec wrangler delete "$DEMO_SERVICE_NAME" --force
elif [[ -n "$SERVICE_NAME" ]]; then
vp exec wrangler delete "$SERVICE_NAME" --force
fi

Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
},
"dependencies": {
"@cloudflare/kumo": "catalog:",
"@cloudflare/workers-response-store": "workspace:*",
"@phosphor-icons/react": "catalog:",
"@vinext/cloudflare": "workspace:*",
"drizzle-orm": "catalog:",
Expand Down
8 changes: 2 additions & 6 deletions apps/web/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import vinext from "vinext";
import { defineConfig } from "vite-plus";
import { cdnAdapter } from "@vinext/cloudflare/cache/cdn-adapter";
import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";
import { responseStoreAdapter } from "@vinext/cloudflare/cache/response-store-adapter";
import { imagesOptimizer } from "@vinext/cloudflare/images/images-optimizer";
import { cloudflare } from "@cloudflare/vite-plugin";

export default defineConfig({
plugins: [
vinext({
cache: {
cdn: cdnAdapter(),
data: kvDataAdapter(),
},
cache: responseStoreAdapter(),
images: {
optimizer: imagesOptimizer(),
},
Expand Down
26 changes: 11 additions & 15 deletions apps/web/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,6 @@
"migrations_dir": "./migrations",
},
],
// KV namespace used as the backing store for vinext's ISR CacheHandler.
// Pages opt into ISR with `export const revalidate = <seconds>`, and the
// rendered output (RSC + HTML) is cached here keyed by route + searchParams.
// Created via: wrangler kv namespace create vinext-web-cache
"kv_namespaces": [
{
"binding": "VINEXT_KV_CACHE",
"id": "08075d24ec854a19a52c13f031723def",
},
],
"r2_buckets": [
{
"binding": "PERFORMANCE_PROFILES",
Expand All @@ -55,10 +45,16 @@
"secrets": {
"required": ["COMPAT_INGEST_SECRET"],
},
"cache": {
"enabled": true,
},
"version_metadata": {
"binding": "CF_VERSION_METADATA",
"cache": { "enabled": false },
"exports": {
"default": { "type": "worker", "cache": { "enabled": false } },
},
"services": [
{
"binding": "RESPONSE_STORE",
"service": "vinext-web-response-store",
"entrypoint": "ResponseStoreService",
},
],
"version_metadata": { "binding": "CF_VERSION_METADATA" },
}
Loading
Loading