diff --git a/.changeset/popular-berries-deliver.md b/.changeset/popular-berries-deliver.md new file mode 100644 index 00000000..e97207d8 --- /dev/null +++ b/.changeset/popular-berries-deliver.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Fix R2 bucket population diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index 91413797..439cf81c 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -55,7 +55,8 @@ "@dotenvx/dotenvx": "catalog:", "@opennextjs/aws": "3.5.4", "enquirer": "^2.4.1", - "glob": "catalog:" + "glob": "catalog:", + "ts-tqdm": "^0.8.6" }, "devDependencies": { "@cloudflare/workers-types": "catalog:", diff --git a/packages/cloudflare/src/api/overrides/incremental-cache/kv-incremental-cache.ts b/packages/cloudflare/src/api/overrides/incremental-cache/kv-incremental-cache.ts index f18c8687..1335eeda 100644 --- a/packages/cloudflare/src/api/overrides/incremental-cache/kv-incremental-cache.ts +++ b/packages/cloudflare/src/api/overrides/incremental-cache/kv-incremental-cache.ts @@ -140,7 +140,7 @@ class KVIncrementalCache implements IncrementalCache { } protected getKVKey(key: string, isFetch?: boolean): string { - return `${this.getBuildId()}/${key}.${isFetch ? "fetch" : "cache"}`; + return `${this.getBuildId()}/${key}.${isFetch ? "fetch" : "cache"}`.replace(/\/+/g, "/"); } protected getAssetUrl(key: string, isFetch?: boolean): string { diff --git a/packages/cloudflare/src/api/overrides/incremental-cache/r2-incremental-cache.ts b/packages/cloudflare/src/api/overrides/incremental-cache/r2-incremental-cache.ts index 6c1f7f0b..725321a4 100644 --- a/packages/cloudflare/src/api/overrides/incremental-cache/r2-incremental-cache.ts +++ b/packages/cloudflare/src/api/overrides/incremental-cache/r2-incremental-cache.ts @@ -72,7 +72,10 @@ class R2IncrementalCache implements IncrementalCache { protected getR2Key(key: string, isFetch?: boolean): string { const directory = getCloudflareContext().env.NEXT_INC_CACHE_R2_PREFIX ?? "incremental-cache"; - return `${directory}/${process.env.NEXT_BUILD_ID ?? "no-build-id"}/${key}.${isFetch ? "fetch" : "cache"}`; + return `${directory}/${process.env.NEXT_BUILD_ID ?? "no-build-id"}/${key}.${isFetch ? "fetch" : "cache"}`.replace( + /\/+/g, + "/" + ); } } diff --git a/packages/cloudflare/src/cli/commands/populate-cache.ts b/packages/cloudflare/src/cli/commands/populate-cache.ts index 5ecafbf4..893fa0a3 100644 --- a/packages/cloudflare/src/cli/commands/populate-cache.ts +++ b/packages/cloudflare/src/cli/commands/populate-cache.ts @@ -11,6 +11,8 @@ import type { } from "@opennextjs/aws/types/open-next.js"; import type { IncrementalCache, TagCache } from "@opennextjs/aws/types/overrides.js"; import { globSync } from "glob"; +import { tqdm } from "ts-tqdm"; +import { unstable_readConfig } from "wrangler"; import { NAME as R2_CACHE_NAME } from "../../api/overrides/incremental-cache/r2-incremental-cache.js"; import { NAME as D1_TAG_NAME } from "../../api/overrides/tag-cache/d1-next-tag-cache.js"; @@ -61,12 +63,28 @@ export async function populateCache( const name = await resolveCacheName(incrementalCache); switch (name) { case R2_CACHE_NAME: { + const config = unstable_readConfig({ env: populateCacheOptions.environment }); + + const binding = (config.r2_buckets ?? []).find( + ({ binding }) => binding === "NEXT_INC_CACHE_R2_BUCKET" + ); + + if (!binding) { + throw new Error("No R2 binding 'NEXT_INC_CACHE_R2_BUCKET' found!"); + } + + const bucket = binding.bucket_name; + + if (!bucket) { + throw new Error("R2 binding 'NEXT_INC_CACHE_R2_BUCKET' should have a 'bucket_name'"); + } + logger.info("\nPopulating R2 incremental cache..."); const assets = getCacheAssetPaths(options); - assets.forEach(({ fsPath, destPath }) => { + for (const { fsPath, destPath } of tqdm(assets)) { const fullDestPath = path.join( - "NEXT_INC_CACHE_R2_BUCKET", + bucket, process.env.NEXT_INC_CACHE_R2_PREFIX ?? "incremental-cache", destPath ); @@ -78,7 +96,7 @@ export async function populateCache( // 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`); break; } diff --git a/packages/cloudflare/src/cli/utils/run-wrangler.ts b/packages/cloudflare/src/cli/utils/run-wrangler.ts index 2f213c67..8e8f226a 100644 --- a/packages/cloudflare/src/cli/utils/run-wrangler.ts +++ b/packages/cloudflare/src/cli/utils/run-wrangler.ts @@ -74,6 +74,10 @@ export function runWrangler(options: BuildOptions, args: string[], wranglerOpts: { shell: true, stdio: wranglerOpts.logging === "error" ? ["ignore", "ignore", "inherit"] : "inherit", + env: { + ...process.env, + ...(wranglerOpts.logging === "error" ? { WRANGLER_LOG: "error" } : undefined), + }, } ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f471cfa1..738baabf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1005,6 +1005,9 @@ importers: glob: specifier: 'catalog:' version: 11.0.0 + ts-tqdm: + specifier: ^0.8.6 + version: 0.8.6 wrangler: specifier: 'catalog:' version: 4.7.0(@cloudflare/workers-types@4.20250224.0) @@ -9132,6 +9135,9 @@ packages: ts-toolbelt@6.15.5: resolution: {integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==} + ts-tqdm@0.8.6: + resolution: {integrity: sha512-3X3M1PZcHtgQbnwizL+xU8CAgbYbeLHrrDwL9xxcZZrV5J+e7loJm1XrXozHjSkl44J0Zg0SgA8rXbh83kCkcQ==} + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -20138,6 +20144,8 @@ snapshots: ts-toolbelt@6.15.5: {} + ts-tqdm@0.8.6: {} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29