Skip to content

Commit

Permalink
feat: ORM-607 disable automatic env loading during CLI cmds (prisma#2…
Browse files Browse the repository at this point in the history
…6291)

This PR disables the automatic env file loading during our CLI command execution if a prisma.config.ts file is detected.

It also refactors the config loading so it happens only at the top in the CLI. The parsed config then gets passed as argument to each subcommand. This follows our architecture strategy of inversion of control.
  • Loading branch information
FGoessler authored Feb 11, 2025
1 parent ab65164 commit 02090bb
Show file tree
Hide file tree
Showing 107 changed files with 1,397 additions and 907 deletions.
8 changes: 5 additions & 3 deletions packages/cli/src/CLI.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PrismaConfig } from '@prisma/config'
import { ensureBinariesExist } from '@prisma/engines'
import type { Command, Commands } from '@prisma/internals'
import { arg, drawBox, format, HelpError, isError, link, logger, unknownCommand } from '@prisma/internals'
Expand All @@ -12,9 +13,10 @@ export class CLI implements Command {
static new(cmds: Commands, ensureBinaries: string[]): CLI {
return new CLI(cmds, ensureBinaries)
}

private constructor(private readonly cmds: Commands, private readonly ensureBinaries: string[]) {}

async parse(argv: string[]): Promise<string | Error> {
async parse(argv: string[], config: PrismaConfig): Promise<string | Error> {
const args = arg(argv, {
'--help': Boolean,
'-h': '--help',
Expand All @@ -33,7 +35,7 @@ export class CLI implements Command {

if (args['--version']) {
await ensureBinariesExist()
return Version.new().parse(argv)
return Version.new().parse(argv, config)
}

// display help for help flag or no subcommand
Expand Down Expand Up @@ -76,7 +78,7 @@ export class CLI implements Command {
argsForCmd = args._.slice(1)
}

return cmd.parse(argsForCmd)
return cmd.parse(argsForCmd, config)
}
// unknown command
return unknownCommand(this.help() as string, args._[0])
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/src/DebugInfo.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PrismaConfig } from '@prisma/config'
import type { Command } from '@prisma/internals'
import {
arg,
Expand Down Expand Up @@ -32,10 +33,11 @@ export class DebugInfo implements Command {
${bold('Options')}
-h, --help Display this help message
--config Custom path to your Prisma config file
--schema Custom path to your Prisma schema
`)

async parse(argv: string[]): Promise<string | Error> {
async parse(argv: string[], config: PrismaConfig): Promise<string | Error> {
const args = arg(argv, {
'--help': Boolean,
'-h': '--help',
Expand All @@ -51,7 +53,7 @@ export class DebugInfo implements Command {
return this.help()
}

await loadEnvFile({ schemaPath: args['--schema'], printMessage: true })
await loadEnvFile({ schemaPath: args['--schema'], printMessage: true, config })

const formatEnvValue = (name: string, text?: string) => {
const value = process.env[name]
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/Format.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'node:fs/promises'
import path from 'node:path'

import { PrismaConfig } from '@prisma/config'
import { arg, Command, format, formatms, formatSchema, HelpError, validate } from '@prisma/internals'
import { getSchemaPathAndPrint } from '@prisma/migrate'
import { bold, dim, red, underline } from 'kleur/colors'
Expand All @@ -23,6 +24,7 @@ ${bold('Usage')}
${bold('Options')}
-h, --help Display this help message
--config Custom path to your Prisma config file
--schema Custom path to your Prisma schema
${bold('Examples')}
Expand All @@ -35,7 +37,7 @@ Or specify a Prisma schema path
`)

public async parse(argv: string[]): Promise<string | Error> {
public async parse(argv: string[], _config: PrismaConfig): Promise<string | Error> {
const before = Math.round(performance.now())
const args = arg(argv, {
'--help': Boolean,
Expand Down
12 changes: 7 additions & 5 deletions packages/cli/src/Generate.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PrismaConfig } from '@prisma/config'
import { enginesVersion } from '@prisma/engines'
import { SqlQueryOutput } from '@prisma/generator-helper'
import {
Expand Down Expand Up @@ -54,6 +55,7 @@ ${bold('Usage')}
${bold('Options')}
-h, --help Display this help message
--config Custom path to your Prisma config file
--schema Custom path to your Prisma schema
--watch Watch the Prisma schema and rerun after a change
--generator Generator to use (may be provided multiple times)
Expand Down Expand Up @@ -101,7 +103,7 @@ ${bold('Examples')}
this.logText += message.join('\n')
})

public async parse(argv: string[]): Promise<string | Error> {
public async parse(argv: string[], config: PrismaConfig): Promise<string | Error> {
const args = arg(argv, {
'--help': Boolean,
'-h': '--help',
Expand Down Expand Up @@ -134,7 +136,7 @@ ${bold('Examples')}

const watchMode = args['--watch'] || false

await loadEnvFile({ schemaPath: args['--schema'], printMessage: true })
await loadEnvFile({ schemaPath: args['--schema'], printMessage: true, config })

const schemaResult = await getSchemaForGenerate(args['--schema'], cwd, Boolean(postinstallCwd))
const promotion = getRandomPromotion()
Expand All @@ -143,7 +145,7 @@ ${bold('Examples')}

const { schemas, schemaPath } = schemaResult
printSchemaLoadedMessage(schemaPath)
const config = await getConfig({ datamodel: schemas, ignoreEnvVarErrors: true })
const engineConfig = await getConfig({ datamodel: schemas, ignoreEnvVarErrors: true })

// TODO Extract logic from here
let hasJsClient
Expand Down Expand Up @@ -265,13 +267,13 @@ Please make sure they have the same version.`
: ''

if (hideHints) {
hint = `${getHardcodedUrlWarning(config)}${breakingChangesStr}${versionsWarning}`
hint = `${getHardcodedUrlWarning(engineConfig)}${breakingChangesStr}${versionsWarning}`
} else {
hint = `
Start by importing your Prisma Client (See: https://pris.ly/d/importing-client)
${renderPromotion(promotion)}
${getHardcodedUrlWarning(config)}${breakingChangesStr}${versionsWarning}`
${getHardcodedUrlWarning(engineConfig)}${breakingChangesStr}${versionsWarning}`
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/Init.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { confirm, input, select } from '@inquirer/prompts'
import { PrismaConfig } from '@prisma/config'
import type { ConnectorType } from '@prisma/generator-helper'
import {
arg,
Expand Down Expand Up @@ -220,7 +221,7 @@ export class Init implements Command {
${dim('$')} prisma init --with-model
`)

async parse(argv: string[]): Promise<any> {
async parse(argv: string[], _config: PrismaConfig): Promise<string | Error> {
const args = arg(argv, {
'--help': Boolean,
'-h': '--help',
Expand Down
17 changes: 12 additions & 5 deletions packages/cli/src/Studio.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PrismaConfig } from '@prisma/config'
import Debug from '@prisma/debug'
import { enginesVersion } from '@prisma/engines'
import {
Expand All @@ -24,7 +25,7 @@ import path from 'path'
// See packages/client/tests/e2e/issues/studio-1128-spawn-enoent/_steps.ts
const debug = Debug('prisma:cli:studio')

const packageJson = require('../package.json') // eslint-disable-line @typescript-eslint/no-var-requires
const packageJson = require('../package.json')

export class Studio implements Command {
public instance?: StudioServer
Expand All @@ -46,6 +47,7 @@ ${bold('Options')}
-p, --port Port to start Studio on
-b, --browser Browser to open Studio in
-n, --hostname Hostname to bind the Express server to
--config Custom path to your Prisma config file
--schema Custom path to your Prisma schema
${bold('Examples')}
Expand All @@ -66,17 +68,22 @@ ${bold('Examples')}
Specify a schema
${dim('$')} prisma studio --schema=./schema.prisma
Specify a custom prisma config file
${dim('$')} prisma studio --config=./prisma.config.ts
`)

/**
* Parses arguments passed to this command, and starts Studio
*
* @param argv Array of all arguments
* @param _config The loaded Prisma config
*/
public async parse(argv: string[]): Promise<string | Error> {
public async parse(argv: string[], config: PrismaConfig): Promise<string | Error> {
const args = arg(argv, {
'--help': Boolean,
'-h': '--help',
'--config': String,
'--port': Number,
'-p': '--port',
'--browser': String,
Expand All @@ -95,7 +102,7 @@ ${bold('Examples')}
return this.help()
}

await loadEnvFile({ schemaPath: args['--schema'], printMessage: true })
await loadEnvFile({ schemaPath: args['--schema'], printMessage: true, config })

const { schemaPath, schemas } = await getSchemaPathAndPrint(args['--schema'])

Expand All @@ -109,7 +116,7 @@ ${bold('Examples')}
schemas,
})

const config = await getConfig({ datamodel: schemas, ignoreEnvVarErrors: true })
const engineConfig = await getConfig({ datamodel: schemas, ignoreEnvVarErrors: true })

process.env.PRISMA_DISABLE_WARNINGS = 'true' // disable client warnings
const studio = new StudioServer({
Expand All @@ -122,7 +129,7 @@ ${bold('Examples')}
resolve: {
'@prisma/client': path.resolve(__dirname, '../prisma-client/index.js'),
},
directUrl: resolveUrl(getDirectUrl(config.datasources[0])),
directUrl: resolveUrl(getDirectUrl(engineConfig.datasources[0])),
},
versions: {
prisma: packageJson.version,
Expand Down
7 changes: 4 additions & 3 deletions packages/cli/src/SubCommand.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getCommand } from '@antfu/ni'
import { PrismaConfig } from '@prisma/config'
import type { Command } from '@prisma/internals'
import { command } from 'execa'
import { existsSync } from 'fs'
Expand All @@ -8,7 +9,7 @@ import { tmpdir } from 'os'
* Sub-CLIs that are installed on demand need to implement this interface
*/
type Runnable = {
run: (args: string[]) => Promise<void>
run: (args: string[], config: PrismaConfig) => Promise<void>
}

/**
Expand All @@ -21,7 +22,7 @@ export class SubCommand implements Command {
this.pkg = pkg
}

async parse(argv: string[]): Promise<string | Error> {
async parse(argv: string[], config: PrismaConfig): Promise<string | Error> {
// we accept forcing a version with @, eg. prisma policy @1.0.0 --help
const [version, ...args] = argv[0]?.startsWith('@') ? argv : ['@latest', ...argv]
const pkg = `${this.pkg}${version}`
Expand All @@ -40,7 +41,7 @@ export class SubCommand implements Command {
// load the module and run it via the Runnable interface
const modulePath = [prefix, 'node_modules', this.pkg, 'dist', 'index.js']
const module: Runnable = await import(modulePath.join('/'))
await module.run(args)
await module.run(args, config)

return ''
}
Expand Down
16 changes: 4 additions & 12 deletions packages/cli/src/Validate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path'

import { loadConfigFromFile } from '@prisma/config'
import { PrismaConfig } from '@prisma/config'
import {
arg,
Command,
Expand Down Expand Up @@ -34,8 +34,8 @@ ${bold('Usage')}
${bold('Options')}
--config Custom path to your Prisma config file
-h, --help Display this help message
--config Custom path to your Prisma config file
--schema Custom path to your Prisma schema
${bold('Examples')}
Expand All @@ -51,10 +51,9 @@ ${bold('Examples')}
`)

public async parse(argv: string[]): Promise<string | Error> {
public async parse(argv: string[], config: PrismaConfig): Promise<string | Error> {
const args = arg(argv, {
'--help': Boolean,
'--config': String,
'-h': '--help',
'--schema': String,
'--telemetry-information': String,
Expand All @@ -68,14 +67,7 @@ ${bold('Examples')}
return this.help()
}

// TODO: deal with the possible error cases returned.
const { config } = await loadConfigFromFile({ configFile: args['--config'] })

if (config) {
console.debug(`Prisma config detected, skipping environment variable loading`)
} else {
await loadEnvFile({ schemaPath: args['--schema'], printMessage: true })
}
await loadEnvFile({ schemaPath: args['--schema'], printMessage: true, config })

const { schemaPath, schemas } = await getSchemaPathAndPrint(args['--schema'])

Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/Version.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PrismaConfig } from '@prisma/config'
import { enginesVersion, getCliQueryEngineBinaryType } from '@prisma/engines'
import { getBinaryTargetForCurrentPlatform } from '@prisma/get-platform'
import type { Command } from '@prisma/internals'
Expand Down Expand Up @@ -46,7 +47,7 @@ export class Version implements Command {
--json Output JSON
`)

async parse(argv: string[]): Promise<string | Error> {
async parse(argv: string[], config: PrismaConfig): Promise<string | Error> {
const args = arg(argv, {
'--help': Boolean,
'-h': '--help',
Expand All @@ -64,7 +65,7 @@ export class Version implements Command {
return this.help()
}

await loadEnvFile({ printMessage: !args['--json'] })
await loadEnvFile({ printMessage: !args['--json'], config })

const binaryTarget = await getBinaryTargetForCurrentPlatform()
const cliQueryEngineBinaryType = getCliQueryEngineBinaryType()
Expand Down
11 changes: 6 additions & 5 deletions packages/cli/src/__tests__/artificial-panic.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { defaultTestConfig } from '@prisma/config'
import { jestContext } from '@prisma/get-platform'
import { serialize } from '@prisma/get-platform/src/test-utils/jestSnapshotSerializer'
import { getDMMF, isRustPanic, MultipleSchemas, relativizePathInPSLError } from '@prisma/internals'
Expand Down Expand Up @@ -36,7 +37,7 @@ describe('artificial-panic introspection', () => {

const command = new DbPull()
try {
await command.parse(['--print'])
await command.parse(['--print'], defaultTestConfig())
} catch (e) {
expect(e).toMatchInlineSnapshot(`
"Error in Schema engine.
Expand Down Expand Up @@ -86,7 +87,7 @@ describe('artificial-panic formatter', () => {

const command = new Format()
try {
await command.parse([])
await command.parse([], defaultTestConfig())
} catch (e) {
expect(serialize(e.message)).toMatchInlineSnapshot(`
""RuntimeError: panicked at prisma-schema-wasm/src/lib.rs:0:0:
Expand Down Expand Up @@ -133,7 +134,7 @@ describe('artificial-panic get-config', () => {

const command = new Validate()
try {
await command.parse([])
await command.parse([], defaultTestConfig())
} catch (e) {
expect(serialize(e.message)).toMatchInlineSnapshot(`
""RuntimeError: panicked at prisma-schema-wasm/src/lib.rs:0:0:
Expand Down Expand Up @@ -181,7 +182,7 @@ describe('artificial-panic validate', () => {

const command = new Validate()
try {
await command.parse([])
await command.parse([], defaultTestConfig())
} catch (e) {
expect(serialize(e.message)).toMatchInlineSnapshot(`
""RuntimeError: panicked at prisma-schema-wasm/src/lib.rs:0:0:
Expand Down Expand Up @@ -219,7 +220,7 @@ describe('artificial-panic validate', () => {

const command = new Format()
try {
await command.parse([])
await command.parse([], defaultTestConfig())
} catch (e) {
expect(serialize(e.message)).toMatchInlineSnapshot(`
""RuntimeError: panicked at prisma-schema-wasm/src/lib.rs:0:0:
Expand Down
Loading

0 comments on commit 02090bb

Please sign in to comment.