Skip to content

Commit

Permalink
fix(client): throw error on caching platforms (#18437)
Browse files Browse the repository at this point in the history
  • Loading branch information
millsp committed Apr 4, 2023
1 parent ed52837 commit 5acfc52
Show file tree
Hide file tree
Showing 29 changed files with 260 additions and 108 deletions.
5 changes: 0 additions & 5 deletions helpers/compile/build.ts
Expand Up @@ -145,11 +145,6 @@ async function dependencyCheck(options: BuildOptions) {
* @param options
*/
export async function build(options: BuildOptions[]) {
// When we trigger pnpm pack for e2e tests we actually don't want to always
// build again to go faster. We re-use what has been already build; and also
// implies you ran pnpm run watch/dev/build before hand.
if (process.env.SKIP_BUILD === 'true') return

void transduce.async(options, dependencyCheck)

return transduce.async(
Expand Down
12 changes: 1 addition & 11 deletions packages/cli/helpers/build.ts
Expand Up @@ -75,17 +75,7 @@ const preinstallBuildConfig: BuildOptions = {
emitTypes: false,
}

// we define the config for install
const installBuildConfig: BuildOptions = {
name: 'install',
entryPoints: ['scripts/install.js'],
outfile: 'install/index',
bundle: true,
minify: true,
emitTypes: false,
}

void build([cliBuildConfig, preinstallBuildConfig, installBuildConfig])
void build([cliBuildConfig, preinstallBuildConfig])

// Utils ::::::::::::::::::::::::::::::::::::::::::::::::::

Expand Down
4 changes: 1 addition & 3 deletions packages/cli/package.json
Expand Up @@ -44,8 +44,7 @@
"runtime/llhttp",
"prisma-client",
"preinstall",
"scripts/preinstall-entry.js",
"scripts/install-entry.js"
"scripts/preinstall-entry.js"
],
"pkg": {
"assets": [
Expand Down Expand Up @@ -107,7 +106,6 @@
"dev": "DEV=true node -r esbuild-register helpers/build.ts",
"build": "node -r esbuild-register helpers/build.ts",
"test": "jest --maxConcurrency=1 --verbose",
"install": "node scripts/install-entry.js",
"tsc": "tsc -d -p tsconfig.build.json",
"preinstall": "node scripts/preinstall-entry.js",
"prepublishOnly": "pnpm run build"
Expand Down
8 changes: 0 additions & 8 deletions packages/cli/scripts/install-entry.js

This file was deleted.

57 changes: 0 additions & 57 deletions packages/cli/scripts/install.js

This file was deleted.

1 change: 1 addition & 0 deletions packages/cli/src/Generate.ts
Expand Up @@ -139,6 +139,7 @@ ${chalk.bold('Examples')}
cliVersion: pkg.version,
dataProxy: !!args['--data-proxy'] || !!process.env.PRISMA_GENERATE_DATAPROXY,
generatorNames: args['--generator'],
postinstall: Boolean(args['--postinstall']),
})

if (!generators || generators.length === 0) {
Expand Down
1 change: 1 addition & 0 deletions packages/client/package.json
Expand Up @@ -98,6 +98,7 @@
"arg": "5.0.2",
"benchmark": "2.1.4",
"chalk": "4.1.2",
"ci-info": "3.8.0",
"decimal.js": "10.4.3",
"esbuild": "0.15.13",
"execa": "5.1.1",
Expand Down
4 changes: 4 additions & 0 deletions packages/client/src/generation/TSClient/TSClient.ts
@@ -1,6 +1,7 @@
import type { GeneratorConfig } from '@prisma/generator-helper'
import type { Platform } from '@prisma/get-platform'
import { getClientEngineType, getEnvPaths, getQueryEngineProtocol } from '@prisma/internals'
import ciInfo from 'ci-info'
import indent from 'indent-string'
import { klona } from 'klona'
import path from 'path'
Expand Down Expand Up @@ -47,6 +48,7 @@ export interface TSClientOptions {
activeProvider: string
dataProxy: boolean
deno?: boolean
postinstall?: boolean
}

export class TSClient implements Generatable {
Expand Down Expand Up @@ -98,6 +100,8 @@ export class TSClient implements Generatable {
datasourceNames: datasources.map((d) => d.name),
activeProvider: this.options.activeProvider,
dataProxy: this.options.dataProxy,
postinstall: this.options.postinstall,
ciName: ciInfo.name ?? undefined,
}

// get relative output dir for it to be preserved even after bundling, or
Expand Down
5 changes: 5 additions & 0 deletions packages/client/src/generation/generateClient.ts
Expand Up @@ -54,6 +54,7 @@ export interface GenerateClientOptions {
clientVersion: string
activeProvider: string
dataProxy: boolean
postinstall?: boolean
}

export interface BuildClientResult {
Expand All @@ -75,6 +76,7 @@ export async function buildClient({
projectRoot,
activeProvider,
dataProxy,
postinstall,
}: O.Required<GenerateClientOptions, 'runtimeDirs'>): Promise<BuildClientResult> {
// we define the basic options for the client generation
const document = getPrismaClientDMMF(dmmf)
Expand All @@ -94,6 +96,7 @@ export async function buildClient({
projectRoot: projectRoot!,
activeProvider,
dataProxy,
postinstall,
}

// we create a regular client that is fit for Node.js
Expand Down Expand Up @@ -203,6 +206,7 @@ export async function generateClient(options: GenerateClientOptions): Promise<vo
engineVersion,
activeProvider,
dataProxy,
postinstall,
} = options

const clientEngineType = getClientEngineType(generator!)
Expand All @@ -223,6 +227,7 @@ export async function generateClient(options: GenerateClientOptions): Promise<vo
projectRoot,
activeProvider,
dataProxy,
postinstall,
})

const denylistsErrors = validateDmmfAgainstDenylists(prismaClientDmmf)
Expand Down
1 change: 1 addition & 0 deletions packages/client/src/generation/generator.ts
Expand Up @@ -52,6 +52,7 @@ if (process.argv[1] === __filename) {
transpile: true,
activeProvider: options.datasources[0]?.activeProvider,
dataProxy: options.dataProxy,
postinstall: options.postinstall,
})
},
})
Expand Down
31 changes: 31 additions & 0 deletions packages/client/src/runtime/core/init/checkPlatformCaching.test.ts
@@ -0,0 +1,31 @@
import { checkPlatformCaching } from './checkPlatformCaching'

test('generated via postinstall on vercel', () => {
expect(() => checkPlatformCaching({ postinstall: true, ciName: 'Vercel', clientVersion: '0.0.0' }))
.toThrowErrorMatchingInlineSnapshot(`
We have detected that you've built your project on Vercel, which caches dependencies.
This leads to an outdated Prisma Client because Prisma's auto-generation isn't triggered.
To fix this, make sure to run the \`prisma generate\` command during your build process.
Learn how: https://pris.ly/d/vercel-build
`)
})

test('generated on vercel', () => {
expect(() => checkPlatformCaching({ postinstall: false, ciName: 'Vercel', clientVersion: '0.0.0' })).not.toThrow()
})

test('generated via postinstall on netlify', () => {
expect(() => checkPlatformCaching({ postinstall: true, ciName: 'Netlify CI', clientVersion: '0.0.0' }))
.toThrowErrorMatchingInlineSnapshot(`
We have detected that you've built your project on Netlify CI, which caches dependencies.
This leads to an outdated Prisma Client because Prisma's auto-generation isn't triggered.
To fix this, make sure to run the \`prisma generate\` command during your build process.
Learn how: https://pris.ly/d/netlify-build
`)
})

test('generated on netlify', () => {
expect(() => checkPlatformCaching({ postinstall: false, ciName: 'Netlify CI', clientVersion: '0.0.0' })).not.toThrow()
})

export {}
43 changes: 43 additions & 0 deletions packages/client/src/runtime/core/init/checkPlatformCaching.ts
@@ -0,0 +1,43 @@
import Debug from '@prisma/debug'
import { PrismaClientInitializationError } from '@prisma/engine-core'

import type { GetPrismaClientConfig } from '../../getPrismaClient'

const debug = Debug('prisma:client')

/**
* Known platforms that have caching issues. Updating this list will also update
* the error message and the link to the docs, so add docs/links as needed. The
* key from this map comes from the `ciName` property of the `ci-info` package.
*/
const cachingPlatforms = {
Vercel: 'vercel',
'Netlify CI': 'netlify',
} as const

type Config = Pick<GetPrismaClientConfig, 'postinstall' | 'ciName' | 'clientVersion'>

/**
* Throws an error if the client has been generated via auto-install and the
* platform is known to have caching issues. In that case, we will display a
* useful error message, and ask the user to run `prisma generate` manually.
* @returns
*/
export function checkPlatformCaching({ postinstall, ciName, clientVersion }: Config) {
debug('checkPlatformCaching:postinstall', postinstall)
debug('checkPlatformCaching:ciName', ciName)

// if client was not generated manually
if (postinstall !== true) return

// and we generated on one a caching CI
if (ciName && ciName in cachingPlatforms) {
throw new PrismaClientInitializationError(
`We have detected that you've built your project on ${ciName}, which caches dependencies.
This leads to an outdated Prisma Client because Prisma's auto-generation isn't triggered.
To fix this, make sure to run the \`prisma generate\` command during your build process.
Learn how: https://pris.ly/d/${cachingPlatforms[ciName]}-build`,
clientVersion,
)
}
}
21 changes: 20 additions & 1 deletion packages/client/src/runtime/getPrismaClient.ts
Expand Up @@ -41,6 +41,7 @@ import { PrismaClientValidationError } from '.'
import { $extends } from './core/extensions/$extends'
import { applyQueryExtensions } from './core/extensions/applyQueryExtensions'
import { MergedExtensionsList } from './core/extensions/MergedExtensionsList'
import { checkPlatformCaching } from './core/init/checkPlatformCaching'
import { MetricsClient } from './core/metrics/MetricsClient'
import { applyModelsAndClientExtensions } from './core/model/applyModelsAndClientExtensions'
import { dmmfToJSModelName } from './core/model/utils/dmmfToJSModelName'
Expand Down Expand Up @@ -219,7 +220,7 @@ export interface GetPrismaClientConfig {
relativePath: string
dirname: string
filename?: string
clientVersion?: string
clientVersion: string
engineVersion?: string
datasourceNames: string[]
activeProvider: string
Expand Down Expand Up @@ -265,6 +266,22 @@ export interface GetPrismaClientConfig {
* @remarks only used for the purpose of data proxy
*/
inlineSchemaHash?: string

/**
* A marker to indicate that the client was not generated via `prisma
* generate` but was generated via `generate --postinstall` script instead.
* @remarks used to error for Vercel/Netlify for schema caching issues
*/
postinstall?: boolean

/**
* Information about the CI where the Prisma Client has been generated. The
* name of the CI environment is stored at generation time because CI
* information is not always available at runtime. Moreover, the edge client
* has no notion of environment variables, so this works around that.
* @remarks used to error for Vercel/Netlify for schema caching issues
*/
ciName?: string
}

const TX_ID = Symbol.for('prisma.client.transaction.id')
Expand Down Expand Up @@ -300,6 +317,8 @@ export function getPrismaClient(config: GetPrismaClientConfig) {
_extensions: MergedExtensionsList

constructor(optionsArg?: PrismaClientOptions) {
checkPlatformCaching(config)

if (optionsArg) {
validatePrismaClientOptions(optionsArg, config.datasourceNames)
}
Expand Down
13 changes: 10 additions & 3 deletions packages/client/tests/e2e/_utils/run.ts
Expand Up @@ -82,13 +82,20 @@ async function main() {

try {
console.log('📦 Packing package tarballs')
await $`cd ${clientPkgPath} && SKIP_BUILD=${args['--skipBuild']} pnpm pack --pack-destination ${__dirname}/../`
await $`cd ${cliPkgPath} && SKIP_BUILD=${args['--skipBuild']} pnpm pack --pack-destination ${__dirname}/../`
await $`cd ${wpPluginPkgPath} && SKIP_BUILD=${args['--skipBuild']} pnpm pack --pack-destination ${__dirname}/../`

if (args['--skipBuild'] !== true) {
await $`cd ${clientPkgPath} && pnpm build`
await $`cd ${cliPkgPath} && pnpm build`
}

await $`cd ${clientPkgPath} && pnpm pack --pack-destination ${__dirname}/../`
await $`cd ${cliPkgPath} && pnpm pack --pack-destination ${__dirname}/../`
await $`cd ${wpPluginPkgPath} && pnpm pack --pack-destination ${__dirname}/../`
} catch (e) {
console.log(e.message)
console.log('🛑 Failed to pack one or more of the packages')
console.log('💡 Make sure to run `watch`, `dev` or `build`')
throw e
} finally {
await restoreOriginal() // when done, we restore the original package.json
}
Expand Down
2 changes: 2 additions & 0 deletions packages/client/tests/e2e/_utils/standard.cmd.sh
Expand Up @@ -43,4 +43,6 @@ OUTPUT_REMOVAL_REGEX="$PNPM_EXDEV_WARN_REGEX|$PNPM_FALLBACK_COPY_REGEX"
# execute the test by running the _steps.ts file with esbuild-register
cd /test/$NAME;
node -r 'esbuild-register' _steps.ts;
# when inline snapshots are created the first time, copy for convencience
cp -r /test/$NAME/tests/* /e2e/$NAME/tests/ 2> /dev/null || true;
) 2>&1 | grep -v -E --line-buffered "$OUTPUT_REMOVAL_REGEX" > /e2e/$NAME/LOGS.txt;
Expand Up @@ -8,7 +8,7 @@
"simple-ext": "./simple-ext/simple-ext-0.0.0.tgz"
},
"devDependencies": {
"@types/node": "16.18.11 ",
"@types/node": "16.18.11",
"prisma": "../../prisma-0.0.0.tgz"
}
}
2 changes: 1 addition & 1 deletion packages/client/tests/e2e/example/package.json
Expand Up @@ -7,7 +7,7 @@
"@prisma/client": "../prisma-client-0.0.0.tgz"
},
"devDependencies": {
"@types/node": "16.18.11 ",
"@types/node": "16.18.11",
"prisma": "../prisma-0.0.0.tgz"
}
}
Expand Up @@ -7,7 +7,7 @@
"@prisma/client": "../../prisma-client-0.0.0.tgz"
},
"devDependencies": {
"@types/node": "16.18.11 ",
"@types/node": "16.18.11",
"prisma": "../../prisma-0.0.0.tgz"
}
}

0 comments on commit 5acfc52

Please sign in to comment.