Skip to content

Commit 8b3f65b

Browse files
mrstorkpiehserhalp
authored
feat: Update to latest blob client (7.3.0) (#398)
* chore: Add buildVersion and useRegionalBlobs to PluginContext * chore: Centralize deploy store configuration * chore: Extract FixtureTestContext and BLOB_TOKEN into their own files * chore: Prepare getBlobServerGets to handle regions * chore: Set and make use of shared build/run USE_REGIONAL_BLOBS environment variable * chore: Use latest @netlify/blobs version * chore: Pin regional blob functionality to a higher version of the cli * chore: mark all runtime modules as external * fix: Ensure ts files are compiled in unit tests * chore: linting * maybe win slash? * test: add fixture using CLI before regional blobs support * test: use createRequestContext in tests instead of manually creating request context objects * Update tests/e2e/page-router.test.ts Co-authored-by: Philippe Serhal <[email protected]> * test: rename unit test for blobs directory --------- Co-authored-by: Michal Piechowiak <[email protected]> Co-authored-by: Philippe Serhal <[email protected]>
1 parent 27ab1f3 commit 8b3f65b

39 files changed

+293
-161
lines changed

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"homepage": "https://github.com/netlify/next-runtime-minimal#readme",
5050
"devDependencies": {
5151
"@fastly/http-compute-js": "1.1.4",
52-
"@netlify/blobs": "^7.0.1",
52+
"@netlify/blobs": "^7.3.0",
5353
"@netlify/build": "^29.37.2",
5454
"@netlify/edge-bundler": "^11.4.0",
5555
"@netlify/edge-functions": "^2.5.1",

src/build/content/static.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import glob from 'fast-glob'
77
import { Mock, beforeEach, describe, expect, test, vi } from 'vitest'
88

99
import { mockFileSystem } from '../../../tests/index.js'
10-
import { FixtureTestContext, createFsFixture } from '../../../tests/utils/fixture.js'
10+
import { type FixtureTestContext } from '../../../tests/utils/contexts.js'
11+
import { createFsFixture } from '../../../tests/utils/fixture.js'
1112
import { PluginContext, RequiredServerFilesManifest } from '../plugin-context.js'
1213

1314
import { copyStaticAssets, copyStaticContent } from './static.js'

src/build/functions/server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ const getHandlerFile = async (ctx: PluginContext): Promise<string> => {
9797
return template
9898
.replaceAll('{{cwd}}', posixJoin(ctx.lambdaWorkingDirectory))
9999
.replace('{{nextServerHandler}}', posixJoin(ctx.nextServerHandler))
100+
.replace('{{useRegionalBlobs}}', ctx.useRegionalBlobs.toString())
100101
}
101102

102103
return await readFile(join(templatesDir, 'handler.tmpl.js'), 'utf-8')

src/build/plugin-context.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,18 @@ test('nx monorepo with package path and different distDir', () => {
195195
expect(ctx.relPublishDir).toBe('dist/apps/my-app/.next')
196196
expect(ctx.publishDir).toBe(join(cwd, 'dist/apps/my-app/.next'))
197197
})
198+
199+
test('should use deploy configuration blobs directory when @netlify/build version supports regional blob awareness', () => {
200+
const { cwd } = mockFileSystem({
201+
'.next/required-server-files.json': JSON.stringify({
202+
config: { distDir: '.next' },
203+
relativeAppDir: '',
204+
} as RequiredServerFilesManifest),
205+
})
206+
207+
const ctx = new PluginContext({
208+
constants: { NETLIFY_BUILD_VERSION: '29.39.1' },
209+
} as NetlifyPluginOptions)
210+
211+
expect(ctx.blobDir).toBe(join(cwd, '.netlify/deploy/v1/blobs/deploy'))
212+
})

src/build/plugin-context.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {
1313
import type { PrerenderManifest, RoutesManifest } from 'next/dist/build/index.js'
1414
import type { MiddlewareManifest } from 'next/dist/build/webpack/plugins/middleware-plugin.js'
1515
import type { NextConfigComplete } from 'next/dist/server/config-shared.js'
16+
import { satisfies } from 'semver'
1617

1718
const MODULE_DIR = fileURLToPath(new URL('.', import.meta.url))
1819
const PLUGIN_DIR = join(MODULE_DIR, '../..')
@@ -135,12 +136,27 @@ export class PluginContext {
135136

136137
/**
137138
* Absolute path of the directory that will be deployed to the blob store
138-
* `.netlify/blobs/deploy`
139+
* region aware: `.netlify/deploy/v1/blobs/deploy`
140+
* default: `.netlify/blobs/deploy`
139141
*/
140142
get blobDir(): string {
143+
if (this.useRegionalBlobs) {
144+
return this.resolveFromPackagePath('.netlify/deploy/v1/blobs/deploy')
145+
}
146+
141147
return this.resolveFromPackagePath('.netlify/blobs/deploy')
142148
}
143149

150+
get buildVersion(): string {
151+
return this.constants.NETLIFY_BUILD_VERSION || 'v0.0.0'
152+
}
153+
154+
get useRegionalBlobs(): boolean {
155+
// Region-aware blobs are only available as of CLI v17.22.1 (i.e. Build v29.39.1)
156+
const REQUIRED_BUILD_VERSION = '>=29.39.1'
157+
return satisfies(this.buildVersion, REQUIRED_BUILD_VERSION, { includePrerelease: true })
158+
}
159+
144160
/**
145161
* Absolute path of the directory containing the files for the serverless lambda function
146162
* `.netlify/functions-internal`

src/build/templates/handler-monorepo.tmpl.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import tracing from '{{cwd}}/.netlify/dist/run/handlers/tracing.js'
77

88
process.chdir('{{cwd}}')
99

10+
// Set feature flag for regional blobs
11+
process.env.USE_REGIONAL_BLOBS = '{{useRegionalBlobs}}'
12+
1013
let cachedHandler
1114
export default async function (req, context) {
1215
if (process.env.NETLIFY_OTLP_TRACE_EXPORTER_URL) {

src/build/templates/handler.tmpl.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import serverHandler from './.netlify/dist/run/handlers/server.js'
66
import { getTracer } from './.netlify/dist/run/handlers/tracer.cjs'
77
import tracing from './.netlify/dist/run/handlers/tracing.js'
88

9+
// Set feature flag for regional blobs
10+
process.env.USE_REGIONAL_BLOBS = '{{useRegionalBlobs}}'
11+
912
export default async function handler(req, context) {
1013
if (process.env.NETLIFY_OTLP_TRACE_EXPORTER_URL) {
1114
tracing.start()

src/run/handlers/cache.cts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44
import { Buffer } from 'node:buffer'
55

6-
import { getDeployStore, Store } from '@netlify/blobs'
6+
import { Store } from '@netlify/blobs'
77
import { purgeCache } from '@netlify/functions'
88
import { type Span } from '@opentelemetry/api'
99
import { NEXT_CACHE_TAGS_HEADER } from 'next/dist/lib/constants.js'
@@ -16,6 +16,7 @@ import type {
1616
NetlifyCacheHandlerValue,
1717
NetlifyIncrementalCacheValue,
1818
} from '../../shared/cache-types.cjs'
19+
import { getRegionalBlobStore } from '../regional-blob-store.cjs'
1920

2021
import { getRequestContext } from './request-context.cjs'
2122
import { getTracer } from './tracer.cjs'
@@ -24,8 +25,6 @@ type TagManifest = { revalidatedAt: number }
2425

2526
type TagManifestBlobCache = Record<string, Promise<TagManifest>>
2627

27-
const fetchBeforeNextPatchedIt = globalThis.fetch
28-
2928
export class NetlifyCacheHandler implements CacheHandler {
3029
options: CacheHandlerContext
3130
revalidatedTags: string[]
@@ -36,7 +35,7 @@ export class NetlifyCacheHandler implements CacheHandler {
3635
constructor(options: CacheHandlerContext) {
3736
this.options = options
3837
this.revalidatedTags = options.revalidatedTags
39-
this.blobStore = getDeployStore({ fetch: fetchBeforeNextPatchedIt, consistency: 'strong' })
38+
this.blobStore = getRegionalBlobStore({ consistency: 'strong' })
4039
this.tagManifestsFetchedFromBlobStoreInCurrentRequest = {}
4140
}
4241

src/run/headers.test.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type { NextConfigComplete } from 'next/dist/server/config-shared.js'
22
import { v4 } from 'uuid'
3-
import { afterEach, describe, expect, test, vi, beforeEach } from 'vitest'
3+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
44

5-
import { FixtureTestContext } from '../../tests/utils/fixture.js'
5+
import { type FixtureTestContext } from '../../tests/utils/contexts.js'
66
import { generateRandomObjectID, startMockBlobStore } from '../../tests/utils/helpers.js'
77

8-
import { setVaryHeaders, setCacheControlHeaders } from './headers.js'
8+
import { createRequestContext } from './handlers/request-context.cjs'
9+
import { setCacheControlHeaders, setVaryHeaders } from './headers.js'
910

1011
beforeEach<FixtureTestContext>(async (ctx) => {
1112
// set for each test a new deployID and siteID
@@ -198,7 +199,7 @@ describe('headers', () => {
198199
const request = new Request(defaultUrl)
199200
vi.spyOn(headers, 'set')
200201

201-
setCacheControlHeaders(headers, request, {})
202+
setCacheControlHeaders(headers, request, createRequestContext())
202203

203204
expect(headers.set).toHaveBeenCalledTimes(0)
204205
})
@@ -208,7 +209,10 @@ describe('headers', () => {
208209
const request = new Request(defaultUrl)
209210
vi.spyOn(headers, 'set')
210211

211-
setCacheControlHeaders(headers, request, { usedFsRead: true })
212+
const requestContext = createRequestContext()
213+
requestContext.usedFsRead = true
214+
215+
setCacheControlHeaders(headers, request, requestContext)
212216

213217
expect(headers.set).toHaveBeenNthCalledWith(
214218
1,
@@ -231,7 +235,7 @@ describe('headers', () => {
231235
const request = new Request(defaultUrl)
232236
vi.spyOn(headers, 'set')
233237

234-
setCacheControlHeaders(headers, request, {})
238+
setCacheControlHeaders(headers, request, createRequestContext())
235239

236240
expect(headers.set).toHaveBeenCalledTimes(0)
237241
})
@@ -245,7 +249,7 @@ describe('headers', () => {
245249
const request = new Request(defaultUrl)
246250
vi.spyOn(headers, 'set')
247251

248-
setCacheControlHeaders(headers, request, {})
252+
setCacheControlHeaders(headers, request, createRequestContext())
249253

250254
expect(headers.set).toHaveBeenCalledTimes(0)
251255
})
@@ -258,7 +262,7 @@ describe('headers', () => {
258262
const request = new Request(defaultUrl)
259263
vi.spyOn(headers, 'set')
260264

261-
setCacheControlHeaders(headers, request, {})
265+
setCacheControlHeaders(headers, request, createRequestContext())
262266

263267
expect(headers.set).toHaveBeenNthCalledWith(
264268
1,
@@ -280,7 +284,7 @@ describe('headers', () => {
280284
const request = new Request(defaultUrl, { method: 'HEAD' })
281285
vi.spyOn(headers, 'set')
282286

283-
setCacheControlHeaders(headers, request, {})
287+
setCacheControlHeaders(headers, request, createRequestContext())
284288

285289
expect(headers.set).toHaveBeenNthCalledWith(
286290
1,
@@ -302,7 +306,7 @@ describe('headers', () => {
302306
const request = new Request(defaultUrl, { method: 'POST' })
303307
vi.spyOn(headers, 'set')
304308

305-
setCacheControlHeaders(headers, request, {})
309+
setCacheControlHeaders(headers, request, createRequestContext())
306310

307311
expect(headers.set).toHaveBeenCalledTimes(0)
308312
})
@@ -315,7 +319,7 @@ describe('headers', () => {
315319
const request = new Request(defaultUrl)
316320
vi.spyOn(headers, 'set')
317321

318-
setCacheControlHeaders(headers, request, {})
322+
setCacheControlHeaders(headers, request, createRequestContext())
319323

320324
expect(headers.set).toHaveBeenNthCalledWith(1, 'cache-control', 'public')
321325
expect(headers.set).toHaveBeenNthCalledWith(
@@ -333,7 +337,7 @@ describe('headers', () => {
333337
const request = new Request(defaultUrl)
334338
vi.spyOn(headers, 'set')
335339

336-
setCacheControlHeaders(headers, request, {})
340+
setCacheControlHeaders(headers, request, createRequestContext())
337341

338342
expect(headers.set).toHaveBeenNthCalledWith(1, 'cache-control', 'max-age=604800')
339343
expect(headers.set).toHaveBeenNthCalledWith(
@@ -351,7 +355,7 @@ describe('headers', () => {
351355
const request = new Request(defaultUrl)
352356
vi.spyOn(headers, 'set')
353357

354-
setCacheControlHeaders(headers, request, {})
358+
setCacheControlHeaders(headers, request, createRequestContext())
355359

356360
expect(headers.set).toHaveBeenNthCalledWith(
357361
1,

0 commit comments

Comments
 (0)