@prisma/cli 3.0.0-dev.86.1 → 3.0.0-dev.88.1
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/commands/app/index.js +64 -51
- package/dist/controllers/app.js +561 -276
- package/dist/lib/app/branch-database-deploy.js +45 -104
- package/dist/lib/app/branch-database.js +22 -184
- 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 +35 -56
- package/dist/lib/app/production-deploy-gate.js +2 -2
- 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 +1 -1
- package/dist/lib/project/setup.js +10 -8
- package/dist/presenters/app.js +45 -40
- package/dist/presenters/project.js +2 -8
- package/dist/shell/diagnostics-output.js +2 -8
- package/dist/shell/runtime.js +7 -3
- package/package.json +5 -3
package/dist/controllers/app.js
CHANGED
|
@@ -10,11 +10,13 @@ import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, pr
|
|
|
10
10
|
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
11
11
|
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
12
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";
|
|
13
14
|
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
15
|
+
import { describeDeployAllFailure, perAppInputsForDeployAll, planAppDeploy } from "../lib/app/deploy-plan.js";
|
|
14
16
|
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
15
17
|
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
16
|
-
import { DEFAULT_LOCAL_DEV_PORT,
|
|
17
|
-
import {
|
|
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";
|
|
18
20
|
import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
|
|
19
21
|
import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
|
|
20
22
|
import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
|
|
@@ -28,32 +30,49 @@ 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
36
|
import { Result, matchError } from "better-result";
|
|
34
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, {
|
|
@@ -910,72 +1106,51 @@ async function confirmDomainRemoval(context, target, hostname) {
|
|
|
910
1106
|
})) throw usageError("Custom domain removal canceled", "The command was canceled before the domain was detached.", "Rerun the command and confirm removal, or pass --yes.", [`prisma-cli app domain remove ${hostname} --app ${target.app.name} --yes`], "app");
|
|
911
1107
|
}
|
|
912
1108
|
function domainCommandError(command, error, hostname) {
|
|
913
|
-
if (error instanceof PreviewDomainApiError)
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
why: "The selected production app does not have a promoted version that can receive a custom domain.",
|
|
959
|
-
fix: "Deploy the app to the production branch, then rerun the domain command.",
|
|
960
|
-
debug: formatDebugDetails(error),
|
|
961
|
-
exitCode: 1,
|
|
962
|
-
nextSteps: ["prisma-cli app deploy --branch production", `prisma-cli app domain add ${hostname}`]
|
|
963
|
-
});
|
|
964
|
-
return domainGenericCommandError("add", error, hostname);
|
|
965
|
-
}
|
|
966
|
-
function domainRetryNotEligibleError(error, hostname) {
|
|
967
|
-
return new CliError({
|
|
968
|
-
code: "DOMAIN_RETRY_NOT_ELIGIBLE",
|
|
969
|
-
domain: "app",
|
|
970
|
-
summary: `Custom domain "${hostname}" is not eligible for retry`,
|
|
971
|
-
why: error.message,
|
|
972
|
-
fix: "Wait for the current verification or TLS step to finish, then rerun retry if the domain fails.",
|
|
973
|
-
debug: formatDebugDetails(error),
|
|
974
|
-
exitCode: 1,
|
|
975
|
-
nextSteps: [`prisma-cli app domain show ${hostname}`]
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
function domainGenericCommandError(command, error, hostname) {
|
|
1109
|
+
if (error instanceof PreviewDomainApiError) {
|
|
1110
|
+
if (command === "add" && (error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
|
|
1111
|
+
if (command === "add" && error.status === 400) return new CliError({
|
|
1112
|
+
code: "DOMAIN_HOSTNAME_INVALID",
|
|
1113
|
+
domain: "app",
|
|
1114
|
+
summary: `Invalid custom domain "${hostname}"`,
|
|
1115
|
+
why: error.message,
|
|
1116
|
+
fix: "Pass a valid hostname like shop.acme.com and make sure DNS can be verified.",
|
|
1117
|
+
debug: formatDebugDetails(error),
|
|
1118
|
+
exitCode: 2,
|
|
1119
|
+
nextSteps: ["prisma-cli app domain add shop.acme.com"]
|
|
1120
|
+
});
|
|
1121
|
+
if (command === "add" && (error.status === 429 || isDomainQuotaError(error))) return new CliError({
|
|
1122
|
+
code: "DOMAIN_QUOTA_EXCEEDED",
|
|
1123
|
+
domain: "app",
|
|
1124
|
+
summary: "Custom domain quota exceeded",
|
|
1125
|
+
why: error.message,
|
|
1126
|
+
fix: "Remove an existing custom domain before adding another one.",
|
|
1127
|
+
debug: formatDebugDetails(error),
|
|
1128
|
+
exitCode: 1,
|
|
1129
|
+
nextSteps: ["prisma-cli app domain remove <hostname>"]
|
|
1130
|
+
});
|
|
1131
|
+
if (command === "add" && error.status === 409) return domainAlreadyRegisteredError(hostname, error);
|
|
1132
|
+
if (command === "add" && error.status === 422) return new CliError({
|
|
1133
|
+
code: "NO_DEPLOYMENTS",
|
|
1134
|
+
domain: "app",
|
|
1135
|
+
summary: "Custom domain requires a live production deployment",
|
|
1136
|
+
why: "The selected production app does not have a promoted version that can receive a custom domain.",
|
|
1137
|
+
fix: "Deploy the app to the production branch, then rerun the domain command.",
|
|
1138
|
+
debug: formatDebugDetails(error),
|
|
1139
|
+
exitCode: 1,
|
|
1140
|
+
nextSteps: ["prisma-cli app deploy --branch production", `prisma-cli app domain add ${hostname}`]
|
|
1141
|
+
});
|
|
1142
|
+
if ((command === "show" || command === "remove" || command === "retry" || command === "wait") && error.status === 404) return domainNotFoundError(hostname);
|
|
1143
|
+
if (command === "retry" && error.status === 409) return new CliError({
|
|
1144
|
+
code: "DOMAIN_RETRY_NOT_ELIGIBLE",
|
|
1145
|
+
domain: "app",
|
|
1146
|
+
summary: `Custom domain "${hostname}" is not eligible for retry`,
|
|
1147
|
+
why: error.message,
|
|
1148
|
+
fix: "Wait for the current verification or TLS step to finish, then rerun retry if the domain fails.",
|
|
1149
|
+
debug: formatDebugDetails(error),
|
|
1150
|
+
exitCode: 1,
|
|
1151
|
+
nextSteps: [`prisma-cli app domain show ${hostname}`]
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
979
1154
|
return new CliError({
|
|
980
1155
|
code: "DEPLOY_FAILED",
|
|
981
1156
|
domain: "app",
|
|
@@ -1128,6 +1303,25 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
|
1128
1303
|
firstDeploy: options.firstDeploy
|
|
1129
1304
|
};
|
|
1130
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
|
+
}
|
|
1131
1325
|
const inferredName = await options.inferName();
|
|
1132
1326
|
const matches = findAppsByName(apps, inferredName.name);
|
|
1133
1327
|
if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, inferredName.name, options.firstDeploy);
|
|
@@ -1377,14 +1571,15 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
|
|
|
1377
1571
|
};
|
|
1378
1572
|
}
|
|
1379
1573
|
async function resolveProjectContext(context, client, explicitProject, options) {
|
|
1380
|
-
const
|
|
1381
|
-
if (!workspace) throw workspaceRequiredError();
|
|
1574
|
+
const authState = await requireAuthenticatedAuthState(context);
|
|
1575
|
+
if (!authState.workspace) throw workspaceRequiredError();
|
|
1382
1576
|
const resolvedResult = await resolveProjectTarget({
|
|
1383
1577
|
context,
|
|
1384
|
-
workspace,
|
|
1578
|
+
workspace: authState.workspace,
|
|
1385
1579
|
explicitProject,
|
|
1386
1580
|
envProjectId: options?.envProjectId,
|
|
1387
|
-
|
|
1581
|
+
projectDir: options?.projectDir,
|
|
1582
|
+
listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
|
|
1388
1583
|
commandName: options?.commandName
|
|
1389
1584
|
});
|
|
1390
1585
|
if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
|
|
@@ -1403,16 +1598,8 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1403
1598
|
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
1404
1599
|
if (!workspace) throw workspaceRequiredError();
|
|
1405
1600
|
const branch = options.branch ?? await resolveDeployBranch(context, void 0);
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
async function resolveDeployProjectSetup(context, provider, workspace, projects, explicitProject, options) {
|
|
1409
|
-
const selected = await resolveNonInteractiveDeployProjectSetup(context, provider, workspace, projects, explicitProject, options);
|
|
1410
|
-
if (selected) return selected;
|
|
1411
|
-
if (canPrompt(context) && !context.flags.yes) return resolveInteractiveDeployProjectSetup(context, provider, workspace, projects);
|
|
1412
|
-
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1413
|
-
}
|
|
1414
|
-
async function resolveNonInteractiveDeployProjectSetup(context, provider, workspace, projects, explicitProject, options) {
|
|
1415
|
-
if (explicitProject) return {
|
|
1601
|
+
const projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
|
|
1602
|
+
if (explicitProject) return withRemoteDeployBranch(provider, {
|
|
1416
1603
|
workspace,
|
|
1417
1604
|
project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
|
|
1418
1605
|
resolution: {
|
|
@@ -1421,11 +1608,11 @@ async function resolveNonInteractiveDeployProjectSetup(context, provider, worksp
|
|
|
1421
1608
|
targetNameSource: "explicit"
|
|
1422
1609
|
},
|
|
1423
1610
|
localPinAction: "linked"
|
|
1424
|
-
};
|
|
1611
|
+
}, branch, context.runtime.signal);
|
|
1425
1612
|
if (options.createProjectName) {
|
|
1426
1613
|
const projectName = options.createProjectName.trim();
|
|
1427
1614
|
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1428
|
-
return {
|
|
1615
|
+
return withRemoteDeployBranch(provider, {
|
|
1429
1616
|
workspace,
|
|
1430
1617
|
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
|
|
1431
1618
|
resolution: {
|
|
@@ -1434,12 +1621,12 @@ async function resolveNonInteractiveDeployProjectSetup(context, provider, worksp
|
|
|
1434
1621
|
targetNameSource: "explicit"
|
|
1435
1622
|
},
|
|
1436
1623
|
localPinAction: "created"
|
|
1437
|
-
};
|
|
1624
|
+
}, branch, context.runtime.signal);
|
|
1438
1625
|
}
|
|
1439
1626
|
if (options.envProjectId) {
|
|
1440
1627
|
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
1441
1628
|
if (!project) throw projectNotFoundError(options.envProjectId, workspace);
|
|
1442
|
-
return {
|
|
1629
|
+
return withRemoteDeployBranch(provider, {
|
|
1443
1630
|
workspace,
|
|
1444
1631
|
project: toProjectSummary(project),
|
|
1445
1632
|
resolution: {
|
|
@@ -1447,14 +1634,14 @@ async function resolveNonInteractiveDeployProjectSetup(context, provider, worksp
|
|
|
1447
1634
|
targetName: options.envProjectId,
|
|
1448
1635
|
targetNameSource: "env"
|
|
1449
1636
|
}
|
|
1450
|
-
};
|
|
1637
|
+
}, branch, context.runtime.signal);
|
|
1451
1638
|
}
|
|
1452
1639
|
const localPin = options.localPin;
|
|
1453
1640
|
if (localPin.kind === "present") {
|
|
1454
1641
|
if (localPin.pin.workspaceId !== workspace.id) throw localResolutionPinStaleError();
|
|
1455
1642
|
const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
|
|
1456
1643
|
if (!project) throw localResolutionPinStaleError();
|
|
1457
|
-
return {
|
|
1644
|
+
return withRemoteDeployBranch(provider, {
|
|
1458
1645
|
workspace,
|
|
1459
1646
|
project: toProjectSummary(project),
|
|
1460
1647
|
resolution: {
|
|
@@ -1462,10 +1649,10 @@ async function resolveNonInteractiveDeployProjectSetup(context, provider, worksp
|
|
|
1462
1649
|
targetName: project.name,
|
|
1463
1650
|
targetNameSource: "local-pin"
|
|
1464
1651
|
}
|
|
1465
|
-
};
|
|
1652
|
+
}, branch, context.runtime.signal);
|
|
1466
1653
|
}
|
|
1467
1654
|
const platformMapping = await resolveDurablePlatformMapping();
|
|
1468
|
-
if (platformMapping && platformMapping.workspace.id === workspace.id) return {
|
|
1655
|
+
if (platformMapping && platformMapping.workspace.id === workspace.id) return withRemoteDeployBranch(provider, {
|
|
1469
1656
|
workspace,
|
|
1470
1657
|
project: toProjectSummary(platformMapping),
|
|
1471
1658
|
resolution: {
|
|
@@ -1473,8 +1660,9 @@ async function resolveNonInteractiveDeployProjectSetup(context, provider, worksp
|
|
|
1473
1660
|
targetName: platformMapping.name,
|
|
1474
1661
|
targetNameSource: "platform-mapping"
|
|
1475
1662
|
}
|
|
1476
|
-
};
|
|
1477
|
-
return
|
|
1663
|
+
}, branch, context.runtime.signal);
|
|
1664
|
+
if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch, context.runtime.signal);
|
|
1665
|
+
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1478
1666
|
}
|
|
1479
1667
|
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
|
|
1480
1668
|
const setup = await promptForProjectSetupChoice({
|
|
@@ -1587,22 +1775,130 @@ async function resolveDeployBranch(context, explicitBranchName) {
|
|
|
1587
1775
|
annotation: "default"
|
|
1588
1776
|
};
|
|
1589
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
|
+
}
|
|
1590
1886
|
async function resolveDeployFramework(context, options) {
|
|
1591
|
-
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
|
|
1887
|
+
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, options.requestedFrameworkAnnotation ?? "set by --framework");
|
|
1592
1888
|
if (options.entrypoint) return {
|
|
1593
1889
|
key: "bun",
|
|
1594
1890
|
buildType: "bun",
|
|
1595
1891
|
displayName: "Bun",
|
|
1596
|
-
annotation: "set by --entry"
|
|
1892
|
+
annotation: options.entrypointAnnotation ?? "set by --entry"
|
|
1597
1893
|
};
|
|
1598
|
-
const detected = await detectDeployFramework(
|
|
1894
|
+
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
1599
1895
|
if (detected) return detected;
|
|
1600
|
-
throw frameworkNotDetectedError(
|
|
1896
|
+
throw frameworkNotDetectedError(options.appDir);
|
|
1601
1897
|
}
|
|
1602
|
-
function resolveDeployRuntime(requestedHttpPort, framework) {
|
|
1898
|
+
function resolveDeployRuntime(requestedHttpPort, requestedHttpPortAnnotation, framework) {
|
|
1603
1899
|
if (requestedHttpPort) return {
|
|
1604
1900
|
port: parseDeployHttpPort(requestedHttpPort),
|
|
1605
|
-
annotation: "set by --http-port"
|
|
1901
|
+
annotation: requestedHttpPortAnnotation ?? "set by --http-port"
|
|
1606
1902
|
};
|
|
1607
1903
|
return {
|
|
1608
1904
|
port: FRAMEWORK_DEFAULT_HTTP_PORT,
|
|
@@ -1617,8 +1913,8 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
|
|
|
1617
1913
|
if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
|
|
1618
1914
|
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
|
|
1619
1915
|
if (packageEntrypoint) return packageEntrypoint;
|
|
1620
|
-
|
|
1621
|
-
|
|
1916
|
+
const defaultEntrypoint = frameworkFromAlias(framework.key)?.defaultEntrypoint;
|
|
1917
|
+
if (!defaultEntrypoint) return;
|
|
1622
1918
|
signal.throwIfAborted();
|
|
1623
1919
|
try {
|
|
1624
1920
|
await access(path.join(cwd, defaultEntrypoint));
|
|
@@ -1632,34 +1928,22 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
|
|
|
1632
1928
|
}
|
|
1633
1929
|
async function detectDeployFramework(cwd, signal) {
|
|
1634
1930
|
const packageJson = await readBunPackageJson(cwd, signal);
|
|
1635
|
-
const
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
};
|
|
1648
|
-
if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
|
|
1649
|
-
key: "tanstack-start",
|
|
1650
|
-
buildType: "tanstack-start",
|
|
1651
|
-
displayName: "TanStack Start",
|
|
1652
|
-
annotation: "detected from package.json"
|
|
1653
|
-
};
|
|
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
|
+
}
|
|
1654
1943
|
return null;
|
|
1655
1944
|
}
|
|
1656
|
-
async function
|
|
1657
|
-
for (const candidate of
|
|
1658
|
-
"next.config.js",
|
|
1659
|
-
"next.config.mjs",
|
|
1660
|
-
"next.config.ts",
|
|
1661
|
-
"next.config.mts"
|
|
1662
|
-
]) {
|
|
1945
|
+
async function detectFrameworkConfigFile(cwd, framework, signal) {
|
|
1946
|
+
for (const candidate of framework.detectConfigFiles) {
|
|
1663
1947
|
const filePath = path.join(cwd, candidate);
|
|
1664
1948
|
signal.throwIfAborted();
|
|
1665
1949
|
try {
|
|
@@ -1669,7 +1953,8 @@ async function detectNextConfig(cwd, signal) {
|
|
|
1669
1953
|
});
|
|
1670
1954
|
return {
|
|
1671
1955
|
exists: true,
|
|
1672
|
-
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
|
|
1956
|
+
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content),
|
|
1957
|
+
path: filePath
|
|
1673
1958
|
};
|
|
1674
1959
|
} catch (error) {
|
|
1675
1960
|
if (signal.aborted) throw error;
|
|
@@ -1678,7 +1963,8 @@ async function detectNextConfig(cwd, signal) {
|
|
|
1678
1963
|
}
|
|
1679
1964
|
return {
|
|
1680
1965
|
exists: false,
|
|
1681
|
-
standalone: false
|
|
1966
|
+
standalone: false,
|
|
1967
|
+
path: null
|
|
1682
1968
|
};
|
|
1683
1969
|
}
|
|
1684
1970
|
function hasPackageDependency(packageJson, dependencyName) {
|
|
@@ -1691,48 +1977,41 @@ function hasDependency(dependencies, dependencyName) {
|
|
|
1691
1977
|
return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
|
|
1692
1978
|
}
|
|
1693
1979
|
function frameworkFromUserFacingValue(value, annotation) {
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
}
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
key: "tanstack-start",
|
|
1720
|
-
buildType: "tanstack-start",
|
|
1721
|
-
displayName: "TanStack Start",
|
|
1722
|
-
annotation
|
|
1723
|
-
};
|
|
1724
|
-
default: throw frameworkNotDetectedError(void 0, value);
|
|
1725
|
-
}
|
|
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
|
+
});
|
|
1726
2005
|
}
|
|
1727
2006
|
function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
1728
|
-
const supported =
|
|
2007
|
+
const supported = FRAMEWORKS.map((framework) => framework.displayName).join(", ");
|
|
1729
2008
|
const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
|
|
1730
2009
|
return new CliError({
|
|
1731
2010
|
code: "FRAMEWORK_NOT_DETECTED",
|
|
1732
2011
|
domain: "app",
|
|
1733
2012
|
summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
|
|
1734
2013
|
why: `Supported Beta frameworks: ${supported}.`,
|
|
1735
|
-
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.`,
|
|
1736
2015
|
exitCode: 2,
|
|
1737
2016
|
nextSteps: [
|
|
1738
2017
|
"prisma-cli app deploy --framework nextjs",
|
|
@@ -1745,14 +2024,14 @@ function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
|
1745
2024
|
}
|
|
1746
2025
|
async function maybeRenderDeploySetupBlock(context, details) {
|
|
1747
2026
|
if (context.flags.json || context.flags.quiet) return;
|
|
1748
|
-
const directory =
|
|
2027
|
+
const directory = formatAppDirectoryLabel(context.runtime.cwd, details.appDir);
|
|
1749
2028
|
const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
|
|
1750
2029
|
context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
|
|
1751
2030
|
}
|
|
1752
2031
|
function maybeRenderDeployBuildSettings(context, resolution) {
|
|
1753
2032
|
if (context.flags.json || context.flags.quiet) return;
|
|
1754
2033
|
const settings = resolution.settings;
|
|
1755
|
-
const title = resolution.status === "
|
|
2034
|
+
const title = resolution.status === "config" ? `Using ${resolution.relativeConfigPath}` : "Build settings";
|
|
1756
2035
|
context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [{
|
|
1757
2036
|
label: "Build Command",
|
|
1758
2037
|
value: settings.buildCommand ?? "none",
|
|
@@ -1789,9 +2068,9 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1789
2068
|
input: context.runtime.stdin,
|
|
1790
2069
|
output: context.runtime.stderr,
|
|
1791
2070
|
message: `Framework (${options.framework.displayName})`,
|
|
1792
|
-
choices:
|
|
1793
|
-
label:
|
|
1794
|
-
value: framework
|
|
2071
|
+
choices: FRAMEWORKS.map((framework) => ({
|
|
2072
|
+
label: framework.displayName,
|
|
2073
|
+
value: framework.key
|
|
1795
2074
|
}))
|
|
1796
2075
|
}), "set by you");
|
|
1797
2076
|
const requestedPort = await textPrompt({
|
|
@@ -1834,14 +2113,6 @@ function maybeRenderDeploySettingsPreview(context, options) {
|
|
|
1834
2113
|
value: `HTTP ${options.runtime.port}`
|
|
1835
2114
|
}]).join("\n")}\n\n`);
|
|
1836
2115
|
}
|
|
1837
|
-
function frameworkDisplayName(framework) {
|
|
1838
|
-
switch (framework) {
|
|
1839
|
-
case "nextjs": return "Next.js";
|
|
1840
|
-
case "hono": return "Hono";
|
|
1841
|
-
case "tanstack-start": return "TanStack Start";
|
|
1842
|
-
case "bun": return "Bun";
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
2116
|
function validateDeployHttpPortText(value) {
|
|
1846
2117
|
if (!value?.trim()) return;
|
|
1847
2118
|
try {
|
|
@@ -1855,6 +2126,11 @@ function formatDeployDirectory(cwd) {
|
|
|
1855
2126
|
const basename = path.basename(cwd);
|
|
1856
2127
|
return basename ? `./${basename}` : ".";
|
|
1857
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
|
+
}
|
|
1858
2134
|
async function readCurrentWorkspaceId(context) {
|
|
1859
2135
|
const state = await context.stateStore.read();
|
|
1860
2136
|
if (state.auth?.workspaceId) return state.auth.workspaceId;
|
|
@@ -1874,15 +2150,25 @@ function getBuildTypeExamples(commandName) {
|
|
|
1874
2150
|
});
|
|
1875
2151
|
}
|
|
1876
2152
|
function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
|
|
1877
|
-
if (buildType !== "auto" && buildType
|
|
2153
|
+
if (buildType !== "auto" && !ENTRYPOINT_BUILD_TYPES.includes(buildType) && entrypoint) {
|
|
1878
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");
|
|
1879
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");
|
|
1880
2156
|
}
|
|
1881
2157
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
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");
|
|
1886
2172
|
}
|
|
1887
2173
|
function parseLocalPort(requestedPort) {
|
|
1888
2174
|
if (!requestedPort) return DEFAULT_LOCAL_DEV_PORT;
|
|
@@ -1918,7 +2204,41 @@ function deployFailedError(summary, error, nextSteps) {
|
|
|
1918
2204
|
function appDeployFailedError(error, progress) {
|
|
1919
2205
|
const why = error instanceof Error ? error.message : String(error);
|
|
1920
2206
|
const debug = formatDebugDetails(error);
|
|
1921
|
-
if (progress.buildStarted && !progress.buildCompleted)
|
|
2207
|
+
if (progress.buildStarted && !progress.buildCompleted) {
|
|
2208
|
+
const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
|
|
2209
|
+
const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
|
|
2210
|
+
const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
|
|
2211
|
+
const nextActions = standaloneOutputFailure ? [{
|
|
2212
|
+
kind: "edit-file",
|
|
2213
|
+
journey: "deploy-app",
|
|
2214
|
+
label: "Add Next.js standalone output",
|
|
2215
|
+
reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
|
|
2216
|
+
}, {
|
|
2217
|
+
kind: "run-command",
|
|
2218
|
+
journey: "deploy-app",
|
|
2219
|
+
label: "Rerun deploy",
|
|
2220
|
+
command: "prisma-cli app deploy"
|
|
2221
|
+
}] : [];
|
|
2222
|
+
return new CliError({
|
|
2223
|
+
code: "BUILD_FAILED",
|
|
2224
|
+
domain: "app",
|
|
2225
|
+
summary: "Build failed locally.",
|
|
2226
|
+
why,
|
|
2227
|
+
fix,
|
|
2228
|
+
debug,
|
|
2229
|
+
meta: { phase: "build" },
|
|
2230
|
+
humanLines: [
|
|
2231
|
+
"Build failed locally.",
|
|
2232
|
+
"",
|
|
2233
|
+
`✗ Built ${why}`,
|
|
2234
|
+
"",
|
|
2235
|
+
`Fix: ${fix}`
|
|
2236
|
+
],
|
|
2237
|
+
exitCode: 1,
|
|
2238
|
+
nextSteps,
|
|
2239
|
+
nextActions
|
|
2240
|
+
});
|
|
2241
|
+
}
|
|
1922
2242
|
if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
|
|
1923
2243
|
const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
|
|
1924
2244
|
const recoveryLines = progress.versionId ? ["See what happened", `prisma-cli app logs --deployment ${progress.versionId}`] : ["Fix", "Retry the command, or rerun with --trace for more detailed diagnostics."];
|
|
@@ -1959,41 +2279,6 @@ function appDeployFailedError(error, progress) {
|
|
|
1959
2279
|
nextSteps: []
|
|
1960
2280
|
});
|
|
1961
2281
|
}
|
|
1962
|
-
function appBuildFailedError(why, debug) {
|
|
1963
|
-
const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
|
|
1964
|
-
const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
|
|
1965
|
-
const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
|
|
1966
|
-
const nextActions = standaloneOutputFailure ? [{
|
|
1967
|
-
kind: "edit-file",
|
|
1968
|
-
journey: "deploy-app",
|
|
1969
|
-
label: "Add Next.js standalone output",
|
|
1970
|
-
reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
|
|
1971
|
-
}, {
|
|
1972
|
-
kind: "run-command",
|
|
1973
|
-
journey: "deploy-app",
|
|
1974
|
-
label: "Rerun deploy",
|
|
1975
|
-
command: "prisma-cli app deploy"
|
|
1976
|
-
}] : [];
|
|
1977
|
-
return new CliError({
|
|
1978
|
-
code: "BUILD_FAILED",
|
|
1979
|
-
domain: "app",
|
|
1980
|
-
summary: "Build failed locally.",
|
|
1981
|
-
why,
|
|
1982
|
-
fix,
|
|
1983
|
-
debug,
|
|
1984
|
-
meta: { phase: "build" },
|
|
1985
|
-
humanLines: [
|
|
1986
|
-
"Build failed locally.",
|
|
1987
|
-
"",
|
|
1988
|
-
`✗ Built ${why}`,
|
|
1989
|
-
"",
|
|
1990
|
-
`Fix: ${fix}`
|
|
1991
|
-
],
|
|
1992
|
-
exitCode: 1,
|
|
1993
|
-
nextSteps,
|
|
1994
|
-
nextActions
|
|
1995
|
-
});
|
|
1996
|
-
}
|
|
1997
2282
|
function localResolutionPinStaleError() {
|
|
1998
2283
|
return new CliError({
|
|
1999
2284
|
code: "LOCAL_STATE_STALE",
|