@prisma/cli 3.0.0-alpha.6 → 3.0.0-alpha.7
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/local-state.js +1 -1
- package/dist/commands/app/index.js +9 -1
- package/dist/controllers/app.js +629 -54
- package/dist/lib/app/bun-project.js +1 -1
- package/dist/lib/app/local-dev.js +1 -1
- package/dist/lib/app/preview-build.js +1 -1
- package/dist/lib/app/preview-interaction.js +2 -35
- package/dist/lib/app/preview-provider.js +110 -22
- package/dist/lib/auth/client.js +1 -1
- package/dist/lib/project/local-pin.js +51 -0
- package/dist/lib/project/resolution.js +71 -21
- package/dist/presenters/app.js +5 -2
- package/dist/presenters/project.js +3 -0
- package/dist/shell/command-meta.js +4 -4
- package/dist/shell/prompt.js +12 -2
- package/package.json +1 -1
package/dist/controllers/app.js
CHANGED
|
@@ -4,21 +4,33 @@ import { CliError, authRequiredError, featureUnavailableError, usageError, works
|
|
|
4
4
|
import { renderCommandHeader } from "../shell/ui.js";
|
|
5
5
|
import { writeJsonEvent } from "../shell/output.js";
|
|
6
6
|
import { canPrompt } from "../shell/runtime.js";
|
|
7
|
-
import { textPrompt } from "../shell/prompt.js";
|
|
7
|
+
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
8
8
|
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
9
9
|
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
10
10
|
import { parseEnvAssignments } from "../lib/app/env-vars.js";
|
|
11
|
+
import { readBunPackageJson } from "../lib/app/bun-project.js";
|
|
11
12
|
import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
|
|
12
|
-
import { resolveProjectTarget } from "../lib/project/resolution.js";
|
|
13
|
+
import { inferTargetName, projectNotFoundError, resolveProjectTarget } from "../lib/project/resolution.js";
|
|
14
|
+
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, readLocalResolutionPin, writeLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
13
15
|
import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
|
|
14
|
-
import { PREVIEW_DEFAULT_REGION
|
|
16
|
+
import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
|
|
15
17
|
import { createPreviewDeployProgress, createPreviewPromoteProgress, createPreviewUpdateEnvProgress } from "../lib/app/preview-progress.js";
|
|
16
18
|
import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
17
19
|
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
18
20
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
19
21
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
22
|
+
import { access, readFile } from "node:fs/promises";
|
|
23
|
+
import path from "node:path";
|
|
20
24
|
import open from "open";
|
|
21
25
|
//#region src/controllers/app.ts
|
|
26
|
+
const DEPLOY_FRAMEWORKS = [
|
|
27
|
+
"nextjs",
|
|
28
|
+
"hono",
|
|
29
|
+
"tanstack-start"
|
|
30
|
+
];
|
|
31
|
+
const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
|
|
32
|
+
const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
|
|
33
|
+
const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
|
|
22
34
|
function isRealMode(context) {
|
|
23
35
|
return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
24
36
|
}
|
|
@@ -80,15 +92,65 @@ async function runAppRun(context, entrypoint, requestedBuildType, requestedPort)
|
|
|
80
92
|
}
|
|
81
93
|
async function runAppDeploy(context, appName, options) {
|
|
82
94
|
ensurePreviewAppMode(context);
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
const
|
|
95
|
+
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
96
|
+
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
97
|
+
const skipLocalPin = Boolean(envProjectId || envAppId);
|
|
98
|
+
const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
|
|
99
|
+
if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
|
|
100
|
+
const explicitBuildType = Boolean(options?.buildType && options.buildType !== "auto");
|
|
101
|
+
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
102
|
+
if (options?.httpPort) parseDeployHttpPort(options.httpPort);
|
|
103
|
+
let framework = await resolveDeployFramework(context, {
|
|
104
|
+
requestedFramework: options?.framework,
|
|
105
|
+
requestedBuildType: options?.buildType,
|
|
106
|
+
explicitBuildType
|
|
107
|
+
});
|
|
108
|
+
let runtime = resolveDeployRuntime(options?.httpPort, framework);
|
|
109
|
+
assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
|
|
86
110
|
const envVars = toOptionalEnvVars(parseEnvAssignments(options?.envAssignments, { commandName: "deploy" }));
|
|
87
|
-
const
|
|
88
|
-
const
|
|
111
|
+
const firstDeploy = !skipLocalPin && localPin.kind === "missing";
|
|
112
|
+
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
113
|
+
allowCreate: true,
|
|
114
|
+
branch,
|
|
115
|
+
envProjectId,
|
|
116
|
+
localPin
|
|
117
|
+
});
|
|
118
|
+
const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
119
|
+
explicitAppName: appName,
|
|
120
|
+
explicitAppId: envAppId,
|
|
121
|
+
firstDeploy,
|
|
122
|
+
inferName: () => inferTargetName(context.runtime.cwd)
|
|
123
|
+
});
|
|
124
|
+
await maybeRenderDeploySetupBlock(context, {
|
|
125
|
+
firstDeploy: selectedApp.firstDeploy,
|
|
126
|
+
showSubsequentAnnotations: skipLocalPin,
|
|
127
|
+
workspaceName: target.workspace.name,
|
|
128
|
+
projectName: target.project.name,
|
|
129
|
+
projectAnnotation: annotationForProjectResolution(target.resolution),
|
|
130
|
+
branchName: target.branch.name,
|
|
131
|
+
branchAnnotation: branch.annotation,
|
|
132
|
+
appName: selectedApp.displayName,
|
|
133
|
+
appAnnotation: selectedApp.annotation,
|
|
134
|
+
framework,
|
|
135
|
+
runtime
|
|
136
|
+
});
|
|
137
|
+
const customized = await maybeCustomizeDeploySettings(context, {
|
|
138
|
+
framework,
|
|
139
|
+
runtime,
|
|
140
|
+
firstDeploy: selectedApp.firstDeploy,
|
|
141
|
+
explicitFramework: Boolean(options?.framework),
|
|
142
|
+
explicitBuildType,
|
|
143
|
+
explicitHttpPort: Boolean(options?.httpPort)
|
|
144
|
+
});
|
|
145
|
+
framework = customized.framework;
|
|
146
|
+
runtime = customized.runtime;
|
|
147
|
+
const buildType = framework.buildType;
|
|
148
|
+
assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
|
|
149
|
+
const portMapping = parseDeployPortMapping(String(runtime.port));
|
|
89
150
|
const deployResult = await provider.deployApp({
|
|
90
151
|
cwd: context.runtime.cwd,
|
|
91
152
|
projectId,
|
|
153
|
+
branchName: target.branch.name,
|
|
92
154
|
appId: selectedApp.appId,
|
|
93
155
|
appName: selectedApp.appName,
|
|
94
156
|
region: selectedApp.region,
|
|
@@ -96,7 +158,7 @@ async function runAppDeploy(context, appName, options) {
|
|
|
96
158
|
buildType,
|
|
97
159
|
portMapping,
|
|
98
160
|
envVars,
|
|
99
|
-
interaction:
|
|
161
|
+
interaction: void 0,
|
|
100
162
|
progress: createPreviewDeployProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
101
163
|
}).catch((error) => {
|
|
102
164
|
throw deployFailedError("App deploy failed", error, ["prisma-cli app list-deploys"]);
|
|
@@ -106,6 +168,14 @@ async function runAppDeploy(context, appName, options) {
|
|
|
106
168
|
name: deployResult.app.name
|
|
107
169
|
});
|
|
108
170
|
await context.stateStore.setKnownLiveDeployment(projectId, deployResult.app.id, deployResult.deployment.id);
|
|
171
|
+
const shouldWriteLocalPin = firstDeploy && !skipLocalPin;
|
|
172
|
+
if (shouldWriteLocalPin) {
|
|
173
|
+
await writeLocalResolutionPin(context.runtime.cwd, {
|
|
174
|
+
workspaceId: target.workspace.id,
|
|
175
|
+
projectId: target.project.id
|
|
176
|
+
});
|
|
177
|
+
await ensureLocalResolutionPinGitignore(context.runtime.cwd);
|
|
178
|
+
}
|
|
109
179
|
return {
|
|
110
180
|
command: "app.deploy",
|
|
111
181
|
result: {
|
|
@@ -117,7 +187,11 @@ async function runAppDeploy(context, appName, options) {
|
|
|
117
187
|
id: deployResult.app.id,
|
|
118
188
|
name: deployResult.app.name
|
|
119
189
|
},
|
|
120
|
-
deployment: deployResult.deployment
|
|
190
|
+
deployment: deployResult.deployment,
|
|
191
|
+
localPin: shouldWriteLocalPin ? {
|
|
192
|
+
path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
193
|
+
written: true
|
|
194
|
+
} : void 0
|
|
121
195
|
},
|
|
122
196
|
warnings: [],
|
|
123
197
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
|
|
@@ -130,8 +204,8 @@ async function runAppUpdateEnv(context, appName, envAssignments, projectRef) {
|
|
|
130
204
|
commandName: "update-env",
|
|
131
205
|
requireAtLeastOne: true
|
|
132
206
|
});
|
|
133
|
-
const { provider, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
134
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId), appName);
|
|
207
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
208
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
135
209
|
if (!selectedApp) throw noDeploymentsError("No deployments available to update environment variables", "The resolved project does not have any deployed app yet.");
|
|
136
210
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
137
211
|
throw deployFailedError("Failed to inspect app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -168,8 +242,8 @@ async function runAppUpdateEnv(context, appName, envAssignments, projectRef) {
|
|
|
168
242
|
async function runAppListEnv(context, appName, projectRef) {
|
|
169
243
|
ensurePreviewAppMode(context);
|
|
170
244
|
emitLegacyEnvDeprecationWarning(context, "app list-env", "project env list");
|
|
171
|
-
const { provider, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
172
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId), appName);
|
|
245
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
246
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
173
247
|
if (!selectedApp) return {
|
|
174
248
|
command: "app.list-env",
|
|
175
249
|
result: {
|
|
@@ -255,8 +329,8 @@ async function runAppListEnv(context, appName, projectRef) {
|
|
|
255
329
|
}
|
|
256
330
|
async function runAppListDeploys(context, appName, projectRef) {
|
|
257
331
|
ensurePreviewAppMode(context);
|
|
258
|
-
const { provider, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
259
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId), appName);
|
|
332
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
333
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
260
334
|
if (!selectedApp) return {
|
|
261
335
|
command: "app.list-deploys",
|
|
262
336
|
result: {
|
|
@@ -292,8 +366,8 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
292
366
|
}
|
|
293
367
|
async function runAppShow(context, appName, projectRef) {
|
|
294
368
|
ensurePreviewAppMode(context);
|
|
295
|
-
const { provider, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
296
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId), appName);
|
|
369
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
370
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
297
371
|
if (!selectedApp) return {
|
|
298
372
|
command: "app.show",
|
|
299
373
|
result: {
|
|
@@ -368,8 +442,8 @@ async function runAppShowDeploy(context, deploymentId) {
|
|
|
368
442
|
}
|
|
369
443
|
async function runAppOpen(context, appName, projectRef) {
|
|
370
444
|
ensurePreviewAppMode(context);
|
|
371
|
-
const { provider, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
372
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId), appName);
|
|
445
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
446
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
373
447
|
if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
|
|
374
448
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
375
449
|
throw deployFailedError("Failed to resolve app URL", error, ["prisma-cli app show"]);
|
|
@@ -402,8 +476,8 @@ async function runAppOpen(context, appName, projectRef) {
|
|
|
402
476
|
}
|
|
403
477
|
async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
404
478
|
ensurePreviewAppMode(context);
|
|
405
|
-
const { provider, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
406
|
-
const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, appName);
|
|
479
|
+
const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
480
|
+
const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
|
|
407
481
|
if (!context.flags.json && !context.flags.quiet) {
|
|
408
482
|
const lines = renderCommandHeader(context.ui, {
|
|
409
483
|
commandLabel: "app logs",
|
|
@@ -433,9 +507,9 @@ async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
|
433
507
|
throw deployFailedError("Failed to stream app logs", error, [`prisma-cli app show-deploy ${target.deployment.id}`, "prisma-cli app list-deploys"]);
|
|
434
508
|
});
|
|
435
509
|
}
|
|
436
|
-
async function resolveExplicitLogDeployment(context, provider, projectId, appName, deploymentId) {
|
|
510
|
+
async function resolveExplicitLogDeployment(context, provider, projectId, branchName, appName, deploymentId) {
|
|
437
511
|
if (appName) {
|
|
438
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId), appName);
|
|
512
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
439
513
|
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
440
514
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
441
515
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -471,7 +545,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, appNam
|
|
|
471
545
|
exitCode: 1,
|
|
472
546
|
nextSteps: ["prisma-cli app list-deploys"]
|
|
473
547
|
});
|
|
474
|
-
const resolvedProjectApp = (await listApps(context, provider, projectId)).find((app) => app.id === shown.app?.id);
|
|
548
|
+
const resolvedProjectApp = (await listApps(context, provider, projectId, branchName)).find((app) => app.id === shown.app?.id);
|
|
475
549
|
if (!resolvedProjectApp) throw new CliError({
|
|
476
550
|
code: "DEPLOYMENT_NOT_FOUND",
|
|
477
551
|
domain: "app",
|
|
@@ -490,8 +564,8 @@ async function resolveExplicitLogDeployment(context, provider, projectId, appNam
|
|
|
490
564
|
deployment: shown.deployment
|
|
491
565
|
};
|
|
492
566
|
}
|
|
493
|
-
async function resolveLiveLogDeployment(context, provider, projectId, appName) {
|
|
494
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId), appName);
|
|
567
|
+
async function resolveLiveLogDeployment(context, provider, projectId, branchName, appName) {
|
|
568
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
495
569
|
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
496
570
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
497
571
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -526,8 +600,8 @@ function writeLogRecord(context, record) {
|
|
|
526
600
|
}
|
|
527
601
|
async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
528
602
|
ensurePreviewAppMode(context);
|
|
529
|
-
const { provider, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
530
|
-
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId), appName, "promote");
|
|
603
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
604
|
+
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
|
|
531
605
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
532
606
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
533
607
|
});
|
|
@@ -566,8 +640,8 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
566
640
|
}
|
|
567
641
|
async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
568
642
|
ensurePreviewAppMode(context);
|
|
569
|
-
const { provider, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
570
|
-
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId), appName, "rollback");
|
|
643
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
644
|
+
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
|
|
571
645
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
572
646
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
573
647
|
});
|
|
@@ -608,8 +682,8 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
608
682
|
}
|
|
609
683
|
async function runAppRemove(context, appName, projectRef) {
|
|
610
684
|
ensurePreviewAppMode(context);
|
|
611
|
-
const { provider, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
612
|
-
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId), appName, "remove");
|
|
685
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
686
|
+
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
|
|
613
687
|
await confirmAppRemoval(context, selectedApp);
|
|
614
688
|
const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
|
|
615
689
|
throw removeFailedError("Failed to remove app", error, ["prisma-cli app show", "prisma-cli app list-deploys"]);
|
|
@@ -629,30 +703,104 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
629
703
|
nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
|
|
630
704
|
};
|
|
631
705
|
}
|
|
632
|
-
async function
|
|
633
|
-
if (explicitAppName) {
|
|
634
|
-
const
|
|
706
|
+
async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
707
|
+
if (options.explicitAppName) {
|
|
708
|
+
const matches = findAppsByName(apps, options.explicitAppName);
|
|
709
|
+
if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, options.explicitAppName, options.firstDeploy);
|
|
710
|
+
const matched = matches[0];
|
|
635
711
|
if (matched) return {
|
|
636
712
|
appId: matched.id,
|
|
637
|
-
|
|
713
|
+
displayName: matched.name,
|
|
714
|
+
annotation: "set by --app",
|
|
715
|
+
firstDeploy: options.firstDeploy
|
|
638
716
|
};
|
|
639
717
|
return {
|
|
640
|
-
appName: explicitAppName,
|
|
718
|
+
appName: options.explicitAppName,
|
|
641
719
|
region: PREVIEW_DEFAULT_REGION,
|
|
642
|
-
|
|
720
|
+
displayName: options.explicitAppName,
|
|
721
|
+
annotation: "set by --app",
|
|
722
|
+
firstDeploy: options.firstDeploy
|
|
643
723
|
};
|
|
644
724
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
725
|
+
if (options.explicitAppId) {
|
|
726
|
+
const matched = apps.find((app) => app.id === options.explicitAppId);
|
|
727
|
+
if (!matched) throw usageError("Selected app does not exist in the resolved project", `The app "${options.explicitAppId}" from ${PRISMA_APP_ID_ENV_VAR} could not be found in resolved project "${projectId}".`, `Unset ${PRISMA_APP_ID_ENV_VAR}, pass --app <name>, or choose an app from prisma-cli app list-deploys.`, ["prisma-cli app list-deploys"], "app");
|
|
728
|
+
return {
|
|
649
729
|
appId: matched.id,
|
|
650
|
-
|
|
730
|
+
displayName: matched.name,
|
|
731
|
+
annotation: `from ${PRISMA_APP_ID_ENV_VAR}`,
|
|
732
|
+
firstDeploy: options.firstDeploy
|
|
651
733
|
};
|
|
652
|
-
if (!canPrompt(context)) throw usageError("Saved app selection is no longer available", "The locally selected app could not be found in the resolved project.", "Pass --app <name>, or rerun prisma-cli app deploy in a TTY to choose or create an app again.", ["prisma-cli app deploy"], "app");
|
|
653
734
|
}
|
|
654
|
-
|
|
655
|
-
|
|
735
|
+
const inferredName = await options.inferName();
|
|
736
|
+
const matches = findAppsByName(apps, inferredName.name);
|
|
737
|
+
if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, inferredName.name, options.firstDeploy);
|
|
738
|
+
const matched = matches[0];
|
|
739
|
+
if (matched) return {
|
|
740
|
+
appId: matched.id,
|
|
741
|
+
displayName: matched.name,
|
|
742
|
+
annotation: "existing app on this branch",
|
|
743
|
+
firstDeploy: options.firstDeploy
|
|
744
|
+
};
|
|
745
|
+
return {
|
|
746
|
+
appName: inferredName.name,
|
|
747
|
+
region: PREVIEW_DEFAULT_REGION,
|
|
748
|
+
displayName: inferredName.name,
|
|
749
|
+
annotation: inferredName.source === "package-name" ? "created from package.json" : "created from directory name",
|
|
750
|
+
firstDeploy: options.firstDeploy
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
async function resolveAmbiguousDeployApp(context, matches, targetName, firstDeploy) {
|
|
754
|
+
if (canPrompt(context)) {
|
|
755
|
+
const createNew = "__create_new_app__";
|
|
756
|
+
const cancel = "__cancel__";
|
|
757
|
+
const selected = await selectPrompt({
|
|
758
|
+
input: context.runtime.stdin,
|
|
759
|
+
output: context.runtime.stderr,
|
|
760
|
+
message: `Multiple apps are named "${targetName}"`,
|
|
761
|
+
choices: [
|
|
762
|
+
...sortApps(matches).map((app) => ({
|
|
763
|
+
label: `${app.name} (${app.id})`,
|
|
764
|
+
value: app
|
|
765
|
+
})),
|
|
766
|
+
{
|
|
767
|
+
label: `Create a new app named "${targetName}"`,
|
|
768
|
+
value: createNew
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
label: "Cancel",
|
|
772
|
+
value: cancel
|
|
773
|
+
}
|
|
774
|
+
]
|
|
775
|
+
});
|
|
776
|
+
if (selected === cancel) throw usageError("App selection canceled", "The command was canceled before an app was selected.", "Re-run the command and choose an app, or pass --app <name>.", ["prisma-cli app deploy --app <name>"], "app");
|
|
777
|
+
if (selected === createNew) return {
|
|
778
|
+
appName: targetName,
|
|
779
|
+
region: PREVIEW_DEFAULT_REGION,
|
|
780
|
+
displayName: targetName,
|
|
781
|
+
annotation: "created from package.json",
|
|
782
|
+
firstDeploy
|
|
783
|
+
};
|
|
784
|
+
return {
|
|
785
|
+
appId: selected.id,
|
|
786
|
+
displayName: selected.name,
|
|
787
|
+
annotation: "selected by you",
|
|
788
|
+
firstDeploy
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
throw new CliError({
|
|
792
|
+
code: "APP_AMBIGUOUS",
|
|
793
|
+
domain: "app",
|
|
794
|
+
summary: "App resolution is ambiguous",
|
|
795
|
+
why: `Multiple apps matched "${targetName}".`,
|
|
796
|
+
fix: "Pass --app <name> to choose the app explicitly.",
|
|
797
|
+
meta: { candidates: matches.map((app) => ({
|
|
798
|
+
id: app.id,
|
|
799
|
+
name: app.name
|
|
800
|
+
})) },
|
|
801
|
+
exitCode: 2,
|
|
802
|
+
nextSteps: ["prisma-cli app deploy --app <name>"]
|
|
803
|
+
});
|
|
656
804
|
}
|
|
657
805
|
async function resolveExistingAppSelection(context, projectId, apps, explicitAppName) {
|
|
658
806
|
if (explicitAppName) {
|
|
@@ -767,8 +915,8 @@ function resolveRollbackTarget(deployments, currentLiveDeploymentId) {
|
|
|
767
915
|
nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
|
|
768
916
|
});
|
|
769
917
|
}
|
|
770
|
-
async function listApps(context, provider, projectId) {
|
|
771
|
-
return provider.listApps(projectId).then(sortApps).catch((error) => {
|
|
918
|
+
async function listApps(context, provider, projectId, branchName) {
|
|
919
|
+
return provider.listApps(projectId, { branchName }).then(sortApps).catch((error) => {
|
|
772
920
|
if (isMissingProjectError(error)) throw new CliError({
|
|
773
921
|
code: "PROJECT_NOT_FOUND",
|
|
774
922
|
domain: "project",
|
|
@@ -819,6 +967,16 @@ async function requireProviderAndProjectContext(context, explicitProject, option
|
|
|
819
967
|
projectId: target.project.id
|
|
820
968
|
};
|
|
821
969
|
}
|
|
970
|
+
async function requireProviderAndDeployProjectContext(context, explicitProject, options) {
|
|
971
|
+
const { client, provider } = await requirePreviewAppProviderWithClient(context);
|
|
972
|
+
const target = await resolveDeployProjectContext(context, client, provider, explicitProject, options);
|
|
973
|
+
return {
|
|
974
|
+
client,
|
|
975
|
+
provider,
|
|
976
|
+
target,
|
|
977
|
+
projectId: target.project.id
|
|
978
|
+
};
|
|
979
|
+
}
|
|
822
980
|
async function resolveProjectContext(context, client, provider, explicitProject, options) {
|
|
823
981
|
const authState = await requireAuthenticatedAuthState(context);
|
|
824
982
|
if (!authState.workspace) throw workspaceRequiredError();
|
|
@@ -845,17 +1003,412 @@ async function resolveProjectContext(context, client, provider, explicitProject,
|
|
|
845
1003
|
prompt: createSelectPromptPort(context),
|
|
846
1004
|
remember: true
|
|
847
1005
|
});
|
|
848
|
-
const
|
|
1006
|
+
const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
|
|
849
1007
|
return {
|
|
850
1008
|
...resolved,
|
|
851
1009
|
branch: {
|
|
852
|
-
name:
|
|
853
|
-
kind: toBranchKind(
|
|
1010
|
+
name: branch.name,
|
|
1011
|
+
kind: toBranchKind(branch.name)
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
async function resolveDeployProjectContext(context, client, provider, explicitProject, options) {
|
|
1016
|
+
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
1017
|
+
if (!workspace) throw workspaceRequiredError();
|
|
1018
|
+
const branch = options.branch ?? await resolveDeployBranch(context, void 0);
|
|
1019
|
+
const projects = await listRealWorkspaceProjects(client, workspace);
|
|
1020
|
+
const createProject = options.allowCreate ? async (name) => {
|
|
1021
|
+
const project = await provider.createProject({ name }).catch((error) => {
|
|
1022
|
+
throw createProjectOnFirstDeployError({
|
|
1023
|
+
error,
|
|
1024
|
+
inferredName: name,
|
|
1025
|
+
workspaceName: workspace.name
|
|
1026
|
+
});
|
|
1027
|
+
});
|
|
1028
|
+
return {
|
|
1029
|
+
id: project.id,
|
|
1030
|
+
name: project.name,
|
|
1031
|
+
workspace
|
|
1032
|
+
};
|
|
1033
|
+
} : void 0;
|
|
1034
|
+
if (explicitProject) return withDeployBranch(await resolveProjectTarget({
|
|
1035
|
+
context,
|
|
1036
|
+
workspace,
|
|
1037
|
+
explicitProject,
|
|
1038
|
+
listProjects: async () => projects,
|
|
1039
|
+
createProject,
|
|
1040
|
+
allowCreate: options.allowCreate,
|
|
1041
|
+
prompt: createSelectPromptPort(context),
|
|
1042
|
+
remember: true
|
|
1043
|
+
}), branch);
|
|
1044
|
+
if (options.envProjectId) {
|
|
1045
|
+
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
1046
|
+
if (!project) throw projectNotFoundError(options.envProjectId, workspace);
|
|
1047
|
+
return withDeployBranch({
|
|
1048
|
+
workspace,
|
|
1049
|
+
project: toProjectSummary(project),
|
|
1050
|
+
resolution: {
|
|
1051
|
+
projectSource: "env",
|
|
1052
|
+
targetName: options.envProjectId,
|
|
1053
|
+
targetNameSource: "env"
|
|
1054
|
+
}
|
|
1055
|
+
}, branch);
|
|
1056
|
+
}
|
|
1057
|
+
if (options.localPin.kind === "present") {
|
|
1058
|
+
if (options.localPin.pin.workspaceId !== workspace.id) throw localResolutionPinStaleError();
|
|
1059
|
+
const project = projects.find((candidate) => candidate.id === options.localPin.pin.projectId);
|
|
1060
|
+
if (!project) throw localResolutionPinStaleError();
|
|
1061
|
+
return withDeployBranch({
|
|
1062
|
+
workspace,
|
|
1063
|
+
project: toProjectSummary(project),
|
|
1064
|
+
resolution: {
|
|
1065
|
+
projectSource: "local-pin",
|
|
1066
|
+
targetName: project.name,
|
|
1067
|
+
targetNameSource: "local-pin"
|
|
1068
|
+
}
|
|
1069
|
+
}, branch);
|
|
1070
|
+
}
|
|
1071
|
+
return withDeployBranch(await resolveProjectTarget({
|
|
1072
|
+
context,
|
|
1073
|
+
workspace,
|
|
1074
|
+
listProjects: async () => projects,
|
|
1075
|
+
createProject,
|
|
1076
|
+
allowCreate: options.allowCreate,
|
|
1077
|
+
prompt: createSelectPromptPort(context),
|
|
1078
|
+
remember: true
|
|
1079
|
+
}), branch);
|
|
1080
|
+
}
|
|
1081
|
+
function withDeployBranch(target, branch) {
|
|
1082
|
+
return {
|
|
1083
|
+
...target,
|
|
1084
|
+
branch: {
|
|
1085
|
+
name: branch.name,
|
|
1086
|
+
kind: toBranchKind(branch.name)
|
|
854
1087
|
}
|
|
855
1088
|
};
|
|
856
1089
|
}
|
|
1090
|
+
function toProjectSummary(project) {
|
|
1091
|
+
return {
|
|
1092
|
+
id: project.id,
|
|
1093
|
+
name: project.name
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
857
1096
|
function toBranchKind(name) {
|
|
858
|
-
return name === "production" ? "production" : "preview";
|
|
1097
|
+
return name === "production" || name === "main" ? "production" : "preview";
|
|
1098
|
+
}
|
|
1099
|
+
async function resolveDeployBranch(context, explicitBranchName) {
|
|
1100
|
+
if (explicitBranchName) return {
|
|
1101
|
+
name: explicitBranchName,
|
|
1102
|
+
annotation: "set by --branch"
|
|
1103
|
+
};
|
|
1104
|
+
const gitBranch = await readLocalGitBranch(context.runtime.cwd);
|
|
1105
|
+
if (gitBranch) return {
|
|
1106
|
+
name: gitBranch,
|
|
1107
|
+
annotation: "from local Git branch"
|
|
1108
|
+
};
|
|
1109
|
+
return {
|
|
1110
|
+
name: "main",
|
|
1111
|
+
annotation: "default"
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
async function readLocalGitBranch(cwd) {
|
|
1115
|
+
const headPath = await resolveGitHeadPath(path.join(cwd, ".git"));
|
|
1116
|
+
if (!headPath) return null;
|
|
1117
|
+
try {
|
|
1118
|
+
const head = (await readFile(headPath, "utf8")).trim();
|
|
1119
|
+
if (head.startsWith("ref: refs/heads/")) return head.slice(16);
|
|
1120
|
+
} catch {
|
|
1121
|
+
return null;
|
|
1122
|
+
}
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
async function resolveGitHeadPath(gitPath) {
|
|
1126
|
+
try {
|
|
1127
|
+
const raw = await readFile(gitPath, "utf8");
|
|
1128
|
+
if (raw.startsWith("gitdir:")) return path.join(path.resolve(path.dirname(gitPath), raw.slice(7).trim()), "HEAD");
|
|
1129
|
+
} catch {}
|
|
1130
|
+
try {
|
|
1131
|
+
await access(path.join(gitPath, "HEAD"));
|
|
1132
|
+
return path.join(gitPath, "HEAD");
|
|
1133
|
+
} catch {
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
async function resolveDeployFramework(context, options) {
|
|
1138
|
+
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
|
|
1139
|
+
if (options.explicitBuildType) {
|
|
1140
|
+
const buildType = normalizeBuildType(options.requestedBuildType);
|
|
1141
|
+
if (buildType !== "auto") return {
|
|
1142
|
+
key: buildType,
|
|
1143
|
+
buildType,
|
|
1144
|
+
displayName: formatBuildTypeName(buildType),
|
|
1145
|
+
annotation: "set by --build-type"
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
const detected = await detectDeployFramework(context.runtime.cwd);
|
|
1149
|
+
if (detected) return detected;
|
|
1150
|
+
throw frameworkNotDetectedError(context.runtime.cwd);
|
|
1151
|
+
}
|
|
1152
|
+
function resolveDeployRuntime(requestedHttpPort, framework) {
|
|
1153
|
+
if (requestedHttpPort) return {
|
|
1154
|
+
port: parseDeployHttpPort(requestedHttpPort),
|
|
1155
|
+
annotation: "set by --http-port"
|
|
1156
|
+
};
|
|
1157
|
+
return {
|
|
1158
|
+
port: FRAMEWORK_DEFAULT_HTTP_PORT,
|
|
1159
|
+
annotation: `${framework.displayName} default`
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
async function detectDeployFramework(cwd) {
|
|
1163
|
+
const packageJson = await readBunPackageJson(cwd);
|
|
1164
|
+
const nextConfig = await detectNextConfig(cwd);
|
|
1165
|
+
if (nextConfig.exists || hasPackageDependency(packageJson, "next")) return {
|
|
1166
|
+
key: "nextjs",
|
|
1167
|
+
buildType: "nextjs",
|
|
1168
|
+
displayName: "Next.js",
|
|
1169
|
+
annotation: nextConfig.standalone ? "standalone output detected" : nextConfig.exists ? "detected from next.config" : "detected from package.json"
|
|
1170
|
+
};
|
|
1171
|
+
if (hasPackageDependency(packageJson, "hono")) return {
|
|
1172
|
+
key: "hono",
|
|
1173
|
+
buildType: "bun",
|
|
1174
|
+
displayName: "Hono",
|
|
1175
|
+
annotation: "detected from package.json"
|
|
1176
|
+
};
|
|
1177
|
+
if (hasPackageDependency(packageJson, "@tanstack/start")) return {
|
|
1178
|
+
key: "tanstack-start",
|
|
1179
|
+
buildType: "tanstack-start",
|
|
1180
|
+
displayName: "TanStack Start",
|
|
1181
|
+
annotation: "detected from package.json"
|
|
1182
|
+
};
|
|
1183
|
+
return null;
|
|
1184
|
+
}
|
|
1185
|
+
async function detectNextConfig(cwd) {
|
|
1186
|
+
for (const candidate of [
|
|
1187
|
+
"next.config.js",
|
|
1188
|
+
"next.config.mjs",
|
|
1189
|
+
"next.config.cjs",
|
|
1190
|
+
"next.config.ts"
|
|
1191
|
+
]) {
|
|
1192
|
+
const filePath = path.join(cwd, candidate);
|
|
1193
|
+
try {
|
|
1194
|
+
const content = await readFile(filePath, "utf8");
|
|
1195
|
+
return {
|
|
1196
|
+
exists: true,
|
|
1197
|
+
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
|
|
1198
|
+
};
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
if (error.code !== "ENOENT") throw error;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
return {
|
|
1204
|
+
exists: false,
|
|
1205
|
+
standalone: false
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
function hasPackageDependency(packageJson, dependencyName) {
|
|
1209
|
+
return hasDependency(packageJson?.dependencies, dependencyName) || hasDependency(packageJson?.devDependencies, dependencyName);
|
|
1210
|
+
}
|
|
1211
|
+
function hasDependency(dependencies, dependencyName) {
|
|
1212
|
+
return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
|
|
1213
|
+
}
|
|
1214
|
+
function frameworkFromUserFacingValue(value, annotation) {
|
|
1215
|
+
switch (value.trim().toLowerCase()) {
|
|
1216
|
+
case "next":
|
|
1217
|
+
case "next.js":
|
|
1218
|
+
case "nextjs": return {
|
|
1219
|
+
key: "nextjs",
|
|
1220
|
+
buildType: "nextjs",
|
|
1221
|
+
displayName: "Next.js",
|
|
1222
|
+
annotation
|
|
1223
|
+
};
|
|
1224
|
+
case "hono": return {
|
|
1225
|
+
key: "hono",
|
|
1226
|
+
buildType: "bun",
|
|
1227
|
+
displayName: "Hono",
|
|
1228
|
+
annotation
|
|
1229
|
+
};
|
|
1230
|
+
case "tanstack":
|
|
1231
|
+
case "tanstack-start":
|
|
1232
|
+
case "@tanstack/start": return {
|
|
1233
|
+
key: "tanstack-start",
|
|
1234
|
+
buildType: "tanstack-start",
|
|
1235
|
+
displayName: "TanStack Start",
|
|
1236
|
+
annotation
|
|
1237
|
+
};
|
|
1238
|
+
default: throw frameworkNotDetectedError(void 0, value);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
1242
|
+
const supported = "Next.js, Hono, TanStack Start";
|
|
1243
|
+
const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
|
|
1244
|
+
return new CliError({
|
|
1245
|
+
code: "FRAMEWORK_NOT_DETECTED",
|
|
1246
|
+
domain: "app",
|
|
1247
|
+
summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
|
|
1248
|
+
why: `Supported Beta frameworks: ${supported}.`,
|
|
1249
|
+
fix: "Add one of these frameworks as a dependency, or pass --framework <nextjs|hono|tanstack-start>.",
|
|
1250
|
+
exitCode: 2,
|
|
1251
|
+
nextSteps: [
|
|
1252
|
+
"prisma-cli app deploy --framework nextjs",
|
|
1253
|
+
"prisma-cli app deploy --framework hono",
|
|
1254
|
+
"prisma-cli app deploy --framework tanstack-start"
|
|
1255
|
+
]
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
async function maybeRenderDeploySetupBlock(context, details) {
|
|
1259
|
+
if (context.flags.json || context.flags.quiet) return;
|
|
1260
|
+
const title = `${details.firstDeploy ? "Set up" : "Deploying"} ${formatDeployDirectory(context.runtime.cwd)}`;
|
|
1261
|
+
const rows = details.firstDeploy ? [
|
|
1262
|
+
{
|
|
1263
|
+
label: "Workspace",
|
|
1264
|
+
value: details.workspaceName
|
|
1265
|
+
},
|
|
1266
|
+
{
|
|
1267
|
+
label: "Project",
|
|
1268
|
+
value: details.projectName,
|
|
1269
|
+
annotation: details.projectAnnotation
|
|
1270
|
+
},
|
|
1271
|
+
{
|
|
1272
|
+
label: "Branch",
|
|
1273
|
+
value: details.branchName,
|
|
1274
|
+
annotation: details.branchAnnotation
|
|
1275
|
+
},
|
|
1276
|
+
{
|
|
1277
|
+
label: "App",
|
|
1278
|
+
value: details.appName,
|
|
1279
|
+
annotation: details.appAnnotation
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
label: "Framework",
|
|
1283
|
+
value: details.framework.displayName,
|
|
1284
|
+
annotation: details.framework.annotation
|
|
1285
|
+
},
|
|
1286
|
+
{
|
|
1287
|
+
label: "Runtime",
|
|
1288
|
+
value: `HTTP ${details.runtime.port}`,
|
|
1289
|
+
annotation: details.runtime.annotation
|
|
1290
|
+
}
|
|
1291
|
+
] : [
|
|
1292
|
+
{
|
|
1293
|
+
label: "Workspace",
|
|
1294
|
+
value: details.workspaceName
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
label: "Project",
|
|
1298
|
+
value: details.projectName,
|
|
1299
|
+
annotation: details.showSubsequentAnnotations ? details.projectAnnotation : void 0
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
label: "Branch",
|
|
1303
|
+
value: details.branchName,
|
|
1304
|
+
annotation: details.showSubsequentAnnotations ? details.branchAnnotation : void 0
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
label: "App",
|
|
1308
|
+
value: details.appName,
|
|
1309
|
+
annotation: details.showSubsequentAnnotations ? details.appAnnotation : void 0
|
|
1310
|
+
}
|
|
1311
|
+
];
|
|
1312
|
+
const lines = details.firstDeploy ? [
|
|
1313
|
+
title,
|
|
1314
|
+
"",
|
|
1315
|
+
...renderDeploySetupRows(context, rows),
|
|
1316
|
+
""
|
|
1317
|
+
] : [
|
|
1318
|
+
title,
|
|
1319
|
+
...renderDeploySetupRows(context, rows),
|
|
1320
|
+
""
|
|
1321
|
+
];
|
|
1322
|
+
context.output.stderr.write(`${lines.join("\n")}\n`);
|
|
1323
|
+
}
|
|
1324
|
+
async function maybeCustomizeDeploySettings(context, options) {
|
|
1325
|
+
if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitBuildType || options.explicitHttpPort || !canPrompt(context)) return {
|
|
1326
|
+
framework: options.framework,
|
|
1327
|
+
runtime: options.runtime
|
|
1328
|
+
};
|
|
1329
|
+
if (!await confirmPrompt({
|
|
1330
|
+
input: context.runtime.stdin,
|
|
1331
|
+
output: context.runtime.stderr,
|
|
1332
|
+
message: "Customize settings?",
|
|
1333
|
+
initialValue: false
|
|
1334
|
+
})) return {
|
|
1335
|
+
framework: options.framework,
|
|
1336
|
+
runtime: options.runtime
|
|
1337
|
+
};
|
|
1338
|
+
const framework = frameworkFromUserFacingValue(await selectPrompt({
|
|
1339
|
+
input: context.runtime.stdin,
|
|
1340
|
+
output: context.runtime.stderr,
|
|
1341
|
+
message: `Framework (${options.framework.displayName})`,
|
|
1342
|
+
choices: DEPLOY_FRAMEWORKS.map((framework) => ({
|
|
1343
|
+
label: frameworkDisplayName(framework),
|
|
1344
|
+
value: framework
|
|
1345
|
+
}))
|
|
1346
|
+
}), "set by you");
|
|
1347
|
+
const requestedPort = await textPrompt({
|
|
1348
|
+
input: context.runtime.stdin,
|
|
1349
|
+
output: context.runtime.stderr,
|
|
1350
|
+
message: `HTTP port (${options.runtime.port})`,
|
|
1351
|
+
placeholder: String(options.runtime.port),
|
|
1352
|
+
validate: validateDeployHttpPortText
|
|
1353
|
+
});
|
|
1354
|
+
const runtime = {
|
|
1355
|
+
port: requestedPort.trim() ? parseDeployHttpPort(requestedPort) : options.runtime.port,
|
|
1356
|
+
annotation: "set by you"
|
|
1357
|
+
};
|
|
1358
|
+
const changedRows = [framework.key !== options.framework.key ? {
|
|
1359
|
+
label: "Framework",
|
|
1360
|
+
value: framework.displayName,
|
|
1361
|
+
annotation: framework.annotation
|
|
1362
|
+
} : null, runtime.port !== options.runtime.port ? {
|
|
1363
|
+
label: "Runtime",
|
|
1364
|
+
value: `HTTP ${runtime.port}`,
|
|
1365
|
+
annotation: runtime.annotation
|
|
1366
|
+
} : null].filter((row) => Boolean(row));
|
|
1367
|
+
if (changedRows.length > 0 && !context.flags.quiet && !context.flags.json) context.output.stderr.write(`${renderDeploySetupRows(context, changedRows).join("\n")}\n\n`);
|
|
1368
|
+
return {
|
|
1369
|
+
framework,
|
|
1370
|
+
runtime
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
function renderDeploySetupRows(context, rows) {
|
|
1374
|
+
const labelWidth = Math.max(...rows.map((row) => row.label.length));
|
|
1375
|
+
const valueWidth = Math.max(...rows.map((row) => row.value.length));
|
|
1376
|
+
return rows.map((row) => {
|
|
1377
|
+
return ` ${row.label.padEnd(labelWidth)} ${row.value.padEnd(valueWidth)}${row.annotation ? ` ${context.ui.dim(row.annotation)}` : ""}`.trimEnd();
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
function annotationForProjectResolution(resolution) {
|
|
1381
|
+
switch (resolution.projectSource) {
|
|
1382
|
+
case "explicit": return "set by --project";
|
|
1383
|
+
case "env": return `from ${PRISMA_PROJECT_ID_ENV_VAR}`;
|
|
1384
|
+
case "local-pin": return "from local pin";
|
|
1385
|
+
case "created": return resolution.targetNameSource === "directory-name" ? "created from directory name" : "created from package.json";
|
|
1386
|
+
case "package-name":
|
|
1387
|
+
case "directory-name": return "linked to existing project";
|
|
1388
|
+
case "platform-mapping":
|
|
1389
|
+
case "remembered-local": return "linked to existing project";
|
|
1390
|
+
case "prompt": return "selected by you";
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
function frameworkDisplayName(framework) {
|
|
1394
|
+
switch (framework) {
|
|
1395
|
+
case "nextjs": return "Next.js";
|
|
1396
|
+
case "hono": return "Hono";
|
|
1397
|
+
case "tanstack-start": return "TanStack Start";
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
function validateDeployHttpPortText(value) {
|
|
1401
|
+
if (!value?.trim()) return;
|
|
1402
|
+
try {
|
|
1403
|
+
parseDeployHttpPort(value);
|
|
1404
|
+
return;
|
|
1405
|
+
} catch (error) {
|
|
1406
|
+
return error instanceof CliError ? error.summary : String(error);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
function formatDeployDirectory(cwd) {
|
|
1410
|
+
const basename = path.basename(cwd);
|
|
1411
|
+
return basename ? `./${basename}` : ".";
|
|
859
1412
|
}
|
|
860
1413
|
async function readCurrentWorkspaceId(context) {
|
|
861
1414
|
const state = await context.stateStore.read();
|
|
@@ -891,9 +1444,12 @@ function parseLocalPort(requestedPort) {
|
|
|
891
1444
|
}
|
|
892
1445
|
function parseDeployPortMapping(requestedPort) {
|
|
893
1446
|
if (!requestedPort) return;
|
|
1447
|
+
return { http: parseDeployHttpPort(requestedPort) };
|
|
1448
|
+
}
|
|
1449
|
+
function parseDeployHttpPort(requestedPort) {
|
|
894
1450
|
const port = Number.parseInt(requestedPort, 10);
|
|
895
1451
|
if (!Number.isInteger(port) || port <= 0 || port > 65535) throw usageError(`Invalid HTTP port "${requestedPort}"`, "HTTP port must be an integer between 1 and 65535.", "Pass --http-port <number> with a valid port value.", ["prisma-cli app deploy --http-port 3000"], "app");
|
|
896
|
-
return
|
|
1452
|
+
return port;
|
|
897
1453
|
}
|
|
898
1454
|
function ensurePreviewAppMode(context) {
|
|
899
1455
|
if (isRealMode(context)) return;
|
|
@@ -911,6 +1467,22 @@ function deployFailedError(summary, error, nextSteps) {
|
|
|
911
1467
|
nextSteps
|
|
912
1468
|
});
|
|
913
1469
|
}
|
|
1470
|
+
function localResolutionPinStaleError() {
|
|
1471
|
+
return new CliError({
|
|
1472
|
+
code: "LOCAL_STATE_STALE",
|
|
1473
|
+
domain: "project",
|
|
1474
|
+
summary: "Local project binding is stale",
|
|
1475
|
+
why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
|
|
1476
|
+
fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} and re-run to re-bootstrap.`,
|
|
1477
|
+
meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
|
|
1478
|
+
exitCode: 1,
|
|
1479
|
+
nextSteps: ["prisma-cli app deploy"]
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
function readDeployEnvOverride(context, name) {
|
|
1483
|
+
const value = context.runtime.env[name]?.trim();
|
|
1484
|
+
return value ? value : void 0;
|
|
1485
|
+
}
|
|
914
1486
|
/**
|
|
915
1487
|
* `app deploy` falls into "create a new project on first deploy" when no
|
|
916
1488
|
* existing project matches the package.json name (or the cwd basename as a
|
|
@@ -1032,6 +1604,9 @@ function isMissingProjectError(error) {
|
|
|
1032
1604
|
function findAppByName(apps, name) {
|
|
1033
1605
|
return apps.find((app) => app.name === name);
|
|
1034
1606
|
}
|
|
1607
|
+
function findAppsByName(apps, name) {
|
|
1608
|
+
return apps.filter((app) => app.name === name);
|
|
1609
|
+
}
|
|
1035
1610
|
function sortApps(apps) {
|
|
1036
1611
|
return apps.slice().sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
|
|
1037
1612
|
}
|