Skip to content

Commit f25f5dd

Browse files
committed
feat: add Blobs directory
1 parent e411f4f commit f25f5dd

File tree

8 files changed

+100
-31
lines changed

8 files changed

+100
-31
lines changed

packages/build/src/utils/blobs.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { fdir } from 'fdir'
55

66
const LEGACY_BLOBS_PATH = '.netlify/blobs/deploy'
77
const DEPLOY_CONFIG_BLOBS_PATH = '.netlify/deploy/v1/blobs/deploy'
8+
const FRAMEWORKS_BLOBS_PATH = '.netlify/v1/blobs/deploy'
89

910
/** Retrieve the absolute path of the deploy scoped internal blob directories */
1011
export const getBlobsDirs = (buildDir: string, packagePath?: string) => [
@@ -22,12 +23,22 @@ export const getBlobsDirs = (buildDir: string, packagePath?: string) => [
2223
* @returns
2324
*/
2425
export const scanForBlobs = async function (buildDir: string, packagePath?: string) {
25-
const blobsDir = path.resolve(buildDir, packagePath || '', DEPLOY_CONFIG_BLOBS_PATH)
26-
const blobsDirScan = await new fdir().onlyCounts().crawl(blobsDir).withPromise()
26+
const frameworkBlobsDir = path.resolve(buildDir, packagePath || '', FRAMEWORKS_BLOBS_PATH)
27+
const frameworkBlobsDirScan = await new fdir().onlyCounts().crawl(frameworkBlobsDir).withPromise()
2728

28-
if (blobsDirScan.files > 0) {
29+
if (frameworkBlobsDirScan.files > 0) {
2930
return {
30-
directory: blobsDir,
31+
directory: frameworkBlobsDir,
32+
isLegacyDirectory: false,
33+
}
34+
}
35+
36+
const deployConfigBlobsDir = path.resolve(buildDir, packagePath || '', DEPLOY_CONFIG_BLOBS_PATH)
37+
const deployConfigBlobsDirScan = await new fdir().onlyCounts().crawl(deployConfigBlobsDir).withPromise()
38+
39+
if (deployConfigBlobsDirScan.files > 0) {
40+
return {
41+
directory: deployConfigBlobsDir,
3142
isLegacyDirectory: false,
3243
}
3344
}
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { mkdir, writeFile } from 'node:fs/promises'
22

33
await Promise.all([
4-
mkdir('.netlify/blobs/deploy/nested', { recursive: true }),
4+
mkdir('.netlify/v1/blobs/deploy/nested', { recursive: true }),
55
mkdir('dist', { recursive: true }),
66
]);
77

88
await Promise.all([
99
writeFile('dist/index.html', '<h1>Hello World</h1>'),
10-
writeFile('.netlify/blobs/deploy/something.txt', 'some value'),
11-
writeFile('.netlify/blobs/deploy/with-metadata.txt', 'another value'),
12-
writeFile('.netlify/blobs/deploy/$with-metadata.txt.json', JSON.stringify({ "meta": "data", "number": 1234 })),
13-
writeFile('.netlify/blobs/deploy/nested/file.txt', 'file value'),
14-
writeFile('.netlify/blobs/deploy/nested/$file.txt.json', JSON.stringify({ "some": "metadata" })),
10+
writeFile('.netlify/v1/blobs/deploy/something.txt', 'some value'),
11+
writeFile('.netlify/v1/blobs/deploy/with-metadata.txt', 'another value'),
12+
writeFile('.netlify/v1/blobs/deploy/$with-metadata.txt.json', JSON.stringify({ "meta": "data", "number": 1234 })),
13+
writeFile('.netlify/v1/blobs/deploy/nested/file.txt', 'file value'),
14+
writeFile('.netlify/v1/blobs/deploy/nested/$file.txt.json', JSON.stringify({ "some": "metadata" })),
1515
])
1616

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { mkdir, writeFile } from 'node:fs/promises'
22

33
await Promise.all([
4-
mkdir('.netlify/blobs/deploy/nested', { recursive: true }),
4+
mkdir('.netlify/v1/blobs/deploy/nested', { recursive: true }),
55
mkdir('dist', { recursive: true }),
66
]);
77

88
await Promise.all([
99
writeFile('dist/index.html', '<h1>Hello World</h1>'),
10-
writeFile('.netlify/blobs/deploy/something.txt', 'some value'),
11-
writeFile('.netlify/blobs/deploy/with-metadata.txt', 'another value'),
12-
writeFile('.netlify/blobs/deploy/$with-metadata.txt.json', JSON.stringify({ "meta": "data", "number": 1234 })),
13-
writeFile('.netlify/blobs/deploy/nested/file.txt', 'file value'),
14-
writeFile('.netlify/blobs/deploy/nested/$file.txt.json', JSON.stringify({ "some": "metadata" })),
10+
writeFile('.netlify/v1/blobs/deploy/something.txt', 'some value'),
11+
writeFile('.netlify/v1/blobs/deploy/with-metadata.txt', 'another value'),
12+
writeFile('.netlify/v1/blobs/deploy/$with-metadata.txt.json', JSON.stringify({ "meta": "data", "number": 1234 })),
13+
writeFile('.netlify/v1/blobs/deploy/nested/file.txt', 'file value'),
14+
writeFile('.netlify/v1/blobs/deploy/nested/$file.txt.json', JSON.stringify({ "some": "metadata" })),
1515
])
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { mkdir, writeFile } from 'node:fs/promises'
22

3-
await mkdir('.netlify/deploy/v1/blobs/deploy/nested', { recursive: true })
3+
await mkdir('.netlify/v1/blobs/deploy/nested', { recursive: true })
44

55
await Promise.all([
6-
writeFile('.netlify/deploy/v1/blobs/deploy/something.txt', 'some value'),
7-
writeFile('.netlify/deploy/v1/blobs/deploy/with-metadata.txt', 'another value'),
8-
writeFile('.netlify/deploy/v1/blobs/deploy/$with-metadata.txt.json', JSON.stringify({ "meta": "data", "number": 1234 })),
9-
writeFile('.netlify/deploy/v1/blobs/deploy/nested/file.txt', 'file value'),
10-
writeFile('.netlify/deploy/v1/blobs/deploy/nested/$file.txt.json', JSON.stringify({ "some": "metadata" })),
6+
writeFile('.netlify/v1/blobs/deploy/something.txt', 'some value'),
7+
writeFile('.netlify/v1/blobs/deploy/with-metadata.txt', 'another value'),
8+
writeFile('.netlify/v1/blobs/deploy/$with-metadata.txt.json', JSON.stringify({ "meta": "data", "number": 1234 })),
9+
writeFile('.netlify/v1/blobs/deploy/nested/file.txt', 'file value'),
10+
writeFile('.netlify/v1/blobs/deploy/nested/$file.txt.json', JSON.stringify({ "some": "metadata" })),
1111
])
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { mkdir, writeFile } from 'node:fs/promises'
2+
3+
await mkdir('.netlify/deploy/v1/blobs/deploy/nested', { recursive: true })
4+
5+
await Promise.all([
6+
writeFile('.netlify/deploy/v1/blobs/deploy/something.txt', 'some value'),
7+
writeFile('.netlify/deploy/v1/blobs/deploy/with-metadata.txt', 'another value'),
8+
writeFile('.netlify/deploy/v1/blobs/deploy/$with-metadata.txt.json', JSON.stringify({ "meta": "data", "number": 1234 })),
9+
writeFile('.netlify/deploy/v1/blobs/deploy/nested/file.txt', 'file value'),
10+
writeFile('.netlify/deploy/v1/blobs/deploy/nested/$file.txt.json', JSON.stringify({ "some": "metadata" })),
11+
])
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[build]
2+
command = "node build.mjs"
3+
base = "/"
4+
publish = "/dist"
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { mkdir, writeFile } from 'node:fs/promises'
22

3-
await mkdir('.netlify/blobs/deploy', { recursive: true })
3+
await mkdir('.netlify/v1/blobs/deploy', { recursive: true })
44

55
await Promise.all([
6-
writeFile('.netlify/blobs/deploy/with-metadata.txt', 'another value'),
7-
writeFile('.netlify/blobs/deploy/$with-metadata.txt.json', 'this is not json'),
6+
writeFile('.netlify/v1/blobs/deploy/with-metadata.txt', 'another value'),
7+
writeFile('.netlify/v1/blobs/deploy/$with-metadata.txt.json', 'this is not json'),
88
])

packages/build/tests/blobs_upload/tests.js

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ test.afterEach.always(async (t) => {
4141
delete process.env.NETLIFY_BLOBS_CONTEXT
4242
})
4343

44-
test.serial("blobs upload, don't run when deploy id is provided and no files in directory", async (t) => {
44+
test.serial('Blobs upload step uploads files when deploy ID is provided and no files in directory', async (t) => {
4545
const {
4646
success,
4747
logs: { stdout },
@@ -57,7 +57,7 @@ test.serial("blobs upload, don't run when deploy id is provided and no files in
5757
})
5858

5959
test.serial(
60-
"blobs upload, don't run when there are files but deploy id is not provided using legacy API",
60+
'Blobs upload step uploads files when there are files but deploy ID is not provided (legacy API)',
6161
async (t) => {
6262
const fixture = await new Fixture('./fixtures/src_with_blobs_legacy').withCopyRoot({ git: false })
6363

@@ -77,7 +77,7 @@ test.serial(
7777
},
7878
)
7979

80-
test.serial('blobs upload, uploads files to deploy store using legacy API', async (t) => {
80+
test.serial('Blobs upload step uploads files to deploy store (legacy API)', async (t) => {
8181
const fixture = await new Fixture('./fixtures/src_with_blobs_legacy').withCopyRoot({ git: false })
8282

8383
const {
@@ -121,7 +121,50 @@ test.serial('blobs upload, uploads files to deploy store using legacy API', asyn
121121
t.true(stdout.join('\n').includes('Uploading blobs to deploy store'))
122122
})
123123

124-
test.serial('blobs upload, uploads files to deploy store', async (t) => {
124+
test.serial('Blobs upload step uploads files to deploy store (legacy deploy config API)', async (t) => {
125+
const fixture = await new Fixture('./fixtures/src_with_blobs_legacy_deploy_config').withCopyRoot({ git: false })
126+
127+
const {
128+
success,
129+
logs: { stdout },
130+
} = await fixture
131+
.withFlags({ deployId: 'abc123', siteId: 'test', token: TOKEN, offline: true, cwd: fixture.repositoryRoot })
132+
.runBuildProgrammatic()
133+
t.true(success)
134+
t.is(t.context.blobRequests.set.length, 6)
135+
136+
const regionRequests = t.context.blobRequests.set.filter((urlPath) => {
137+
const url = new URL(urlPath, 'http://localhost')
138+
139+
return url.searchParams.has('region')
140+
})
141+
142+
t.is(regionRequests.length, 3)
143+
144+
const storeOpts = { deployID: 'abc123', siteID: 'test', token: TOKEN }
145+
if (semver.lt(nodeVersion, '18.0.0')) {
146+
const nodeFetch = await import('node-fetch')
147+
storeOpts.fetch = nodeFetch.default
148+
}
149+
150+
const store = getDeployStore(storeOpts)
151+
152+
const blob1 = await store.getWithMetadata('something.txt')
153+
t.is(blob1.data, 'some value')
154+
t.deepEqual(blob1.metadata, {})
155+
156+
const blob2 = await store.getWithMetadata('with-metadata.txt')
157+
t.is(blob2.data, 'another value')
158+
t.deepEqual(blob2.metadata, { meta: 'data', number: 1234 })
159+
160+
const blob3 = await store.getWithMetadata('nested/file.txt')
161+
t.is(blob3.data, 'file value')
162+
t.deepEqual(blob3.metadata, { some: 'metadata' })
163+
164+
t.true(stdout.join('\n').includes('Uploading blobs to deploy store'))
165+
})
166+
167+
test.serial('Blobs upload step uploads files to deploy store', async (t) => {
125168
const fixture = await new Fixture('./fixtures/src_with_blobs').withCopyRoot({ git: false })
126169

127170
const {
@@ -167,13 +210,13 @@ test.serial('blobs upload, uploads files to deploy store', async (t) => {
167210
t.true(stdout.join('\n').includes('Uploading blobs to deploy store'))
168211
})
169212

170-
test.serial('blobs upload, cancels deploy if blob metadata is malformed', async (t) => {
213+
test.serial('Blobs upload step cancels deploy if blob metadata is malformed', async (t) => {
171214
const fixture = await new Fixture('./fixtures/src_with_malformed_blobs_metadata').withCopyRoot({ git: false })
172215
const { success, severityCode } = await fixture
173216
.withFlags({ deployId: 'abc123', siteId: 'test', token: TOKEN, offline: true, debug: false })
174217
.runBuildProgrammatic()
175218

176-
const blobsDir = join(fixture.repositoryRoot, '.netlify', 'blobs', 'deploy')
219+
const blobsDir = join(fixture.repositoryRoot, '.netlify', 'v1', 'blobs', 'deploy')
177220
await t.notThrowsAsync(access(blobsDir))
178221

179222
t.is(t.context.blobRequests.set, undefined)

0 commit comments

Comments
 (0)