@prisma/cli 3.0.0-beta.1 → 3.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/adapters/git.js +8 -3
- package/dist/adapters/local-state.js +12 -4
- package/dist/adapters/mock-api.js +81 -2
- package/dist/adapters/token-storage.js +64 -23
- package/dist/cli.js +18 -3
- package/dist/cli2.js +9 -5
- package/dist/commands/app/index.js +84 -59
- package/dist/commands/branch/index.js +2 -27
- package/dist/commands/database/index.js +159 -0
- package/dist/commands/env.js +8 -4
- package/dist/controllers/app-env-api.js +54 -0
- package/dist/controllers/app-env-file.js +181 -0
- package/dist/controllers/app-env.js +286 -131
- package/dist/controllers/app.js +699 -244
- package/dist/controllers/auth.js +8 -8
- package/dist/controllers/branch.js +78 -48
- package/dist/controllers/database.js +318 -0
- package/dist/controllers/project.js +156 -85
- package/dist/lib/app/branch-database-deploy.js +325 -0
- package/dist/lib/app/branch-database.js +215 -0
- package/dist/lib/app/bun-project.js +12 -5
- package/dist/lib/app/compute-config.js +147 -0
- package/dist/lib/app/deploy-output.js +10 -1
- package/dist/lib/app/deploy-plan.js +58 -0
- package/dist/lib/app/env-config.js +1 -1
- package/dist/lib/app/env-file.js +82 -0
- package/dist/lib/app/env-vars.js +28 -2
- package/dist/lib/app/local-dev.js +8 -49
- package/dist/lib/app/preview-branch-database.js +102 -0
- package/dist/lib/app/preview-build-settings.js +376 -0
- package/dist/lib/app/preview-build.js +314 -97
- package/dist/lib/app/preview-provider.js +178 -65
- package/dist/lib/app/production-deploy-gate.js +161 -0
- package/dist/lib/auth/auth-ops.js +30 -21
- package/dist/lib/auth/guard.js +3 -2
- package/dist/lib/auth/login.js +116 -14
- package/dist/lib/database/provider.js +167 -0
- package/dist/lib/diagnostics.js +15 -0
- package/dist/lib/fs/home-path.js +24 -0
- package/dist/lib/git/local-branch.js +53 -0
- package/dist/lib/git/local-status.js +57 -0
- package/dist/lib/project/interactive-setup.js +1 -1
- package/dist/lib/project/local-pin.js +182 -33
- package/dist/lib/project/resolution.js +204 -50
- package/dist/lib/project/setup.js +66 -19
- package/dist/output/patterns.js +1 -1
- package/dist/presenters/app-env.js +149 -14
- package/dist/presenters/app.js +178 -23
- package/dist/presenters/branch.js +37 -102
- package/dist/presenters/database.js +274 -0
- package/dist/presenters/project.js +57 -39
- package/dist/presenters/verbose-context.js +64 -0
- package/dist/shell/command-meta.js +103 -13
- package/dist/shell/command-runner.js +57 -19
- package/dist/shell/diagnostics-output.js +57 -0
- package/dist/shell/errors.js +12 -1
- package/dist/shell/help.js +30 -20
- package/dist/shell/output.js +10 -1
- package/dist/shell/runtime.js +11 -7
- package/dist/shell/ui.js +42 -3
- package/dist/shell/update-check.js +247 -0
- package/dist/use-cases/branch.js +20 -68
- package/dist/use-cases/create-cli-gateways.js +2 -17
- package/dist/use-cases/project.js +2 -1
- package/package.json +26 -10
package/dist/controllers/app.js
CHANGED
|
@@ -1,54 +1,79 @@
|
|
|
1
1
|
import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
|
|
2
2
|
import { FileTokenStorage } from "../adapters/token-storage.js";
|
|
3
3
|
import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
4
|
+
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
4
5
|
import { renderCommandHeader } from "../shell/ui.js";
|
|
5
|
-
import { writeJsonEvent } from "../shell/output.js";
|
|
6
6
|
import { canPrompt } from "../shell/runtime.js";
|
|
7
|
-
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
8
|
-
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
9
|
-
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
10
|
-
import { parseEnvAssignments } from "../lib/app/env-vars.js";
|
|
11
|
-
import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
|
|
12
|
-
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
13
|
-
import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
|
|
14
|
-
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
15
7
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
8
|
+
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
9
|
+
import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
10
|
+
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
11
|
+
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
12
|
+
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
13
|
+
import { COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeConfigErrorToCliError, computeFrameworkToBuildType, computeTargetAppDir, inferComputeTargetFromCwd, loadComputeConfig as loadComputeConfig$1, mergeComputeDeployInputs, mergeComputeLocalInputs, selectComputeDeployTarget } from "../lib/app/compute-config.js";
|
|
14
|
+
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
15
|
+
import { describeDeployAllFailure, perAppInputsForDeployAll, planAppDeploy } from "../lib/app/deploy-plan.js";
|
|
16
|
+
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
17
|
+
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
18
|
+
import { DEFAULT_LOCAL_DEV_PORT, runLocalApp } from "../lib/app/local-dev.js";
|
|
19
|
+
import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredPreviewBuildSettings, resolveInferredPreviewBuildSettings } from "../lib/app/preview-build-settings.js";
|
|
19
20
|
import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
|
|
20
21
|
import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
|
|
21
22
|
import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
|
|
22
23
|
import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
23
|
-
import {
|
|
24
|
+
import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
|
|
25
|
+
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
26
|
+
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
27
|
+
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
28
|
+
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
29
|
+
import { writeJsonEvent } from "../shell/output.js";
|
|
24
30
|
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
25
31
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
26
32
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
33
|
+
import { ENTRYPOINT_BUILD_TYPES, FRAMEWORKS, LOCAL_DEV_BUILD_TYPES, frameworkByKey, frameworkFromAlias, isConfigBackedBuildType } from "@prisma/compute-sdk/config";
|
|
27
34
|
import { access, readFile } from "node:fs/promises";
|
|
28
35
|
import path from "node:path";
|
|
36
|
+
import { Result, matchError } from "better-result";
|
|
29
37
|
import open from "open";
|
|
30
38
|
//#region src/controllers/app.ts
|
|
31
|
-
const DEPLOY_FRAMEWORKS = [
|
|
32
|
-
"nextjs",
|
|
33
|
-
"hono",
|
|
34
|
-
"tanstack-start",
|
|
35
|
-
"bun"
|
|
36
|
-
];
|
|
37
|
-
const TANSTACK_START_PACKAGES = ["@tanstack/react-start", "@tanstack/solid-start"];
|
|
38
39
|
const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
|
|
39
40
|
const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
|
|
40
41
|
const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
|
|
41
42
|
function isRealMode(context) {
|
|
42
43
|
return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
43
44
|
}
|
|
44
|
-
async function runAppBuild(context,
|
|
45
|
-
const
|
|
46
|
-
|
|
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;
|
|
47
70
|
try {
|
|
48
71
|
const { artifact, buildType: actualBuildType } = await executePreviewBuild({
|
|
49
|
-
appPath:
|
|
50
|
-
entrypoint,
|
|
51
|
-
buildType
|
|
72
|
+
appPath: appDir,
|
|
73
|
+
entrypoint: merged.entrypoint,
|
|
74
|
+
buildType,
|
|
75
|
+
buildSettings,
|
|
76
|
+
signal: context.runtime.signal
|
|
52
77
|
});
|
|
53
78
|
return {
|
|
54
79
|
command: "app.build",
|
|
@@ -65,25 +90,42 @@ async function runAppBuild(context, entrypoint, requestedBuildType) {
|
|
|
65
90
|
throw buildFailedError("Local app build failed", error);
|
|
66
91
|
}
|
|
67
92
|
}
|
|
68
|
-
async function runAppRun(context,
|
|
93
|
+
async function runAppRun(context, options) {
|
|
69
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");
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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;
|
|
74
115
|
let runResult;
|
|
75
116
|
try {
|
|
76
117
|
runResult = await runLocalApp({
|
|
77
|
-
appPath:
|
|
78
|
-
buildType:
|
|
118
|
+
appPath: appDir,
|
|
119
|
+
buildType: framework.buildType,
|
|
79
120
|
entrypoint,
|
|
80
121
|
port,
|
|
81
|
-
env: context.runtime.env
|
|
122
|
+
env: context.runtime.env,
|
|
123
|
+
signal: context.runtime.signal
|
|
82
124
|
});
|
|
83
125
|
} catch (error) {
|
|
84
126
|
throw runFailedError("Local app run failed", error);
|
|
85
127
|
}
|
|
86
|
-
if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM")
|
|
128
|
+
if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM") throw new DOMException("Command canceled", "AbortError");
|
|
87
129
|
else if (runResult.exitCode !== 0) throw runFailedError("Local app run failed", `The ${formatFrameworkName(runResult.framework)} process exited with code ${runResult.exitCode}.`, runResult.exitCode);
|
|
88
130
|
return {
|
|
89
131
|
command: "app.run",
|
|
@@ -99,6 +141,106 @@ async function runAppRun(context, entrypoint, requestedBuildType, requestedPort)
|
|
|
99
141
|
}
|
|
100
142
|
async function runAppDeploy(context, appName, options) {
|
|
101
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) {
|
|
102
244
|
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
103
245
|
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
104
246
|
assertExclusiveDeployProjectInputs({
|
|
@@ -106,14 +248,27 @@ async function runAppDeploy(context, appName, options) {
|
|
|
106
248
|
createProjectName: options?.createProjectName,
|
|
107
249
|
envProjectId
|
|
108
250
|
});
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
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);
|
|
265
|
+
if (localPinReadResult.isErr()) throw localPinReadErrorToDeployError(localPinReadResult.error);
|
|
266
|
+
const localPin = localPinReadResult.value;
|
|
112
267
|
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
113
|
-
if (
|
|
268
|
+
if (merged.httpPort) parseDeployHttpPort(merged.httpPort.value);
|
|
114
269
|
assertSupportedEntrypointForRequestedDeployShape({
|
|
115
|
-
requestedFramework:
|
|
116
|
-
entrypoint:
|
|
270
|
+
requestedFramework: merged.framework?.value,
|
|
271
|
+
entrypoint: merged.entrypoint?.value
|
|
117
272
|
});
|
|
118
273
|
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
119
274
|
branch,
|
|
@@ -123,25 +278,32 @@ async function runAppDeploy(context, appName, options) {
|
|
|
123
278
|
});
|
|
124
279
|
let localPinResult;
|
|
125
280
|
if (target.localPinAction) {
|
|
126
|
-
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
|
|
127
|
-
|
|
128
|
-
|
|
281
|
+
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction, projectDir);
|
|
282
|
+
if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
|
|
283
|
+
const projectSetup = setupResult.value;
|
|
284
|
+
localPinResult = projectSetup.localPin;
|
|
285
|
+
maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
|
|
129
286
|
}
|
|
130
287
|
let framework = await resolveDeployFramework(context, {
|
|
131
|
-
requestedFramework:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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" }));
|
|
137
297
|
const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
138
298
|
explicitAppName: appName,
|
|
139
299
|
explicitAppId: envAppId,
|
|
300
|
+
configAppName: merged.configAppName,
|
|
140
301
|
firstDeploy: Boolean(target.localPinAction),
|
|
141
|
-
inferName: () => inferTargetName(context.runtime.
|
|
302
|
+
inferName: () => inferTargetName(appDir, context.runtime.signal)
|
|
142
303
|
});
|
|
143
304
|
await maybeRenderDeploySetupBlock(context, {
|
|
144
305
|
includeDirectory: !target.localPinAction,
|
|
306
|
+
appDir,
|
|
145
307
|
projectName: target.project.name,
|
|
146
308
|
branchName: target.branch.name,
|
|
147
309
|
appName: selectedApp.displayName
|
|
@@ -150,20 +312,46 @@ async function runAppDeploy(context, appName, options) {
|
|
|
150
312
|
framework,
|
|
151
313
|
runtime,
|
|
152
314
|
firstDeploy: selectedApp.firstDeploy,
|
|
153
|
-
explicitFramework: Boolean(
|
|
154
|
-
explicitEntrypoint: Boolean(
|
|
155
|
-
explicitHttpPort: Boolean(
|
|
315
|
+
explicitFramework: Boolean(merged.framework),
|
|
316
|
+
explicitEntrypoint: Boolean(merged.entrypoint),
|
|
317
|
+
explicitHttpPort: Boolean(merged.httpPort)
|
|
156
318
|
});
|
|
157
319
|
framework = customized.framework;
|
|
158
320
|
runtime = customized.runtime;
|
|
321
|
+
const productionDeployGate = await enforceProductionDeployGate(context, provider, {
|
|
322
|
+
appId: selectedApp.appId,
|
|
323
|
+
appName: selectedApp.displayName,
|
|
324
|
+
branchKind: target.branch.kind,
|
|
325
|
+
prod: options?.prod === true
|
|
326
|
+
});
|
|
159
327
|
const buildType = framework.buildType;
|
|
160
|
-
assertSupportedEntrypoint(buildType,
|
|
161
|
-
const entrypoint = await resolveDeployEntrypoint(
|
|
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,
|
|
339
|
+
buildType,
|
|
340
|
+
signal: context.runtime.signal
|
|
341
|
+
});
|
|
342
|
+
const legacyWarnings = await handleLegacyBuildSettings(context, appDir, buildSettingsResolution.settings);
|
|
343
|
+
maybeRenderDeployBuildSettings(context, buildSettingsResolution);
|
|
162
344
|
const portMapping = parseDeployPortMapping(String(runtime.port));
|
|
345
|
+
const branchDatabaseSetup = await maybeSetupBranchDatabase(context, provider, projectId, toBranchDatabaseDeployBranch(target.branch), {
|
|
346
|
+
db: options?.db,
|
|
347
|
+
providedEnvVars: envVars,
|
|
348
|
+
firstProductionDeploy: productionDeployGate.firstProductionDeploy,
|
|
349
|
+
projectDir
|
|
350
|
+
});
|
|
163
351
|
const progressState = createPreviewDeployProgressState();
|
|
164
352
|
const deployStartedAt = Date.now();
|
|
165
353
|
const deployResult = await provider.deployApp({
|
|
166
|
-
cwd:
|
|
354
|
+
cwd: appDir,
|
|
167
355
|
projectId,
|
|
168
356
|
branchName: target.branch.name,
|
|
169
357
|
appId: selectedApp.appId,
|
|
@@ -171,9 +359,11 @@ async function runAppDeploy(context, appName, options) {
|
|
|
171
359
|
region: selectedApp.region,
|
|
172
360
|
entrypoint,
|
|
173
361
|
buildType,
|
|
362
|
+
buildSettings: buildSettingsResolution.settings,
|
|
174
363
|
portMapping,
|
|
175
364
|
envVars,
|
|
176
365
|
interaction: void 0,
|
|
366
|
+
signal: context.runtime.signal,
|
|
177
367
|
progress: createPreviewDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
|
|
178
368
|
}).catch((error) => {
|
|
179
369
|
throw appDeployFailedError(error, progressState);
|
|
@@ -189,35 +379,65 @@ async function runAppDeploy(context, appName, options) {
|
|
|
189
379
|
result: {
|
|
190
380
|
workspace: target.workspace,
|
|
191
381
|
project: target.project,
|
|
192
|
-
branch: target.branch,
|
|
382
|
+
branch: toResultBranch(target.branch),
|
|
193
383
|
resolution: target.resolution,
|
|
384
|
+
branchDatabase: branchDatabaseSetup.result,
|
|
194
385
|
app: {
|
|
195
386
|
id: deployResult.app.id,
|
|
196
387
|
name: deployResult.app.name
|
|
197
388
|
},
|
|
198
389
|
deployment: deployResult.deployment,
|
|
390
|
+
deploySettings: {
|
|
391
|
+
config: {
|
|
392
|
+
path: buildSettingsResolution.relativeConfigPath,
|
|
393
|
+
status: buildSettingsResolution.status
|
|
394
|
+
},
|
|
395
|
+
buildCommand: {
|
|
396
|
+
value: buildSettingsResolution.settings.buildCommand,
|
|
397
|
+
source: buildSettingsResolution.settings.buildCommandSource
|
|
398
|
+
},
|
|
399
|
+
outputDirectory: {
|
|
400
|
+
value: buildSettingsResolution.settings.outputDirectory,
|
|
401
|
+
source: buildSettingsResolution.settings.outputDirectorySource
|
|
402
|
+
},
|
|
403
|
+
framework: {
|
|
404
|
+
key: framework.key,
|
|
405
|
+
buildType,
|
|
406
|
+
name: framework.displayName,
|
|
407
|
+
source: framework.annotation
|
|
408
|
+
},
|
|
409
|
+
entrypoint: entrypoint ?? null,
|
|
410
|
+
httpPort: runtime.port,
|
|
411
|
+
region: selectedApp.region ?? null,
|
|
412
|
+
envVars: envVarNames(envVars)
|
|
413
|
+
},
|
|
199
414
|
durationMs: deployDurationMs,
|
|
200
415
|
localPin: localPinResult
|
|
201
416
|
},
|
|
202
|
-
warnings: [],
|
|
417
|
+
warnings: [...legacyWarnings, ...branchDatabaseSetup.warnings],
|
|
203
418
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
|
|
204
419
|
};
|
|
205
420
|
}
|
|
206
|
-
async function runAppListDeploys(context, appName, projectRef) {
|
|
421
|
+
async function runAppListDeploys(context, appName, projectRef, configTarget) {
|
|
207
422
|
ensurePreviewAppMode(context);
|
|
208
|
-
const
|
|
209
|
-
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);
|
|
210
429
|
if (!selectedApp) return {
|
|
211
430
|
command: "app.list-deploys",
|
|
212
431
|
result: {
|
|
213
432
|
projectId,
|
|
433
|
+
verboseContext: toAppVerboseContext(target),
|
|
214
434
|
app: null,
|
|
215
435
|
deployments: []
|
|
216
436
|
},
|
|
217
437
|
warnings: [],
|
|
218
438
|
nextSteps: ["prisma-cli app deploy"]
|
|
219
439
|
};
|
|
220
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
440
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
221
441
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app deploy"]);
|
|
222
442
|
});
|
|
223
443
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -230,6 +450,7 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
230
450
|
command: "app.list-deploys",
|
|
231
451
|
result: {
|
|
232
452
|
projectId,
|
|
453
|
+
verboseContext: toAppVerboseContext(target),
|
|
233
454
|
app: {
|
|
234
455
|
id: deploymentsResult.app.id,
|
|
235
456
|
name: deploymentsResult.app.name
|
|
@@ -240,14 +461,19 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
240
461
|
nextSteps: deployments.length > 0 ? [`prisma-cli app show-deploy ${deployments[0]?.id}`] : ["prisma-cli app deploy"]
|
|
241
462
|
};
|
|
242
463
|
}
|
|
243
|
-
async function runAppShow(context, appName, projectRef) {
|
|
464
|
+
async function runAppShow(context, appName, projectRef, configTarget) {
|
|
244
465
|
ensurePreviewAppMode(context);
|
|
245
|
-
const
|
|
246
|
-
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);
|
|
247
472
|
if (!selectedApp) return {
|
|
248
473
|
command: "app.show",
|
|
249
474
|
result: {
|
|
250
475
|
projectId,
|
|
476
|
+
verboseContext: toAppVerboseContext(target),
|
|
251
477
|
app: null,
|
|
252
478
|
liveDeployment: null,
|
|
253
479
|
liveUrl: null,
|
|
@@ -256,7 +482,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
256
482
|
warnings: [],
|
|
257
483
|
nextSteps: ["prisma-cli app deploy"]
|
|
258
484
|
};
|
|
259
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
485
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
260
486
|
throw deployFailedError("Failed to inspect app", error, ["prisma-cli app list-deploys"]);
|
|
261
487
|
});
|
|
262
488
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -270,6 +496,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
270
496
|
command: "app.show",
|
|
271
497
|
result: {
|
|
272
498
|
projectId,
|
|
499
|
+
verboseContext: toAppVerboseContext(target),
|
|
273
500
|
app: {
|
|
274
501
|
id: deploymentsResult.app.id,
|
|
275
502
|
name: deploymentsResult.app.name
|
|
@@ -284,7 +511,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
284
511
|
}
|
|
285
512
|
async function runAppShowDeploy(context, deploymentId) {
|
|
286
513
|
ensurePreviewAppMode(context);
|
|
287
|
-
const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId).catch((error) => {
|
|
514
|
+
const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
|
|
288
515
|
throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
|
|
289
516
|
});
|
|
290
517
|
if (!deployment) throw new CliError({
|
|
@@ -316,12 +543,17 @@ async function runAppShowDeploy(context, deploymentId) {
|
|
|
316
543
|
nextSteps: []
|
|
317
544
|
};
|
|
318
545
|
}
|
|
319
|
-
async function runAppOpen(context, appName, projectRef) {
|
|
546
|
+
async function runAppOpen(context, appName, projectRef, configTarget) {
|
|
320
547
|
ensurePreviewAppMode(context);
|
|
321
|
-
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
|
+
});
|
|
322
554
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
323
555
|
if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
|
|
324
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
556
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
325
557
|
throw deployFailedError("Failed to resolve app URL", error, ["prisma-cli app show"]);
|
|
326
558
|
});
|
|
327
559
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -334,11 +566,16 @@ async function runAppOpen(context, appName, projectRef) {
|
|
|
334
566
|
if (!liveDeployment) throw noDeploymentsError("No deployments available to open", `The selected app "${deploymentsResult.app.name}" does not have any deployments yet.`);
|
|
335
567
|
if (!deploymentsResult.app.liveUrl) throw featureUnavailableError("Live URL is not available for the selected app", "Deployments exist, but the provider does not expose a stable live service URL for this app yet.", "Run prisma-cli app show to inspect the current deployment state and try again after the app reports a live URL.", ["prisma-cli app show"], "app");
|
|
336
568
|
const shouldOpen = canPrompt(context);
|
|
337
|
-
if (shouldOpen)
|
|
569
|
+
if (shouldOpen) {
|
|
570
|
+
context.runtime.signal.throwIfAborted();
|
|
571
|
+
await open(deploymentsResult.app.liveUrl);
|
|
572
|
+
context.runtime.signal.throwIfAborted();
|
|
573
|
+
}
|
|
338
574
|
return {
|
|
339
575
|
command: "app.open",
|
|
340
576
|
result: {
|
|
341
577
|
projectId,
|
|
578
|
+
verboseContext: toAppVerboseContext(target),
|
|
342
579
|
app: {
|
|
343
580
|
id: deploymentsResult.app.id,
|
|
344
581
|
name: deploymentsResult.app.name
|
|
@@ -355,7 +592,8 @@ async function runAppDomainAdd(context, hostname, options) {
|
|
|
355
592
|
const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
|
|
356
593
|
const added = await target.provider.addDomain({
|
|
357
594
|
appId: target.app.id,
|
|
358
|
-
hostname: normalizedHostname
|
|
595
|
+
hostname: normalizedHostname,
|
|
596
|
+
signal: context.runtime.signal
|
|
359
597
|
}).catch((error) => {
|
|
360
598
|
throw domainCommandError("add", error, normalizedHostname);
|
|
361
599
|
});
|
|
@@ -373,8 +611,8 @@ async function runAppDomainAdd(context, hostname, options) {
|
|
|
373
611
|
async function runAppDomainShow(context, hostname, options) {
|
|
374
612
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
375
613
|
const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
|
|
376
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show");
|
|
377
|
-
const detail = await target.provider.showDomain(domain.id).catch((error) => {
|
|
614
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show", context.runtime.signal);
|
|
615
|
+
const detail = await target.provider.showDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
378
616
|
throw domainCommandError("show", error, normalizedHostname);
|
|
379
617
|
});
|
|
380
618
|
return {
|
|
@@ -390,9 +628,9 @@ async function runAppDomainShow(context, hostname, options) {
|
|
|
390
628
|
async function runAppDomainRemove(context, hostname, options) {
|
|
391
629
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
392
630
|
const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
|
|
393
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove");
|
|
631
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove", context.runtime.signal);
|
|
394
632
|
await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
|
|
395
|
-
await target.provider.removeDomain(domain.id).catch((error) => {
|
|
633
|
+
await target.provider.removeDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
396
634
|
throw domainCommandError("remove", error, normalizedHostname);
|
|
397
635
|
});
|
|
398
636
|
return {
|
|
@@ -409,8 +647,8 @@ async function runAppDomainRemove(context, hostname, options) {
|
|
|
409
647
|
async function runAppDomainRetry(context, hostname, options) {
|
|
410
648
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
411
649
|
const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
|
|
412
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry");
|
|
413
|
-
const retried = await target.provider.retryDomain(domain.id).catch((error) => {
|
|
650
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry", context.runtime.signal);
|
|
651
|
+
const retried = await target.provider.retryDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
414
652
|
throw domainCommandError("retry", error, normalizedHostname);
|
|
415
653
|
});
|
|
416
654
|
return {
|
|
@@ -427,7 +665,7 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
427
665
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
428
666
|
const timeoutMs = parseDomainWaitTimeout(options?.timeout);
|
|
429
667
|
const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
|
|
430
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait");
|
|
668
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait", context.runtime.signal);
|
|
431
669
|
if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
|
|
432
670
|
`app domain wait -> Waiting for ${normalizedHostname} to become active.`,
|
|
433
671
|
"",
|
|
@@ -470,21 +708,25 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
470
708
|
exitCode: 1,
|
|
471
709
|
nextSteps: [`prisma-cli app domain show ${normalizedHostname}`]
|
|
472
710
|
});
|
|
473
|
-
await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)));
|
|
474
|
-
current = await target.provider.showDomain(current.id).catch((error) => {
|
|
711
|
+
await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)), context.runtime.signal);
|
|
712
|
+
current = await target.provider.showDomain(current.id, { signal: context.runtime.signal }).catch((error) => {
|
|
475
713
|
throw domainCommandError("wait", error, normalizedHostname);
|
|
476
714
|
});
|
|
477
715
|
}
|
|
478
716
|
}
|
|
479
|
-
async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
717
|
+
async function runAppLogs(context, appName, deploymentId, projectRef, configTarget) {
|
|
480
718
|
ensurePreviewAppMode(context);
|
|
481
|
-
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
|
+
});
|
|
482
725
|
const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
|
|
483
726
|
if (!context.flags.json && !context.flags.quiet) {
|
|
484
727
|
const lines = renderCommandHeader(context.ui, {
|
|
485
728
|
commandLabel: "app logs",
|
|
486
729
|
description: "Streaming logs for the selected deployment.",
|
|
487
|
-
docsPath: "docs/product/command-spec.md#prisma-cli-app-logs---app-name---deployment-id",
|
|
488
730
|
rows: [
|
|
489
731
|
{
|
|
490
732
|
key: "project",
|
|
@@ -504,6 +746,7 @@ async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
|
504
746
|
}
|
|
505
747
|
await provider.streamDeploymentLogs({
|
|
506
748
|
deploymentId: target.deployment.id,
|
|
749
|
+
signal: context.runtime.signal,
|
|
507
750
|
onRecord: (record) => writeLogRecord(context, record)
|
|
508
751
|
}).catch((error) => {
|
|
509
752
|
throw deployFailedError("Failed to stream app logs", error, [`prisma-cli app show-deploy ${target.deployment.id}`, "prisma-cli app list-deploys"]);
|
|
@@ -513,7 +756,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
|
|
|
513
756
|
if (appName) {
|
|
514
757
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
515
758
|
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
516
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
759
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
517
760
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
518
761
|
});
|
|
519
762
|
const deployment = requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name);
|
|
@@ -526,7 +769,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
|
|
|
526
769
|
deployment
|
|
527
770
|
};
|
|
528
771
|
}
|
|
529
|
-
const shown = await provider.showDeployment(deploymentId).catch((error) => {
|
|
772
|
+
const shown = await provider.showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
|
|
530
773
|
throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
|
|
531
774
|
});
|
|
532
775
|
if (!shown) throw new CliError({
|
|
@@ -569,7 +812,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
|
|
|
569
812
|
async function resolveLiveLogDeployment(context, provider, projectId, branchName, appName) {
|
|
570
813
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
571
814
|
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
572
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
815
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
573
816
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
574
817
|
});
|
|
575
818
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -600,11 +843,16 @@ function writeLogRecord(context, record) {
|
|
|
600
843
|
if (!record.text.endsWith("\n")) context.output.stdout.write("\n");
|
|
601
844
|
}
|
|
602
845
|
}
|
|
603
|
-
async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
846
|
+
async function runAppPromote(context, deploymentId, appName, projectRef, configTarget) {
|
|
604
847
|
ensurePreviewAppMode(context);
|
|
605
|
-
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
|
+
});
|
|
606
854
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
|
|
607
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
855
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
608
856
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
609
857
|
});
|
|
610
858
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -617,6 +865,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
617
865
|
if (!targetAlreadyLive) await provider.promoteDeployment({
|
|
618
866
|
appId: selectedApp.id,
|
|
619
867
|
deploymentId: targetDeployment.id,
|
|
868
|
+
signal: context.runtime.signal,
|
|
620
869
|
progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
621
870
|
}).catch((error) => {
|
|
622
871
|
throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
|
|
@@ -626,6 +875,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
626
875
|
command: "app.promote",
|
|
627
876
|
result: {
|
|
628
877
|
projectId,
|
|
878
|
+
verboseContext: toAppVerboseContext(target),
|
|
629
879
|
app: {
|
|
630
880
|
id: deploymentsResult.app.id,
|
|
631
881
|
name: deploymentsResult.app.name
|
|
@@ -640,11 +890,16 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
640
890
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
641
891
|
};
|
|
642
892
|
}
|
|
643
|
-
async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
893
|
+
async function runAppRollback(context, appName, deploymentId, projectRef, configTarget) {
|
|
644
894
|
ensurePreviewAppMode(context);
|
|
645
|
-
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
|
+
});
|
|
646
901
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
|
|
647
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
902
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
648
903
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
649
904
|
});
|
|
650
905
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -658,6 +913,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
658
913
|
if (!targetAlreadyLive) await provider.promoteDeployment({
|
|
659
914
|
appId: selectedApp.id,
|
|
660
915
|
deploymentId: targetDeployment.id,
|
|
916
|
+
signal: context.runtime.signal,
|
|
661
917
|
progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
662
918
|
}).catch((error) => {
|
|
663
919
|
throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
|
|
@@ -667,6 +923,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
667
923
|
command: "app.rollback",
|
|
668
924
|
result: {
|
|
669
925
|
projectId,
|
|
926
|
+
verboseContext: toAppVerboseContext(target),
|
|
670
927
|
app: {
|
|
671
928
|
id: deploymentsResult.app.id,
|
|
672
929
|
name: deploymentsResult.app.name
|
|
@@ -682,12 +939,17 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
682
939
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
683
940
|
};
|
|
684
941
|
}
|
|
685
|
-
async function runAppRemove(context, appName, projectRef) {
|
|
942
|
+
async function runAppRemove(context, appName, projectRef, configTarget) {
|
|
686
943
|
ensurePreviewAppMode(context);
|
|
687
|
-
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
|
+
});
|
|
688
950
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
|
|
689
951
|
await confirmAppRemoval(context, selectedApp);
|
|
690
|
-
const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
|
|
952
|
+
const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
691
953
|
throw removeFailedError("Failed to remove app", error, ["prisma-cli app show", "prisma-cli app list-deploys"]);
|
|
692
954
|
});
|
|
693
955
|
const warnings = await cleanupRemovedAppState(context, projectId, removedApp.id);
|
|
@@ -695,6 +957,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
695
957
|
command: "app.remove",
|
|
696
958
|
result: {
|
|
697
959
|
projectId,
|
|
960
|
+
verboseContext: toAppVerboseContext(target),
|
|
698
961
|
app: {
|
|
699
962
|
id: removedApp.id,
|
|
700
963
|
name: removedApp.name
|
|
@@ -707,6 +970,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
707
970
|
}
|
|
708
971
|
async function resolveAppDomainTarget(context, options, commandName = "app domain") {
|
|
709
972
|
ensurePreviewAppMode(context);
|
|
973
|
+
const compute = await resolveComputeManagementContext(context, options?.configTarget, commandName.replace(/^app /, ""));
|
|
710
974
|
const branch = resolveDomainBranch(options?.branchName);
|
|
711
975
|
if (toBranchKind(branch.name) !== "production") throw new CliError({
|
|
712
976
|
code: "BRANCH_NOT_DEPLOYABLE",
|
|
@@ -722,10 +986,11 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
|
|
|
722
986
|
const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
|
|
723
987
|
branch,
|
|
724
988
|
commandName,
|
|
725
|
-
envProjectId
|
|
989
|
+
envProjectId,
|
|
990
|
+
projectDir: compute.projectDir
|
|
726
991
|
});
|
|
727
992
|
const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
728
|
-
explicitAppName: options?.appName,
|
|
993
|
+
explicitAppName: options?.appName ?? compute.configAppName,
|
|
729
994
|
explicitAppId: envAppId
|
|
730
995
|
});
|
|
731
996
|
await context.stateStore.setSelectedApp(projectId, {
|
|
@@ -738,7 +1003,7 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
|
|
|
738
1003
|
resultTarget: {
|
|
739
1004
|
workspace: target.workspace,
|
|
740
1005
|
project: target.project,
|
|
741
|
-
branch: target.branch,
|
|
1006
|
+
branch: toResultBranch(target.branch),
|
|
742
1007
|
app: {
|
|
743
1008
|
id: selectedApp.id,
|
|
744
1009
|
name: selectedApp.name
|
|
@@ -762,8 +1027,8 @@ async function resolveDomainAppSelection(context, projectId, apps, options) {
|
|
|
762
1027
|
if (selectedApp) return selectedApp;
|
|
763
1028
|
throw usageError("Custom domain requires an existing app on the production branch", "The resolved production branch does not have an app that can receive a custom domain.", "Deploy or promote an app to production first, then rerun the domain command.", ["prisma-cli app deploy --branch production", "prisma-cli app show"], "app");
|
|
764
1029
|
}
|
|
765
|
-
async function resolveDomainByHostname(provider, appId, hostname, command) {
|
|
766
|
-
const matched = (await provider.listDomains(appId).catch((error) => {
|
|
1030
|
+
async function resolveDomainByHostname(provider, appId, hostname, command, signal) {
|
|
1031
|
+
const matched = (await provider.listDomains(appId, { signal }).catch((error) => {
|
|
767
1032
|
throw domainCommandError(command, error, hostname);
|
|
768
1033
|
})).find((domain) => sameDomainHostname(domain.hostname, hostname));
|
|
769
1034
|
if (matched) return matched;
|
|
@@ -994,9 +1259,20 @@ function formatElapsed(milliseconds) {
|
|
|
994
1259
|
const remainingSeconds = seconds % 60;
|
|
995
1260
|
return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
|
|
996
1261
|
}
|
|
997
|
-
async function sleep(milliseconds) {
|
|
1262
|
+
async function sleep(milliseconds, signal) {
|
|
998
1263
|
if (milliseconds <= 0) return;
|
|
999
|
-
|
|
1264
|
+
signal.throwIfAborted();
|
|
1265
|
+
await new Promise((resolve, reject) => {
|
|
1266
|
+
const onAbort = () => {
|
|
1267
|
+
clearTimeout(timeout);
|
|
1268
|
+
reject(signal.reason);
|
|
1269
|
+
};
|
|
1270
|
+
const timeout = setTimeout(() => {
|
|
1271
|
+
signal.removeEventListener("abort", onAbort);
|
|
1272
|
+
resolve();
|
|
1273
|
+
}, milliseconds);
|
|
1274
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1275
|
+
});
|
|
1000
1276
|
}
|
|
1001
1277
|
async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
1002
1278
|
if (options.explicitAppName) {
|
|
@@ -1027,6 +1303,25 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
|
1027
1303
|
firstDeploy: options.firstDeploy
|
|
1028
1304
|
};
|
|
1029
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
|
+
}
|
|
1030
1325
|
const inferredName = await options.inferName();
|
|
1031
1326
|
const matches = findAppsByName(apps, inferredName.name);
|
|
1032
1327
|
if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, inferredName.name, options.firstDeploy);
|
|
@@ -1211,7 +1506,10 @@ function resolveRollbackTarget(deployments, currentLiveDeploymentId) {
|
|
|
1211
1506
|
});
|
|
1212
1507
|
}
|
|
1213
1508
|
async function listApps(context, provider, projectId, branchName) {
|
|
1214
|
-
return provider.listApps(projectId, {
|
|
1509
|
+
return provider.listApps(projectId, {
|
|
1510
|
+
branchName,
|
|
1511
|
+
signal: context.runtime.signal
|
|
1512
|
+
}).then(sortApps).catch((error) => {
|
|
1215
1513
|
if (isMissingProjectError(error)) throw new CliError({
|
|
1216
1514
|
code: "PROJECT_NOT_FOUND",
|
|
1217
1515
|
domain: "project",
|
|
@@ -1229,20 +1527,20 @@ async function requirePreviewAppProvider(context) {
|
|
|
1229
1527
|
return provider;
|
|
1230
1528
|
}
|
|
1231
1529
|
async function requirePreviewAppProviderWithClient(context) {
|
|
1232
|
-
const client = await requireComputeAuth(context.runtime.env);
|
|
1530
|
+
const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
|
|
1233
1531
|
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
1234
1532
|
return {
|
|
1235
1533
|
client,
|
|
1236
|
-
provider: createPreviewAppProvider(client, createPreviewLogAuthOptions(context.runtime.env))
|
|
1534
|
+
provider: createPreviewAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
|
|
1237
1535
|
};
|
|
1238
1536
|
}
|
|
1239
|
-
function createPreviewLogAuthOptions(env) {
|
|
1537
|
+
function createPreviewLogAuthOptions(env, signal) {
|
|
1240
1538
|
const rawToken = env[SERVICE_TOKEN_ENV_VAR]?.trim();
|
|
1241
1539
|
if (rawToken) return {
|
|
1242
1540
|
baseUrl: getApiBaseUrl(env),
|
|
1243
1541
|
getToken: async () => rawToken
|
|
1244
1542
|
};
|
|
1245
|
-
const tokenStorage = new FileTokenStorage(env);
|
|
1543
|
+
const tokenStorage = new FileTokenStorage(env, signal);
|
|
1246
1544
|
return {
|
|
1247
1545
|
baseUrl: getApiBaseUrl(env),
|
|
1248
1546
|
getToken: async () => {
|
|
@@ -1275,18 +1573,22 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
|
|
|
1275
1573
|
async function resolveProjectContext(context, client, explicitProject, options) {
|
|
1276
1574
|
const authState = await requireAuthenticatedAuthState(context);
|
|
1277
1575
|
if (!authState.workspace) throw workspaceRequiredError();
|
|
1278
|
-
const
|
|
1576
|
+
const resolvedResult = await resolveProjectTarget({
|
|
1279
1577
|
context,
|
|
1280
1578
|
workspace: authState.workspace,
|
|
1281
1579
|
explicitProject,
|
|
1282
1580
|
envProjectId: options?.envProjectId,
|
|
1283
|
-
|
|
1581
|
+
projectDir: options?.projectDir,
|
|
1582
|
+
listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
|
|
1284
1583
|
commandName: options?.commandName
|
|
1285
1584
|
});
|
|
1585
|
+
if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
|
|
1586
|
+
const resolved = resolvedResult.value;
|
|
1286
1587
|
const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
|
|
1287
1588
|
return {
|
|
1288
1589
|
...resolved,
|
|
1289
1590
|
branch: {
|
|
1591
|
+
id: null,
|
|
1290
1592
|
name: branch.name,
|
|
1291
1593
|
kind: toBranchKind(branch.name)
|
|
1292
1594
|
}
|
|
@@ -1296,8 +1598,8 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1296
1598
|
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
1297
1599
|
if (!workspace) throw workspaceRequiredError();
|
|
1298
1600
|
const branch = options.branch ?? await resolveDeployBranch(context, void 0);
|
|
1299
|
-
const projects = await listRealWorkspaceProjects(client, workspace);
|
|
1300
|
-
if (explicitProject) return
|
|
1601
|
+
const projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
|
|
1602
|
+
if (explicitProject) return withRemoteDeployBranch(provider, {
|
|
1301
1603
|
workspace,
|
|
1302
1604
|
project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
|
|
1303
1605
|
resolution: {
|
|
@@ -1306,25 +1608,25 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1306
1608
|
targetNameSource: "explicit"
|
|
1307
1609
|
},
|
|
1308
1610
|
localPinAction: "linked"
|
|
1309
|
-
}, branch);
|
|
1611
|
+
}, branch, context.runtime.signal);
|
|
1310
1612
|
if (options.createProjectName) {
|
|
1311
1613
|
const projectName = options.createProjectName.trim();
|
|
1312
1614
|
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1313
|
-
return
|
|
1615
|
+
return withRemoteDeployBranch(provider, {
|
|
1314
1616
|
workspace,
|
|
1315
|
-
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace)),
|
|
1617
|
+
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
|
|
1316
1618
|
resolution: {
|
|
1317
1619
|
projectSource: "created",
|
|
1318
1620
|
targetName: projectName,
|
|
1319
1621
|
targetNameSource: "explicit"
|
|
1320
1622
|
},
|
|
1321
1623
|
localPinAction: "created"
|
|
1322
|
-
}, branch);
|
|
1624
|
+
}, branch, context.runtime.signal);
|
|
1323
1625
|
}
|
|
1324
1626
|
if (options.envProjectId) {
|
|
1325
1627
|
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
1326
1628
|
if (!project) throw projectNotFoundError(options.envProjectId, workspace);
|
|
1327
|
-
return
|
|
1629
|
+
return withRemoteDeployBranch(provider, {
|
|
1328
1630
|
workspace,
|
|
1329
1631
|
project: toProjectSummary(project),
|
|
1330
1632
|
resolution: {
|
|
@@ -1332,14 +1634,14 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1332
1634
|
targetName: options.envProjectId,
|
|
1333
1635
|
targetNameSource: "env"
|
|
1334
1636
|
}
|
|
1335
|
-
}, branch);
|
|
1637
|
+
}, branch, context.runtime.signal);
|
|
1336
1638
|
}
|
|
1337
1639
|
const localPin = options.localPin;
|
|
1338
1640
|
if (localPin.kind === "present") {
|
|
1339
1641
|
if (localPin.pin.workspaceId !== workspace.id) throw localResolutionPinStaleError();
|
|
1340
1642
|
const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
|
|
1341
1643
|
if (!project) throw localResolutionPinStaleError();
|
|
1342
|
-
return
|
|
1644
|
+
return withRemoteDeployBranch(provider, {
|
|
1343
1645
|
workspace,
|
|
1344
1646
|
project: toProjectSummary(project),
|
|
1345
1647
|
resolution: {
|
|
@@ -1347,10 +1649,10 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1347
1649
|
targetName: project.name,
|
|
1348
1650
|
targetNameSource: "local-pin"
|
|
1349
1651
|
}
|
|
1350
|
-
}, branch);
|
|
1652
|
+
}, branch, context.runtime.signal);
|
|
1351
1653
|
}
|
|
1352
1654
|
const platformMapping = await resolveDurablePlatformMapping();
|
|
1353
|
-
if (platformMapping && platformMapping.workspace.id === workspace.id) return
|
|
1655
|
+
if (platformMapping && platformMapping.workspace.id === workspace.id) return withRemoteDeployBranch(provider, {
|
|
1354
1656
|
workspace,
|
|
1355
1657
|
project: toProjectSummary(platformMapping),
|
|
1356
1658
|
resolution: {
|
|
@@ -1358,15 +1660,15 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1358
1660
|
targetName: platformMapping.name,
|
|
1359
1661
|
targetNameSource: "platform-mapping"
|
|
1360
1662
|
}
|
|
1361
|
-
}, branch);
|
|
1362
|
-
if (canPrompt(context) && !context.flags.yes) return
|
|
1363
|
-
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd));
|
|
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));
|
|
1364
1666
|
}
|
|
1365
1667
|
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
|
|
1366
1668
|
const setup = await promptForProjectSetupChoice({
|
|
1367
1669
|
context,
|
|
1368
1670
|
projects,
|
|
1369
|
-
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace),
|
|
1671
|
+
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal),
|
|
1370
1672
|
cancel: {
|
|
1371
1673
|
why: "Deploy needs a Project before it can continue.",
|
|
1372
1674
|
fix: "Choose an existing Project or create a new one, then rerun deploy.",
|
|
@@ -1384,8 +1686,11 @@ async function resolveInteractiveDeployProjectSetup(context, provider, workspace
|
|
|
1384
1686
|
localPinAction: setup.action
|
|
1385
1687
|
};
|
|
1386
1688
|
}
|
|
1387
|
-
async function createProjectForDeploySetup(provider, projectName, workspace) {
|
|
1388
|
-
const created = await provider.createProject({
|
|
1689
|
+
async function createProjectForDeploySetup(provider, projectName, workspace, signal) {
|
|
1690
|
+
const created = await provider.createProject({
|
|
1691
|
+
name: projectName,
|
|
1692
|
+
signal
|
|
1693
|
+
}).catch((error) => {
|
|
1389
1694
|
throw projectCreateFailedError(error, projectName, workspace, {
|
|
1390
1695
|
nextSteps: [
|
|
1391
1696
|
"prisma-cli project list",
|
|
@@ -1402,18 +1707,46 @@ async function createProjectForDeploySetup(provider, projectName, workspace) {
|
|
|
1402
1707
|
workspace
|
|
1403
1708
|
};
|
|
1404
1709
|
}
|
|
1405
|
-
function
|
|
1710
|
+
async function withRemoteDeployBranch(provider, target, branch, signal) {
|
|
1711
|
+
const remoteBranch = await provider.resolveBranch(target.project.id, {
|
|
1712
|
+
branchName: branch.name,
|
|
1713
|
+
signal
|
|
1714
|
+
});
|
|
1406
1715
|
return {
|
|
1407
1716
|
...target,
|
|
1408
1717
|
branch: {
|
|
1409
|
-
|
|
1410
|
-
|
|
1718
|
+
id: remoteBranch.id,
|
|
1719
|
+
name: remoteBranch.name,
|
|
1720
|
+
kind: remoteBranch.role
|
|
1411
1721
|
}
|
|
1412
1722
|
};
|
|
1413
1723
|
}
|
|
1414
1724
|
function toBranchKind(name) {
|
|
1415
1725
|
return name === "production" || name === "main" ? "production" : "preview";
|
|
1416
1726
|
}
|
|
1727
|
+
function toResultBranch(branch) {
|
|
1728
|
+
return {
|
|
1729
|
+
id: branch.id,
|
|
1730
|
+
name: branch.name,
|
|
1731
|
+
kind: branch.kind
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
function toAppVerboseContext(target) {
|
|
1735
|
+
return {
|
|
1736
|
+
workspace: target.workspace,
|
|
1737
|
+
project: target.project,
|
|
1738
|
+
branch: target.branch,
|
|
1739
|
+
resolution: target.resolution
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
function toBranchDatabaseDeployBranch(branch) {
|
|
1743
|
+
if (!branch.id) throw new Error(`Deploy branch "${branch.name}" was not resolved remotely.`);
|
|
1744
|
+
return {
|
|
1745
|
+
id: branch.id,
|
|
1746
|
+
name: branch.name,
|
|
1747
|
+
kind: branch.kind
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1417
1750
|
function assertExclusiveDeployProjectInputs(options) {
|
|
1418
1751
|
const provided = [
|
|
1419
1752
|
options.projectRef ? "--project" : null,
|
|
@@ -1432,7 +1765,7 @@ async function resolveDeployBranch(context, explicitBranchName) {
|
|
|
1432
1765
|
name: explicitBranchName,
|
|
1433
1766
|
annotation: "set by --branch"
|
|
1434
1767
|
};
|
|
1435
|
-
const gitBranch = await readLocalGitBranch(context.runtime.cwd);
|
|
1768
|
+
const gitBranch = await readLocalGitBranch(context.runtime.cwd, context.runtime.signal);
|
|
1436
1769
|
if (gitBranch) return {
|
|
1437
1770
|
name: gitBranch,
|
|
1438
1771
|
annotation: "from local Git branch"
|
|
@@ -1442,45 +1775,130 @@ async function resolveDeployBranch(context, explicitBranchName) {
|
|
|
1442
1775
|
annotation: "default"
|
|
1443
1776
|
};
|
|
1444
1777
|
}
|
|
1445
|
-
async function
|
|
1446
|
-
|
|
1447
|
-
if (
|
|
1448
|
-
|
|
1449
|
-
const
|
|
1450
|
-
if (
|
|
1451
|
-
|
|
1452
|
-
return null;
|
|
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;
|
|
1453
1785
|
}
|
|
1454
|
-
|
|
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
|
+
};
|
|
1455
1806
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
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();
|
|
1461
1824
|
try {
|
|
1462
|
-
await access(
|
|
1463
|
-
|
|
1464
|
-
} catch {
|
|
1465
|
-
|
|
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
|
+
}
|
|
1466
1884
|
}
|
|
1467
1885
|
}
|
|
1468
1886
|
async function resolveDeployFramework(context, options) {
|
|
1469
|
-
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
|
|
1887
|
+
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, options.requestedFrameworkAnnotation ?? "set by --framework");
|
|
1470
1888
|
if (options.entrypoint) return {
|
|
1471
1889
|
key: "bun",
|
|
1472
1890
|
buildType: "bun",
|
|
1473
1891
|
displayName: "Bun",
|
|
1474
|
-
annotation: "set by --entry"
|
|
1892
|
+
annotation: options.entrypointAnnotation ?? "set by --entry"
|
|
1475
1893
|
};
|
|
1476
|
-
const detected = await detectDeployFramework(context.runtime.
|
|
1894
|
+
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
1477
1895
|
if (detected) return detected;
|
|
1478
|
-
throw frameworkNotDetectedError(
|
|
1896
|
+
throw frameworkNotDetectedError(options.appDir);
|
|
1479
1897
|
}
|
|
1480
|
-
function resolveDeployRuntime(requestedHttpPort, framework) {
|
|
1898
|
+
function resolveDeployRuntime(requestedHttpPort, requestedHttpPortAnnotation, framework) {
|
|
1481
1899
|
if (requestedHttpPort) return {
|
|
1482
1900
|
port: parseDeployHttpPort(requestedHttpPort),
|
|
1483
|
-
annotation: "set by --http-port"
|
|
1901
|
+
annotation: requestedHttpPortAnnotation ?? "set by --http-port"
|
|
1484
1902
|
};
|
|
1485
1903
|
return {
|
|
1486
1904
|
port: FRAMEWORK_DEFAULT_HTTP_PORT,
|
|
@@ -1491,65 +1909,62 @@ function assertSupportedEntrypointForRequestedDeployShape(options) {
|
|
|
1491
1909
|
if (!options.requestedFramework) return;
|
|
1492
1910
|
assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
|
|
1493
1911
|
}
|
|
1494
|
-
async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint) {
|
|
1912
|
+
async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signal) {
|
|
1495
1913
|
if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
|
|
1496
|
-
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd));
|
|
1914
|
+
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
|
|
1497
1915
|
if (packageEntrypoint) return packageEntrypoint;
|
|
1498
|
-
|
|
1499
|
-
|
|
1916
|
+
const defaultEntrypoint = frameworkFromAlias(framework.key)?.defaultEntrypoint;
|
|
1917
|
+
if (!defaultEntrypoint) return;
|
|
1918
|
+
signal.throwIfAborted();
|
|
1500
1919
|
try {
|
|
1501
1920
|
await access(path.join(cwd, defaultEntrypoint));
|
|
1921
|
+
signal.throwIfAborted();
|
|
1502
1922
|
return defaultEntrypoint;
|
|
1503
1923
|
} catch (error) {
|
|
1924
|
+
if (signal.aborted) throw error;
|
|
1504
1925
|
if (error.code !== "ENOENT") throw error;
|
|
1505
1926
|
return;
|
|
1506
1927
|
}
|
|
1507
1928
|
}
|
|
1508
|
-
async function detectDeployFramework(cwd) {
|
|
1509
|
-
const packageJson = await readBunPackageJson(cwd);
|
|
1510
|
-
const
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
};
|
|
1523
|
-
if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
|
|
1524
|
-
key: "tanstack-start",
|
|
1525
|
-
buildType: "tanstack-start",
|
|
1526
|
-
displayName: "TanStack Start",
|
|
1527
|
-
annotation: "detected from package.json"
|
|
1528
|
-
};
|
|
1929
|
+
async function detectDeployFramework(cwd, signal) {
|
|
1930
|
+
const packageJson = await readBunPackageJson(cwd, signal);
|
|
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
|
+
}
|
|
1529
1943
|
return null;
|
|
1530
1944
|
}
|
|
1531
|
-
async function
|
|
1532
|
-
for (const candidate of
|
|
1533
|
-
"next.config.js",
|
|
1534
|
-
"next.config.mjs",
|
|
1535
|
-
"next.config.cjs",
|
|
1536
|
-
"next.config.ts",
|
|
1537
|
-
"next.config.mts"
|
|
1538
|
-
]) {
|
|
1945
|
+
async function detectFrameworkConfigFile(cwd, framework, signal) {
|
|
1946
|
+
for (const candidate of framework.detectConfigFiles) {
|
|
1539
1947
|
const filePath = path.join(cwd, candidate);
|
|
1948
|
+
signal.throwIfAborted();
|
|
1540
1949
|
try {
|
|
1541
|
-
const content = await readFile(filePath,
|
|
1950
|
+
const content = await readFile(filePath, {
|
|
1951
|
+
encoding: "utf8",
|
|
1952
|
+
signal
|
|
1953
|
+
});
|
|
1542
1954
|
return {
|
|
1543
1955
|
exists: true,
|
|
1544
|
-
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
|
|
1956
|
+
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content),
|
|
1957
|
+
path: filePath
|
|
1545
1958
|
};
|
|
1546
1959
|
} catch (error) {
|
|
1960
|
+
if (signal.aborted) throw error;
|
|
1547
1961
|
if (error.code !== "ENOENT") throw error;
|
|
1548
1962
|
}
|
|
1549
1963
|
}
|
|
1550
1964
|
return {
|
|
1551
1965
|
exists: false,
|
|
1552
|
-
standalone: false
|
|
1966
|
+
standalone: false,
|
|
1967
|
+
path: null
|
|
1553
1968
|
};
|
|
1554
1969
|
}
|
|
1555
1970
|
function hasPackageDependency(packageJson, dependencyName) {
|
|
@@ -1562,48 +1977,41 @@ function hasDependency(dependencies, dependencyName) {
|
|
|
1562
1977
|
return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
|
|
1563
1978
|
}
|
|
1564
1979
|
function frameworkFromUserFacingValue(value, annotation) {
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
key: "tanstack-start",
|
|
1591
|
-
buildType: "tanstack-start",
|
|
1592
|
-
displayName: "TanStack Start",
|
|
1593
|
-
annotation
|
|
1594
|
-
};
|
|
1595
|
-
default: throw frameworkNotDetectedError(void 0, value);
|
|
1596
|
-
}
|
|
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
|
+
});
|
|
1597
2005
|
}
|
|
1598
2006
|
function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
1599
|
-
const supported =
|
|
2007
|
+
const supported = FRAMEWORKS.map((framework) => framework.displayName).join(", ");
|
|
1600
2008
|
const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
|
|
1601
2009
|
return new CliError({
|
|
1602
2010
|
code: "FRAMEWORK_NOT_DETECTED",
|
|
1603
2011
|
domain: "app",
|
|
1604
2012
|
summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
|
|
1605
2013
|
why: `Supported Beta frameworks: ${supported}.`,
|
|
1606
|
-
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.`,
|
|
1607
2015
|
exitCode: 2,
|
|
1608
2016
|
nextSteps: [
|
|
1609
2017
|
"prisma-cli app deploy --framework nextjs",
|
|
@@ -1616,10 +2024,24 @@ function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
|
1616
2024
|
}
|
|
1617
2025
|
async function maybeRenderDeploySetupBlock(context, details) {
|
|
1618
2026
|
if (context.flags.json || context.flags.quiet) return;
|
|
1619
|
-
const directory =
|
|
2027
|
+
const directory = formatAppDirectoryLabel(context.runtime.cwd, details.appDir);
|
|
1620
2028
|
const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
|
|
1621
2029
|
context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
|
|
1622
2030
|
}
|
|
2031
|
+
function maybeRenderDeployBuildSettings(context, resolution) {
|
|
2032
|
+
if (context.flags.json || context.flags.quiet) return;
|
|
2033
|
+
const settings = resolution.settings;
|
|
2034
|
+
const title = resolution.status === "config" ? `Using ${resolution.relativeConfigPath}` : "Build settings";
|
|
2035
|
+
context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [{
|
|
2036
|
+
label: "Build Command",
|
|
2037
|
+
value: settings.buildCommand ?? "none",
|
|
2038
|
+
origin: settings.buildCommandSource ?? void 0
|
|
2039
|
+
}, {
|
|
2040
|
+
label: "Output Directory",
|
|
2041
|
+
value: settings.outputDirectory,
|
|
2042
|
+
origin: settings.outputDirectorySource ?? void 0
|
|
2043
|
+
}]).join("\n")}\n\n`);
|
|
2044
|
+
}
|
|
1623
2045
|
function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
|
|
1624
2046
|
if (context.flags.json || context.flags.quiet) return;
|
|
1625
2047
|
context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
|
|
@@ -1629,10 +2051,14 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1629
2051
|
framework: options.framework,
|
|
1630
2052
|
runtime: options.runtime
|
|
1631
2053
|
};
|
|
2054
|
+
maybeRenderDeploySettingsPreview(context, {
|
|
2055
|
+
framework: options.framework,
|
|
2056
|
+
runtime: options.runtime
|
|
2057
|
+
});
|
|
1632
2058
|
if (!await confirmPrompt({
|
|
1633
2059
|
input: context.runtime.stdin,
|
|
1634
2060
|
output: context.runtime.stderr,
|
|
1635
|
-
message: "Customize settings?",
|
|
2061
|
+
message: "Customize build settings?",
|
|
1636
2062
|
initialValue: false
|
|
1637
2063
|
})) return {
|
|
1638
2064
|
framework: options.framework,
|
|
@@ -1642,9 +2068,9 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1642
2068
|
input: context.runtime.stdin,
|
|
1643
2069
|
output: context.runtime.stderr,
|
|
1644
2070
|
message: `Framework (${options.framework.displayName})`,
|
|
1645
|
-
choices:
|
|
1646
|
-
label:
|
|
1647
|
-
value: framework
|
|
2071
|
+
choices: FRAMEWORKS.map((framework) => ({
|
|
2072
|
+
label: framework.displayName,
|
|
2073
|
+
value: framework.key
|
|
1648
2074
|
}))
|
|
1649
2075
|
}), "set by you");
|
|
1650
2076
|
const requestedPort = await textPrompt({
|
|
@@ -1677,13 +2103,15 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1677
2103
|
runtime
|
|
1678
2104
|
};
|
|
1679
2105
|
}
|
|
1680
|
-
function
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
2106
|
+
function maybeRenderDeploySettingsPreview(context, options) {
|
|
2107
|
+
if (context.flags.quiet || context.flags.json) return;
|
|
2108
|
+
context.output.stderr.write(`Detected ${options.framework.displayName}\n${renderDeploySettingsPreview(context.ui, [{
|
|
2109
|
+
key: "framework",
|
|
2110
|
+
value: options.framework.displayName
|
|
2111
|
+
}, {
|
|
2112
|
+
key: "runtime",
|
|
2113
|
+
value: `HTTP ${options.runtime.port}`
|
|
2114
|
+
}]).join("\n")}\n\n`);
|
|
1687
2115
|
}
|
|
1688
2116
|
function validateDeployHttpPortText(value) {
|
|
1689
2117
|
if (!value?.trim()) return;
|
|
@@ -1698,10 +2126,15 @@ function formatDeployDirectory(cwd) {
|
|
|
1698
2126
|
const basename = path.basename(cwd);
|
|
1699
2127
|
return basename ? `./${basename}` : ".";
|
|
1700
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
|
+
}
|
|
1701
2134
|
async function readCurrentWorkspaceId(context) {
|
|
1702
2135
|
const state = await context.stateStore.read();
|
|
1703
2136
|
if (state.auth?.workspaceId) return state.auth.workspaceId;
|
|
1704
|
-
return (await readAuthState(context.runtime.env)).workspace?.id ?? null;
|
|
2137
|
+
return (await readAuthState(context.runtime.env, context.runtime.signal)).workspace?.id ?? null;
|
|
1705
2138
|
}
|
|
1706
2139
|
function normalizeBuildType(requestedBuildType) {
|
|
1707
2140
|
if (!requestedBuildType) return "auto";
|
|
@@ -1717,15 +2150,25 @@ function getBuildTypeExamples(commandName) {
|
|
|
1717
2150
|
});
|
|
1718
2151
|
}
|
|
1719
2152
|
function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
|
|
1720
|
-
if (buildType !== "auto" && buildType
|
|
2153
|
+
if (buildType !== "auto" && !ENTRYPOINT_BUILD_TYPES.includes(buildType) && entrypoint) {
|
|
1721
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");
|
|
1722
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");
|
|
1723
2156
|
}
|
|
1724
2157
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
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");
|
|
1729
2172
|
}
|
|
1730
2173
|
function parseLocalPort(requestedPort) {
|
|
1731
2174
|
if (!requestedPort) return DEFAULT_LOCAL_DEV_PORT;
|
|
@@ -1852,6 +2295,18 @@ function localResolutionPinStaleError() {
|
|
|
1852
2295
|
]
|
|
1853
2296
|
});
|
|
1854
2297
|
}
|
|
2298
|
+
function localPinReadErrorToDeployError(error) {
|
|
2299
|
+
return matchError(error, {
|
|
2300
|
+
LocalResolutionPinInvalidJsonError: () => localResolutionPinStaleError(),
|
|
2301
|
+
LocalResolutionPinInvalidShapeError: () => localResolutionPinStaleError(),
|
|
2302
|
+
LocalResolutionPinReadAbortedError: (error) => {
|
|
2303
|
+
throw error;
|
|
2304
|
+
},
|
|
2305
|
+
UnhandledException: (error) => {
|
|
2306
|
+
throw error;
|
|
2307
|
+
}
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
1855
2310
|
function readDeployEnvOverride(context, name) {
|
|
1856
2311
|
const value = context.runtime.env[name]?.trim();
|
|
1857
2312
|
return value ? value : void 0;
|