diff --git a/.changeset/chilly-knives-taste.md b/.changeset/chilly-knives-taste.md new file mode 100644 index 00000000..03c0f2e6 --- /dev/null +++ b/.changeset/chilly-knives-taste.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": patch +--- + +feat: support passing the wrangler environment when populating the cache diff --git a/examples/common/config-e2e.ts b/examples/common/config-e2e.ts index a84d3460..a6327445 100644 --- a/examples/common/config-e2e.ts +++ b/examples/common/config-e2e.ts @@ -23,13 +23,14 @@ export function configurePlaywright( let command: string; let timeout: number; if (isWorker) { + const env = app === "r2-incremental-cache" ? "--env e2e" : ""; if (isCI) { // Do not build on CI - there is a preceding build step - command = `pnpm preview:worker -- --port ${port} --inspector-port ${inspectorPort}`; + command = `pnpm preview:worker -- --port ${port} --inspector-port ${inspectorPort} ${env}`; timeout = 100_000; } else { timeout = 500_000; - command = `pnpm preview -- --port ${port} --inspector-port ${inspectorPort}`; + command = `pnpm preview -- --port ${port} --inspector-port ${inspectorPort} ${env}`; } } else { timeout = 100_000; diff --git a/examples/overrides/r2-incremental-cache/package.json b/examples/overrides/r2-incremental-cache/package.json index f6bd1725..0f5af158 100644 --- a/examples/overrides/r2-incremental-cache/package.json +++ b/examples/overrides/r2-incremental-cache/package.json @@ -7,8 +7,7 @@ "build": "next build", "start": "next start", "lint": "next lint", - "d1:clean": "wrangler d1 execute NEXT_CACHE_D1 --command \"DROP TABLE IF EXISTS tags; DROP TABLE IF EXISTS revalidations\"", - "build:worker": "pnpm d1:clean && pnpm opennextjs-cloudflare build", + "build:worker": "pnpm opennextjs-cloudflare build", "preview:worker": "pnpm opennextjs-cloudflare preview", "preview": "pnpm build:worker && pnpm preview:worker", "e2e": "playwright test -c e2e/playwright.config.ts" diff --git a/examples/overrides/r2-incremental-cache/wrangler.jsonc b/examples/overrides/r2-incremental-cache/wrangler.jsonc index c0e4413e..7a9c4216 100644 --- a/examples/overrides/r2-incremental-cache/wrangler.jsonc +++ b/examples/overrides/r2-incremental-cache/wrangler.jsonc @@ -8,24 +8,31 @@ "directory": ".open-next/assets", "binding": "ASSETS" }, - "d1_databases": [ - { - "binding": "NEXT_CACHE_D1", - "database_id": "NEXT_CACHE_D1", - "database_name": "NEXT_CACHE_D1" + "env": { + "e2e": { + "d1_databases": [ + { + "binding": "NEXT_CACHE_D1", + "database_id": "NEXT_CACHE_D1", + "database_name": "NEXT_CACHE_D1" + } + ], + "services": [ + { + "binding": "NEXT_CACHE_REVALIDATION_WORKER", + "service": "r2-incremental-cache-e2e" + } + ], + "r2_buckets": [ + { + "binding": "NEXT_CACHE_R2_BUCKET", + "bucket_name": "NEXT_CACHE_R2_BUCKET", + "preview_bucket_name": "NEXT_CACHE_R2_BUCKET" + } + ] + }, + "prod": { + // left blank to test that environment configuration works properly } - ], - "services": [ - { - "binding": "NEXT_CACHE_REVALIDATION_WORKER", - "service": "r2-incremental-cache" - } - ], - "r2_buckets": [ - { - "binding": "NEXT_CACHE_R2_BUCKET", - "bucket_name": "NEXT_CACHE_R2_BUCKET", - "preview_bucket_name": "NEXT_CACHE_R2_BUCKET" - } - ] + } } diff --git a/packages/cloudflare/src/cli/args.ts b/packages/cloudflare/src/cli/args.ts index 1dbec4f2..87927659 100644 --- a/packages/cloudflare/src/cli/args.ts +++ b/packages/cloudflare/src/cli/args.ts @@ -2,7 +2,8 @@ import { mkdirSync, type Stats, statSync } from "node:fs"; import { resolve } from "node:path"; import { parseArgs } from "node:util"; -import { isWranglerTarget, WranglerTarget } from "./utils/run-wrangler.js"; +import type { WranglerTarget } from "./utils/run-wrangler.js"; +import { getWranglerEnvironmentFlag, isWranglerTarget } from "./utils/run-wrangler.js"; export type Arguments = ( | { @@ -11,8 +12,15 @@ export type Arguments = ( skipWranglerConfigCheck: boolean; minify: boolean; } - | { command: "preview" | "deploy"; passthroughArgs: string[] } - | { command: "populateCache"; target: WranglerTarget } + | { + command: "preview" | "deploy"; + passthroughArgs: string[]; + } + | { + command: "populateCache"; + target: WranglerTarget; + environment?: string; + } ) & { outputDir?: string }; export function getArgs(): Arguments { @@ -29,6 +37,8 @@ export function getArgs(): Arguments { const outputDir = values.output ? resolve(values.output) : undefined; if (outputDir) assertDirArg(outputDir, "output", true); + const passthroughArgs = getPassthroughArgs(); + switch (positionals[0]) { case "build": return { @@ -43,12 +53,21 @@ export function getArgs(): Arguments { }; case "preview": case "deploy": - return { command: positionals[0], outputDir, passthroughArgs: getPassthroughArgs() }; + return { + command: positionals[0], + outputDir, + passthroughArgs, + }; case "populateCache": if (!isWranglerTarget(positionals[1])) { throw new Error(`Error: invalid target for populating the cache, expected 'local' | 'remote'`); } - return { command: "populateCache", outputDir, target: positionals[1] }; + return { + command: "populateCache", + outputDir, + target: positionals[1], + environment: getWranglerEnvironmentFlag(passthroughArgs), + }; default: throw new Error("Error: invalid command, expected 'build' | 'preview' | 'deploy' | 'populateCache'"); } diff --git a/packages/cloudflare/src/cli/deploy/deploy.ts b/packages/cloudflare/src/cli/deploy/deploy.ts index 31f71477..81e2a963 100644 --- a/packages/cloudflare/src/cli/deploy/deploy.ts +++ b/packages/cloudflare/src/cli/deploy/deploy.ts @@ -2,13 +2,17 @@ import { BuildOptions } from "@opennextjs/aws/build/helper.js"; import { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; import { populateCache } from "../populate-cache/populate-cache.js"; -import { runWrangler } from "../utils/run-wrangler.js"; +import { getWranglerEnvironmentFlag, runWrangler } from "../utils/run-wrangler.js"; export async function deploy( options: BuildOptions, config: OpenNextConfig, deployOptions: { passthroughArgs: string[] } ) { - await populateCache(options, config, { target: "remote" }); + await populateCache(options, config, { + target: "remote", + environment: getWranglerEnvironmentFlag(deployOptions.passthroughArgs), + }); + runWrangler(options, ["deploy", ...deployOptions.passthroughArgs], { logging: "all" }); } diff --git a/packages/cloudflare/src/cli/populate-cache/populate-cache.ts b/packages/cloudflare/src/cli/populate-cache/populate-cache.ts index 949a04f7..300e4489 100644 --- a/packages/cloudflare/src/cli/populate-cache/populate-cache.ts +++ b/packages/cloudflare/src/cli/populate-cache/populate-cache.ts @@ -46,7 +46,7 @@ function getCacheAssetPaths(opts: BuildOptions) { export async function populateCache( options: BuildOptions, config: OpenNextConfig, - populateCacheOptions: { target: WranglerTarget } + populateCacheOptions: { target: WranglerTarget; environment?: string } ) { const { incrementalCache, tagCache } = config.default.override ?? {}; @@ -72,7 +72,9 @@ export async function populateCache( runWrangler( options, ["r2 object put", JSON.stringify(fullDestPath), `--file ${JSON.stringify(fsPath)}`], - { ...populateCacheOptions, excludeRemoteFlag: true, logging: "error" } + // NOTE: R2 does not support the environment flag and results in the following error: + // Incorrect type for the 'cacheExpiry' field on 'HttpMetadata': the provided value is not of type 'date'. + { target: populateCacheOptions.target, excludeRemoteFlag: true, logging: "error" } ); }); logger.info(`Successfully populated cache with ${assets.length} assets`); diff --git a/packages/cloudflare/src/cli/preview/preview.ts b/packages/cloudflare/src/cli/preview/preview.ts index b4df9d0d..0963f879 100644 --- a/packages/cloudflare/src/cli/preview/preview.ts +++ b/packages/cloudflare/src/cli/preview/preview.ts @@ -2,13 +2,17 @@ import { BuildOptions } from "@opennextjs/aws/build/helper.js"; import { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; import { populateCache } from "../populate-cache/populate-cache.js"; -import { runWrangler } from "../utils/run-wrangler.js"; +import { getWranglerEnvironmentFlag, runWrangler } from "../utils/run-wrangler.js"; export async function preview( options: BuildOptions, config: OpenNextConfig, previewOptions: { passthroughArgs: string[] } ) { - await populateCache(options, config, { target: "local" }); + await populateCache(options, config, { + target: "local", + environment: getWranglerEnvironmentFlag(previewOptions.passthroughArgs), + }); + runWrangler(options, ["dev", ...previewOptions.passthroughArgs], { logging: "all" }); } diff --git a/packages/cloudflare/src/cli/utils/run-wrangler.ts b/packages/cloudflare/src/cli/utils/run-wrangler.ts index 6852d7b6..a7e9ea32 100644 --- a/packages/cloudflare/src/cli/utils/run-wrangler.ts +++ b/packages/cloudflare/src/cli/utils/run-wrangler.ts @@ -5,17 +5,21 @@ import logger from "@opennextjs/aws/logger.js"; export type WranglerTarget = "local" | "remote"; -export function runWrangler( - options: BuildOptions, - args: string[], - wranglerOpts: { target?: WranglerTarget; excludeRemoteFlag?: boolean; logging?: "all" | "error" } = {} -) { +type WranglerOptions = { + target?: WranglerTarget; + environment?: string; + excludeRemoteFlag?: boolean; + logging?: "all" | "error"; +}; + +export function runWrangler(options: BuildOptions, args: string[], wranglerOpts: WranglerOptions = {}) { const result = spawnSync( options.packager, [ "exec", "wrangler", ...args, + wranglerOpts.environment && `--env ${wranglerOpts.environment}`, wranglerOpts.target === "remote" && !wranglerOpts.excludeRemoteFlag && "--remote", wranglerOpts.target === "local" && "--local", ].filter((v): v is string => !!v), @@ -34,3 +38,24 @@ export function runWrangler( export function isWranglerTarget(v: string | undefined): v is WranglerTarget { return !!v && ["local", "remote"].includes(v); } + +/** + * Find the value of the environment flag (`--env` / `-e`) used by Wrangler. + * + * @param args - CLI arguments. + * @returns Value of the environment flag. + */ +export function getWranglerEnvironmentFlag(args: string[]) { + for (let i = 0; i <= args.length; i++) { + const arg = args[i]; + if (!arg) continue; + + if (arg === "--env" || arg === "-e") { + return args[i + 1]; + } + + if (arg.startsWith("--env=") || arg.startsWith("-e=")) { + return arg.split("=")[1]; + } + } +}