@prisma/cli 3.0.0-beta.10 → 3.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/token-storage.js +2 -2
- package/dist/cli.js +7 -7
- package/dist/cli2.js +3 -3
- package/dist/commands/app/index.js +64 -51
- package/dist/controllers/app-env.js +7 -6
- package/dist/controllers/app.js +477 -163
- package/dist/controllers/branch.js +1 -1
- package/dist/controllers/database.js +1 -1
- package/dist/controllers/project.js +4 -4
- package/dist/lib/app/branch-database-deploy.js +12 -60
- package/dist/lib/app/branch-database.js +1 -102
- package/dist/lib/app/compute-config.js +147 -0
- package/dist/lib/app/deploy-plan.js +58 -0
- package/dist/lib/app/local-dev.js +3 -60
- package/dist/lib/app/preview-build-settings.js +95 -104
- package/dist/lib/app/preview-build.js +61 -35
- package/dist/lib/app/preview-provider.js +23 -19
- package/dist/lib/app/production-deploy-gate.js +3 -3
- package/dist/lib/auth/login.js +31 -24
- package/dist/lib/diagnostics.js +1 -1
- package/dist/lib/fs/home-path.js +24 -0
- package/dist/lib/git/local-branch.js +15 -3
- package/dist/lib/project/resolution.js +2 -2
- package/dist/lib/project/setup.js +10 -7
- package/dist/output/patterns.js +1 -1
- package/dist/presenters/app.js +44 -39
- package/dist/presenters/branch.js +1 -1
- package/dist/presenters/database.js +2 -2
- package/dist/presenters/project.js +3 -9
- package/dist/shell/command-runner.js +37 -27
- package/dist/shell/diagnostics-output.js +2 -8
- package/dist/shell/help.js +30 -20
- package/dist/shell/runtime.js +8 -4
- package/dist/shell/ui.js +19 -2
- package/package.json +17 -3
package/dist/controllers/app.js
CHANGED
|
@@ -1,59 +1,78 @@
|
|
|
1
1
|
import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
|
|
2
2
|
import { FileTokenStorage } from "../adapters/token-storage.js";
|
|
3
3
|
import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
4
|
+
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
4
5
|
import { renderCommandHeader } from "../shell/ui.js";
|
|
5
|
-
import { writeJsonEvent } from "../shell/output.js";
|
|
6
6
|
import { canPrompt } from "../shell/runtime.js";
|
|
7
|
-
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
8
|
-
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
9
|
-
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
10
|
-
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
11
|
-
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
12
|
-
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
13
|
-
import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
|
|
14
|
-
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
15
7
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
8
|
+
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
16
9
|
import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
17
10
|
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
11
|
+
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
12
|
+
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
13
|
+
import { COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeConfigErrorToCliError, computeFrameworkToBuildType, computeTargetAppDir, inferComputeTargetFromCwd, loadComputeConfig as loadComputeConfig$1, mergeComputeDeployInputs, mergeComputeLocalInputs, selectComputeDeployTarget } from "../lib/app/compute-config.js";
|
|
14
|
+
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
15
|
+
import { describeDeployAllFailure, perAppInputsForDeployAll, planAppDeploy } from "../lib/app/deploy-plan.js";
|
|
16
|
+
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
17
|
+
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
18
|
+
import { DEFAULT_LOCAL_DEV_PORT, runLocalApp } from "../lib/app/local-dev.js";
|
|
19
|
+
import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredPreviewBuildSettings, resolveInferredPreviewBuildSettings } from "../lib/app/preview-build-settings.js";
|
|
21
20
|
import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
|
|
22
21
|
import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
|
|
23
|
-
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
24
22
|
import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
|
|
25
23
|
import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
26
24
|
import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
|
|
27
|
-
import {
|
|
25
|
+
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
26
|
+
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
27
|
+
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
28
|
+
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
29
|
+
import { writeJsonEvent } from "../shell/output.js";
|
|
28
30
|
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
29
31
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
30
32
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
33
|
+
import { ENTRYPOINT_BUILD_TYPES, FRAMEWORKS, LOCAL_DEV_BUILD_TYPES, frameworkByKey, frameworkFromAlias, isConfigBackedBuildType } from "@prisma/compute-sdk/config";
|
|
31
34
|
import { access, readFile } from "node:fs/promises";
|
|
32
35
|
import path from "node:path";
|
|
33
|
-
import open from "open";
|
|
34
36
|
import { Result, matchError } from "better-result";
|
|
37
|
+
import open from "open";
|
|
35
38
|
//#region src/controllers/app.ts
|
|
36
|
-
const DEPLOY_FRAMEWORKS = [
|
|
37
|
-
"nextjs",
|
|
38
|
-
"hono",
|
|
39
|
-
"tanstack-start",
|
|
40
|
-
"bun"
|
|
41
|
-
];
|
|
42
|
-
const TANSTACK_START_PACKAGES = ["@tanstack/react-start", "@tanstack/solid-start"];
|
|
43
39
|
const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
|
|
44
40
|
const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
|
|
45
41
|
const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
|
|
46
42
|
function isRealMode(context) {
|
|
47
43
|
return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
48
44
|
}
|
|
49
|
-
async function runAppBuild(context,
|
|
50
|
-
const
|
|
51
|
-
|
|
45
|
+
async function runAppBuild(context, options) {
|
|
46
|
+
const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "build");
|
|
47
|
+
const merged = mergeComputeLocalInputs({
|
|
48
|
+
cli: {
|
|
49
|
+
entrypoint: options?.entrypoint,
|
|
50
|
+
buildType: options?.buildType
|
|
51
|
+
},
|
|
52
|
+
target: compute.target
|
|
53
|
+
});
|
|
54
|
+
const appDir = await resolveComputeAppDir(context, compute);
|
|
55
|
+
let buildType = normalizeBuildType(merged.buildType);
|
|
56
|
+
if (compute.target?.build && buildType === "auto") {
|
|
57
|
+
const detected = await detectDeployFramework(appDir, context.runtime.signal);
|
|
58
|
+
if (!detected) throw frameworkNotDetectedError(appDir);
|
|
59
|
+
buildType = detected.buildType;
|
|
60
|
+
}
|
|
61
|
+
assertSupportedEntrypoint(buildType, merged.entrypoint, "build");
|
|
62
|
+
if (compute.target?.build && buildType !== "auto") assertConfigBackedBuildSettings(buildType);
|
|
63
|
+
const buildSettings = compute.config && compute.target?.build && isConfigBackedBuildType(buildType) ? (await resolveConfiguredPreviewBuildSettings({
|
|
64
|
+
appPath: appDir,
|
|
65
|
+
buildType,
|
|
66
|
+
configured: compute.target.build,
|
|
67
|
+
configPath: compute.config.configPath,
|
|
68
|
+
signal: context.runtime.signal
|
|
69
|
+
})).settings : void 0;
|
|
52
70
|
try {
|
|
53
71
|
const { artifact, buildType: actualBuildType } = await executePreviewBuild({
|
|
54
|
-
appPath:
|
|
55
|
-
entrypoint,
|
|
72
|
+
appPath: appDir,
|
|
73
|
+
entrypoint: merged.entrypoint,
|
|
56
74
|
buildType,
|
|
75
|
+
buildSettings,
|
|
57
76
|
signal: context.runtime.signal
|
|
58
77
|
});
|
|
59
78
|
return {
|
|
@@ -71,17 +90,33 @@ async function runAppBuild(context, entrypoint, requestedBuildType) {
|
|
|
71
90
|
throw buildFailedError("Local app build failed", error);
|
|
72
91
|
}
|
|
73
92
|
}
|
|
74
|
-
async function runAppRun(context,
|
|
93
|
+
async function runAppRun(context, options) {
|
|
75
94
|
if (context.flags.json) throw usageError("App run does not support --json", "This command streams the framework dev server directly and cannot return structured JSON.", "Rerun without --json to pass framework logs through directly.", ["prisma-cli app run"], "app");
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
95
|
+
const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "run");
|
|
96
|
+
const merged = mergeComputeLocalInputs({
|
|
97
|
+
cli: {
|
|
98
|
+
entrypoint: options?.entrypoint,
|
|
99
|
+
buildType: options?.buildType,
|
|
100
|
+
port: options?.port
|
|
101
|
+
},
|
|
102
|
+
target: compute.target
|
|
103
|
+
});
|
|
104
|
+
if (merged.buildTypeFromConfig && compute.target?.framework && !frameworkByKey(compute.target.framework).hasLocalDevServer) throw usageError(`App run does not support the ${compute.target?.framework} framework yet`, `${compute.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME$1} sets a framework that has no local dev server in the current preview.`, "Run the framework dev server directly, or pass --build-type nextjs or --build-type bun to override.", ["prisma-cli app run --build-type nextjs", "prisma-cli app run --build-type bun --entry server.ts"], "app");
|
|
105
|
+
const appDir = await resolveComputeAppDir(context, compute);
|
|
106
|
+
const buildType = normalizeBuildType(merged.buildType);
|
|
107
|
+
assertSupportedEntrypoint(buildType, merged.entrypoint, "run");
|
|
108
|
+
const port = parseLocalPort(merged.port);
|
|
109
|
+
const framework = await resolveLocalRunFramework(context, {
|
|
110
|
+
requestedBuildType: buildType,
|
|
111
|
+
configFramework: compute.target?.framework ?? null,
|
|
112
|
+
appDir
|
|
113
|
+
});
|
|
114
|
+
const entrypoint = framework.buildType === "bun" ? await resolveDeployEntrypoint(appDir, framework, merged.entrypoint, context.runtime.signal) : merged.entrypoint;
|
|
80
115
|
let runResult;
|
|
81
116
|
try {
|
|
82
117
|
runResult = await runLocalApp({
|
|
83
|
-
appPath:
|
|
84
|
-
buildType:
|
|
118
|
+
appPath: appDir,
|
|
119
|
+
buildType: framework.buildType,
|
|
85
120
|
entrypoint,
|
|
86
121
|
port,
|
|
87
122
|
env: context.runtime.env,
|
|
@@ -106,6 +141,106 @@ async function runAppRun(context, entrypoint, requestedBuildType, requestedPort)
|
|
|
106
141
|
}
|
|
107
142
|
async function runAppDeploy(context, appName, options) {
|
|
108
143
|
ensurePreviewAppMode(context);
|
|
144
|
+
const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
|
|
145
|
+
if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, "deploy");
|
|
146
|
+
const config = loaded.value;
|
|
147
|
+
const plan = planAppDeploy({
|
|
148
|
+
config,
|
|
149
|
+
requestedTarget: options?.configTarget ?? (config ? inferComputeTargetFromCwd(config, context.runtime.cwd) : void 0),
|
|
150
|
+
hasCreateProject: options?.createProjectName !== void 0
|
|
151
|
+
});
|
|
152
|
+
if (plan.mode === "all") return runAppDeployAll(context, config, plan.targets, appName, options);
|
|
153
|
+
return runSingleAppDeploy(context, appName, options, config);
|
|
154
|
+
}
|
|
155
|
+
async function runAppDeployAll(context, config, plannedTargets, appName, options) {
|
|
156
|
+
assertNoPerAppInputsForDeployAll(context, config, appName, options);
|
|
157
|
+
const deployments = [];
|
|
158
|
+
const warnings = [];
|
|
159
|
+
for (const planned of plannedTargets) {
|
|
160
|
+
maybeRenderDeployAllTargetHeader(context, planned);
|
|
161
|
+
const targetOptions = {
|
|
162
|
+
...options,
|
|
163
|
+
configTarget: planned.targetKey,
|
|
164
|
+
createProjectName: planned.bindsCreateProject ? options?.createProjectName : void 0
|
|
165
|
+
};
|
|
166
|
+
try {
|
|
167
|
+
const single = await runSingleAppDeploy(context, void 0, targetOptions, config);
|
|
168
|
+
deployments.push({
|
|
169
|
+
target: planned.targetKey,
|
|
170
|
+
result: single.result
|
|
171
|
+
});
|
|
172
|
+
warnings.push(...single.warnings);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throw deployAllFailedError(error, config, planned.index, deployments);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
command: "app.deploy",
|
|
179
|
+
result: { deployments },
|
|
180
|
+
warnings,
|
|
181
|
+
nextSteps: ["prisma-cli app list-deploys <app>"]
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function assertNoPerAppInputsForDeployAll(context, config, appName, options) {
|
|
185
|
+
const used = perAppInputsForDeployAll({
|
|
186
|
+
appName,
|
|
187
|
+
framework: options?.framework,
|
|
188
|
+
entrypoint: options?.entrypoint,
|
|
189
|
+
httpPort: options?.httpPort,
|
|
190
|
+
envAssignments: options?.envAssignments,
|
|
191
|
+
appIdEnvVar: {
|
|
192
|
+
name: PRISMA_APP_ID_ENV_VAR,
|
|
193
|
+
value: readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR)
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
if (used.length === 0) return;
|
|
197
|
+
const targets = config.targets.map((target) => target.key).join(", ");
|
|
198
|
+
throw usageError(`Deploying all apps does not accept ${used.join(", ")}`, `Without a target, app deploy deploys every configured app (${targets}), so per-app inputs are ambiguous.`, "Pass the app target to apply per-app inputs to one app, or remove them to deploy all apps.", config.targets.map((target) => `prisma-cli app deploy ${target.key}`), "app");
|
|
199
|
+
}
|
|
200
|
+
function maybeRenderDeployAllTargetHeader(context, planned) {
|
|
201
|
+
if (context.flags.json || context.flags.quiet) return;
|
|
202
|
+
context.output.stderr.write(`${planned.index > 0 ? "\n" : ""}── ${planned.targetKey} (${planned.index + 1}/${planned.total}) ──\n\n`);
|
|
203
|
+
}
|
|
204
|
+
function deployAllFailedError(error, config, failedIndex, deployments) {
|
|
205
|
+
if (!(error instanceof CliError)) return error;
|
|
206
|
+
const failure = describeDeployAllFailure({
|
|
207
|
+
targetKeys: config.targets.map((target) => target.key),
|
|
208
|
+
failedIndex,
|
|
209
|
+
completed: deployments.map(({ target, result }) => ({
|
|
210
|
+
target,
|
|
211
|
+
deploymentId: result.deployment.id,
|
|
212
|
+
url: result.deployment.url
|
|
213
|
+
}))
|
|
214
|
+
});
|
|
215
|
+
const contextSentence = failure.contextLines.join(" ");
|
|
216
|
+
return new CliError({
|
|
217
|
+
code: error.code,
|
|
218
|
+
domain: error.domain,
|
|
219
|
+
summary: error.summary,
|
|
220
|
+
why: error.humanLines ? error.why : [error.why, contextSentence].filter(Boolean).join(" "),
|
|
221
|
+
fix: error.fix,
|
|
222
|
+
debug: error.debug,
|
|
223
|
+
where: error.where,
|
|
224
|
+
meta: {
|
|
225
|
+
...error.meta,
|
|
226
|
+
deployAll: {
|
|
227
|
+
failedTarget: failure.failedTarget,
|
|
228
|
+
completed: failure.completed,
|
|
229
|
+
notAttempted: failure.notAttempted
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
docsUrl: error.docsUrl,
|
|
233
|
+
exitCode: error.exitCode,
|
|
234
|
+
nextSteps: error.nextSteps,
|
|
235
|
+
nextActions: error.nextActions,
|
|
236
|
+
humanLines: error.humanLines ? [
|
|
237
|
+
...error.humanLines,
|
|
238
|
+
"",
|
|
239
|
+
...failure.contextLines
|
|
240
|
+
] : void 0
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
|
|
109
244
|
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
110
245
|
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
111
246
|
assertExclusiveDeployProjectInputs({
|
|
@@ -113,14 +248,27 @@ async function runAppDeploy(context, appName, options) {
|
|
|
113
248
|
createProjectName: options?.createProjectName,
|
|
114
249
|
envProjectId
|
|
115
250
|
});
|
|
116
|
-
const
|
|
251
|
+
const computeConfig = await resolveComputeTargetOrThrow(context, options?.configTarget, "deploy", { preloaded: preloadedConfig });
|
|
252
|
+
const merged = mergeComputeDeployInputs({
|
|
253
|
+
cli: {
|
|
254
|
+
framework: options?.framework,
|
|
255
|
+
entrypoint: options?.entrypoint,
|
|
256
|
+
httpPort: options?.httpPort,
|
|
257
|
+
envInputs: options?.envAssignments
|
|
258
|
+
},
|
|
259
|
+
target: computeConfig.target,
|
|
260
|
+
configFilename: computeConfig.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME$1
|
|
261
|
+
});
|
|
262
|
+
const appDir = await resolveComputeAppDir(context, computeConfig);
|
|
263
|
+
const projectDir = computeConfig.config?.configDir ?? context.runtime.cwd;
|
|
264
|
+
const localPinReadResult = Boolean(envProjectId || options?.projectRef || options?.createProjectName) ? Result.ok({ kind: "missing" }) : await readLocalResolutionPin(projectDir, context.runtime.signal);
|
|
117
265
|
if (localPinReadResult.isErr()) throw localPinReadErrorToDeployError(localPinReadResult.error);
|
|
118
266
|
const localPin = localPinReadResult.value;
|
|
119
267
|
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
120
|
-
if (
|
|
268
|
+
if (merged.httpPort) parseDeployHttpPort(merged.httpPort.value);
|
|
121
269
|
assertSupportedEntrypointForRequestedDeployShape({
|
|
122
|
-
requestedFramework:
|
|
123
|
-
entrypoint:
|
|
270
|
+
requestedFramework: merged.framework?.value,
|
|
271
|
+
entrypoint: merged.entrypoint?.value
|
|
124
272
|
});
|
|
125
273
|
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
126
274
|
branch,
|
|
@@ -130,27 +278,32 @@ async function runAppDeploy(context, appName, options) {
|
|
|
130
278
|
});
|
|
131
279
|
let localPinResult;
|
|
132
280
|
if (target.localPinAction) {
|
|
133
|
-
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
|
|
281
|
+
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction, projectDir);
|
|
134
282
|
if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
|
|
135
283
|
const projectSetup = setupResult.value;
|
|
136
284
|
localPinResult = projectSetup.localPin;
|
|
137
285
|
maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
|
|
138
286
|
}
|
|
139
287
|
let framework = await resolveDeployFramework(context, {
|
|
140
|
-
requestedFramework:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
288
|
+
requestedFramework: merged.framework?.value,
|
|
289
|
+
requestedFrameworkAnnotation: merged.framework?.annotation,
|
|
290
|
+
entrypoint: merged.entrypoint?.value,
|
|
291
|
+
entrypointAnnotation: merged.entrypoint?.annotation,
|
|
292
|
+
appDir
|
|
293
|
+
});
|
|
294
|
+
let runtime = resolveDeployRuntime(merged.httpPort?.value, merged.httpPort?.annotation, framework);
|
|
295
|
+
assertSupportedEntrypoint(framework.buildType, merged.entrypoint?.value, "deploy");
|
|
296
|
+
const envVars = toOptionalEnvVars(await parseEnvInputs(merged.envInputsFromConfig ? projectDir : context.runtime.cwd, merged.envInputs, { commandName: "deploy" }));
|
|
146
297
|
const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
147
298
|
explicitAppName: appName,
|
|
148
299
|
explicitAppId: envAppId,
|
|
300
|
+
configAppName: merged.configAppName,
|
|
149
301
|
firstDeploy: Boolean(target.localPinAction),
|
|
150
|
-
inferName: () => inferTargetName(
|
|
302
|
+
inferName: () => inferTargetName(appDir, context.runtime.signal)
|
|
151
303
|
});
|
|
152
304
|
await maybeRenderDeploySetupBlock(context, {
|
|
153
305
|
includeDirectory: !target.localPinAction,
|
|
306
|
+
appDir,
|
|
154
307
|
projectName: target.project.name,
|
|
155
308
|
branchName: target.branch.name,
|
|
156
309
|
appName: selectedApp.displayName
|
|
@@ -159,9 +312,9 @@ async function runAppDeploy(context, appName, options) {
|
|
|
159
312
|
framework,
|
|
160
313
|
runtime,
|
|
161
314
|
firstDeploy: selectedApp.firstDeploy,
|
|
162
|
-
explicitFramework: Boolean(
|
|
163
|
-
explicitEntrypoint: Boolean(
|
|
164
|
-
explicitHttpPort: Boolean(
|
|
315
|
+
explicitFramework: Boolean(merged.framework),
|
|
316
|
+
explicitEntrypoint: Boolean(merged.entrypoint),
|
|
317
|
+
explicitHttpPort: Boolean(merged.httpPort)
|
|
165
318
|
});
|
|
166
319
|
framework = customized.framework;
|
|
167
320
|
runtime = customized.runtime;
|
|
@@ -172,24 +325,33 @@ async function runAppDeploy(context, appName, options) {
|
|
|
172
325
|
prod: options?.prod === true
|
|
173
326
|
});
|
|
174
327
|
const buildType = framework.buildType;
|
|
175
|
-
assertSupportedEntrypoint(buildType,
|
|
176
|
-
const entrypoint = await resolveDeployEntrypoint(
|
|
177
|
-
|
|
178
|
-
|
|
328
|
+
assertSupportedEntrypoint(buildType, merged.entrypoint?.value, "deploy");
|
|
329
|
+
const entrypoint = await resolveDeployEntrypoint(appDir, framework, merged.entrypoint?.value, context.runtime.signal);
|
|
330
|
+
if (computeConfig.target?.build) assertConfigBackedBuildSettings(buildType);
|
|
331
|
+
const buildSettingsResolution = computeConfig.config && computeConfig.target?.build && isConfigBackedBuildType(buildType) ? await resolveConfiguredPreviewBuildSettings({
|
|
332
|
+
appPath: appDir,
|
|
333
|
+
buildType,
|
|
334
|
+
configured: computeConfig.target.build,
|
|
335
|
+
configPath: computeConfig.config.configPath,
|
|
336
|
+
signal: context.runtime.signal
|
|
337
|
+
}) : await resolveInferredPreviewBuildSettings({
|
|
338
|
+
appPath: appDir,
|
|
179
339
|
buildType,
|
|
180
340
|
signal: context.runtime.signal
|
|
181
341
|
});
|
|
342
|
+
const legacyWarnings = await handleLegacyBuildSettings(context, appDir, buildSettingsResolution.settings);
|
|
182
343
|
maybeRenderDeployBuildSettings(context, buildSettingsResolution);
|
|
183
344
|
const portMapping = parseDeployPortMapping(String(runtime.port));
|
|
184
345
|
const branchDatabaseSetup = await maybeSetupBranchDatabase(context, provider, projectId, toBranchDatabaseDeployBranch(target.branch), {
|
|
185
346
|
db: options?.db,
|
|
186
347
|
providedEnvVars: envVars,
|
|
187
|
-
firstProductionDeploy: productionDeployGate.firstProductionDeploy
|
|
348
|
+
firstProductionDeploy: productionDeployGate.firstProductionDeploy,
|
|
349
|
+
projectDir
|
|
188
350
|
});
|
|
189
351
|
const progressState = createPreviewDeployProgressState();
|
|
190
352
|
const deployStartedAt = Date.now();
|
|
191
353
|
const deployResult = await provider.deployApp({
|
|
192
|
-
cwd:
|
|
354
|
+
cwd: appDir,
|
|
193
355
|
projectId,
|
|
194
356
|
branchName: target.branch.name,
|
|
195
357
|
appId: selectedApp.appId,
|
|
@@ -252,14 +414,18 @@ async function runAppDeploy(context, appName, options) {
|
|
|
252
414
|
durationMs: deployDurationMs,
|
|
253
415
|
localPin: localPinResult
|
|
254
416
|
},
|
|
255
|
-
warnings: branchDatabaseSetup.warnings,
|
|
417
|
+
warnings: [...legacyWarnings, ...branchDatabaseSetup.warnings],
|
|
256
418
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
|
|
257
419
|
};
|
|
258
420
|
}
|
|
259
|
-
async function runAppListDeploys(context, appName, projectRef) {
|
|
421
|
+
async function runAppListDeploys(context, appName, projectRef, configTarget) {
|
|
260
422
|
ensurePreviewAppMode(context);
|
|
261
|
-
const
|
|
262
|
-
const
|
|
423
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "list-deploys");
|
|
424
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
425
|
+
commandName: "app list-deploys",
|
|
426
|
+
projectDir: compute.projectDir
|
|
427
|
+
});
|
|
428
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
|
|
263
429
|
if (!selectedApp) return {
|
|
264
430
|
command: "app.list-deploys",
|
|
265
431
|
result: {
|
|
@@ -295,10 +461,14 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
295
461
|
nextSteps: deployments.length > 0 ? [`prisma-cli app show-deploy ${deployments[0]?.id}`] : ["prisma-cli app deploy"]
|
|
296
462
|
};
|
|
297
463
|
}
|
|
298
|
-
async function runAppShow(context, appName, projectRef) {
|
|
464
|
+
async function runAppShow(context, appName, projectRef, configTarget) {
|
|
299
465
|
ensurePreviewAppMode(context);
|
|
300
|
-
const
|
|
301
|
-
const
|
|
466
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "show");
|
|
467
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
468
|
+
commandName: "app show",
|
|
469
|
+
projectDir: compute.projectDir
|
|
470
|
+
});
|
|
471
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
|
|
302
472
|
if (!selectedApp) return {
|
|
303
473
|
command: "app.show",
|
|
304
474
|
result: {
|
|
@@ -373,9 +543,14 @@ async function runAppShowDeploy(context, deploymentId) {
|
|
|
373
543
|
nextSteps: []
|
|
374
544
|
};
|
|
375
545
|
}
|
|
376
|
-
async function runAppOpen(context, appName, projectRef) {
|
|
546
|
+
async function runAppOpen(context, appName, projectRef, configTarget) {
|
|
377
547
|
ensurePreviewAppMode(context);
|
|
378
|
-
const
|
|
548
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "open");
|
|
549
|
+
appName = appName ?? compute.configAppName;
|
|
550
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
551
|
+
commandName: "app open",
|
|
552
|
+
projectDir: compute.projectDir
|
|
553
|
+
});
|
|
379
554
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
380
555
|
if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
|
|
381
556
|
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
@@ -539,15 +714,19 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
539
714
|
});
|
|
540
715
|
}
|
|
541
716
|
}
|
|
542
|
-
async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
717
|
+
async function runAppLogs(context, appName, deploymentId, projectRef, configTarget) {
|
|
543
718
|
ensurePreviewAppMode(context);
|
|
544
|
-
const
|
|
719
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "logs");
|
|
720
|
+
appName = appName ?? compute.configAppName;
|
|
721
|
+
const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
722
|
+
commandName: "app logs",
|
|
723
|
+
projectDir: compute.projectDir
|
|
724
|
+
});
|
|
545
725
|
const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
|
|
546
726
|
if (!context.flags.json && !context.flags.quiet) {
|
|
547
727
|
const lines = renderCommandHeader(context.ui, {
|
|
548
728
|
commandLabel: "app logs",
|
|
549
729
|
description: "Streaming logs for the selected deployment.",
|
|
550
|
-
docsPath: "docs/product/command-spec.md#prisma-cli-app-logs---app-name---deployment-id",
|
|
551
730
|
rows: [
|
|
552
731
|
{
|
|
553
732
|
key: "project",
|
|
@@ -664,9 +843,14 @@ function writeLogRecord(context, record) {
|
|
|
664
843
|
if (!record.text.endsWith("\n")) context.output.stdout.write("\n");
|
|
665
844
|
}
|
|
666
845
|
}
|
|
667
|
-
async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
846
|
+
async function runAppPromote(context, deploymentId, appName, projectRef, configTarget) {
|
|
668
847
|
ensurePreviewAppMode(context);
|
|
669
|
-
const
|
|
848
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "promote");
|
|
849
|
+
appName = appName ?? compute.configAppName;
|
|
850
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
851
|
+
commandName: "app promote",
|
|
852
|
+
projectDir: compute.projectDir
|
|
853
|
+
});
|
|
670
854
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
|
|
671
855
|
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
672
856
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -706,9 +890,14 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
706
890
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
707
891
|
};
|
|
708
892
|
}
|
|
709
|
-
async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
893
|
+
async function runAppRollback(context, appName, deploymentId, projectRef, configTarget) {
|
|
710
894
|
ensurePreviewAppMode(context);
|
|
711
|
-
const
|
|
895
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "rollback");
|
|
896
|
+
appName = appName ?? compute.configAppName;
|
|
897
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
898
|
+
commandName: "app rollback",
|
|
899
|
+
projectDir: compute.projectDir
|
|
900
|
+
});
|
|
712
901
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
|
|
713
902
|
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
714
903
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -750,9 +939,14 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
750
939
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
751
940
|
};
|
|
752
941
|
}
|
|
753
|
-
async function runAppRemove(context, appName, projectRef) {
|
|
942
|
+
async function runAppRemove(context, appName, projectRef, configTarget) {
|
|
754
943
|
ensurePreviewAppMode(context);
|
|
755
|
-
const
|
|
944
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "remove");
|
|
945
|
+
appName = appName ?? compute.configAppName;
|
|
946
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
947
|
+
commandName: "app remove",
|
|
948
|
+
projectDir: compute.projectDir
|
|
949
|
+
});
|
|
756
950
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
|
|
757
951
|
await confirmAppRemoval(context, selectedApp);
|
|
758
952
|
const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
@@ -776,6 +970,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
776
970
|
}
|
|
777
971
|
async function resolveAppDomainTarget(context, options, commandName = "app domain") {
|
|
778
972
|
ensurePreviewAppMode(context);
|
|
973
|
+
const compute = await resolveComputeManagementContext(context, options?.configTarget, commandName.replace(/^app /, ""));
|
|
779
974
|
const branch = resolveDomainBranch(options?.branchName);
|
|
780
975
|
if (toBranchKind(branch.name) !== "production") throw new CliError({
|
|
781
976
|
code: "BRANCH_NOT_DEPLOYABLE",
|
|
@@ -791,10 +986,11 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
|
|
|
791
986
|
const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
|
|
792
987
|
branch,
|
|
793
988
|
commandName,
|
|
794
|
-
envProjectId
|
|
989
|
+
envProjectId,
|
|
990
|
+
projectDir: compute.projectDir
|
|
795
991
|
});
|
|
796
992
|
const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
797
|
-
explicitAppName: options?.appName,
|
|
993
|
+
explicitAppName: options?.appName ?? compute.configAppName,
|
|
798
994
|
explicitAppId: envAppId
|
|
799
995
|
});
|
|
800
996
|
await context.stateStore.setSelectedApp(projectId, {
|
|
@@ -1107,6 +1303,25 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
|
1107
1303
|
firstDeploy: options.firstDeploy
|
|
1108
1304
|
};
|
|
1109
1305
|
}
|
|
1306
|
+
if (options.configAppName) {
|
|
1307
|
+
const configName = options.configAppName;
|
|
1308
|
+
const matches = findAppsByName(apps, configName.value);
|
|
1309
|
+
if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, configName.value, options.firstDeploy);
|
|
1310
|
+
const matched = matches[0];
|
|
1311
|
+
if (matched) return {
|
|
1312
|
+
appId: matched.id,
|
|
1313
|
+
displayName: matched.name,
|
|
1314
|
+
annotation: configName.annotation,
|
|
1315
|
+
firstDeploy: options.firstDeploy
|
|
1316
|
+
};
|
|
1317
|
+
return {
|
|
1318
|
+
appName: configName.value,
|
|
1319
|
+
region: PREVIEW_DEFAULT_REGION,
|
|
1320
|
+
displayName: configName.value,
|
|
1321
|
+
annotation: configName.annotation,
|
|
1322
|
+
firstDeploy: options.firstDeploy
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1110
1325
|
const inferredName = await options.inferName();
|
|
1111
1326
|
const matches = findAppsByName(apps, inferredName.name);
|
|
1112
1327
|
if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, inferredName.name, options.firstDeploy);
|
|
@@ -1363,6 +1578,7 @@ async function resolveProjectContext(context, client, explicitProject, options)
|
|
|
1363
1578
|
workspace: authState.workspace,
|
|
1364
1579
|
explicitProject,
|
|
1365
1580
|
envProjectId: options?.envProjectId,
|
|
1581
|
+
projectDir: options?.projectDir,
|
|
1366
1582
|
listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
|
|
1367
1583
|
commandName: options?.commandName
|
|
1368
1584
|
});
|
|
@@ -1559,22 +1775,130 @@ async function resolveDeployBranch(context, explicitBranchName) {
|
|
|
1559
1775
|
annotation: "default"
|
|
1560
1776
|
};
|
|
1561
1777
|
}
|
|
1778
|
+
async function resolveComputeTargetOrThrow(context, configTarget, commandName, options) {
|
|
1779
|
+
let config;
|
|
1780
|
+
if (options?.preloaded !== void 0) config = options.preloaded;
|
|
1781
|
+
else {
|
|
1782
|
+
const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
|
|
1783
|
+
if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, commandName);
|
|
1784
|
+
config = loaded.value;
|
|
1785
|
+
}
|
|
1786
|
+
if (!config) {
|
|
1787
|
+
if (configTarget) throw usageError(`App target "${configTarget}" requires a compute config file`, `No ${COMPUTE_CONFIG_FILENAME$1} exists in the current directory, so there are no named app targets.`, `Create ${COMPUTE_CONFIG_FILENAME$1} with an apps entry named "${configTarget}", or rerun without the target argument.`, [`prisma-cli app ${commandName}`], "app");
|
|
1788
|
+
return {
|
|
1789
|
+
config: null,
|
|
1790
|
+
target: null
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
const requestedTarget = configTarget ?? inferComputeTargetFromCwd(config, context.runtime.cwd);
|
|
1794
|
+
const selected = selectComputeDeployTarget(config, requestedTarget);
|
|
1795
|
+
if (selected.isErr()) {
|
|
1796
|
+
if (options?.targetOptional && selected.error instanceof ComputeConfigTargetRequiredError) return {
|
|
1797
|
+
config,
|
|
1798
|
+
target: null
|
|
1799
|
+
};
|
|
1800
|
+
throw computeConfigErrorToCliError(selected.error, commandName);
|
|
1801
|
+
}
|
|
1802
|
+
return {
|
|
1803
|
+
config,
|
|
1804
|
+
target: selected.value
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Compute-config context for app management commands: the project directory
|
|
1809
|
+
* (where `.prisma/local.json` lives) and the config-selected app name, which
|
|
1810
|
+
* ranks below `--app` but above the remembered app selection.
|
|
1811
|
+
*/
|
|
1812
|
+
async function resolveComputeManagementContext(context, configTarget, commandName) {
|
|
1813
|
+
const compute = await resolveComputeTargetOrThrow(context, configTarget, commandName, { targetOptional: true });
|
|
1814
|
+
return {
|
|
1815
|
+
projectDir: compute.config?.configDir ?? context.runtime.cwd,
|
|
1816
|
+
configAppName: compute.target?.name ?? compute.target?.key ?? void 0
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1819
|
+
async function resolveComputeAppDir(context, compute) {
|
|
1820
|
+
if (!compute.config || !compute.target) return context.runtime.cwd;
|
|
1821
|
+
const appDir = computeTargetAppDir(compute.config, compute.target);
|
|
1822
|
+
if (!compute.target.root) return appDir;
|
|
1823
|
+
context.runtime.signal.throwIfAborted();
|
|
1824
|
+
try {
|
|
1825
|
+
await access(appDir);
|
|
1826
|
+
context.runtime.signal.throwIfAborted();
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
if (context.runtime.signal.aborted) throw error;
|
|
1829
|
+
throw new CliError({
|
|
1830
|
+
code: "COMPUTE_CONFIG_INVALID",
|
|
1831
|
+
domain: "app",
|
|
1832
|
+
summary: `App root "${compute.target.root}" does not exist`,
|
|
1833
|
+
why: `${compute.config.relativeConfigPath} points the selected app at "${compute.target.root}", but that directory does not exist.`,
|
|
1834
|
+
fix: `Fix the root path in ${compute.config.relativeConfigPath} or create the directory.`,
|
|
1835
|
+
where: appDir,
|
|
1836
|
+
meta: {
|
|
1837
|
+
appRoot: compute.target.root,
|
|
1838
|
+
appDir
|
|
1839
|
+
},
|
|
1840
|
+
exitCode: 2,
|
|
1841
|
+
nextSteps: ["prisma-cli app deploy"]
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
return appDir;
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* `prisma.app.json` is no longer read or written. A leftover file that
|
|
1848
|
+
* matches the effective settings only warns; one with custom values fails
|
|
1849
|
+
* with migration guidance so builds never silently change.
|
|
1850
|
+
*/
|
|
1851
|
+
async function handleLegacyBuildSettings(context, appDir, effective) {
|
|
1852
|
+
const legacy = await detectLegacyBuildSettings({
|
|
1853
|
+
appPath: appDir,
|
|
1854
|
+
effective,
|
|
1855
|
+
signal: context.runtime.signal
|
|
1856
|
+
});
|
|
1857
|
+
switch (legacy.kind) {
|
|
1858
|
+
case "absent": return [];
|
|
1859
|
+
case "matching": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and matches the resolved build settings. Delete it.`];
|
|
1860
|
+
case "invalid": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and could not be parsed. Delete it.`];
|
|
1861
|
+
case "custom": {
|
|
1862
|
+
const buildBlock = [
|
|
1863
|
+
"build: {",
|
|
1864
|
+
` command: ${legacy.buildCommand === null ? "null" : JSON.stringify(legacy.buildCommand)},`,
|
|
1865
|
+
` outputDirectory: ${JSON.stringify(legacy.outputDirectory)},`,
|
|
1866
|
+
"}"
|
|
1867
|
+
].join(" ");
|
|
1868
|
+
throw new CliError({
|
|
1869
|
+
code: "BUILD_SETTINGS_MIGRATION_REQUIRED",
|
|
1870
|
+
domain: "app",
|
|
1871
|
+
summary: `${PRISMA_APP_CONFIG_FILENAME} is no longer supported`,
|
|
1872
|
+
why: `${PRISMA_APP_CONFIG_FILENAME} contains custom build settings that differ from the resolved defaults, and the file is no longer read.`,
|
|
1873
|
+
fix: `Move the settings into prisma.compute.ts as \`${buildBlock}\` on this app, then delete ${PRISMA_APP_CONFIG_FILENAME}.`,
|
|
1874
|
+
where: legacy.configPath,
|
|
1875
|
+
meta: {
|
|
1876
|
+
configPath: legacy.configPath,
|
|
1877
|
+
buildCommand: legacy.buildCommand,
|
|
1878
|
+
outputDirectory: legacy.outputDirectory
|
|
1879
|
+
},
|
|
1880
|
+
exitCode: 2,
|
|
1881
|
+
nextSteps: ["prisma-cli app deploy"]
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1562
1886
|
async function resolveDeployFramework(context, options) {
|
|
1563
|
-
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
|
|
1887
|
+
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, options.requestedFrameworkAnnotation ?? "set by --framework");
|
|
1564
1888
|
if (options.entrypoint) return {
|
|
1565
1889
|
key: "bun",
|
|
1566
1890
|
buildType: "bun",
|
|
1567
1891
|
displayName: "Bun",
|
|
1568
|
-
annotation: "set by --entry"
|
|
1892
|
+
annotation: options.entrypointAnnotation ?? "set by --entry"
|
|
1569
1893
|
};
|
|
1570
|
-
const detected = await detectDeployFramework(
|
|
1894
|
+
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
1571
1895
|
if (detected) return detected;
|
|
1572
|
-
throw frameworkNotDetectedError(
|
|
1896
|
+
throw frameworkNotDetectedError(options.appDir);
|
|
1573
1897
|
}
|
|
1574
|
-
function resolveDeployRuntime(requestedHttpPort, framework) {
|
|
1898
|
+
function resolveDeployRuntime(requestedHttpPort, requestedHttpPortAnnotation, framework) {
|
|
1575
1899
|
if (requestedHttpPort) return {
|
|
1576
1900
|
port: parseDeployHttpPort(requestedHttpPort),
|
|
1577
|
-
annotation: "set by --http-port"
|
|
1901
|
+
annotation: requestedHttpPortAnnotation ?? "set by --http-port"
|
|
1578
1902
|
};
|
|
1579
1903
|
return {
|
|
1580
1904
|
port: FRAMEWORK_DEFAULT_HTTP_PORT,
|
|
@@ -1589,8 +1913,8 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
|
|
|
1589
1913
|
if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
|
|
1590
1914
|
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
|
|
1591
1915
|
if (packageEntrypoint) return packageEntrypoint;
|
|
1592
|
-
|
|
1593
|
-
|
|
1916
|
+
const defaultEntrypoint = frameworkFromAlias(framework.key)?.defaultEntrypoint;
|
|
1917
|
+
if (!defaultEntrypoint) return;
|
|
1594
1918
|
signal.throwIfAborted();
|
|
1595
1919
|
try {
|
|
1596
1920
|
await access(path.join(cwd, defaultEntrypoint));
|
|
@@ -1604,34 +1928,22 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
|
|
|
1604
1928
|
}
|
|
1605
1929
|
async function detectDeployFramework(cwd, signal) {
|
|
1606
1930
|
const packageJson = await readBunPackageJson(cwd, signal);
|
|
1607
|
-
const
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
};
|
|
1620
|
-
if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
|
|
1621
|
-
key: "tanstack-start",
|
|
1622
|
-
buildType: "tanstack-start",
|
|
1623
|
-
displayName: "TanStack Start",
|
|
1624
|
-
annotation: "detected from package.json"
|
|
1625
|
-
};
|
|
1931
|
+
for (const framework of FRAMEWORKS) {
|
|
1932
|
+
if (framework.detectConfigFiles.length === 0 && framework.detectPackages.length === 0) continue;
|
|
1933
|
+
const configFile = await detectFrameworkConfigFile(cwd, framework, signal);
|
|
1934
|
+
if (!configFile.exists && !hasAnyPackageDependency(packageJson, framework.detectPackages)) continue;
|
|
1935
|
+
const annotation = framework.key === "nextjs" && configFile.standalone ? "standalone output detected" : configFile.exists ? `detected from ${path.basename(configFile.path)}` : "detected from package.json";
|
|
1936
|
+
return {
|
|
1937
|
+
key: framework.key,
|
|
1938
|
+
buildType: framework.buildType,
|
|
1939
|
+
displayName: framework.displayName,
|
|
1940
|
+
annotation
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1626
1943
|
return null;
|
|
1627
1944
|
}
|
|
1628
|
-
async function
|
|
1629
|
-
for (const candidate of
|
|
1630
|
-
"next.config.js",
|
|
1631
|
-
"next.config.mjs",
|
|
1632
|
-
"next.config.ts",
|
|
1633
|
-
"next.config.mts"
|
|
1634
|
-
]) {
|
|
1945
|
+
async function detectFrameworkConfigFile(cwd, framework, signal) {
|
|
1946
|
+
for (const candidate of framework.detectConfigFiles) {
|
|
1635
1947
|
const filePath = path.join(cwd, candidate);
|
|
1636
1948
|
signal.throwIfAborted();
|
|
1637
1949
|
try {
|
|
@@ -1641,7 +1953,8 @@ async function detectNextConfig(cwd, signal) {
|
|
|
1641
1953
|
});
|
|
1642
1954
|
return {
|
|
1643
1955
|
exists: true,
|
|
1644
|
-
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
|
|
1956
|
+
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content),
|
|
1957
|
+
path: filePath
|
|
1645
1958
|
};
|
|
1646
1959
|
} catch (error) {
|
|
1647
1960
|
if (signal.aborted) throw error;
|
|
@@ -1650,7 +1963,8 @@ async function detectNextConfig(cwd, signal) {
|
|
|
1650
1963
|
}
|
|
1651
1964
|
return {
|
|
1652
1965
|
exists: false,
|
|
1653
|
-
standalone: false
|
|
1966
|
+
standalone: false,
|
|
1967
|
+
path: null
|
|
1654
1968
|
};
|
|
1655
1969
|
}
|
|
1656
1970
|
function hasPackageDependency(packageJson, dependencyName) {
|
|
@@ -1663,48 +1977,41 @@ function hasDependency(dependencies, dependencyName) {
|
|
|
1663
1977
|
return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
|
|
1664
1978
|
}
|
|
1665
1979
|
function frameworkFromUserFacingValue(value, annotation) {
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
key: "tanstack-start",
|
|
1692
|
-
buildType: "tanstack-start",
|
|
1693
|
-
displayName: "TanStack Start",
|
|
1694
|
-
annotation
|
|
1695
|
-
};
|
|
1696
|
-
default: throw frameworkNotDetectedError(void 0, value);
|
|
1697
|
-
}
|
|
1980
|
+
const framework = frameworkFromAlias(value);
|
|
1981
|
+
if (!framework) throw frameworkNotDetectedError(void 0, value);
|
|
1982
|
+
return {
|
|
1983
|
+
key: framework.key,
|
|
1984
|
+
buildType: framework.buildType,
|
|
1985
|
+
displayName: framework.displayName,
|
|
1986
|
+
annotation
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
/**
|
|
1990
|
+
* The nuxt and astro strategies build with their framework CLI and stage
|
|
1991
|
+
* fixed output, so a compute config `build` block has nothing to apply to.
|
|
1992
|
+
* Erroring beats silently ignoring committed settings.
|
|
1993
|
+
*/
|
|
1994
|
+
function assertConfigBackedBuildSettings(buildType) {
|
|
1995
|
+
if (isConfigBackedBuildType(buildType)) return;
|
|
1996
|
+
const displayName = FRAMEWORKS.find((framework) => framework.buildType === buildType)?.displayName ?? buildType;
|
|
1997
|
+
throw new CliError({
|
|
1998
|
+
code: "BUILD_SETTINGS_UNSUPPORTED",
|
|
1999
|
+
domain: "app",
|
|
2000
|
+
summary: `build settings are not supported for ${displayName} apps`,
|
|
2001
|
+
why: `${displayName} deploys run \`${buildType} build\` and package its output automatically.`,
|
|
2002
|
+
fix: "Remove the `build` block from prisma.compute.ts for this app.",
|
|
2003
|
+
exitCode: 2
|
|
2004
|
+
});
|
|
1698
2005
|
}
|
|
1699
2006
|
function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
1700
|
-
const supported =
|
|
2007
|
+
const supported = FRAMEWORKS.map((framework) => framework.displayName).join(", ");
|
|
1701
2008
|
const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
|
|
1702
2009
|
return new CliError({
|
|
1703
2010
|
code: "FRAMEWORK_NOT_DETECTED",
|
|
1704
2011
|
domain: "app",
|
|
1705
2012
|
summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
|
|
1706
2013
|
why: `Supported Beta frameworks: ${supported}.`,
|
|
1707
|
-
fix:
|
|
2014
|
+
fix: `Add one of these frameworks as a dependency, pass --framework <${FRAMEWORKS.map((framework) => framework.key).join("|")}>, or pass --entry <path> for a Bun app.`,
|
|
1708
2015
|
exitCode: 2,
|
|
1709
2016
|
nextSteps: [
|
|
1710
2017
|
"prisma-cli app deploy --framework nextjs",
|
|
@@ -1717,14 +2024,14 @@ function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
|
1717
2024
|
}
|
|
1718
2025
|
async function maybeRenderDeploySetupBlock(context, details) {
|
|
1719
2026
|
if (context.flags.json || context.flags.quiet) return;
|
|
1720
|
-
const directory =
|
|
2027
|
+
const directory = formatAppDirectoryLabel(context.runtime.cwd, details.appDir);
|
|
1721
2028
|
const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
|
|
1722
2029
|
context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
|
|
1723
2030
|
}
|
|
1724
2031
|
function maybeRenderDeployBuildSettings(context, resolution) {
|
|
1725
2032
|
if (context.flags.json || context.flags.quiet) return;
|
|
1726
2033
|
const settings = resolution.settings;
|
|
1727
|
-
const title = resolution.status === "
|
|
2034
|
+
const title = resolution.status === "config" ? `Using ${resolution.relativeConfigPath}` : "Build settings";
|
|
1728
2035
|
context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [{
|
|
1729
2036
|
label: "Build Command",
|
|
1730
2037
|
value: settings.buildCommand ?? "none",
|
|
@@ -1761,9 +2068,9 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1761
2068
|
input: context.runtime.stdin,
|
|
1762
2069
|
output: context.runtime.stderr,
|
|
1763
2070
|
message: `Framework (${options.framework.displayName})`,
|
|
1764
|
-
choices:
|
|
1765
|
-
label:
|
|
1766
|
-
value: framework
|
|
2071
|
+
choices: FRAMEWORKS.map((framework) => ({
|
|
2072
|
+
label: framework.displayName,
|
|
2073
|
+
value: framework.key
|
|
1767
2074
|
}))
|
|
1768
2075
|
}), "set by you");
|
|
1769
2076
|
const requestedPort = await textPrompt({
|
|
@@ -1806,14 +2113,6 @@ function maybeRenderDeploySettingsPreview(context, options) {
|
|
|
1806
2113
|
value: `HTTP ${options.runtime.port}`
|
|
1807
2114
|
}]).join("\n")}\n\n`);
|
|
1808
2115
|
}
|
|
1809
|
-
function frameworkDisplayName(framework) {
|
|
1810
|
-
switch (framework) {
|
|
1811
|
-
case "nextjs": return "Next.js";
|
|
1812
|
-
case "hono": return "Hono";
|
|
1813
|
-
case "tanstack-start": return "TanStack Start";
|
|
1814
|
-
case "bun": return "Bun";
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
2116
|
function validateDeployHttpPortText(value) {
|
|
1818
2117
|
if (!value?.trim()) return;
|
|
1819
2118
|
try {
|
|
@@ -1827,6 +2126,11 @@ function formatDeployDirectory(cwd) {
|
|
|
1827
2126
|
const basename = path.basename(cwd);
|
|
1828
2127
|
return basename ? `./${basename}` : ".";
|
|
1829
2128
|
}
|
|
2129
|
+
function formatAppDirectoryLabel(cwd, appDir) {
|
|
2130
|
+
if (appDir === cwd) return formatDeployDirectory(cwd);
|
|
2131
|
+
const relative = path.relative(cwd, appDir).split(path.sep).join("/");
|
|
2132
|
+
return relative.startsWith("..") ? relative : `./${relative}`;
|
|
2133
|
+
}
|
|
1830
2134
|
async function readCurrentWorkspaceId(context) {
|
|
1831
2135
|
const state = await context.stateStore.read();
|
|
1832
2136
|
if (state.auth?.workspaceId) return state.auth.workspaceId;
|
|
@@ -1846,15 +2150,25 @@ function getBuildTypeExamples(commandName) {
|
|
|
1846
2150
|
});
|
|
1847
2151
|
}
|
|
1848
2152
|
function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
|
|
1849
|
-
if (buildType !== "auto" && buildType
|
|
2153
|
+
if (buildType !== "auto" && !ENTRYPOINT_BUILD_TYPES.includes(buildType) && entrypoint) {
|
|
1850
2154
|
if (commandName === "deploy") throw usageError(`App deploy does not accept --entry with ${formatBuildTypeName(buildType)}`, `${formatBuildTypeName(buildType)} apps derive their runtime entrypoint from build output.`, "Remove --entry, or use --framework bun when you want to target a Bun entrypoint directly.", [`prisma-cli app deploy --framework ${buildType}`, "prisma-cli app deploy --framework bun --entry server.ts"], "app");
|
|
1851
2155
|
throw usageError(`App ${commandName} does not accept --entry with --build-type ${buildType}`, `${formatBuildTypeName(buildType)} apps do not use an entrypoint flag in the current preview.`, `Remove --entry, or rerun prisma-cli app ${commandName} with --build-type bun when you want to target a Bun entrypoint directly.`, [`prisma-cli app ${commandName} --build-type ${buildType}`, `prisma-cli app ${commandName} --build-type bun --entry server.ts`], "app");
|
|
1852
2156
|
}
|
|
1853
2157
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
2158
|
+
/**
|
|
2159
|
+
* Resolves the framework for `app run` with the same detection as deploy, so
|
|
2160
|
+
* a repo that deploys without flags also runs without flags. Local dev server
|
|
2161
|
+
* support is intentionally narrower than deploy build support: only Next.js
|
|
2162
|
+
* and Bun/Hono have dev servers in the current preview.
|
|
2163
|
+
*/
|
|
2164
|
+
async function resolveLocalRunFramework(context, options) {
|
|
2165
|
+
if (LOCAL_DEV_BUILD_TYPES.includes(options.requestedBuildType)) {
|
|
2166
|
+
if (options.configFramework && computeFrameworkToBuildType(options.configFramework) === options.requestedBuildType) return frameworkFromUserFacingValue(options.configFramework, `set by ${COMPUTE_CONFIG_FILENAME$1}`);
|
|
2167
|
+
return frameworkFromUserFacingValue(options.requestedBuildType, "set by --build-type");
|
|
2168
|
+
}
|
|
2169
|
+
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
2170
|
+
if (detected && LOCAL_DEV_BUILD_TYPES.includes(detected.buildType)) return detected;
|
|
2171
|
+
throw usageError("App run requires an explicit framework when detection is ambiguous", "This preview only starts local dev servers for clear Next.js or Bun project shapes.", "Pass --build-type nextjs for a Next.js app, or pass --build-type bun with --entry <path> for a Bun app.", ["prisma-cli app run --build-type nextjs", "prisma-cli app run --build-type bun --entry server.ts"], "app");
|
|
1858
2172
|
}
|
|
1859
2173
|
function parseLocalPort(requestedPort) {
|
|
1860
2174
|
if (!requestedPort) return DEFAULT_LOCAL_DEV_PORT;
|