Skip to content

Commit 8d7c0a5

Browse files
fix: deploy DB migrations (#8103)
We are generating migration files but we're not including them in the deployed files. This PR addresses that.
1 parent 3c5c76f commit 8d7c0a5

3 files changed

Lines changed: 88 additions & 1 deletion

File tree

src/utils/deploy/deploy-site.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import hashFiles from './hash-files.js'
1919
import hashFns from './hash-fns.js'
2020
import {
2121
deployFileNormalizer,
22+
getDbMigrationsDistPathIfExists,
2223
getDeployConfigPathIfExists,
2324
getEdgeFunctionsDistPathIfExists,
2425
isEdgeFunctionFile,
@@ -98,6 +99,7 @@ export const deploySite = async (
9899

99100
const edgeFunctionsDistPath = await getEdgeFunctionsDistPathIfExists(workingDir)
100101
const deployConfigPath = await getDeployConfigPathIfExists(workingDir)
102+
const dbMigrationsDistPath = await getDbMigrationsDistPathIfExists(workingDir)
101103
const [
102104
{ files: staticFiles, filesShaMap: staticShaMap },
103105
{ fnConfig, fnShaMap, functionSchedules, functions, functionsWithNativeModules },
@@ -106,7 +108,7 @@ export const deploySite = async (
106108
hashFiles({
107109
assetType,
108110
concurrentHash,
109-
directories: [dir, edgeFunctionsDistPath, deployConfigPath].filter(Boolean),
111+
directories: [dir, edgeFunctionsDistPath, deployConfigPath, dbMigrationsDistPath].filter(Boolean),
110112
filter,
111113
hashAlgorithm,
112114
normalizer: deployFileNormalizer.bind(null, workingDir),

src/utils/deploy/process-files.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import { EDGE_FUNCTIONS_FOLDER, PUBLIC_URL_PATH } from '../../lib/edge-functions
66
import { File } from './file.js'
77

88
const DEPLOY_CONFIG_PATH = 'deploy-config'
9+
const DB_MIGRATIONS_PUBLIC_URL_PATH = '.netlify/internal/db/migrations'
910

1011
const deployConfigPathPath = getPathInProject([DEPLOY_CONFIG_PATH])
1112
const edgeFunctionsDistPath = getPathInProject([EDGE_FUNCTIONS_FOLDER])
13+
const dbMigrationsDistPath = getPathInProject(['internal', 'db', 'migrations'])
1214

1315
export const deployFileNormalizer = (workingDir: string, file: File) => {
1416
let { normalizedPath } = file
@@ -22,6 +24,11 @@ export const deployFileNormalizer = (workingDir: string, file: File) => {
2224
case join(workingDir, deployConfigPathPath):
2325
normalizedPath = `.netlify/${DEPLOY_CONFIG_PATH}/${file.normalizedPath}`
2426

27+
break
28+
29+
case join(workingDir, dbMigrationsDistPath):
30+
normalizedPath = `${DB_MIGRATIONS_PUBLIC_URL_PATH}/${file.normalizedPath}`
31+
2532
break
2633
}
2734

@@ -37,6 +44,9 @@ export const getDeployConfigPathIfExists = async (workingDir: string) =>
3744
export const getEdgeFunctionsDistPathIfExists = async (workingDir: string) =>
3845
getDirectoryIfExists(join(workingDir, edgeFunctionsDistPath))
3946

47+
export const getDbMigrationsDistPathIfExists = async (workingDir: string) =>
48+
getDirectoryIfExists(join(workingDir, dbMigrationsDistPath))
49+
4050
const getDirectoryIfExists = async (directoryPath: string) => {
4151
try {
4252
const stats = await stat(directoryPath)

tests/integration/commands/deploy/deploy.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,81 @@ describe.concurrent('deploy command', () => {
401401
})
402402
})
403403

404+
test('should deploy DB migration files when internal migrations directory exists', async (t) => {
405+
await withMockDeploy(async (mockApi, deployState) => {
406+
await withSiteBuilder(t, async (builder) => {
407+
builder
408+
.withContentFile({
409+
path: 'public/index.html',
410+
content: '<h1>Site with DB migrations</h1>',
411+
})
412+
.withContentFile({
413+
path: '.netlify/internal/db/migrations/0000_oval_proudstar/migration.sql',
414+
content: 'CREATE TABLE users (id serial PRIMARY KEY);',
415+
})
416+
.withContentFile({
417+
path: '.netlify/internal/db/migrations/0001_second_migration/migration.sql',
418+
content: 'ALTER TABLE users ADD COLUMN name text;',
419+
})
420+
.withNetlifyToml({
421+
config: {
422+
build: { publish: 'public' },
423+
},
424+
})
425+
426+
await builder.build()
427+
428+
const deploy = await callCli(
429+
['deploy', '--json', '--no-build', '--dir', 'public'],
430+
getCLIOptions({ apiUrl: mockApi.apiUrl, builder }),
431+
).then(parseDeploy)
432+
433+
expect(deploy.site_id).toBe('site_id')
434+
435+
const body = deployState.getDeployBody()
436+
expect(body).not.toBeNull()
437+
438+
const fileKeys = Object.keys(body!.files!)
439+
expect(fileKeys).toContain('index.html')
440+
expect(fileKeys).toContain('.netlify/internal/db/migrations/0000_oval_proudstar/migration.sql')
441+
expect(fileKeys).toContain('.netlify/internal/db/migrations/0001_second_migration/migration.sql')
442+
})
443+
})
444+
})
445+
446+
test('should not include DB migrations in deploy when internal migrations directory does not exist', async (t) => {
447+
await withMockDeploy(async (mockApi, deployState) => {
448+
await withSiteBuilder(t, async (builder) => {
449+
builder
450+
.withContentFile({
451+
path: 'public/index.html',
452+
content: '<h1>Site without DB migrations</h1>',
453+
})
454+
.withNetlifyToml({
455+
config: {
456+
build: { publish: 'public' },
457+
},
458+
})
459+
460+
await builder.build()
461+
462+
const deploy = await callCli(
463+
['deploy', '--json', '--no-build', '--dir', 'public'],
464+
getCLIOptions({ apiUrl: mockApi.apiUrl, builder }),
465+
).then(parseDeploy)
466+
467+
expect(deploy.site_id).toBe('site_id')
468+
469+
const body = deployState.getDeployBody()
470+
expect(body).not.toBeNull()
471+
472+
const fileKeys = Object.keys(body!.files!)
473+
expect(fileKeys).toContain('index.html')
474+
expect(fileKeys.some((key) => key.includes('db/migrations'))).toBe(false)
475+
})
476+
})
477+
})
478+
404479
test('runs build command before deploy by default', async (t) => {
405480
await withMockDeploy(async (mockApi, deployState) => {
406481
await withSiteBuilder(t, async (builder) => {

0 commit comments

Comments
 (0)