@prisma/cli 8.0.0-rc.1 → 8.0.0-rc.2-dev.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +15410 -16
- package/dist/deploy-framework-C7bQM00A.js +23 -0
- package/dist/{v8/sender.js → sender.js} +2 -2
- package/package.json +11 -15
- package/dist/adapters/git.js +0 -54
- package/dist/adapters/local-state.js +0 -155
- package/dist/auth/client.js +0 -30
- package/dist/auth/errors.js +0 -50
- package/dist/auth/guard.js +0 -39
- package/dist/auth/login.js +0 -471
- package/dist/auth/operations.js +0 -208
- package/dist/auth/recipient.js +0 -42
- package/dist/auth/token-storage.js +0 -439
- package/dist/auth/workspaces.js +0 -171
- package/dist/cli-command.js +0 -14
- package/dist/cli-name.js +0 -12
- package/dist/cli2.js +0 -136
- package/dist/command-arguments.js +0 -12
- package/dist/commands/agent/index.js +0 -60
- package/dist/commands/app/index.js +0 -350
- package/dist/commands/auth/index.js +0 -95
- package/dist/commands/branch/index.js +0 -27
- package/dist/commands/bucket/index.js +0 -123
- package/dist/commands/build/index.js +0 -29
- package/dist/commands/database/index.js +0 -249
- package/dist/commands/env.js +0 -99
- package/dist/commands/feedback/index.js +0 -20
- package/dist/commands/git/index.js +0 -36
- package/dist/commands/init/index.js +0 -33
- package/dist/commands/project/index.js +0 -118
- package/dist/commands/version/index.js +0 -18
- package/dist/controllers/agent-setup.js +0 -52
- package/dist/controllers/agent.js +0 -228
- package/dist/controllers/app-env-api.js +0 -55
- package/dist/controllers/app-env-file.js +0 -181
- package/dist/controllers/app-env.js +0 -502
- package/dist/controllers/app.js +0 -2508
- package/dist/controllers/auth.js +0 -124
- package/dist/controllers/branch.js +0 -92
- package/dist/controllers/bucket.js +0 -184
- package/dist/controllers/build.js +0 -88
- package/dist/controllers/database.js +0 -463
- package/dist/controllers/feedback.js +0 -90
- package/dist/controllers/init.js +0 -814
- package/dist/controllers/project.js +0 -892
- package/dist/controllers/select-prompt-port.js +0 -13
- package/dist/controllers/version.js +0 -12
- package/dist/errors.js +0 -102
- package/dist/lib/agent/cli-command.js +0 -20
- package/dist/lib/agent/constants.js +0 -12
- package/dist/lib/agent/package-manager.js +0 -99
- package/dist/lib/agent/setup-status.js +0 -83
- package/dist/lib/app/app-provider.js +0 -617
- package/dist/lib/app/branch-database-api.js +0 -102
- package/dist/lib/app/branch-database-deploy.js +0 -353
- package/dist/lib/app/branch-database.js +0 -234
- package/dist/lib/app/build-settings.js +0 -98
- package/dist/lib/app/build.js +0 -82
- package/dist/lib/app/bun-project.js +0 -45
- package/dist/lib/app/compute-config.js +0 -145
- package/dist/lib/app/deploy-output.js +0 -24
- package/dist/lib/app/deploy-plan.js +0 -59
- package/dist/lib/app/deploy-progress.js +0 -100
- package/dist/lib/app/domain-guidance.js +0 -14
- package/dist/lib/app/env-config.js +0 -67
- package/dist/lib/app/env-file.js +0 -82
- package/dist/lib/app/env-vars.js +0 -50
- package/dist/lib/app/local-dev.js +0 -109
- package/dist/lib/app/production-deploy-gate.js +0 -162
- package/dist/lib/app/read-branch.js +0 -30
- package/dist/lib/bucket/provider.js +0 -139
- package/dist/lib/database/provider.js +0 -385
- package/dist/lib/diagnostics.js +0 -16
- package/dist/lib/feedback.js +0 -15
- package/dist/lib/fs/home-path.js +0 -24
- package/dist/lib/git/local-branch.js +0 -53
- package/dist/lib/git/local-status.js +0 -57
- package/dist/lib/project/interactive-setup.js +0 -57
- package/dist/lib/project/local-pin.js +0 -200
- package/dist/lib/project/provider.js +0 -93
- package/dist/lib/project/resolution.js +0 -389
- package/dist/lib/project/setup.js +0 -138
- package/dist/lib/version.js +0 -56
- package/dist/lib/workspace-id.js +0 -18
- package/dist/output/patterns.js +0 -90
- package/dist/presenters/agent.js +0 -74
- package/dist/presenters/app-env.js +0 -265
- package/dist/presenters/app.js +0 -672
- package/dist/presenters/auth.js +0 -184
- package/dist/presenters/branch.js +0 -46
- package/dist/presenters/bucket.js +0 -174
- package/dist/presenters/database.js +0 -462
- package/dist/presenters/feedback.js +0 -26
- package/dist/presenters/init.js +0 -30
- package/dist/presenters/project.js +0 -268
- package/dist/presenters/verbose-context.js +0 -64
- package/dist/presenters/version.js +0 -29
- package/dist/shell/cli-command.js +0 -2
- package/dist/shell/command-meta.js +0 -923
- package/dist/shell/command-runner.js +0 -119
- package/dist/shell/diagnostics-output.js +0 -57
- package/dist/shell/global-flags.js +0 -37
- package/dist/shell/help.js +0 -90
- package/dist/shell/output.js +0 -144
- package/dist/shell/prompt.js +0 -48
- package/dist/shell/runtime.js +0 -38
- package/dist/shell/ui.js +0 -116
- package/dist/state-dir.js +0 -12
- package/dist/update-check.js +0 -248
- package/dist/v8/cli.js +0 -14699
package/dist/controllers/app.js
DELETED
|
@@ -1,2508 +0,0 @@
|
|
|
1
|
-
import { formatCommandArgument } from "../command-arguments.js";
|
|
2
|
-
import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../errors.js";
|
|
3
|
-
import { renderCommandHeader } from "../shell/ui.js";
|
|
4
|
-
import { getApiBaseUrl } from "../auth/client.js";
|
|
5
|
-
import { sameWorkspaceId } from "../lib/workspace-id.js";
|
|
6
|
-
import { FileTokenStorage } from "../auth/token-storage.js";
|
|
7
|
-
import { authenticatedManagementApiClient } from "../auth/guard.js";
|
|
8
|
-
import { readAuthState } from "../auth/operations.js";
|
|
9
|
-
import { canPrompt } from "../shell/runtime.js";
|
|
10
|
-
import { writeJsonEvent } from "../shell/output.js";
|
|
11
|
-
import { APP_BUILD_TYPES, APP_BUILD_TYPE_LABELS, RESOLVED_APP_BUILD_TYPES, executeAppBuild } from "../lib/app/build.js";
|
|
12
|
-
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
13
|
-
import { DomainApiError, createAppProvider } from "../lib/app/app-provider.js";
|
|
14
|
-
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
15
|
-
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
16
|
-
import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings } from "../lib/app/build-settings.js";
|
|
17
|
-
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
18
|
-
import { computeConfigErrorToCliError, computeFrameworkToBuildType, loadComputeConfig as loadComputeConfig$1, mergeComputeDeployInputs, mergeComputeLocalInputs } from "../lib/app/compute-config.js";
|
|
19
|
-
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
20
|
-
import { describeDeployAllFailure, perAppInputsForDeployAll, planAppDeploy } from "../lib/app/deploy-plan.js";
|
|
21
|
-
import { createDeployProgress, createDeployProgressState, createPromoteProgress } from "../lib/app/deploy-progress.js";
|
|
22
|
-
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
23
|
-
import { DEFAULT_LOCAL_DEV_PORT, runLocalApp } from "../lib/app/local-dev.js";
|
|
24
|
-
import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
|
|
25
|
-
import { resolveReadBranch } from "../lib/app/read-branch.js";
|
|
26
|
-
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
27
|
-
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
28
|
-
import { buildProjectSetupNextActions, inferTargetName, localProjectWorkspaceMismatchError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
29
|
-
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
30
|
-
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
31
|
-
import { maybePromptForAgentSetup } from "./agent-setup.js";
|
|
32
|
-
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
33
|
-
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
34
|
-
import { listRealWorkspaceProjects } from "./project.js";
|
|
35
|
-
import path from "node:path";
|
|
36
|
-
import { access } from "node:fs/promises";
|
|
37
|
-
import { COMPUTE_CONFIG_FILENAME, COMPUTE_REGIONS, ComputeConfigTargetRequiredError, ENTRYPOINT_BUILD_TYPES, FRAMEWORKS, LOCAL_DEV_BUILD_TYPES, computeTargetAppDir, frameworkByKey, frameworkFromAlias, inferComputeTargetFromCwd, isConfigBackedBuildType, selectComputeDeployTarget } from "@prisma/compute-sdk/config";
|
|
38
|
-
import { SERVICE_TOKEN_ENV_VAR } from "@prisma/cli-engine";
|
|
39
|
-
import open from "open";
|
|
40
|
-
import { detectComputeAppFromDirectory } from "@prisma/compute-sdk/config/directory";
|
|
41
|
-
import { matchError } from "better-result";
|
|
42
|
-
//#region src/controllers/app.ts
|
|
43
|
-
const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
|
|
44
|
-
const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
|
|
45
|
-
const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
|
|
46
|
-
const COMPUTE_REGION_IDS = new Set(COMPUTE_REGIONS);
|
|
47
|
-
const APP_COMMAND_PREFIX = /^app /;
|
|
48
|
-
const TRAILING_DOT = /\.$/;
|
|
49
|
-
const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
50
|
-
const CNAME_TO_PHRASE = /\bcname(?:s)?\s+to\b/;
|
|
51
|
-
const PRISMA_BUILD_HOSTNAME = /\b((?:[a-z0-9-]+\.)+prisma\.build)\b/i;
|
|
52
|
-
const WAIT_TIMEOUT_DURATION = /^(\d+)(ms|s|m|h)$/;
|
|
53
|
-
const WAIT_TIMEOUT_UNIT_MULTIPLIER_MS = {
|
|
54
|
-
ms: 1,
|
|
55
|
-
s: 1e3,
|
|
56
|
-
m: 60 * 1e3,
|
|
57
|
-
h: 3600 * 1e3
|
|
58
|
-
};
|
|
59
|
-
const NEXTJS_MENTION = /next\.?js/i;
|
|
60
|
-
const STANDALONE_OUTPUT_MENTION = /standalone output/i;
|
|
61
|
-
async function runAppBuild(context, options) {
|
|
62
|
-
const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "build");
|
|
63
|
-
const merged = mergeComputeLocalInputs({
|
|
64
|
-
cli: {
|
|
65
|
-
entrypoint: options?.entrypoint,
|
|
66
|
-
buildType: options?.buildType
|
|
67
|
-
},
|
|
68
|
-
target: compute.target
|
|
69
|
-
});
|
|
70
|
-
const appDir = await resolveComputeAppDir(context, compute);
|
|
71
|
-
let buildType = normalizeBuildType(merged.buildType);
|
|
72
|
-
if (compute.target?.build && buildType === "auto") {
|
|
73
|
-
const detected = await detectDeployFramework(appDir, context.runtime.signal);
|
|
74
|
-
if (!detected) throw frameworkNotDetectedError(appDir);
|
|
75
|
-
buildType = detected.buildType;
|
|
76
|
-
}
|
|
77
|
-
assertSupportedEntrypoint(buildType, merged.entrypoint, "build");
|
|
78
|
-
if (compute.target?.build && buildType !== "auto") assertConfigBackedBuildSettings(buildType);
|
|
79
|
-
const buildSettings = compute.config && compute.target?.build && isConfigBackedBuildType(buildType) ? (await resolveConfiguredAppBuildSettings({
|
|
80
|
-
appPath: appDir,
|
|
81
|
-
buildType,
|
|
82
|
-
configured: compute.target.build,
|
|
83
|
-
configPath: compute.config.configPath,
|
|
84
|
-
signal: context.runtime.signal
|
|
85
|
-
})).settings : void 0;
|
|
86
|
-
try {
|
|
87
|
-
const { artifact, buildType: actualBuildType } = await executeAppBuild({
|
|
88
|
-
appPath: appDir,
|
|
89
|
-
entrypoint: merged.entrypoint,
|
|
90
|
-
buildType,
|
|
91
|
-
buildSettings,
|
|
92
|
-
signal: context.runtime.signal
|
|
93
|
-
});
|
|
94
|
-
return {
|
|
95
|
-
command: "app.build",
|
|
96
|
-
result: {
|
|
97
|
-
directory: artifact.directory,
|
|
98
|
-
entrypoint: artifact.entrypoint,
|
|
99
|
-
buildType: actualBuildType
|
|
100
|
-
},
|
|
101
|
-
warnings: [],
|
|
102
|
-
nextSteps: ["prisma-cli app deploy"]
|
|
103
|
-
};
|
|
104
|
-
} catch (error) {
|
|
105
|
-
if (buildType === "auto" && isAutoBuildDetectionError(error)) throw usageError("App build requires an explicit framework when detection is ambiguous", `This preview auto-detects clear project shapes for ${RESOLVED_APP_BUILD_TYPES.map(formatBuildTypeName).join(", ")}.`, "Pass a supported --build-type value, or pass --entry <path> for a Bun app.", getBuildTypeExamples("build"), "app");
|
|
106
|
-
throw buildFailedError("Local app build failed", error);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
async function runAppRun(context, options) {
|
|
110
|
-
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");
|
|
111
|
-
const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "run");
|
|
112
|
-
const merged = mergeComputeLocalInputs({
|
|
113
|
-
cli: {
|
|
114
|
-
entrypoint: options?.entrypoint,
|
|
115
|
-
buildType: options?.buildType,
|
|
116
|
-
port: options?.port
|
|
117
|
-
},
|
|
118
|
-
target: compute.target
|
|
119
|
-
});
|
|
120
|
-
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} 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");
|
|
121
|
-
const appDir = await resolveComputeAppDir(context, compute);
|
|
122
|
-
const buildType = normalizeBuildType(merged.buildType);
|
|
123
|
-
assertSupportedEntrypoint(buildType, merged.entrypoint, "run");
|
|
124
|
-
const port = parseLocalPort(merged.port);
|
|
125
|
-
const framework = await resolveLocalRunFramework(context, {
|
|
126
|
-
requestedBuildType: buildType,
|
|
127
|
-
configFramework: compute.target?.framework ?? null,
|
|
128
|
-
appDir,
|
|
129
|
-
entrypoint: merged.entrypoint
|
|
130
|
-
});
|
|
131
|
-
const entrypoint = framework.buildType === "bun" ? await resolveDeployEntrypoint(appDir, framework, merged.entrypoint, context.runtime.signal) : merged.entrypoint;
|
|
132
|
-
let runResult;
|
|
133
|
-
try {
|
|
134
|
-
runResult = await runLocalApp({
|
|
135
|
-
appPath: appDir,
|
|
136
|
-
buildType: framework.buildType,
|
|
137
|
-
entrypoint,
|
|
138
|
-
port,
|
|
139
|
-
env: context.runtime.env,
|
|
140
|
-
signal: context.runtime.signal
|
|
141
|
-
});
|
|
142
|
-
} catch (error) {
|
|
143
|
-
throw runFailedError("Local app run failed", error);
|
|
144
|
-
}
|
|
145
|
-
if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM") throw new DOMException("Command canceled", "AbortError");
|
|
146
|
-
else if (runResult.exitCode !== 0) throw runFailedError("Local app run failed", `The ${formatFrameworkName(runResult.framework)} process exited with code ${runResult.exitCode}.`, runResult.exitCode);
|
|
147
|
-
return {
|
|
148
|
-
command: "app.run",
|
|
149
|
-
result: {
|
|
150
|
-
framework: runResult.framework,
|
|
151
|
-
entrypoint: runResult.entrypoint,
|
|
152
|
-
port: runResult.port,
|
|
153
|
-
command: runResult.command
|
|
154
|
-
},
|
|
155
|
-
warnings: [],
|
|
156
|
-
nextSteps: []
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
async function runAppDeploy(context, appName, options) {
|
|
160
|
-
const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
|
|
161
|
-
if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, "deploy");
|
|
162
|
-
const config = loaded.value;
|
|
163
|
-
const plan = planAppDeploy({
|
|
164
|
-
config,
|
|
165
|
-
requestedTarget: options?.configTarget ?? (config ? inferComputeTargetFromCwd(config, context.runtime.cwd) : void 0),
|
|
166
|
-
hasCreateProject: options?.createProjectName !== void 0
|
|
167
|
-
});
|
|
168
|
-
if (plan.mode === "all") return runAppDeployAll(context, config, plan.targets, appName, options);
|
|
169
|
-
return runSingleAppDeploy(context, appName, options, config);
|
|
170
|
-
}
|
|
171
|
-
async function runAppDeployAll(context, config, plannedTargets, appName, options) {
|
|
172
|
-
assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options);
|
|
173
|
-
const deployments = [];
|
|
174
|
-
const warnings = [];
|
|
175
|
-
for (const planned of plannedTargets) {
|
|
176
|
-
maybeRenderDeployAllTargetHeader(context, planned);
|
|
177
|
-
const targetOptions = {
|
|
178
|
-
...options,
|
|
179
|
-
configTarget: planned.targetKey,
|
|
180
|
-
createProjectName: planned.bindsCreateProject ? options?.createProjectName : void 0
|
|
181
|
-
};
|
|
182
|
-
try {
|
|
183
|
-
const single = await runSingleAppDeploy(context, void 0, targetOptions, config);
|
|
184
|
-
deployments.push({
|
|
185
|
-
target: planned.targetKey,
|
|
186
|
-
result: single.result
|
|
187
|
-
});
|
|
188
|
-
warnings.push(...single.warnings);
|
|
189
|
-
} catch (error) {
|
|
190
|
-
throw deployAllFailedError(error, plannedTargets, planned.index, deployments);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return {
|
|
194
|
-
command: "app.deploy",
|
|
195
|
-
result: { deployments },
|
|
196
|
-
warnings,
|
|
197
|
-
nextSteps: ["prisma-cli app list-deploys <app>"]
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
function assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options) {
|
|
201
|
-
const used = perAppInputsForDeployAll({
|
|
202
|
-
appName,
|
|
203
|
-
framework: options?.framework,
|
|
204
|
-
entrypoint: options?.entrypoint,
|
|
205
|
-
httpPort: options?.httpPort,
|
|
206
|
-
region: options?.region,
|
|
207
|
-
envAssignments: options?.envAssignments,
|
|
208
|
-
appIdEnvVar: {
|
|
209
|
-
name: PRISMA_APP_ID_ENV_VAR,
|
|
210
|
-
value: readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR)
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
if (used.length === 0) return;
|
|
214
|
-
const targetKeys = plannedTargets.map((target) => target.targetKey);
|
|
215
|
-
throw usageError(`Deploying all apps does not accept ${used.join(", ")}`, `Without a target, app deploy deploys every configured app (${targetKeys.join(", ")}), 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.", targetKeys.map((target) => `prisma-cli app deploy ${target}`), "app");
|
|
216
|
-
}
|
|
217
|
-
function maybeRenderDeployAllTargetHeader(context, planned) {
|
|
218
|
-
if (context.flags.json || context.flags.quiet) return;
|
|
219
|
-
context.output.stderr.write(`${planned.index > 0 ? "\n" : ""}── ${planned.targetKey} (${planned.index + 1}/${planned.total}) ──\n\n`);
|
|
220
|
-
}
|
|
221
|
-
function deployAllFailedError(error, plannedTargets, failedIndex, deployments) {
|
|
222
|
-
if (!(error instanceof CliError)) return error;
|
|
223
|
-
const failure = describeDeployAllFailure({
|
|
224
|
-
targetKeys: plannedTargets.map((target) => target.targetKey),
|
|
225
|
-
failedIndex,
|
|
226
|
-
completed: deployments.map(({ target, result }) => ({
|
|
227
|
-
target,
|
|
228
|
-
deploymentId: result.deployment.id,
|
|
229
|
-
url: result.deployment.url
|
|
230
|
-
}))
|
|
231
|
-
});
|
|
232
|
-
const contextSentence = failure.contextLines.join(" ");
|
|
233
|
-
return new CliError({
|
|
234
|
-
code: error.code,
|
|
235
|
-
domain: error.domain,
|
|
236
|
-
summary: error.summary,
|
|
237
|
-
why: error.humanLines ? error.why : [error.why, contextSentence].filter(Boolean).join(" "),
|
|
238
|
-
fix: error.fix,
|
|
239
|
-
debug: error.debug,
|
|
240
|
-
where: error.where,
|
|
241
|
-
meta: {
|
|
242
|
-
...error.meta,
|
|
243
|
-
deployAll: {
|
|
244
|
-
failedTarget: failure.failedTarget,
|
|
245
|
-
completed: failure.completed,
|
|
246
|
-
notAttempted: failure.notAttempted
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
docsUrl: error.docsUrl,
|
|
250
|
-
exitCode: error.exitCode,
|
|
251
|
-
nextSteps: error.nextSteps,
|
|
252
|
-
nextActions: error.nextActions,
|
|
253
|
-
humanLines: error.humanLines ? [
|
|
254
|
-
...error.humanLines,
|
|
255
|
-
"",
|
|
256
|
-
...failure.contextLines
|
|
257
|
-
] : void 0
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
|
|
261
|
-
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
262
|
-
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
263
|
-
assertExclusiveDeployProjectInputs({
|
|
264
|
-
projectRef: options?.projectRef,
|
|
265
|
-
createProjectName: options?.createProjectName,
|
|
266
|
-
envProjectId
|
|
267
|
-
});
|
|
268
|
-
const computeConfig = await resolveComputeTargetOrThrow(context, options?.configTarget, "deploy", { preloaded: preloadedConfig });
|
|
269
|
-
const merged = mergeComputeDeployInputs({
|
|
270
|
-
cli: {
|
|
271
|
-
framework: options?.framework,
|
|
272
|
-
entrypoint: options?.entrypoint,
|
|
273
|
-
httpPort: options?.httpPort,
|
|
274
|
-
region: options?.region,
|
|
275
|
-
envInputs: options?.envAssignments
|
|
276
|
-
},
|
|
277
|
-
target: computeConfig.target,
|
|
278
|
-
configFilename: computeConfig.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME
|
|
279
|
-
});
|
|
280
|
-
const appDir = await resolveComputeAppDir(context, computeConfig);
|
|
281
|
-
const projectDir = computeConfig.config?.configDir ?? context.runtime.cwd;
|
|
282
|
-
const agentSetupWarnings = await maybePromptForAgentSetup(context, projectDir);
|
|
283
|
-
const localPin = await readDeployLocalPin({
|
|
284
|
-
projectDir,
|
|
285
|
-
signal: context.runtime.signal,
|
|
286
|
-
envProjectId,
|
|
287
|
-
projectRef: options?.projectRef,
|
|
288
|
-
createProjectName: options?.createProjectName
|
|
289
|
-
});
|
|
290
|
-
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
291
|
-
if (merged.httpPort) parseDeployHttpPort(merged.httpPort.value);
|
|
292
|
-
const deployRegion = normalizeDeployRegionInput(merged.region);
|
|
293
|
-
assertSupportedEntrypointForRequestedDeployShape({
|
|
294
|
-
requestedFramework: merged.framework?.value,
|
|
295
|
-
entrypoint: merged.entrypoint?.value
|
|
296
|
-
});
|
|
297
|
-
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
298
|
-
branch,
|
|
299
|
-
createProjectName: options?.createProjectName,
|
|
300
|
-
createProjectRegion: deployRegion?.value,
|
|
301
|
-
envProjectId,
|
|
302
|
-
localPin
|
|
303
|
-
});
|
|
304
|
-
const localPinResult = await bindDeployProjectDirectory(context, target, projectDir);
|
|
305
|
-
let framework = await resolveDeployFramework(context, {
|
|
306
|
-
requestedFramework: merged.framework?.value,
|
|
307
|
-
requestedFrameworkAnnotation: merged.framework?.annotation,
|
|
308
|
-
entrypoint: merged.entrypoint?.value,
|
|
309
|
-
entrypointAnnotation: merged.entrypoint?.annotation,
|
|
310
|
-
appDir
|
|
311
|
-
});
|
|
312
|
-
let runtime = resolveDeployRuntime(merged.httpPort?.value, merged.httpPort?.annotation, framework);
|
|
313
|
-
assertSupportedEntrypoint(framework.buildType, merged.entrypoint?.value, "deploy");
|
|
314
|
-
const envVars = toOptionalEnvVars(await parseEnvInputs(merged.envInputsFromConfig ? projectDir : context.runtime.cwd, merged.envInputs, { commandName: "deploy" }));
|
|
315
|
-
const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
316
|
-
explicitAppName: appName,
|
|
317
|
-
explicitAppId: envAppId,
|
|
318
|
-
configAppName: merged.configAppName,
|
|
319
|
-
configRegion: deployRegion,
|
|
320
|
-
firstDeploy: Boolean(target.localPinAction),
|
|
321
|
-
inferName: () => inferTargetName(appDir, context.runtime.signal)
|
|
322
|
-
});
|
|
323
|
-
await maybeRenderDeploySetupBlock(context, {
|
|
324
|
-
includeDirectory: !target.localPinAction,
|
|
325
|
-
appDir,
|
|
326
|
-
projectName: target.project.name,
|
|
327
|
-
branchName: target.branch.name,
|
|
328
|
-
appName: selectedApp.displayName
|
|
329
|
-
});
|
|
330
|
-
const customized = await maybeCustomizeDeploySettings(context, {
|
|
331
|
-
framework,
|
|
332
|
-
runtime,
|
|
333
|
-
firstDeploy: selectedApp.firstDeploy,
|
|
334
|
-
explicitFramework: Boolean(merged.framework),
|
|
335
|
-
explicitEntrypoint: Boolean(merged.entrypoint),
|
|
336
|
-
explicitHttpPort: Boolean(merged.httpPort)
|
|
337
|
-
});
|
|
338
|
-
framework = customized.framework;
|
|
339
|
-
runtime = customized.runtime;
|
|
340
|
-
const noPromote = options?.noPromote === true;
|
|
341
|
-
const productionDeployGate = noPromote ? { firstProductionDeploy: false } : await enforceProductionDeployGate(context, provider, {
|
|
342
|
-
appId: selectedApp.appId,
|
|
343
|
-
appName: selectedApp.displayName,
|
|
344
|
-
branchKind: target.branch.kind,
|
|
345
|
-
prod: options?.prod === true
|
|
346
|
-
});
|
|
347
|
-
const buildType = framework.buildType;
|
|
348
|
-
assertSupportedEntrypoint(buildType, merged.entrypoint?.value, "deploy");
|
|
349
|
-
const entrypoint = await resolveDeployEntrypoint(appDir, framework, merged.entrypoint?.value, context.runtime.signal);
|
|
350
|
-
const buildSettingsResolution = await resolveDeployBuildSettings({
|
|
351
|
-
computeConfig,
|
|
352
|
-
appDir,
|
|
353
|
-
buildType,
|
|
354
|
-
signal: context.runtime.signal
|
|
355
|
-
});
|
|
356
|
-
const legacyWarnings = await handleLegacyBuildSettings(context, appDir, buildSettingsResolution.settings);
|
|
357
|
-
maybeRenderDeployBuildSettings(context, buildSettingsResolution);
|
|
358
|
-
const portMapping = parseDeployPortMapping(String(runtime.port));
|
|
359
|
-
const branchDatabaseSetup = await maybeSetupBranchDatabase(context, provider, projectId, toBranchDatabaseDeployBranch(target.branch), {
|
|
360
|
-
db: options?.db,
|
|
361
|
-
providedEnvVars: envVars,
|
|
362
|
-
firstProductionDeploy: productionDeployGate.firstProductionDeploy,
|
|
363
|
-
projectDir
|
|
364
|
-
});
|
|
365
|
-
const progressState = createDeployProgressState();
|
|
366
|
-
const deployStartedAt = Date.now();
|
|
367
|
-
const deployResult = await provider.deployApp({
|
|
368
|
-
cwd: appDir,
|
|
369
|
-
projectId,
|
|
370
|
-
branchName: target.branch.name,
|
|
371
|
-
appId: selectedApp.appId,
|
|
372
|
-
appName: selectedApp.appName,
|
|
373
|
-
region: selectedApp.region,
|
|
374
|
-
entrypoint,
|
|
375
|
-
buildType,
|
|
376
|
-
buildSettings: buildSettingsResolution.settings,
|
|
377
|
-
portMapping,
|
|
378
|
-
envVars,
|
|
379
|
-
skipPromote: noPromote,
|
|
380
|
-
interaction: void 0,
|
|
381
|
-
signal: context.runtime.signal,
|
|
382
|
-
progress: createDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
|
|
383
|
-
}).catch((error) => {
|
|
384
|
-
throw appDeployFailedError(error, progressState);
|
|
385
|
-
});
|
|
386
|
-
const deployDurationMs = Date.now() - deployStartedAt;
|
|
387
|
-
await cacheDeployedApp(context, projectId, deployResult);
|
|
388
|
-
return {
|
|
389
|
-
command: "app.deploy",
|
|
390
|
-
result: {
|
|
391
|
-
workspace: target.workspace,
|
|
392
|
-
project: target.project,
|
|
393
|
-
branch: toResultBranch(target.branch),
|
|
394
|
-
resolution: target.resolution,
|
|
395
|
-
branchDatabase: branchDatabaseSetup.result,
|
|
396
|
-
app: {
|
|
397
|
-
id: deployResult.app.id,
|
|
398
|
-
name: deployResult.app.name
|
|
399
|
-
},
|
|
400
|
-
deployment: deployResult.deployment,
|
|
401
|
-
promoted: deployResult.promoted,
|
|
402
|
-
deploySettings: {
|
|
403
|
-
config: {
|
|
404
|
-
path: computeConfig.config?.relativeConfigPath ?? null,
|
|
405
|
-
status: buildSettingsResolution.status
|
|
406
|
-
},
|
|
407
|
-
buildCommand: {
|
|
408
|
-
value: buildSettingsResolution.settings.buildCommand,
|
|
409
|
-
source: buildSettingsResolution.settings.buildCommandSource
|
|
410
|
-
},
|
|
411
|
-
outputDirectory: {
|
|
412
|
-
value: buildSettingsResolution.settings.outputDirectory,
|
|
413
|
-
source: buildSettingsResolution.settings.outputDirectorySource
|
|
414
|
-
},
|
|
415
|
-
framework: {
|
|
416
|
-
key: framework.key,
|
|
417
|
-
buildType,
|
|
418
|
-
name: framework.displayName,
|
|
419
|
-
source: framework.annotation
|
|
420
|
-
},
|
|
421
|
-
entrypoint: entrypoint ?? buildSettingsResolution.settings.entrypoint ?? null,
|
|
422
|
-
httpPort: runtime.port,
|
|
423
|
-
region: deployResult.app.region ?? selectedApp.region ?? null,
|
|
424
|
-
regionSource: deployRegion?.annotation ?? null,
|
|
425
|
-
envVars: envVarNames(envVars)
|
|
426
|
-
},
|
|
427
|
-
durationMs: deployDurationMs,
|
|
428
|
-
localPin: localPinResult
|
|
429
|
-
},
|
|
430
|
-
warnings: [
|
|
431
|
-
...agentSetupWarnings,
|
|
432
|
-
...legacyWarnings,
|
|
433
|
-
...branchDatabaseSetup.warnings
|
|
434
|
-
],
|
|
435
|
-
nextSteps: deployResult.promoted ? ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`] : [`prisma-cli app promote ${deployResult.deployment.id}`, `prisma-cli app show-deploy ${deployResult.deployment.id}`]
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
async function readDeployLocalPin(options) {
|
|
439
|
-
if (options.envProjectId || options.projectRef || options.createProjectName) return { kind: "missing" };
|
|
440
|
-
const readResult = await readLocalResolutionPin(options.projectDir, options.signal);
|
|
441
|
-
if (readResult.isErr()) throw localPinReadErrorToDeployError(readResult.error);
|
|
442
|
-
return readResult.value;
|
|
443
|
-
}
|
|
444
|
-
async function bindDeployProjectDirectory(context, target, projectDir) {
|
|
445
|
-
if (!target.localPinAction) return;
|
|
446
|
-
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction, projectDir);
|
|
447
|
-
if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
|
|
448
|
-
const projectSetup = setupResult.value;
|
|
449
|
-
maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
|
|
450
|
-
return projectSetup.localPin;
|
|
451
|
-
}
|
|
452
|
-
async function cacheDeployedApp(context, projectId, deployResult) {
|
|
453
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
454
|
-
id: deployResult.app.id,
|
|
455
|
-
name: deployResult.app.name
|
|
456
|
-
});
|
|
457
|
-
const knownLiveDeploymentId = deployResult.promoted ? deployResult.deployment.id : deployResult.app.liveDeploymentId;
|
|
458
|
-
if (knownLiveDeploymentId) await context.stateStore.setKnownLiveDeployment(projectId, deployResult.app.id, knownLiveDeploymentId);
|
|
459
|
-
}
|
|
460
|
-
async function resolveDeployBuildSettings(options) {
|
|
461
|
-
const { computeConfig, appDir, buildType, signal } = options;
|
|
462
|
-
if (computeConfig.target?.build) assertConfigBackedBuildSettings(buildType);
|
|
463
|
-
if (computeConfig.config && computeConfig.target?.build && isConfigBackedBuildType(buildType)) return resolveConfiguredAppBuildSettings({
|
|
464
|
-
appPath: appDir,
|
|
465
|
-
buildType,
|
|
466
|
-
configured: computeConfig.target.build,
|
|
467
|
-
configPath: computeConfig.config.configPath,
|
|
468
|
-
signal
|
|
469
|
-
});
|
|
470
|
-
return resolveInferredAppBuildSettings({
|
|
471
|
-
appPath: appDir,
|
|
472
|
-
buildType,
|
|
473
|
-
signal
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
async function runAppListDeploys(context, appName, projectRef, configTarget) {
|
|
477
|
-
const compute = await resolveComputeManagementContext(context, configTarget, "list-deploys");
|
|
478
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
479
|
-
commandName: "app list-deploys",
|
|
480
|
-
projectDir: compute.projectDir
|
|
481
|
-
});
|
|
482
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
|
|
483
|
-
if (!selectedApp) return {
|
|
484
|
-
command: "app.list-deploys",
|
|
485
|
-
result: {
|
|
486
|
-
projectId,
|
|
487
|
-
verboseContext: toAppVerboseContext(target),
|
|
488
|
-
app: null,
|
|
489
|
-
deployments: []
|
|
490
|
-
},
|
|
491
|
-
warnings: [],
|
|
492
|
-
nextSteps: ["prisma-cli app deploy"]
|
|
493
|
-
};
|
|
494
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
495
|
-
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app deploy"]);
|
|
496
|
-
});
|
|
497
|
-
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
498
|
-
const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId).slice().sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.id.localeCompare(left.id));
|
|
499
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
500
|
-
id: deploymentsResult.app.id,
|
|
501
|
-
name: deploymentsResult.app.name
|
|
502
|
-
});
|
|
503
|
-
return {
|
|
504
|
-
command: "app.list-deploys",
|
|
505
|
-
result: {
|
|
506
|
-
projectId,
|
|
507
|
-
verboseContext: toAppVerboseContext(target),
|
|
508
|
-
app: {
|
|
509
|
-
id: deploymentsResult.app.id,
|
|
510
|
-
name: deploymentsResult.app.name
|
|
511
|
-
},
|
|
512
|
-
deployments
|
|
513
|
-
},
|
|
514
|
-
warnings: [],
|
|
515
|
-
nextSteps: deployments.length > 0 ? [`prisma-cli app show-deploy ${deployments[0]?.id}`] : ["prisma-cli app deploy"]
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
async function runAppShow(context, appName, projectRef, configTarget) {
|
|
519
|
-
const compute = await resolveComputeManagementContext(context, configTarget, "show");
|
|
520
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
521
|
-
commandName: "app show",
|
|
522
|
-
projectDir: compute.projectDir
|
|
523
|
-
});
|
|
524
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
|
|
525
|
-
if (!selectedApp) return {
|
|
526
|
-
command: "app.show",
|
|
527
|
-
result: {
|
|
528
|
-
projectId,
|
|
529
|
-
verboseContext: toAppVerboseContext(target),
|
|
530
|
-
app: null,
|
|
531
|
-
liveDeployment: null,
|
|
532
|
-
liveUrl: null,
|
|
533
|
-
recentDeployments: []
|
|
534
|
-
},
|
|
535
|
-
warnings: [],
|
|
536
|
-
nextSteps: ["prisma-cli app deploy"]
|
|
537
|
-
};
|
|
538
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
539
|
-
throw deployFailedError("Failed to inspect app", error, ["prisma-cli app list-deploys"]);
|
|
540
|
-
});
|
|
541
|
-
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
542
|
-
const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId).slice().sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.id.localeCompare(left.id));
|
|
543
|
-
const liveDeployment = currentLiveDeploymentId ? deployments.find((deployment) => deployment.id === currentLiveDeploymentId) ?? null : null;
|
|
544
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
545
|
-
id: deploymentsResult.app.id,
|
|
546
|
-
name: deploymentsResult.app.name
|
|
547
|
-
});
|
|
548
|
-
return {
|
|
549
|
-
command: "app.show",
|
|
550
|
-
result: {
|
|
551
|
-
projectId,
|
|
552
|
-
verboseContext: toAppVerboseContext(target),
|
|
553
|
-
app: {
|
|
554
|
-
id: deploymentsResult.app.id,
|
|
555
|
-
name: deploymentsResult.app.name
|
|
556
|
-
},
|
|
557
|
-
liveDeployment,
|
|
558
|
-
liveUrl: deploymentsResult.app.liveUrl,
|
|
559
|
-
recentDeployments: deployments.slice(0, 5)
|
|
560
|
-
},
|
|
561
|
-
warnings: [],
|
|
562
|
-
nextSteps: buildAppShowNextSteps(deploymentsResult.app.liveUrl, liveDeployment, deployments)
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
async function runAppShowDeploy(context, deploymentId) {
|
|
566
|
-
const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
|
|
567
|
-
throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
|
|
568
|
-
});
|
|
569
|
-
if (!deployment) throw new CliError({
|
|
570
|
-
code: "DEPLOYMENT_NOT_FOUND",
|
|
571
|
-
domain: "app",
|
|
572
|
-
summary: `Deployment "${deploymentId}" not found`,
|
|
573
|
-
why: "The requested deployment does not exist or is no longer available.",
|
|
574
|
-
fix: "Run prisma-cli app list-deploys to choose an available deployment id.",
|
|
575
|
-
exitCode: 1,
|
|
576
|
-
nextSteps: ["prisma-cli app list-deploys"]
|
|
577
|
-
});
|
|
578
|
-
const workspaceId = deployment?.app ? await readCurrentWorkspaceId(context) : null;
|
|
579
|
-
const rememberedProject = workspaceId ? await context.stateStore.readRememberedProject(workspaceId) : null;
|
|
580
|
-
const knownLiveDeploymentId = deployment?.app && rememberedProject ? await context.stateStore.readKnownLiveDeployment(rememberedProject.id, deployment.app.id) : null;
|
|
581
|
-
const liveDeploymentId = deployment.app?.liveDeploymentId || knownLiveDeploymentId;
|
|
582
|
-
return {
|
|
583
|
-
command: "app.show-deploy",
|
|
584
|
-
result: {
|
|
585
|
-
app: deployment.app ? {
|
|
586
|
-
id: deployment.app.id,
|
|
587
|
-
name: deployment.app.name
|
|
588
|
-
} : null,
|
|
589
|
-
deployment: {
|
|
590
|
-
...deployment.deployment,
|
|
591
|
-
live: liveDeploymentId ? deployment.deployment.id === liveDeploymentId : deployment.deployment.live
|
|
592
|
-
}
|
|
593
|
-
},
|
|
594
|
-
warnings: [],
|
|
595
|
-
nextSteps: []
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
async function runAppOpen(context, appNameArg, projectRef, configTarget) {
|
|
599
|
-
const compute = await resolveComputeManagementContext(context, configTarget, "open");
|
|
600
|
-
const appName = appNameArg ?? compute.configAppName;
|
|
601
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
602
|
-
commandName: "app open",
|
|
603
|
-
projectDir: compute.projectDir
|
|
604
|
-
});
|
|
605
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
606
|
-
if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
|
|
607
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
608
|
-
throw deployFailedError("Failed to resolve app URL", error, ["prisma-cli app show"]);
|
|
609
|
-
});
|
|
610
|
-
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
611
|
-
const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId).slice().sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.id.localeCompare(left.id));
|
|
612
|
-
const liveDeployment = currentLiveDeploymentId ? deployments.find((deployment) => deployment.id === currentLiveDeploymentId) ?? null : null;
|
|
613
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
614
|
-
id: deploymentsResult.app.id,
|
|
615
|
-
name: deploymentsResult.app.name
|
|
616
|
-
});
|
|
617
|
-
if (!liveDeployment) throw noDeploymentsError("No deployments available to open", `The selected app "${deploymentsResult.app.name}" does not have any deployments yet.`);
|
|
618
|
-
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");
|
|
619
|
-
const shouldOpen = canPrompt(context);
|
|
620
|
-
if (shouldOpen) {
|
|
621
|
-
context.runtime.signal.throwIfAborted();
|
|
622
|
-
await open(deploymentsResult.app.liveUrl);
|
|
623
|
-
context.runtime.signal.throwIfAborted();
|
|
624
|
-
}
|
|
625
|
-
return {
|
|
626
|
-
command: "app.open",
|
|
627
|
-
result: {
|
|
628
|
-
projectId,
|
|
629
|
-
verboseContext: toAppVerboseContext(target),
|
|
630
|
-
app: {
|
|
631
|
-
id: deploymentsResult.app.id,
|
|
632
|
-
name: deploymentsResult.app.name
|
|
633
|
-
},
|
|
634
|
-
url: deploymentsResult.app.liveUrl,
|
|
635
|
-
opened: shouldOpen
|
|
636
|
-
},
|
|
637
|
-
warnings: [],
|
|
638
|
-
nextSteps: ["prisma-cli app show", `prisma-cli app show-deploy ${liveDeployment.id}`]
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
async function runAppDomainAdd(context, hostname, options) {
|
|
642
|
-
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
643
|
-
const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
|
|
644
|
-
const added = await target.provider.addDomain({
|
|
645
|
-
appId: target.app.id,
|
|
646
|
-
hostname: normalizedHostname,
|
|
647
|
-
signal: context.runtime.signal
|
|
648
|
-
}).catch((error) => {
|
|
649
|
-
throw domainCommandError("add", error, normalizedHostname);
|
|
650
|
-
});
|
|
651
|
-
return {
|
|
652
|
-
command: "app.domain.add",
|
|
653
|
-
result: {
|
|
654
|
-
...target.resultTarget,
|
|
655
|
-
domain: toAppDomainSummary(added.domain),
|
|
656
|
-
existing: added.existing
|
|
657
|
-
},
|
|
658
|
-
warnings: [],
|
|
659
|
-
nextSteps: [`prisma-cli app domain wait ${normalizedHostname}`, `prisma-cli app domain show ${normalizedHostname}`]
|
|
660
|
-
};
|
|
661
|
-
}
|
|
662
|
-
async function runAppDomainShow(context, hostname, options) {
|
|
663
|
-
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
664
|
-
const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
|
|
665
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show", context.runtime.signal);
|
|
666
|
-
const detail = await target.provider.showDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
667
|
-
throw domainCommandError("show", error, normalizedHostname);
|
|
668
|
-
});
|
|
669
|
-
return {
|
|
670
|
-
command: "app.domain.show",
|
|
671
|
-
result: {
|
|
672
|
-
...target.resultTarget,
|
|
673
|
-
domain: toAppDomainSummary(detail)
|
|
674
|
-
},
|
|
675
|
-
warnings: [],
|
|
676
|
-
nextSteps: buildDomainShowNextSteps(detail)
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
async function runAppDomainRemove(context, hostname, options) {
|
|
680
|
-
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
681
|
-
const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
|
|
682
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove", context.runtime.signal);
|
|
683
|
-
await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
|
|
684
|
-
await target.provider.removeDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
685
|
-
throw domainCommandError("remove", error, normalizedHostname);
|
|
686
|
-
});
|
|
687
|
-
return {
|
|
688
|
-
command: "app.domain.remove",
|
|
689
|
-
result: {
|
|
690
|
-
...target.resultTarget,
|
|
691
|
-
hostname: normalizedHostname,
|
|
692
|
-
removed: true
|
|
693
|
-
},
|
|
694
|
-
warnings: [],
|
|
695
|
-
nextSteps: []
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
|
-
async function runAppDomainRetry(context, hostname, options) {
|
|
699
|
-
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
700
|
-
const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
|
|
701
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry", context.runtime.signal);
|
|
702
|
-
const retried = await target.provider.retryDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
703
|
-
throw domainCommandError("retry", error, normalizedHostname);
|
|
704
|
-
});
|
|
705
|
-
return {
|
|
706
|
-
command: "app.domain.retry",
|
|
707
|
-
result: {
|
|
708
|
-
...target.resultTarget,
|
|
709
|
-
domain: toAppDomainSummary(retried)
|
|
710
|
-
},
|
|
711
|
-
warnings: [],
|
|
712
|
-
nextSteps: [`prisma-cli app domain wait ${normalizedHostname}`]
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
async function runAppDomainWait(context, hostname, options) {
|
|
716
|
-
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
717
|
-
const timeoutMs = parseDomainWaitTimeout(options?.timeout);
|
|
718
|
-
const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
|
|
719
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait", context.runtime.signal);
|
|
720
|
-
if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
|
|
721
|
-
`app domain wait -> Waiting for ${normalizedHostname} to become active.`,
|
|
722
|
-
"",
|
|
723
|
-
`Workspace: ${target.resultTarget.workspace.name} Project: ${target.resultTarget.project.name} Branch: ${target.resultTarget.branch.name} App: ${target.resultTarget.app.name}`,
|
|
724
|
-
""
|
|
725
|
-
].join("\n"));
|
|
726
|
-
const start = Date.now();
|
|
727
|
-
const deadline = start + timeoutMs;
|
|
728
|
-
const pollIntervalMs = readDomainWaitPollIntervalMs(context);
|
|
729
|
-
let lastStatus = null;
|
|
730
|
-
let current = domain;
|
|
731
|
-
while (true) {
|
|
732
|
-
emitDomainWaitStatus(context, {
|
|
733
|
-
hostname: normalizedHostname,
|
|
734
|
-
domainId: current.id,
|
|
735
|
-
previousStatus: lastStatus,
|
|
736
|
-
status: current.status,
|
|
737
|
-
elapsedMs: Date.now() - start
|
|
738
|
-
});
|
|
739
|
-
lastStatus = current.status;
|
|
740
|
-
if (current.status === "active") {
|
|
741
|
-
if (!context.flags.json && !context.flags.quiet) context.output.stderr.write(`\n${normalizedHostname} is live at https://${normalizedHostname}\n`);
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
if (current.status === "failed") throw new CliError({
|
|
745
|
-
code: "DOMAIN_VERIFICATION_FAILED",
|
|
746
|
-
domain: "app",
|
|
747
|
-
summary: `Custom domain "${normalizedHostname}" failed verification`,
|
|
748
|
-
why: formatDomainFailureWhy(current),
|
|
749
|
-
fix: formatDomainFailureFix(current) ?? `Run prisma-cli app domain retry ${normalizedHostname}.`,
|
|
750
|
-
exitCode: 1,
|
|
751
|
-
nextSteps: [`prisma-cli app domain show ${normalizedHostname}`, `prisma-cli app domain retry ${normalizedHostname}`]
|
|
752
|
-
});
|
|
753
|
-
if (timeoutMs === 0 || Date.now() >= deadline) throw new CliError({
|
|
754
|
-
code: "DOMAIN_VERIFICATION_TIMEOUT",
|
|
755
|
-
domain: "app",
|
|
756
|
-
summary: `Timed out waiting for "${normalizedHostname}" to become active`,
|
|
757
|
-
why: `The domain is still "${current.status}".`,
|
|
758
|
-
fix: `Run prisma-cli app domain show ${normalizedHostname} to inspect the current status, or retry wait with a longer --timeout.`,
|
|
759
|
-
exitCode: 1,
|
|
760
|
-
nextSteps: [`prisma-cli app domain show ${normalizedHostname}`]
|
|
761
|
-
});
|
|
762
|
-
await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)), context.runtime.signal);
|
|
763
|
-
current = await target.provider.showDomain(current.id, { signal: context.runtime.signal }).catch((error) => {
|
|
764
|
-
throw domainCommandError("wait", error, normalizedHostname);
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
async function runAppLogs(context, appNameArg, deploymentId, projectRef, configTarget) {
|
|
769
|
-
const compute = await resolveComputeManagementContext(context, configTarget, "logs");
|
|
770
|
-
const appName = appNameArg ?? compute.configAppName;
|
|
771
|
-
const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
772
|
-
commandName: "app logs",
|
|
773
|
-
projectDir: compute.projectDir
|
|
774
|
-
});
|
|
775
|
-
const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
|
|
776
|
-
if (!context.flags.json && !context.flags.quiet) {
|
|
777
|
-
const lines = renderCommandHeader(context.ui, {
|
|
778
|
-
commandLabel: "app logs",
|
|
779
|
-
description: "Streaming logs for the selected deployment.",
|
|
780
|
-
rows: [
|
|
781
|
-
{
|
|
782
|
-
key: "project",
|
|
783
|
-
value: projectId
|
|
784
|
-
},
|
|
785
|
-
{
|
|
786
|
-
key: "app",
|
|
787
|
-
value: target.app.name
|
|
788
|
-
},
|
|
789
|
-
{
|
|
790
|
-
key: "deployment",
|
|
791
|
-
value: target.deployment.id
|
|
792
|
-
}
|
|
793
|
-
]
|
|
794
|
-
});
|
|
795
|
-
if (lines.length > 0) context.output.stderr.write(`${lines.join("\n")}\n`);
|
|
796
|
-
}
|
|
797
|
-
await provider.streamDeploymentLogs({
|
|
798
|
-
deploymentId: target.deployment.id,
|
|
799
|
-
signal: context.runtime.signal,
|
|
800
|
-
onRecord: (record) => writeLogRecord(context, record)
|
|
801
|
-
}).catch((error) => {
|
|
802
|
-
throw deployFailedError("Failed to stream app logs", error, [`prisma-cli app show-deploy ${target.deployment.id}`, "prisma-cli app list-deploys"]);
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
async function resolveExplicitLogDeployment(context, provider, projectId, branchName, appName, deploymentId) {
|
|
806
|
-
if (appName) {
|
|
807
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
808
|
-
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
809
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
810
|
-
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
811
|
-
});
|
|
812
|
-
const deployment = requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name);
|
|
813
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
814
|
-
id: deploymentsResult.app.id,
|
|
815
|
-
name: deploymentsResult.app.name
|
|
816
|
-
});
|
|
817
|
-
return {
|
|
818
|
-
app: deploymentsResult.app,
|
|
819
|
-
deployment
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
const shown = await provider.showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
|
|
823
|
-
throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
|
|
824
|
-
});
|
|
825
|
-
if (!shown) throw new CliError({
|
|
826
|
-
code: "DEPLOYMENT_NOT_FOUND",
|
|
827
|
-
domain: "app",
|
|
828
|
-
summary: `Deployment "${deploymentId}" not found`,
|
|
829
|
-
why: "The requested deployment does not exist or is no longer available.",
|
|
830
|
-
fix: "Run prisma-cli app list-deploys to choose an available deployment id.",
|
|
831
|
-
exitCode: 1,
|
|
832
|
-
nextSteps: ["prisma-cli app list-deploys"]
|
|
833
|
-
});
|
|
834
|
-
if (!shown.app) throw new CliError({
|
|
835
|
-
code: "DEPLOYMENT_NOT_FOUND",
|
|
836
|
-
domain: "app",
|
|
837
|
-
summary: `Deployment "${deploymentId}" is not attached to an app`,
|
|
838
|
-
why: "The requested deployment could be found, but its app could not be resolved.",
|
|
839
|
-
fix: "Run prisma-cli app list-deploys to choose an available deployment id for the selected app.",
|
|
840
|
-
exitCode: 1,
|
|
841
|
-
nextSteps: ["prisma-cli app list-deploys"]
|
|
842
|
-
});
|
|
843
|
-
const resolvedProjectApp = (await listApps(context, provider, projectId, branchName)).find((app) => app.id === shown.app?.id);
|
|
844
|
-
if (!resolvedProjectApp) throw new CliError({
|
|
845
|
-
code: "DEPLOYMENT_NOT_FOUND",
|
|
846
|
-
domain: "app",
|
|
847
|
-
summary: `Deployment "${deploymentId}" not found in the resolved project`,
|
|
848
|
-
why: "The requested deployment does not belong to an app in the resolved project.",
|
|
849
|
-
fix: "Run prisma-cli app list-deploys to choose an available deployment id for this project.",
|
|
850
|
-
exitCode: 1,
|
|
851
|
-
nextSteps: ["prisma-cli app list-deploys"]
|
|
852
|
-
});
|
|
853
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
854
|
-
id: resolvedProjectApp.id,
|
|
855
|
-
name: resolvedProjectApp.name
|
|
856
|
-
});
|
|
857
|
-
return {
|
|
858
|
-
app: resolvedProjectApp,
|
|
859
|
-
deployment: shown.deployment
|
|
860
|
-
};
|
|
861
|
-
}
|
|
862
|
-
async function resolveLiveLogDeployment(context, provider, projectId, branchName, appName) {
|
|
863
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
864
|
-
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
865
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
866
|
-
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
867
|
-
});
|
|
868
|
-
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
869
|
-
const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId);
|
|
870
|
-
const deployment = currentLiveDeploymentId ? deployments.find((candidate) => candidate.id === currentLiveDeploymentId) ?? null : null;
|
|
871
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
872
|
-
id: deploymentsResult.app.id,
|
|
873
|
-
name: deploymentsResult.app.name
|
|
874
|
-
});
|
|
875
|
-
if (!deployment) throw noDeploymentsError("No deployments available to stream logs", `The selected app "${deploymentsResult.app.name}" does not have any deployments yet.`);
|
|
876
|
-
return {
|
|
877
|
-
app: deploymentsResult.app,
|
|
878
|
-
deployment
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
function writeLogRecord(context, record) {
|
|
882
|
-
if (context.flags.json) {
|
|
883
|
-
writeJsonEvent(context.output, {
|
|
884
|
-
type: record.type,
|
|
885
|
-
command: "app.logs",
|
|
886
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
887
|
-
data: record
|
|
888
|
-
});
|
|
889
|
-
return;
|
|
890
|
-
}
|
|
891
|
-
if (record.type === "log") {
|
|
892
|
-
context.output.stdout.write(record.text);
|
|
893
|
-
if (!record.text.endsWith("\n")) context.output.stdout.write("\n");
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
async function runAppPromote(context, deploymentId, appNameArg, projectRef, configTarget) {
|
|
897
|
-
const compute = await resolveComputeManagementContext(context, configTarget, "promote");
|
|
898
|
-
const appName = appNameArg ?? compute.configAppName;
|
|
899
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
900
|
-
commandName: "app promote",
|
|
901
|
-
projectDir: compute.projectDir
|
|
902
|
-
});
|
|
903
|
-
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
|
|
904
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
905
|
-
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
906
|
-
});
|
|
907
|
-
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
908
|
-
const targetDeployment = requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name);
|
|
909
|
-
const targetAlreadyLive = currentLiveDeploymentId === targetDeployment.id;
|
|
910
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
911
|
-
id: deploymentsResult.app.id,
|
|
912
|
-
name: deploymentsResult.app.name
|
|
913
|
-
});
|
|
914
|
-
if (!targetAlreadyLive) await provider.promoteDeployment({
|
|
915
|
-
appId: selectedApp.id,
|
|
916
|
-
deploymentId: targetDeployment.id,
|
|
917
|
-
signal: context.runtime.signal,
|
|
918
|
-
progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
919
|
-
}).catch((error) => {
|
|
920
|
-
throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
|
|
921
|
-
});
|
|
922
|
-
await context.stateStore.setKnownLiveDeployment(projectId, deploymentsResult.app.id, targetDeployment.id);
|
|
923
|
-
return {
|
|
924
|
-
command: "app.promote",
|
|
925
|
-
result: {
|
|
926
|
-
projectId,
|
|
927
|
-
verboseContext: toAppVerboseContext(target),
|
|
928
|
-
app: {
|
|
929
|
-
id: deploymentsResult.app.id,
|
|
930
|
-
name: deploymentsResult.app.name
|
|
931
|
-
},
|
|
932
|
-
deployment: {
|
|
933
|
-
...targetDeployment,
|
|
934
|
-
status: "running",
|
|
935
|
-
live: true
|
|
936
|
-
}
|
|
937
|
-
},
|
|
938
|
-
warnings: targetAlreadyLive ? ["The selected deployment is already live for this app."] : [],
|
|
939
|
-
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
940
|
-
};
|
|
941
|
-
}
|
|
942
|
-
async function runAppRollback(context, appNameArg, deploymentId, projectRef, configTarget) {
|
|
943
|
-
const compute = await resolveComputeManagementContext(context, configTarget, "rollback");
|
|
944
|
-
const appName = appNameArg ?? compute.configAppName;
|
|
945
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
946
|
-
commandName: "app rollback",
|
|
947
|
-
projectDir: compute.projectDir
|
|
948
|
-
});
|
|
949
|
-
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
|
|
950
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
951
|
-
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
952
|
-
});
|
|
953
|
-
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
954
|
-
const currentLiveDeployment = currentLiveDeploymentId ? deploymentsResult.deployments.find((deployment) => deployment.id === currentLiveDeploymentId) ?? null : null;
|
|
955
|
-
const targetDeployment = deploymentId ? requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name) : resolveRollbackTarget(deploymentsResult.deployments, currentLiveDeploymentId);
|
|
956
|
-
const targetAlreadyLive = currentLiveDeploymentId === targetDeployment.id;
|
|
957
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
958
|
-
id: deploymentsResult.app.id,
|
|
959
|
-
name: deploymentsResult.app.name
|
|
960
|
-
});
|
|
961
|
-
if (!targetAlreadyLive) await provider.promoteDeployment({
|
|
962
|
-
appId: selectedApp.id,
|
|
963
|
-
deploymentId: targetDeployment.id,
|
|
964
|
-
signal: context.runtime.signal,
|
|
965
|
-
progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
966
|
-
}).catch((error) => {
|
|
967
|
-
throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
|
|
968
|
-
});
|
|
969
|
-
await context.stateStore.setKnownLiveDeployment(projectId, deploymentsResult.app.id, targetDeployment.id);
|
|
970
|
-
return {
|
|
971
|
-
command: "app.rollback",
|
|
972
|
-
result: {
|
|
973
|
-
projectId,
|
|
974
|
-
verboseContext: toAppVerboseContext(target),
|
|
975
|
-
app: {
|
|
976
|
-
id: deploymentsResult.app.id,
|
|
977
|
-
name: deploymentsResult.app.name
|
|
978
|
-
},
|
|
979
|
-
deployment: {
|
|
980
|
-
...targetDeployment,
|
|
981
|
-
status: "running",
|
|
982
|
-
live: true
|
|
983
|
-
},
|
|
984
|
-
previousLiveDeploymentId: currentLiveDeployment?.id ?? null
|
|
985
|
-
},
|
|
986
|
-
warnings: targetAlreadyLive ? ["The selected deployment is already live for this app."] : [],
|
|
987
|
-
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
988
|
-
};
|
|
989
|
-
}
|
|
990
|
-
/**
|
|
991
|
-
* Removes an app and every deployment it owns in the resolved branch.
|
|
992
|
-
*
|
|
993
|
-
* @param branchName Scopes the removal to this branch. When omitted the branch
|
|
994
|
-
* is inferred from the local Git branch, falling back to the production branch.
|
|
995
|
-
*/
|
|
996
|
-
async function runAppRemove(context, appNameArg, projectRef, configTarget, branchName) {
|
|
997
|
-
const compute = await resolveComputeManagementContext(context, configTarget, "remove");
|
|
998
|
-
const appName = appNameArg ?? compute.configAppName;
|
|
999
|
-
if (branchName !== void 0 && branchName.trim() === "") throw usageError("The --branch value cannot be empty", "app remove scopes the removal to the given branch; an empty --branch would silently fall back to the inferred (possibly production) branch.", "Pass a non-empty branch name, e.g. --branch <branch>, or omit --branch to use the inferred branch.", ["prisma-cli app remove --app <name> --branch <branch>"], "app");
|
|
1000
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
1001
|
-
branch: branchName !== void 0 ? await resolveDeployBranch(context, branchName) : void 0,
|
|
1002
|
-
commandName: "app remove",
|
|
1003
|
-
projectDir: compute.projectDir
|
|
1004
|
-
});
|
|
1005
|
-
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
|
|
1006
|
-
await confirmAppRemoval(context, selectedApp);
|
|
1007
|
-
const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
1008
|
-
throw removeFailedError("Failed to remove app", error, ["prisma-cli app show", "prisma-cli app list-deploys"]);
|
|
1009
|
-
});
|
|
1010
|
-
const warnings = await cleanupRemovedAppState(context, projectId, removedApp.id);
|
|
1011
|
-
return {
|
|
1012
|
-
command: "app.remove",
|
|
1013
|
-
result: {
|
|
1014
|
-
projectId,
|
|
1015
|
-
verboseContext: toAppVerboseContext(target),
|
|
1016
|
-
app: {
|
|
1017
|
-
id: removedApp.id,
|
|
1018
|
-
name: removedApp.name
|
|
1019
|
-
},
|
|
1020
|
-
removed: true
|
|
1021
|
-
},
|
|
1022
|
-
warnings,
|
|
1023
|
-
nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
|
|
1024
|
-
};
|
|
1025
|
-
}
|
|
1026
|
-
async function resolveAppDomainTarget(context, options, commandName = "app domain") {
|
|
1027
|
-
const compute = await resolveComputeManagementContext(context, options?.configTarget, commandName.replace(APP_COMMAND_PREFIX, ""));
|
|
1028
|
-
const branch = resolveDomainBranch(options?.branchName);
|
|
1029
|
-
if (toBranchKind(branch.name) !== "production") throw new CliError({
|
|
1030
|
-
code: "BRANCH_NOT_DEPLOYABLE",
|
|
1031
|
-
domain: "branch",
|
|
1032
|
-
summary: "Custom domains require the production branch",
|
|
1033
|
-
why: `Custom domains on preview branch "${branch.name}" are not supported in Public Beta.`,
|
|
1034
|
-
fix: "Use --branch production, or attach the domain after promoting/deploying to the production branch.",
|
|
1035
|
-
exitCode: 2,
|
|
1036
|
-
nextSteps: ["prisma-cli app domain add <hostname> --branch production"]
|
|
1037
|
-
});
|
|
1038
|
-
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
1039
|
-
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
1040
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
|
|
1041
|
-
branch,
|
|
1042
|
-
commandName,
|
|
1043
|
-
envProjectId,
|
|
1044
|
-
projectDir: compute.projectDir
|
|
1045
|
-
});
|
|
1046
|
-
const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
1047
|
-
explicitAppName: options?.appName ?? compute.configAppName,
|
|
1048
|
-
explicitAppId: envAppId
|
|
1049
|
-
});
|
|
1050
|
-
await context.stateStore.setSelectedApp(projectId, {
|
|
1051
|
-
id: selectedApp.id,
|
|
1052
|
-
name: selectedApp.name
|
|
1053
|
-
});
|
|
1054
|
-
return {
|
|
1055
|
-
provider,
|
|
1056
|
-
app: selectedApp,
|
|
1057
|
-
resultTarget: {
|
|
1058
|
-
workspace: target.workspace,
|
|
1059
|
-
project: target.project,
|
|
1060
|
-
branch: toResultBranch(target.branch),
|
|
1061
|
-
app: {
|
|
1062
|
-
id: selectedApp.id,
|
|
1063
|
-
name: selectedApp.name
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
};
|
|
1067
|
-
}
|
|
1068
|
-
function resolveDomainBranch(explicitBranchName) {
|
|
1069
|
-
return {
|
|
1070
|
-
name: explicitBranchName?.trim() || "production",
|
|
1071
|
-
annotation: explicitBranchName ? "set by --branch" : "production default"
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
|
-
async function resolveDomainAppSelection(context, projectId, apps, options) {
|
|
1075
|
-
if (options.explicitAppId) {
|
|
1076
|
-
const matched = apps.find((app) => app.id === options.explicitAppId);
|
|
1077
|
-
if (!matched) throw usageError("Selected app does not exist in the resolved production branch", `The app "${options.explicitAppId}" from ${PRISMA_APP_ID_ENV_VAR} could not be found in resolved project "${projectId}".`, `Unset ${PRISMA_APP_ID_ENV_VAR}, pass --app <name>, or deploy the app on the production branch.`, ["prisma-cli app deploy --branch production"], "app");
|
|
1078
|
-
return matched;
|
|
1079
|
-
}
|
|
1080
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, apps, options.explicitAppName);
|
|
1081
|
-
if (selectedApp) return selectedApp;
|
|
1082
|
-
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");
|
|
1083
|
-
}
|
|
1084
|
-
async function resolveDomainByHostname(provider, appId, hostname, command, signal) {
|
|
1085
|
-
const matched = (await provider.listDomains(appId, { signal }).catch((error) => {
|
|
1086
|
-
throw domainCommandError(command, error, hostname);
|
|
1087
|
-
})).find((domain) => sameDomainHostname(domain.hostname, hostname));
|
|
1088
|
-
if (matched) return matched;
|
|
1089
|
-
throw domainNotFoundError(hostname);
|
|
1090
|
-
}
|
|
1091
|
-
function normalizeDomainHostname(hostname) {
|
|
1092
|
-
const normalized = hostname.trim().replace(TRAILING_DOT, "").toLowerCase();
|
|
1093
|
-
if (!isValidDomainHostname(normalized)) throw new CliError({
|
|
1094
|
-
code: "DOMAIN_HOSTNAME_INVALID",
|
|
1095
|
-
domain: "app",
|
|
1096
|
-
summary: `Invalid custom domain "${hostname}"`,
|
|
1097
|
-
why: "Custom domains must be valid hostnames without protocol, path, wildcard, or port.",
|
|
1098
|
-
fix: "Pass a hostname like shop.acme.com.",
|
|
1099
|
-
exitCode: 2,
|
|
1100
|
-
nextSteps: ["prisma-cli app domain add shop.acme.com"]
|
|
1101
|
-
});
|
|
1102
|
-
return normalized;
|
|
1103
|
-
}
|
|
1104
|
-
function isValidDomainHostname(hostname) {
|
|
1105
|
-
if (hostname.length < 1 || hostname.length > 253) return false;
|
|
1106
|
-
if (hostname.includes("://") || hostname.includes("/") || hostname.includes(":") || hostname.startsWith("*.")) return false;
|
|
1107
|
-
const labels = hostname.split(".");
|
|
1108
|
-
if (labels.length < 2) return false;
|
|
1109
|
-
return labels.every((label) => DOMAIN_LABEL.test(label));
|
|
1110
|
-
}
|
|
1111
|
-
function sameDomainHostname(left, right) {
|
|
1112
|
-
return left.trim().replace(TRAILING_DOT, "").toLowerCase() === right.trim().replace(TRAILING_DOT, "").toLowerCase();
|
|
1113
|
-
}
|
|
1114
|
-
function toAppDomainSummary(domain) {
|
|
1115
|
-
return {
|
|
1116
|
-
id: domain.id,
|
|
1117
|
-
type: domain.type,
|
|
1118
|
-
url: domain.url,
|
|
1119
|
-
hostname: domain.hostname,
|
|
1120
|
-
appId: domain.appId,
|
|
1121
|
-
status: domain.status,
|
|
1122
|
-
foundryStatus: domain.foundryStatus,
|
|
1123
|
-
failureReason: domain.failureReason,
|
|
1124
|
-
failureCategory: domain.failureCategory,
|
|
1125
|
-
certExpiresAt: domain.certExpiresAt,
|
|
1126
|
-
createdAt: domain.createdAt,
|
|
1127
|
-
updatedAt: domain.updatedAt,
|
|
1128
|
-
dnsRecords: toAppDomainDnsRecords(domain)
|
|
1129
|
-
};
|
|
1130
|
-
}
|
|
1131
|
-
function toAppDomainDnsRecords(domain) {
|
|
1132
|
-
return domain.dnsRecords.map((record) => ({
|
|
1133
|
-
type: record.type,
|
|
1134
|
-
name: record.name,
|
|
1135
|
-
value: record.value,
|
|
1136
|
-
ttl: record.ttl
|
|
1137
|
-
}));
|
|
1138
|
-
}
|
|
1139
|
-
function buildDomainShowNextSteps(domain) {
|
|
1140
|
-
if (domain.status === "active") return [];
|
|
1141
|
-
if (domain.status === "failed") return [`prisma-cli app domain retry ${domain.hostname}`];
|
|
1142
|
-
return [`prisma-cli app domain wait ${domain.hostname}`];
|
|
1143
|
-
}
|
|
1144
|
-
async function confirmDomainRemoval(context, target, hostname) {
|
|
1145
|
-
if (context.flags.yes) return;
|
|
1146
|
-
if (!canPrompt(context)) throw new CliError({
|
|
1147
|
-
code: "CONFIRMATION_REQUIRED",
|
|
1148
|
-
domain: "app",
|
|
1149
|
-
summary: "Custom domain removal requires confirmation in the current mode",
|
|
1150
|
-
why: "This command detaches a domain and cannot prompt for confirmation in the current mode.",
|
|
1151
|
-
fix: `Pass --yes to confirm removal of "${hostname}", or rerun prisma-cli app domain remove in an interactive TTY.`,
|
|
1152
|
-
exitCode: 1,
|
|
1153
|
-
nextSteps: [`prisma-cli app domain remove ${hostname} --app ${target.app.name} --yes`]
|
|
1154
|
-
});
|
|
1155
|
-
if (!await confirmPrompt({
|
|
1156
|
-
input: context.runtime.stdin,
|
|
1157
|
-
output: context.output.stderr,
|
|
1158
|
-
signal: context.runtime.signal,
|
|
1159
|
-
message: `Detach ${hostname} from App "${target.app.name}"?`,
|
|
1160
|
-
initialValue: false
|
|
1161
|
-
})) throw usageError("Custom domain removal canceled", "The command was canceled before the domain was detached.", "Rerun the command and confirm removal, or pass --yes.", [`prisma-cli app domain remove ${hostname} --app ${target.app.name} --yes`], "app");
|
|
1162
|
-
}
|
|
1163
|
-
function domainCommandError(command, error, hostname) {
|
|
1164
|
-
if (error instanceof DomainApiError) {
|
|
1165
|
-
const apiError = command === "add" ? domainAddApiError(hostname, error) : domainLookupApiError(command, hostname, error);
|
|
1166
|
-
if (apiError) return apiError;
|
|
1167
|
-
}
|
|
1168
|
-
return new CliError({
|
|
1169
|
-
code: "DEPLOY_FAILED",
|
|
1170
|
-
domain: "app",
|
|
1171
|
-
summary: `Custom domain ${command} failed`,
|
|
1172
|
-
why: error instanceof Error ? error.message : String(error),
|
|
1173
|
-
fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
|
|
1174
|
-
debug: formatDebugDetails(error),
|
|
1175
|
-
exitCode: 1,
|
|
1176
|
-
nextSteps: [`prisma-cli app domain show ${hostname}`]
|
|
1177
|
-
});
|
|
1178
|
-
}
|
|
1179
|
-
function domainAddApiError(hostname, error) {
|
|
1180
|
-
if ((error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
|
|
1181
|
-
if (error.status === 400) return new CliError({
|
|
1182
|
-
code: "DOMAIN_HOSTNAME_INVALID",
|
|
1183
|
-
domain: "app",
|
|
1184
|
-
summary: `Invalid custom domain "${hostname}"`,
|
|
1185
|
-
why: error.message,
|
|
1186
|
-
fix: "Pass a valid hostname like shop.acme.com and make sure DNS can be verified.",
|
|
1187
|
-
debug: formatDebugDetails(error),
|
|
1188
|
-
exitCode: 2,
|
|
1189
|
-
nextSteps: ["prisma-cli app domain add shop.acme.com"]
|
|
1190
|
-
});
|
|
1191
|
-
if (error.status === 429 || isDomainQuotaError(error)) return new CliError({
|
|
1192
|
-
code: "DOMAIN_QUOTA_EXCEEDED",
|
|
1193
|
-
domain: "app",
|
|
1194
|
-
summary: "Custom domain quota exceeded",
|
|
1195
|
-
why: error.message,
|
|
1196
|
-
fix: "Remove an existing custom domain before adding another one.",
|
|
1197
|
-
debug: formatDebugDetails(error),
|
|
1198
|
-
exitCode: 1,
|
|
1199
|
-
nextSteps: ["prisma-cli app domain remove <hostname>"]
|
|
1200
|
-
});
|
|
1201
|
-
if (error.status === 409) return domainAlreadyRegisteredError(hostname, error);
|
|
1202
|
-
if (error.status === 422) return new CliError({
|
|
1203
|
-
code: "NO_DEPLOYMENTS",
|
|
1204
|
-
domain: "app",
|
|
1205
|
-
summary: "Custom domain requires a live production deployment",
|
|
1206
|
-
why: "The selected production app does not have a promoted version that can receive a custom domain.",
|
|
1207
|
-
fix: "Deploy the app to the production branch, then rerun the domain command.",
|
|
1208
|
-
debug: formatDebugDetails(error),
|
|
1209
|
-
exitCode: 1,
|
|
1210
|
-
nextSteps: ["prisma-cli app deploy --branch production", `prisma-cli app domain add ${hostname}`]
|
|
1211
|
-
});
|
|
1212
|
-
return null;
|
|
1213
|
-
}
|
|
1214
|
-
function domainLookupApiError(command, hostname, error) {
|
|
1215
|
-
if (error.status === 404) return domainNotFoundError(hostname);
|
|
1216
|
-
if (command === "retry" && error.status === 409) return new CliError({
|
|
1217
|
-
code: "DOMAIN_RETRY_NOT_ELIGIBLE",
|
|
1218
|
-
domain: "app",
|
|
1219
|
-
summary: `Custom domain "${hostname}" is not eligible for retry`,
|
|
1220
|
-
why: error.message,
|
|
1221
|
-
fix: "Wait for the current verification or TLS step to finish, then rerun retry if the domain fails.",
|
|
1222
|
-
debug: formatDebugDetails(error),
|
|
1223
|
-
exitCode: 1,
|
|
1224
|
-
nextSteps: [`prisma-cli app domain show ${hostname}`]
|
|
1225
|
-
});
|
|
1226
|
-
return null;
|
|
1227
|
-
}
|
|
1228
|
-
function isDomainQuotaError(error) {
|
|
1229
|
-
if (error.status !== 409) return false;
|
|
1230
|
-
const text = `${error.message} ${error.hint ?? ""}`.toLowerCase();
|
|
1231
|
-
return text.includes("quota") || text.includes("maximum") || text.includes("limit");
|
|
1232
|
-
}
|
|
1233
|
-
function domainAlreadyRegisteredError(hostname, error) {
|
|
1234
|
-
return new CliError({
|
|
1235
|
-
code: "DOMAIN_ALREADY_REGISTERED",
|
|
1236
|
-
domain: "app",
|
|
1237
|
-
summary: `Custom domain "${hostname}" is already registered`,
|
|
1238
|
-
why: error.hint ?? error.message,
|
|
1239
|
-
fix: "Select the app that owns this hostname and remove it there, or contact support if you cannot access it.",
|
|
1240
|
-
debug: formatDebugDetails(error),
|
|
1241
|
-
exitCode: 1,
|
|
1242
|
-
nextSteps: [`Select the owning app and remove ${hostname} there.`, "Contact Prisma support if you cannot access the owning app."]
|
|
1243
|
-
});
|
|
1244
|
-
}
|
|
1245
|
-
function isDomainDnsError(error) {
|
|
1246
|
-
const text = `${error.message} ${error.hint ?? ""}`.toLowerCase();
|
|
1247
|
-
return text.includes("dns is not configured") || text.includes("dns verification failed") || text.includes("no cname") || text.includes("cname record") || text.includes("no a/aaaa") || CNAME_TO_PHRASE.test(text);
|
|
1248
|
-
}
|
|
1249
|
-
function domainDnsNotConfiguredError(hostname, error) {
|
|
1250
|
-
const target = extractDomainDnsTarget(error);
|
|
1251
|
-
const record = target ? `CNAME ${hostname} -> ${target}` : null;
|
|
1252
|
-
return new CliError({
|
|
1253
|
-
code: "DOMAIN_DNS_NOT_CONFIGURED",
|
|
1254
|
-
domain: "app",
|
|
1255
|
-
summary: `DNS is not configured for "${hostname}"`,
|
|
1256
|
-
why: error.hint ?? error.message,
|
|
1257
|
-
fix: record ? `Add ${record} at your DNS provider, then rerun the domain command.` : "The platform did not return the required DNS target. Re-run with --trace for the underlying API response details.",
|
|
1258
|
-
debug: formatDebugDetails(error),
|
|
1259
|
-
exitCode: 1,
|
|
1260
|
-
nextSteps: record ? [`add ${record}`, `prisma-cli app domain add ${hostname}`] : [`prisma-cli app domain add ${hostname} --trace`]
|
|
1261
|
-
});
|
|
1262
|
-
}
|
|
1263
|
-
function extractDomainDnsTarget(error) {
|
|
1264
|
-
const text = `${error.hint ?? ""} ${error.message}`;
|
|
1265
|
-
return PRISMA_BUILD_HOSTNAME.exec(text)?.[1]?.toLowerCase() ?? null;
|
|
1266
|
-
}
|
|
1267
|
-
function domainNotFoundError(hostname) {
|
|
1268
|
-
return new CliError({
|
|
1269
|
-
code: "DOMAIN_NOT_FOUND",
|
|
1270
|
-
domain: "app",
|
|
1271
|
-
summary: `Custom domain "${hostname}" not found`,
|
|
1272
|
-
why: "The hostname is not attached to the selected app.",
|
|
1273
|
-
fix: "Check the hostname and selected app, or add the domain first.",
|
|
1274
|
-
exitCode: 1,
|
|
1275
|
-
nextSteps: [`prisma-cli app domain add ${hostname}`]
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
function formatDomainFailureWhy(domain) {
|
|
1279
|
-
if (domain.failureReason) return domain.failureCategory ? `${domain.failureCategory}: ${domain.failureReason}` : domain.failureReason;
|
|
1280
|
-
return "The platform reported a terminal failed state for this custom domain.";
|
|
1281
|
-
}
|
|
1282
|
-
function parseDomainWaitTimeout(value) {
|
|
1283
|
-
if (!value) return 900 * 1e3;
|
|
1284
|
-
const trimmed = value.trim().toLowerCase();
|
|
1285
|
-
if (trimmed === "0") return 0;
|
|
1286
|
-
const match = WAIT_TIMEOUT_DURATION.exec(trimmed);
|
|
1287
|
-
if (!match) throw usageError(`Invalid timeout "${value}"`, "Timeout must be a duration such as 0, 30s, 15m, or 1h.", "Pass --timeout 15m, or --timeout 0 to poll once.", ["prisma-cli app domain wait shop.acme.com --timeout 15m"], "app");
|
|
1288
|
-
return Number.parseInt(match[1], 10) * (WAIT_TIMEOUT_UNIT_MULTIPLIER_MS[match[2]] ?? 1);
|
|
1289
|
-
}
|
|
1290
|
-
function readDomainWaitPollIntervalMs(context) {
|
|
1291
|
-
const raw = context.runtime.env.PRISMA_CLI_DOMAIN_WAIT_POLL_MS;
|
|
1292
|
-
if (!raw) return 5e3;
|
|
1293
|
-
const parsed = Number.parseInt(raw, 10);
|
|
1294
|
-
return Number.isInteger(parsed) && parsed > 0 ? parsed : 5e3;
|
|
1295
|
-
}
|
|
1296
|
-
function emitDomainWaitStatus(context, event) {
|
|
1297
|
-
if (context.flags.json) {
|
|
1298
|
-
writeJsonEvent(context.output, {
|
|
1299
|
-
type: "status",
|
|
1300
|
-
command: "app.domain.wait",
|
|
1301
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1302
|
-
data: {
|
|
1303
|
-
hostname: event.hostname,
|
|
1304
|
-
domainId: event.domainId,
|
|
1305
|
-
previousStatus: event.previousStatus,
|
|
1306
|
-
status: event.status,
|
|
1307
|
-
elapsedMs: event.elapsedMs
|
|
1308
|
-
}
|
|
1309
|
-
});
|
|
1310
|
-
return;
|
|
1311
|
-
}
|
|
1312
|
-
if (context.flags.quiet) return;
|
|
1313
|
-
if (event.previousStatus === event.status) return;
|
|
1314
|
-
const transition = event.previousStatus ? `${event.previousStatus} -> ${event.status}` : event.status;
|
|
1315
|
-
context.output.stderr.write(` ${transition} (${formatElapsed(event.elapsedMs)})\n`);
|
|
1316
|
-
}
|
|
1317
|
-
function formatElapsed(milliseconds) {
|
|
1318
|
-
const seconds = Math.max(Math.floor(milliseconds / 1e3), 0);
|
|
1319
|
-
const minutes = Math.floor(seconds / 60);
|
|
1320
|
-
const remainingSeconds = seconds % 60;
|
|
1321
|
-
return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
|
|
1322
|
-
}
|
|
1323
|
-
async function sleep(milliseconds, signal) {
|
|
1324
|
-
if (milliseconds <= 0) return;
|
|
1325
|
-
signal.throwIfAborted();
|
|
1326
|
-
await new Promise((resolve, reject) => {
|
|
1327
|
-
const onAbort = () => {
|
|
1328
|
-
clearTimeout(timeout);
|
|
1329
|
-
reject(signal.reason);
|
|
1330
|
-
};
|
|
1331
|
-
const timeout = setTimeout(() => {
|
|
1332
|
-
signal.removeEventListener("abort", onAbort);
|
|
1333
|
-
resolve();
|
|
1334
|
-
}, milliseconds);
|
|
1335
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
|
-
async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
1339
|
-
const newAppRegion = deployNewAppRegion(options.configRegion);
|
|
1340
|
-
if (options.explicitAppName) return resolveDeployAppByName(context, apps, {
|
|
1341
|
-
name: options.explicitAppName,
|
|
1342
|
-
matchedAnnotation: "set by --app",
|
|
1343
|
-
newAnnotation: "set by --app",
|
|
1344
|
-
requestedRegion: options.configRegion,
|
|
1345
|
-
newAppRegion,
|
|
1346
|
-
firstDeploy: options.firstDeploy
|
|
1347
|
-
});
|
|
1348
|
-
if (options.explicitAppId) {
|
|
1349
|
-
const matched = apps.find((app) => app.id === options.explicitAppId);
|
|
1350
|
-
if (!matched) throw usageError("Selected app does not exist in the resolved project", `The app "${options.explicitAppId}" from ${PRISMA_APP_ID_ENV_VAR} could not be found in resolved project "${projectId}".`, `Unset ${PRISMA_APP_ID_ENV_VAR}, pass --app <name>, or choose an app from prisma-cli app list-deploys.`, ["prisma-cli app list-deploys"], "app");
|
|
1351
|
-
assertDeployRegionMatchesExistingApp(matched, options.configRegion);
|
|
1352
|
-
return {
|
|
1353
|
-
appId: matched.id,
|
|
1354
|
-
displayName: matched.name,
|
|
1355
|
-
annotation: `from ${PRISMA_APP_ID_ENV_VAR}`,
|
|
1356
|
-
firstDeploy: options.firstDeploy
|
|
1357
|
-
};
|
|
1358
|
-
}
|
|
1359
|
-
if (options.configAppName) {
|
|
1360
|
-
const configName = options.configAppName;
|
|
1361
|
-
return resolveDeployAppByName(context, apps, {
|
|
1362
|
-
name: configName.value,
|
|
1363
|
-
matchedAnnotation: configName.annotation,
|
|
1364
|
-
newAnnotation: configName.annotation,
|
|
1365
|
-
requestedRegion: options.configRegion,
|
|
1366
|
-
newAppRegion,
|
|
1367
|
-
firstDeploy: options.firstDeploy
|
|
1368
|
-
});
|
|
1369
|
-
}
|
|
1370
|
-
const inferredName = await options.inferName();
|
|
1371
|
-
const newAnnotation = inferredName.source === "package-name" ? "created from package.json" : "created from directory name";
|
|
1372
|
-
return resolveDeployAppByName(context, apps, {
|
|
1373
|
-
name: inferredName.name,
|
|
1374
|
-
matchedAnnotation: "existing app on this branch",
|
|
1375
|
-
newAnnotation,
|
|
1376
|
-
requestedRegion: options.configRegion,
|
|
1377
|
-
newAppRegion,
|
|
1378
|
-
firstDeploy: options.firstDeploy
|
|
1379
|
-
});
|
|
1380
|
-
}
|
|
1381
|
-
async function resolveDeployAppByName(context, apps, options) {
|
|
1382
|
-
const matches = findAppsByName(apps, options.name);
|
|
1383
|
-
if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, options.name, options.requestedRegion, options.newAppRegion, options.firstDeploy);
|
|
1384
|
-
const matched = matches[0];
|
|
1385
|
-
if (matched) {
|
|
1386
|
-
assertDeployRegionMatchesExistingApp(matched, options.requestedRegion);
|
|
1387
|
-
return {
|
|
1388
|
-
appId: matched.id,
|
|
1389
|
-
displayName: matched.name,
|
|
1390
|
-
annotation: options.matchedAnnotation,
|
|
1391
|
-
firstDeploy: options.firstDeploy
|
|
1392
|
-
};
|
|
1393
|
-
}
|
|
1394
|
-
return {
|
|
1395
|
-
appName: options.name,
|
|
1396
|
-
region: options.newAppRegion,
|
|
1397
|
-
displayName: options.name,
|
|
1398
|
-
annotation: options.newAnnotation,
|
|
1399
|
-
firstDeploy: options.firstDeploy
|
|
1400
|
-
};
|
|
1401
|
-
}
|
|
1402
|
-
function assertDeployRegionMatchesExistingApp(app, requestedRegion) {
|
|
1403
|
-
if (requestedRegion?.annotation !== "set by --region" || !app.region || app.region === requestedRegion.value) return;
|
|
1404
|
-
throw usageError("App already exists in another region", `The selected app "${app.name}" is in region "${app.region}", but --region requested "${requestedRegion.value}".`, "Remove --region to deploy the existing app, or pass --app <new-name> to create a new app in that region.", [`prisma-cli app deploy --app ${formatCommandArgument(app.name)}`, `prisma-cli app deploy --app <new-name> --region ${formatCommandArgument(requestedRegion.value)}`], "app");
|
|
1405
|
-
}
|
|
1406
|
-
async function resolveAmbiguousDeployApp(context, matches, targetName, requestedRegion, newAppRegion, firstDeploy) {
|
|
1407
|
-
if (canPrompt(context)) {
|
|
1408
|
-
const createNew = "__create_new_app__";
|
|
1409
|
-
const cancel = "__cancel__";
|
|
1410
|
-
const selected = await selectPrompt({
|
|
1411
|
-
input: context.runtime.stdin,
|
|
1412
|
-
output: context.runtime.stderr,
|
|
1413
|
-
message: `Multiple apps are named "${targetName}"`,
|
|
1414
|
-
choices: [
|
|
1415
|
-
...sortApps(matches).map((app) => ({
|
|
1416
|
-
label: `${app.name} (${app.id})`,
|
|
1417
|
-
value: app
|
|
1418
|
-
})),
|
|
1419
|
-
{
|
|
1420
|
-
label: `Create a new app named "${targetName}"`,
|
|
1421
|
-
value: createNew
|
|
1422
|
-
},
|
|
1423
|
-
{
|
|
1424
|
-
label: "Cancel",
|
|
1425
|
-
value: cancel
|
|
1426
|
-
}
|
|
1427
|
-
]
|
|
1428
|
-
});
|
|
1429
|
-
if (selected === cancel) throw usageError("App selection canceled", "The command was canceled before an app was selected.", "Re-run the command and choose an app, or pass --app <name>.", ["prisma-cli app deploy --app <name>"], "app");
|
|
1430
|
-
if (selected === createNew) return {
|
|
1431
|
-
appName: targetName,
|
|
1432
|
-
region: newAppRegion,
|
|
1433
|
-
displayName: targetName,
|
|
1434
|
-
annotation: "created from package.json",
|
|
1435
|
-
firstDeploy
|
|
1436
|
-
};
|
|
1437
|
-
assertDeployRegionMatchesExistingApp(selected, requestedRegion);
|
|
1438
|
-
return {
|
|
1439
|
-
appId: selected.id,
|
|
1440
|
-
displayName: selected.name,
|
|
1441
|
-
annotation: "selected by you",
|
|
1442
|
-
firstDeploy
|
|
1443
|
-
};
|
|
1444
|
-
}
|
|
1445
|
-
throw new CliError({
|
|
1446
|
-
code: "APP_AMBIGUOUS",
|
|
1447
|
-
domain: "app",
|
|
1448
|
-
summary: "App resolution is ambiguous",
|
|
1449
|
-
why: `Multiple apps matched "${targetName}".`,
|
|
1450
|
-
fix: "Pass --app <name> to choose the app explicitly.",
|
|
1451
|
-
meta: { candidates: matches.map((app) => ({
|
|
1452
|
-
id: app.id,
|
|
1453
|
-
name: app.name
|
|
1454
|
-
})) },
|
|
1455
|
-
exitCode: 2,
|
|
1456
|
-
nextSteps: ["prisma-cli app deploy --app <name>"]
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
function deployNewAppRegion(configRegion) {
|
|
1460
|
-
return configRegion?.value;
|
|
1461
|
-
}
|
|
1462
|
-
async function resolveExistingAppSelection(context, projectId, apps, explicitAppName) {
|
|
1463
|
-
if (explicitAppName) {
|
|
1464
|
-
const matched = findAppByName(apps, explicitAppName);
|
|
1465
|
-
if (!matched) throw usageError("Selected app does not exist in the resolved project", `The app "${explicitAppName}" could not be found in resolved project "${projectId}".`, "Pass the name of an existing app, or rerun prisma-cli app list-deploys in a TTY to choose one.", ["prisma-cli app list-deploys"], "app");
|
|
1466
|
-
return matched;
|
|
1467
|
-
}
|
|
1468
|
-
const savedSelection = await context.stateStore.readSelectedApp(projectId);
|
|
1469
|
-
if (savedSelection) {
|
|
1470
|
-
const matched = apps.find((app) => app.id === savedSelection.id) ?? findAppByName(apps, savedSelection.name);
|
|
1471
|
-
if (matched) return matched;
|
|
1472
|
-
if (!canPrompt(context)) throw usageError("Saved app selection is no longer available", "The locally selected app could not be found in the resolved project.", "Pass --app <name>, or rerun prisma-cli app list-deploys in a TTY to choose an available app.", ["prisma-cli app list-deploys"], "app");
|
|
1473
|
-
}
|
|
1474
|
-
if (apps.length === 0) return null;
|
|
1475
|
-
if (!canPrompt(context)) throw usageError("App selection required in non-interactive mode", "This command cannot choose an app in the current mode.", "Pass --app <name>, or rerun prisma-cli app list-deploys in a TTY to choose an app.", ["prisma-cli app list-deploys"], "app");
|
|
1476
|
-
const selectedId = await createSelectPromptPort(context).select({
|
|
1477
|
-
message: "Select an app",
|
|
1478
|
-
choices: sortApps(apps).map((app) => ({
|
|
1479
|
-
label: app.name,
|
|
1480
|
-
value: app.id
|
|
1481
|
-
}))
|
|
1482
|
-
});
|
|
1483
|
-
return apps.find((app) => app.id === selectedId) ?? null;
|
|
1484
|
-
}
|
|
1485
|
-
async function requireReleaseAppSelection(context, projectId, apps, explicitAppName, commandName) {
|
|
1486
|
-
const selectedApp = await resolveExistingAppSelection(context, projectId, apps, explicitAppName);
|
|
1487
|
-
if (selectedApp) return selectedApp;
|
|
1488
|
-
throw usageError(`App ${commandName} requires an existing app`, "The resolved project does not have an app that can be selected for this command.", `Deploy an app first, or rerun prisma-cli app ${commandName} with --app <name> after an app exists.`, ["prisma-cli app deploy", "prisma-cli app list-deploys"], "app");
|
|
1489
|
-
}
|
|
1490
|
-
async function confirmAppRemoval(context, app) {
|
|
1491
|
-
if (context.flags.yes) return;
|
|
1492
|
-
if (!canPrompt(context)) throw new CliError({
|
|
1493
|
-
code: "CONFIRMATION_REQUIRED",
|
|
1494
|
-
domain: "app",
|
|
1495
|
-
summary: "App remove requires confirmation in the current mode",
|
|
1496
|
-
why: "This command is destructive and cannot prompt for confirmation in the current mode.",
|
|
1497
|
-
fix: `Pass --yes to confirm removal of "${app.name}", or rerun prisma-cli app remove in an interactive TTY.`,
|
|
1498
|
-
exitCode: 1,
|
|
1499
|
-
nextSteps: [`prisma-cli app remove --app ${app.name} --yes`]
|
|
1500
|
-
});
|
|
1501
|
-
await textPrompt({
|
|
1502
|
-
input: context.runtime.stdin,
|
|
1503
|
-
output: context.output.stderr,
|
|
1504
|
-
signal: context.runtime.signal,
|
|
1505
|
-
message: `Type ${app.name} to confirm app removal`,
|
|
1506
|
-
placeholder: app.name,
|
|
1507
|
-
validate: (value) => value === app.name ? void 0 : `Type "${app.name}" to confirm removal.`
|
|
1508
|
-
});
|
|
1509
|
-
}
|
|
1510
|
-
async function cleanupRemovedAppState(context, projectId, appId) {
|
|
1511
|
-
const warnings = [];
|
|
1512
|
-
try {
|
|
1513
|
-
await context.stateStore.clearSelectedApp(projectId, appId);
|
|
1514
|
-
} catch (error) {
|
|
1515
|
-
warnings.push(localStateCleanupWarning("selected app", error));
|
|
1516
|
-
}
|
|
1517
|
-
try {
|
|
1518
|
-
await context.stateStore.clearKnownLiveDeployment(projectId, appId);
|
|
1519
|
-
} catch (error) {
|
|
1520
|
-
warnings.push(localStateCleanupWarning("known live deployment", error));
|
|
1521
|
-
}
|
|
1522
|
-
return warnings;
|
|
1523
|
-
}
|
|
1524
|
-
function requireDeploymentForApp(deployments, deploymentId, appName) {
|
|
1525
|
-
const deployment = deployments.find((candidate) => candidate.id === deploymentId);
|
|
1526
|
-
if (deployment) return deployment;
|
|
1527
|
-
throw new CliError({
|
|
1528
|
-
code: "DEPLOYMENT_NOT_FOUND",
|
|
1529
|
-
domain: "app",
|
|
1530
|
-
summary: `Deployment "${deploymentId}" not found for app "${appName}"`,
|
|
1531
|
-
why: "The requested deployment does not belong to the resolved app or is no longer available.",
|
|
1532
|
-
fix: "Run prisma-cli app list-deploys to choose an available deployment id for this app.",
|
|
1533
|
-
exitCode: 1,
|
|
1534
|
-
nextSteps: ["prisma-cli app list-deploys"]
|
|
1535
|
-
});
|
|
1536
|
-
}
|
|
1537
|
-
/**
|
|
1538
|
-
* Resolves the app's live deployment from the app pointer, the provider's live
|
|
1539
|
-
* flag, then locally cached state.
|
|
1540
|
-
*
|
|
1541
|
-
* @returns the live deployment id, or null when no authoritative signal exists.
|
|
1542
|
-
* Callers must treat null as "not known to be live" rather than assuming the
|
|
1543
|
-
* newest deployment is live.
|
|
1544
|
-
*/
|
|
1545
|
-
async function resolveCurrentLiveDeploymentId(context, projectId, app, deployments) {
|
|
1546
|
-
if (app.liveDeploymentId && deployments.some((deployment) => deployment.id === app.liveDeploymentId)) return app.liveDeploymentId;
|
|
1547
|
-
const providerLiveDeployment = deployments.find((deployment) => deployment.live === true);
|
|
1548
|
-
if (providerLiveDeployment) return providerLiveDeployment.id;
|
|
1549
|
-
const knownLiveDeploymentId = await context.stateStore.readKnownLiveDeployment(projectId, app.id);
|
|
1550
|
-
if (knownLiveDeploymentId && deployments.some((deployment) => deployment.id === knownLiveDeploymentId)) return knownLiveDeploymentId;
|
|
1551
|
-
return null;
|
|
1552
|
-
}
|
|
1553
|
-
function buildAppShowNextSteps(liveUrl, liveDeployment, deployments) {
|
|
1554
|
-
const nextSteps = [];
|
|
1555
|
-
if (liveUrl) nextSteps.push("prisma-cli app open");
|
|
1556
|
-
if (liveDeployment) nextSteps.push(`prisma-cli app show-deploy ${liveDeployment.id}`);
|
|
1557
|
-
else if (deployments[0]) nextSteps.push(`prisma-cli app show-deploy ${deployments[0].id}`);
|
|
1558
|
-
else nextSteps.push("prisma-cli app deploy");
|
|
1559
|
-
return nextSteps;
|
|
1560
|
-
}
|
|
1561
|
-
function applyLiveDeploymentHint(deployments, currentLiveDeploymentId) {
|
|
1562
|
-
if (!currentLiveDeploymentId) return deployments.map((deployment) => ({
|
|
1563
|
-
...deployment,
|
|
1564
|
-
live: deployment.live ?? null
|
|
1565
|
-
}));
|
|
1566
|
-
return deployments.map((deployment) => ({
|
|
1567
|
-
...deployment,
|
|
1568
|
-
live: deployment.id === currentLiveDeploymentId
|
|
1569
|
-
}));
|
|
1570
|
-
}
|
|
1571
|
-
function resolveRollbackTarget(deployments, currentLiveDeploymentId) {
|
|
1572
|
-
const previousDeployment = deployments.find((deployment) => deployment.id !== currentLiveDeploymentId);
|
|
1573
|
-
if (previousDeployment) return previousDeployment;
|
|
1574
|
-
throw new CliError({
|
|
1575
|
-
code: "NO_PREVIOUS_DEPLOYMENT",
|
|
1576
|
-
domain: "app",
|
|
1577
|
-
summary: "No previous deployment available for rollback",
|
|
1578
|
-
why: "The selected app does not have an earlier deployment to switch back to.",
|
|
1579
|
-
fix: "Deploy a second version first, or rerun prisma-cli app rollback --to <deployment-id> for a specific earlier deployment.",
|
|
1580
|
-
exitCode: 1,
|
|
1581
|
-
nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
|
|
1582
|
-
});
|
|
1583
|
-
}
|
|
1584
|
-
async function listApps(context, provider, projectId, branchName) {
|
|
1585
|
-
return provider.listApps(projectId, {
|
|
1586
|
-
branchName,
|
|
1587
|
-
signal: context.runtime.signal
|
|
1588
|
-
}).then(sortApps).catch((error) => {
|
|
1589
|
-
if (isMissingProjectError(error)) throw new CliError({
|
|
1590
|
-
code: "PROJECT_NOT_FOUND",
|
|
1591
|
-
domain: "project",
|
|
1592
|
-
summary: "Project not found",
|
|
1593
|
-
why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`,
|
|
1594
|
-
fix: "Pass --project <id-or-name>, or run prisma-cli project show to inspect this directory's binding.",
|
|
1595
|
-
exitCode: 1,
|
|
1596
|
-
nextSteps: ["prisma-cli project show", "prisma-cli project link <id-or-name>"]
|
|
1597
|
-
});
|
|
1598
|
-
throw deployFailedError("Failed to list apps", error, ["prisma-cli project show"]);
|
|
1599
|
-
});
|
|
1600
|
-
}
|
|
1601
|
-
async function requirePreviewAppProvider(context) {
|
|
1602
|
-
const { provider } = await requirePreviewAppProviderWithClient(context);
|
|
1603
|
-
return provider;
|
|
1604
|
-
}
|
|
1605
|
-
async function requirePreviewAppProviderWithClient(context) {
|
|
1606
|
-
const client = await authenticatedManagementApiClient(context.runtime.env, context.runtime.signal);
|
|
1607
|
-
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
1608
|
-
return {
|
|
1609
|
-
client,
|
|
1610
|
-
provider: createAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
|
|
1611
|
-
};
|
|
1612
|
-
}
|
|
1613
|
-
function createPreviewLogAuthOptions(env, signal) {
|
|
1614
|
-
const rawToken = env[SERVICE_TOKEN_ENV_VAR]?.trim();
|
|
1615
|
-
if (rawToken) return {
|
|
1616
|
-
baseUrl: getApiBaseUrl(env),
|
|
1617
|
-
getToken: async () => rawToken
|
|
1618
|
-
};
|
|
1619
|
-
const tokenStorage = new FileTokenStorage(env, signal);
|
|
1620
|
-
return {
|
|
1621
|
-
baseUrl: getApiBaseUrl(env),
|
|
1622
|
-
getToken: async () => {
|
|
1623
|
-
const tokens = await tokenStorage.getTokens();
|
|
1624
|
-
if (!tokens) throw new Error("Authentication token is no longer available. Run prisma-cli auth login and try again.");
|
|
1625
|
-
return tokens.accessToken;
|
|
1626
|
-
}
|
|
1627
|
-
};
|
|
1628
|
-
}
|
|
1629
|
-
async function requireProviderAndProjectContext(context, explicitProject, options) {
|
|
1630
|
-
const { client, provider } = await requirePreviewAppProviderWithClient(context);
|
|
1631
|
-
const target = await resolveProjectContext(context, client, explicitProject, options);
|
|
1632
|
-
return {
|
|
1633
|
-
client,
|
|
1634
|
-
provider,
|
|
1635
|
-
target,
|
|
1636
|
-
projectId: target.project.id
|
|
1637
|
-
};
|
|
1638
|
-
}
|
|
1639
|
-
async function requireProviderAndDeployProjectContext(context, explicitProject, options) {
|
|
1640
|
-
const { client, provider } = await requirePreviewAppProviderWithClient(context);
|
|
1641
|
-
const target = await resolveDeployProjectContext(context, client, provider, explicitProject, options);
|
|
1642
|
-
return {
|
|
1643
|
-
client,
|
|
1644
|
-
provider,
|
|
1645
|
-
target,
|
|
1646
|
-
projectId: target.project.id
|
|
1647
|
-
};
|
|
1648
|
-
}
|
|
1649
|
-
async function resolveProjectContext(context, client, explicitProject, options) {
|
|
1650
|
-
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
1651
|
-
if (!workspace) throw workspaceRequiredError();
|
|
1652
|
-
const resolvedResult = await resolveProjectTarget({
|
|
1653
|
-
context,
|
|
1654
|
-
workspace,
|
|
1655
|
-
explicitProject,
|
|
1656
|
-
envProjectId: options?.envProjectId,
|
|
1657
|
-
projectDir: options?.projectDir,
|
|
1658
|
-
listProjects: () => listRealWorkspaceProjects(client, context.runtime.signal),
|
|
1659
|
-
commandName: options?.commandName
|
|
1660
|
-
});
|
|
1661
|
-
if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
|
|
1662
|
-
const resolved = resolvedResult.value;
|
|
1663
|
-
const requested = options?.branch ?? await resolveDeployBranch(context, void 0);
|
|
1664
|
-
const remoteBranch = options?.branch ? null : await resolveReadBranch(client, {
|
|
1665
|
-
projectId: resolved.project.id,
|
|
1666
|
-
branchName: requested.name,
|
|
1667
|
-
signal: context.runtime.signal
|
|
1668
|
-
});
|
|
1669
|
-
return {
|
|
1670
|
-
...resolved,
|
|
1671
|
-
branch: remoteBranch ?? {
|
|
1672
|
-
id: null,
|
|
1673
|
-
name: requested.name,
|
|
1674
|
-
kind: toBranchKind(requested.name)
|
|
1675
|
-
}
|
|
1676
|
-
};
|
|
1677
|
-
}
|
|
1678
|
-
async function resolveDeployProjectContext(context, client, provider, explicitProject, options) {
|
|
1679
|
-
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
1680
|
-
if (!workspace) throw workspaceRequiredError();
|
|
1681
|
-
const branch = options.branch ?? await resolveDeployBranch(context, void 0);
|
|
1682
|
-
const projects = await listRealWorkspaceProjects(client, context.runtime.signal);
|
|
1683
|
-
if (explicitProject) return withRemoteDeployBranch(provider, {
|
|
1684
|
-
workspace,
|
|
1685
|
-
project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
|
|
1686
|
-
resolution: {
|
|
1687
|
-
projectSource: "explicit",
|
|
1688
|
-
targetName: explicitProject,
|
|
1689
|
-
targetNameSource: "explicit"
|
|
1690
|
-
},
|
|
1691
|
-
localPinAction: "linked"
|
|
1692
|
-
}, branch, context.runtime.signal);
|
|
1693
|
-
if (options.createProjectName) return withRemoteDeployBranch(provider, await resolveCreatedDeployProject(provider, options.createProjectName, workspace, context.runtime.signal, options.createProjectRegion), branch, context.runtime.signal);
|
|
1694
|
-
if (options.envProjectId) return withRemoteDeployBranch(provider, resolveEnvDeployProject(options.envProjectId, projects, workspace), branch, context.runtime.signal);
|
|
1695
|
-
const localPin = options.localPin;
|
|
1696
|
-
if (localPin.kind === "present") return withRemoteDeployBranch(provider, resolveLocalPinDeployProject(localPin.pin, projects, workspace), branch, context.runtime.signal);
|
|
1697
|
-
const platformMapping = await resolveDurablePlatformMapping();
|
|
1698
|
-
if (platformMapping && sameWorkspaceId(platformMapping.workspace.id, workspace.id)) return withRemoteDeployBranch(provider, {
|
|
1699
|
-
workspace,
|
|
1700
|
-
project: toProjectSummary(platformMapping),
|
|
1701
|
-
resolution: {
|
|
1702
|
-
projectSource: "platform-mapping",
|
|
1703
|
-
targetName: platformMapping.name,
|
|
1704
|
-
targetNameSource: "platform-mapping"
|
|
1705
|
-
}
|
|
1706
|
-
}, branch, context.runtime.signal);
|
|
1707
|
-
if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, options.createProjectRegion), branch, context.runtime.signal);
|
|
1708
|
-
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1709
|
-
}
|
|
1710
|
-
async function resolveCreatedDeployProject(provider, createProjectName, workspace, signal, createProjectRegion) {
|
|
1711
|
-
const projectName = createProjectName.trim();
|
|
1712
|
-
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1713
|
-
return {
|
|
1714
|
-
workspace,
|
|
1715
|
-
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, signal, createProjectRegion)),
|
|
1716
|
-
resolution: {
|
|
1717
|
-
projectSource: "created",
|
|
1718
|
-
targetName: projectName,
|
|
1719
|
-
targetNameSource: "explicit"
|
|
1720
|
-
},
|
|
1721
|
-
localPinAction: "created"
|
|
1722
|
-
};
|
|
1723
|
-
}
|
|
1724
|
-
function resolveEnvDeployProject(envProjectId, projects, workspace) {
|
|
1725
|
-
const project = projects.find((candidate) => candidate.id === envProjectId);
|
|
1726
|
-
if (!project) throw projectNotFoundError(envProjectId, workspace);
|
|
1727
|
-
return {
|
|
1728
|
-
workspace,
|
|
1729
|
-
project: toProjectSummary(project),
|
|
1730
|
-
resolution: {
|
|
1731
|
-
projectSource: "env",
|
|
1732
|
-
targetName: envProjectId,
|
|
1733
|
-
targetNameSource: "env"
|
|
1734
|
-
}
|
|
1735
|
-
};
|
|
1736
|
-
}
|
|
1737
|
-
function resolveLocalPinDeployProject(pin, projects, workspace) {
|
|
1738
|
-
if (!sameWorkspaceId(pin.workspaceId, workspace.id)) throw localProjectWorkspaceMismatchError({
|
|
1739
|
-
pinnedWorkspaceId: pin.workspaceId,
|
|
1740
|
-
pinnedProjectId: pin.projectId,
|
|
1741
|
-
activeWorkspace: workspace
|
|
1742
|
-
});
|
|
1743
|
-
const project = projects.find((candidate) => candidate.id === pin.projectId);
|
|
1744
|
-
if (!project) throw localResolutionPinStaleError();
|
|
1745
|
-
return {
|
|
1746
|
-
workspace,
|
|
1747
|
-
project: toProjectSummary(project),
|
|
1748
|
-
resolution: {
|
|
1749
|
-
projectSource: "local-pin",
|
|
1750
|
-
targetName: project.name,
|
|
1751
|
-
targetNameSource: "local-pin"
|
|
1752
|
-
}
|
|
1753
|
-
};
|
|
1754
|
-
}
|
|
1755
|
-
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, createProjectRegion) {
|
|
1756
|
-
const setup = await promptForProjectSetupChoice({
|
|
1757
|
-
context,
|
|
1758
|
-
projects,
|
|
1759
|
-
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, createProjectRegion),
|
|
1760
|
-
cancel: {
|
|
1761
|
-
why: "Deploy needs a Project before it can continue.",
|
|
1762
|
-
fix: "Choose an existing Project or create a new one, then rerun deploy.",
|
|
1763
|
-
nextSteps: ["prisma-cli app deploy --project <id-or-name>", "prisma-cli app deploy --create-project <name>"]
|
|
1764
|
-
}
|
|
1765
|
-
});
|
|
1766
|
-
return {
|
|
1767
|
-
workspace,
|
|
1768
|
-
project: setup.project,
|
|
1769
|
-
resolution: {
|
|
1770
|
-
projectSource: setup.action === "created" ? "created" : "prompt",
|
|
1771
|
-
targetName: setup.targetName,
|
|
1772
|
-
targetNameSource: setup.targetNameSource
|
|
1773
|
-
},
|
|
1774
|
-
localPinAction: setup.action
|
|
1775
|
-
};
|
|
1776
|
-
}
|
|
1777
|
-
async function createProjectForDeploySetup(provider, projectName, workspace, signal, region) {
|
|
1778
|
-
const created = await provider.createProject({
|
|
1779
|
-
name: projectName,
|
|
1780
|
-
region,
|
|
1781
|
-
signal
|
|
1782
|
-
}).catch((error) => {
|
|
1783
|
-
throw projectCreateFailedError(error, projectName, workspace, {
|
|
1784
|
-
nextSteps: [
|
|
1785
|
-
"prisma-cli project list",
|
|
1786
|
-
"prisma-cli app deploy --project <id-or-name>",
|
|
1787
|
-
`prisma-cli app deploy --create-project ${formatCommandArgument(projectName)}`
|
|
1788
|
-
],
|
|
1789
|
-
permissionFix: "Choose an existing Project with --project, or grant the token permission to create Projects in this workspace.",
|
|
1790
|
-
fallbackFix: "Choose an existing Project with --project, or retry after addressing the platform error above."
|
|
1791
|
-
});
|
|
1792
|
-
});
|
|
1793
|
-
return {
|
|
1794
|
-
id: created.id,
|
|
1795
|
-
name: created.name,
|
|
1796
|
-
...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {},
|
|
1797
|
-
workspace
|
|
1798
|
-
};
|
|
1799
|
-
}
|
|
1800
|
-
async function withRemoteDeployBranch(provider, target, branch, signal) {
|
|
1801
|
-
const remoteBranch = await provider.resolveBranch(target.project.id, {
|
|
1802
|
-
branchName: branch.name,
|
|
1803
|
-
signal
|
|
1804
|
-
});
|
|
1805
|
-
return {
|
|
1806
|
-
...target,
|
|
1807
|
-
branch: {
|
|
1808
|
-
id: remoteBranch.id,
|
|
1809
|
-
name: remoteBranch.name,
|
|
1810
|
-
kind: remoteBranch.role
|
|
1811
|
-
}
|
|
1812
|
-
};
|
|
1813
|
-
}
|
|
1814
|
-
function toBranchKind(name) {
|
|
1815
|
-
return name === "production" || name === "main" ? "production" : "preview";
|
|
1816
|
-
}
|
|
1817
|
-
function toResultBranch(branch) {
|
|
1818
|
-
return {
|
|
1819
|
-
id: branch.id,
|
|
1820
|
-
name: branch.name,
|
|
1821
|
-
kind: branch.kind
|
|
1822
|
-
};
|
|
1823
|
-
}
|
|
1824
|
-
function toAppVerboseContext(target) {
|
|
1825
|
-
return {
|
|
1826
|
-
workspace: target.workspace,
|
|
1827
|
-
project: target.project,
|
|
1828
|
-
branch: target.branch,
|
|
1829
|
-
resolution: target.resolution
|
|
1830
|
-
};
|
|
1831
|
-
}
|
|
1832
|
-
function toBranchDatabaseDeployBranch(branch) {
|
|
1833
|
-
if (!branch.id) throw new Error(`Deploy branch "${branch.name}" was not resolved remotely.`);
|
|
1834
|
-
return {
|
|
1835
|
-
id: branch.id,
|
|
1836
|
-
name: branch.name,
|
|
1837
|
-
kind: branch.kind
|
|
1838
|
-
};
|
|
1839
|
-
}
|
|
1840
|
-
function assertExclusiveDeployProjectInputs(options) {
|
|
1841
|
-
const provided = [
|
|
1842
|
-
options.projectRef ? "--project" : null,
|
|
1843
|
-
options.createProjectName ? "--create-project" : null,
|
|
1844
|
-
options.envProjectId ? PRISMA_PROJECT_ID_ENV_VAR : null
|
|
1845
|
-
].filter((value) => Boolean(value));
|
|
1846
|
-
if (provided.length <= 1) return;
|
|
1847
|
-
throw usageError("Project selection is ambiguous", `${provided.join(", ")} cannot be used together.`, "Choose exactly one Project source for this deploy.", [
|
|
1848
|
-
"prisma-cli app deploy --project <id-or-name>",
|
|
1849
|
-
"prisma-cli app deploy --create-project <name>",
|
|
1850
|
-
`unset ${PRISMA_PROJECT_ID_ENV_VAR}`
|
|
1851
|
-
], "project");
|
|
1852
|
-
}
|
|
1853
|
-
async function resolveDeployBranch(context, explicitBranchName) {
|
|
1854
|
-
if (explicitBranchName) return {
|
|
1855
|
-
name: explicitBranchName,
|
|
1856
|
-
annotation: "set by --branch"
|
|
1857
|
-
};
|
|
1858
|
-
const gitBranch = await readLocalGitBranch(context.runtime.cwd, context.runtime.signal);
|
|
1859
|
-
if (gitBranch) return {
|
|
1860
|
-
name: gitBranch,
|
|
1861
|
-
annotation: "from local Git branch"
|
|
1862
|
-
};
|
|
1863
|
-
return {
|
|
1864
|
-
name: "main",
|
|
1865
|
-
annotation: "default"
|
|
1866
|
-
};
|
|
1867
|
-
}
|
|
1868
|
-
async function resolveComputeTargetOrThrow(context, configTarget, commandName, options) {
|
|
1869
|
-
let config;
|
|
1870
|
-
if (options?.preloaded !== void 0) config = options.preloaded;
|
|
1871
|
-
else {
|
|
1872
|
-
const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
|
|
1873
|
-
if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, commandName);
|
|
1874
|
-
config = loaded.value;
|
|
1875
|
-
}
|
|
1876
|
-
if (!config) {
|
|
1877
|
-
if (configTarget) throw usageError(`App target "${configTarget}" requires a compute config file`, `No ${COMPUTE_CONFIG_FILENAME} exists in the current directory, so there are no named app targets.`, `Create ${COMPUTE_CONFIG_FILENAME} with an apps entry named "${configTarget}", or rerun without the target argument.`, [`prisma-cli app ${commandName}`], "app");
|
|
1878
|
-
return {
|
|
1879
|
-
config: null,
|
|
1880
|
-
target: null
|
|
1881
|
-
};
|
|
1882
|
-
}
|
|
1883
|
-
const requestedTarget = configTarget ?? inferComputeTargetFromCwd(config, context.runtime.cwd);
|
|
1884
|
-
const selected = selectComputeDeployTarget(config, requestedTarget);
|
|
1885
|
-
if (selected.isErr()) {
|
|
1886
|
-
if (options?.targetOptional && selected.error instanceof ComputeConfigTargetRequiredError) return {
|
|
1887
|
-
config,
|
|
1888
|
-
target: null
|
|
1889
|
-
};
|
|
1890
|
-
throw computeConfigErrorToCliError(selected.error, commandName);
|
|
1891
|
-
}
|
|
1892
|
-
return {
|
|
1893
|
-
config,
|
|
1894
|
-
target: selected.value
|
|
1895
|
-
};
|
|
1896
|
-
}
|
|
1897
|
-
/**
|
|
1898
|
-
* Compute-config context for app management commands: the project directory
|
|
1899
|
-
* (where `.prisma/local.json` lives) and the config-selected app name, which
|
|
1900
|
-
* ranks below `--app` but above the remembered app selection.
|
|
1901
|
-
*/
|
|
1902
|
-
async function resolveComputeManagementContext(context, configTarget, commandName) {
|
|
1903
|
-
const compute = await resolveComputeTargetOrThrow(context, configTarget, commandName, { targetOptional: true });
|
|
1904
|
-
return {
|
|
1905
|
-
projectDir: compute.config?.configDir ?? context.runtime.cwd,
|
|
1906
|
-
configAppName: compute.target?.name ?? compute.target?.key ?? void 0
|
|
1907
|
-
};
|
|
1908
|
-
}
|
|
1909
|
-
async function resolveComputeAppDir(context, compute) {
|
|
1910
|
-
if (!compute.config || !compute.target) return context.runtime.cwd;
|
|
1911
|
-
const appDir = computeTargetAppDir(compute.config, compute.target);
|
|
1912
|
-
if (!compute.target.root) return appDir;
|
|
1913
|
-
context.runtime.signal.throwIfAborted();
|
|
1914
|
-
try {
|
|
1915
|
-
await access(appDir);
|
|
1916
|
-
context.runtime.signal.throwIfAborted();
|
|
1917
|
-
} catch (error) {
|
|
1918
|
-
if (context.runtime.signal.aborted) throw error;
|
|
1919
|
-
throw new CliError({
|
|
1920
|
-
code: "COMPUTE_CONFIG_INVALID",
|
|
1921
|
-
domain: "app",
|
|
1922
|
-
summary: `App root "${compute.target.root}" does not exist`,
|
|
1923
|
-
why: `${compute.config.relativeConfigPath} points the selected app at "${compute.target.root}", but that directory does not exist.`,
|
|
1924
|
-
fix: `Fix the root path in ${compute.config.relativeConfigPath} or create the directory.`,
|
|
1925
|
-
where: appDir,
|
|
1926
|
-
meta: {
|
|
1927
|
-
appRoot: compute.target.root,
|
|
1928
|
-
appDir
|
|
1929
|
-
},
|
|
1930
|
-
exitCode: 2,
|
|
1931
|
-
nextSteps: ["prisma-cli app deploy"]
|
|
1932
|
-
});
|
|
1933
|
-
}
|
|
1934
|
-
return appDir;
|
|
1935
|
-
}
|
|
1936
|
-
/**
|
|
1937
|
-
* `prisma.app.json` is no longer read or written. A leftover file that
|
|
1938
|
-
* matches the effective settings only warns; one with custom values fails
|
|
1939
|
-
* with migration guidance so builds never silently change.
|
|
1940
|
-
*/
|
|
1941
|
-
async function handleLegacyBuildSettings(context, appDir, effective) {
|
|
1942
|
-
const legacy = await detectLegacyBuildSettings({
|
|
1943
|
-
appPath: appDir,
|
|
1944
|
-
effective,
|
|
1945
|
-
signal: context.runtime.signal
|
|
1946
|
-
});
|
|
1947
|
-
switch (legacy.kind) {
|
|
1948
|
-
case "absent": return [];
|
|
1949
|
-
case "matching": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and matches the resolved build settings. Delete it.`];
|
|
1950
|
-
case "invalid": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and could not be parsed. Delete it.`];
|
|
1951
|
-
case "custom": {
|
|
1952
|
-
const buildBlock = [
|
|
1953
|
-
"build: {",
|
|
1954
|
-
` command: ${legacy.buildCommand === null ? "null" : JSON.stringify(legacy.buildCommand)},`,
|
|
1955
|
-
` outputDirectory: ${JSON.stringify(legacy.outputDirectory)},`,
|
|
1956
|
-
"}"
|
|
1957
|
-
].join(" ");
|
|
1958
|
-
throw new CliError({
|
|
1959
|
-
code: "BUILD_SETTINGS_MIGRATION_REQUIRED",
|
|
1960
|
-
domain: "app",
|
|
1961
|
-
summary: `${PRISMA_APP_CONFIG_FILENAME} is no longer supported`,
|
|
1962
|
-
why: `${PRISMA_APP_CONFIG_FILENAME} contains custom build settings that differ from the resolved defaults, and the file is no longer read.`,
|
|
1963
|
-
fix: `Move the settings into prisma.compute.ts as \`${buildBlock}\` on this app, then delete ${PRISMA_APP_CONFIG_FILENAME}.`,
|
|
1964
|
-
where: legacy.configPath,
|
|
1965
|
-
meta: {
|
|
1966
|
-
configPath: legacy.configPath,
|
|
1967
|
-
buildCommand: legacy.buildCommand,
|
|
1968
|
-
outputDirectory: legacy.outputDirectory
|
|
1969
|
-
},
|
|
1970
|
-
exitCode: 2,
|
|
1971
|
-
nextSteps: ["prisma-cli app deploy"]
|
|
1972
|
-
});
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
async function resolveDeployFramework(context, options) {
|
|
1977
|
-
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, options.requestedFrameworkAnnotation ?? "set by --framework");
|
|
1978
|
-
if (options.entrypoint) return {
|
|
1979
|
-
key: "bun",
|
|
1980
|
-
buildType: "bun",
|
|
1981
|
-
displayName: "Bun",
|
|
1982
|
-
annotation: options.entrypointAnnotation ?? "set by --entry"
|
|
1983
|
-
};
|
|
1984
|
-
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
1985
|
-
if (detected) return detected;
|
|
1986
|
-
throw frameworkNotDetectedError(options.appDir);
|
|
1987
|
-
}
|
|
1988
|
-
function resolveDeployRuntime(requestedHttpPort, requestedHttpPortAnnotation, framework) {
|
|
1989
|
-
if (requestedHttpPort) return {
|
|
1990
|
-
port: parseDeployHttpPort(requestedHttpPort),
|
|
1991
|
-
annotation: requestedHttpPortAnnotation ?? "set by --http-port"
|
|
1992
|
-
};
|
|
1993
|
-
return {
|
|
1994
|
-
port: FRAMEWORK_DEFAULT_HTTP_PORT,
|
|
1995
|
-
annotation: `${framework.displayName} default`
|
|
1996
|
-
};
|
|
1997
|
-
}
|
|
1998
|
-
function assertSupportedEntrypointForRequestedDeployShape(options) {
|
|
1999
|
-
if (!options.requestedFramework) return;
|
|
2000
|
-
assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
|
|
2001
|
-
}
|
|
2002
|
-
async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signal) {
|
|
2003
|
-
if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
|
|
2004
|
-
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
|
|
2005
|
-
if (packageEntrypoint) return packageEntrypoint;
|
|
2006
|
-
const defaultEntrypoint = frameworkFromAlias(framework.key)?.defaultEntrypoint;
|
|
2007
|
-
if (!defaultEntrypoint) return;
|
|
2008
|
-
signal.throwIfAborted();
|
|
2009
|
-
try {
|
|
2010
|
-
await access(path.join(cwd, defaultEntrypoint));
|
|
2011
|
-
signal.throwIfAborted();
|
|
2012
|
-
return defaultEntrypoint;
|
|
2013
|
-
} catch (error) {
|
|
2014
|
-
if (signal.aborted) throw error;
|
|
2015
|
-
if (error.code !== "ENOENT") throw error;
|
|
2016
|
-
return;
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
async function detectDeployFramework(cwd, signal) {
|
|
2020
|
-
const detected = await detectComputeAppFromDirectory({
|
|
2021
|
-
appPath: cwd,
|
|
2022
|
-
signal
|
|
2023
|
-
});
|
|
2024
|
-
if (!detected) return null;
|
|
2025
|
-
let annotation = "detected from package.json";
|
|
2026
|
-
if (detected.configFile?.standaloneOutput) annotation = "standalone output detected";
|
|
2027
|
-
else if (detected.configFile) annotation = `detected from ${path.basename(detected.configFile.path)}`;
|
|
2028
|
-
return {
|
|
2029
|
-
key: detected.framework,
|
|
2030
|
-
buildType: detected.buildType,
|
|
2031
|
-
displayName: detected.frameworkName,
|
|
2032
|
-
annotation
|
|
2033
|
-
};
|
|
2034
|
-
}
|
|
2035
|
-
function frameworkFromUserFacingValue(value, annotation) {
|
|
2036
|
-
const framework = frameworkFromAlias(value);
|
|
2037
|
-
if (!framework) throw frameworkNotDetectedError(void 0, value);
|
|
2038
|
-
return {
|
|
2039
|
-
key: framework.key,
|
|
2040
|
-
buildType: framework.buildType,
|
|
2041
|
-
displayName: framework.displayName,
|
|
2042
|
-
annotation
|
|
2043
|
-
};
|
|
2044
|
-
}
|
|
2045
|
-
function assertConfigBackedBuildSettings(buildType) {
|
|
2046
|
-
if (isConfigBackedBuildType(buildType)) return;
|
|
2047
|
-
const displayName = FRAMEWORKS.find((framework) => framework.buildType === buildType)?.displayName ?? buildType;
|
|
2048
|
-
throw new CliError({
|
|
2049
|
-
code: "BUILD_SETTINGS_UNSUPPORTED",
|
|
2050
|
-
domain: "app",
|
|
2051
|
-
summary: `build settings are not supported for ${displayName} apps`,
|
|
2052
|
-
why: `${displayName} deploys run \`${buildType} build\` and package its output automatically.`,
|
|
2053
|
-
fix: "Remove the `build` block from prisma.compute.ts for this app.",
|
|
2054
|
-
exitCode: 2
|
|
2055
|
-
});
|
|
2056
|
-
}
|
|
2057
|
-
function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
2058
|
-
const supported = FRAMEWORKS.map((framework) => framework.displayName).join(", ");
|
|
2059
|
-
const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
|
|
2060
|
-
return new CliError({
|
|
2061
|
-
code: "FRAMEWORK_NOT_DETECTED",
|
|
2062
|
-
domain: "app",
|
|
2063
|
-
summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
|
|
2064
|
-
why: `Supported Beta frameworks: ${supported}.`,
|
|
2065
|
-
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.`,
|
|
2066
|
-
exitCode: 2,
|
|
2067
|
-
nextSteps: [
|
|
2068
|
-
"prisma-cli app deploy --framework nextjs",
|
|
2069
|
-
"prisma-cli app deploy --framework hono",
|
|
2070
|
-
"prisma-cli app deploy --framework tanstack-start",
|
|
2071
|
-
"prisma-cli app deploy --framework bun --entry server.ts",
|
|
2072
|
-
"prisma-cli app deploy --entry server.ts"
|
|
2073
|
-
]
|
|
2074
|
-
});
|
|
2075
|
-
}
|
|
2076
|
-
async function maybeRenderDeploySetupBlock(context, details) {
|
|
2077
|
-
if (context.flags.json || context.flags.quiet) return;
|
|
2078
|
-
const directory = formatAppDirectoryLabel(context.runtime.cwd, details.appDir);
|
|
2079
|
-
const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
|
|
2080
|
-
context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
|
|
2081
|
-
}
|
|
2082
|
-
function maybeRenderDeployBuildSettings(context, resolution) {
|
|
2083
|
-
if (context.flags.json || context.flags.quiet) return;
|
|
2084
|
-
const settings = resolution.settings;
|
|
2085
|
-
const title = resolution.status === "config" ? `Using ${resolution.relativeConfigPath}` : "Build settings";
|
|
2086
|
-
context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [
|
|
2087
|
-
{
|
|
2088
|
-
label: "Build Command",
|
|
2089
|
-
value: settings.buildCommand ?? "none",
|
|
2090
|
-
origin: settings.buildCommandSource ?? void 0
|
|
2091
|
-
},
|
|
2092
|
-
{
|
|
2093
|
-
label: "Output Directory",
|
|
2094
|
-
value: settings.outputDirectory,
|
|
2095
|
-
origin: settings.outputDirectorySource ?? void 0
|
|
2096
|
-
},
|
|
2097
|
-
...settings.entrypoint ? [{
|
|
2098
|
-
label: "Entrypoint",
|
|
2099
|
-
value: settings.entrypoint,
|
|
2100
|
-
origin: settings.entrypointSource ?? void 0
|
|
2101
|
-
}] : []
|
|
2102
|
-
]).join("\n")}\n\n`);
|
|
2103
|
-
}
|
|
2104
|
-
function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
|
|
2105
|
-
if (context.flags.json || context.flags.quiet) return;
|
|
2106
|
-
context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
|
|
2107
|
-
}
|
|
2108
|
-
async function maybeCustomizeDeploySettings(context, options) {
|
|
2109
|
-
if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitEntrypoint || options.explicitHttpPort || !canPrompt(context)) return {
|
|
2110
|
-
framework: options.framework,
|
|
2111
|
-
runtime: options.runtime
|
|
2112
|
-
};
|
|
2113
|
-
maybeRenderDeploySettingsPreview(context, {
|
|
2114
|
-
framework: options.framework,
|
|
2115
|
-
runtime: options.runtime
|
|
2116
|
-
});
|
|
2117
|
-
if (!await confirmPrompt({
|
|
2118
|
-
input: context.runtime.stdin,
|
|
2119
|
-
output: context.runtime.stderr,
|
|
2120
|
-
signal: context.runtime.signal,
|
|
2121
|
-
message: "Customize build settings?",
|
|
2122
|
-
initialValue: false
|
|
2123
|
-
})) return {
|
|
2124
|
-
framework: options.framework,
|
|
2125
|
-
runtime: options.runtime
|
|
2126
|
-
};
|
|
2127
|
-
const framework = frameworkFromUserFacingValue(await selectPrompt({
|
|
2128
|
-
input: context.runtime.stdin,
|
|
2129
|
-
output: context.runtime.stderr,
|
|
2130
|
-
signal: context.runtime.signal,
|
|
2131
|
-
message: `Framework (${options.framework.displayName})`,
|
|
2132
|
-
choices: FRAMEWORKS.map((framework) => ({
|
|
2133
|
-
label: framework.displayName,
|
|
2134
|
-
value: framework.key
|
|
2135
|
-
}))
|
|
2136
|
-
}), "set by you");
|
|
2137
|
-
const requestedPort = await textPrompt({
|
|
2138
|
-
input: context.runtime.stdin,
|
|
2139
|
-
output: context.runtime.stderr,
|
|
2140
|
-
signal: context.runtime.signal,
|
|
2141
|
-
message: `HTTP port (${options.runtime.port})`,
|
|
2142
|
-
placeholder: String(options.runtime.port),
|
|
2143
|
-
validate: validateDeployHttpPortText
|
|
2144
|
-
});
|
|
2145
|
-
const runtime = {
|
|
2146
|
-
port: requestedPort.trim() ? parseDeployHttpPort(requestedPort) : options.runtime.port,
|
|
2147
|
-
annotation: "set by you"
|
|
2148
|
-
};
|
|
2149
|
-
const changedRows = [framework.key !== options.framework.key ? {
|
|
2150
|
-
label: "Framework",
|
|
2151
|
-
value: framework.displayName,
|
|
2152
|
-
annotation: framework.annotation
|
|
2153
|
-
} : null, runtime.port !== options.runtime.port ? {
|
|
2154
|
-
label: "Runtime",
|
|
2155
|
-
value: `HTTP ${runtime.port}`,
|
|
2156
|
-
annotation: runtime.annotation
|
|
2157
|
-
} : null].filter((row) => Boolean(row));
|
|
2158
|
-
if (changedRows.length > 0 && !context.flags.quiet && !context.flags.json) context.output.stderr.write(`${renderDeployOutputRows(context.ui, changedRows.map((row) => ({
|
|
2159
|
-
label: row.label,
|
|
2160
|
-
value: row.value,
|
|
2161
|
-
origin: row.annotation
|
|
2162
|
-
}))).join("\n")}\n\n`);
|
|
2163
|
-
return {
|
|
2164
|
-
framework,
|
|
2165
|
-
runtime
|
|
2166
|
-
};
|
|
2167
|
-
}
|
|
2168
|
-
function maybeRenderDeploySettingsPreview(context, options) {
|
|
2169
|
-
if (context.flags.quiet || context.flags.json) return;
|
|
2170
|
-
context.output.stderr.write(`Detected ${options.framework.displayName}\n${renderDeploySettingsPreview(context.ui, [{
|
|
2171
|
-
key: "framework",
|
|
2172
|
-
value: options.framework.displayName
|
|
2173
|
-
}, {
|
|
2174
|
-
key: "runtime",
|
|
2175
|
-
value: `HTTP ${options.runtime.port}`
|
|
2176
|
-
}]).join("\n")}\n\n`);
|
|
2177
|
-
}
|
|
2178
|
-
function validateDeployHttpPortText(value) {
|
|
2179
|
-
if (!value?.trim()) return;
|
|
2180
|
-
try {
|
|
2181
|
-
parseDeployHttpPort(value);
|
|
2182
|
-
return;
|
|
2183
|
-
} catch (error) {
|
|
2184
|
-
return error instanceof CliError ? error.summary : String(error);
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
function formatDeployDirectory(cwd) {
|
|
2188
|
-
const basename = path.basename(cwd);
|
|
2189
|
-
return basename ? `./${basename}` : ".";
|
|
2190
|
-
}
|
|
2191
|
-
function formatAppDirectoryLabel(cwd, appDir) {
|
|
2192
|
-
if (appDir === cwd) return formatDeployDirectory(cwd);
|
|
2193
|
-
const relative = path.relative(cwd, appDir).split(path.sep).join("/");
|
|
2194
|
-
return relative.startsWith("..") ? relative : `./${relative}`;
|
|
2195
|
-
}
|
|
2196
|
-
async function readCurrentWorkspaceId(context) {
|
|
2197
|
-
const state = await context.stateStore.read();
|
|
2198
|
-
if (state.auth?.workspaceId) return state.auth.workspaceId;
|
|
2199
|
-
return (await readAuthState(context.runtime.env, context.runtime.signal)).workspace?.id ?? null;
|
|
2200
|
-
}
|
|
2201
|
-
function normalizeBuildType(requestedBuildType) {
|
|
2202
|
-
if (!requestedBuildType) return "auto";
|
|
2203
|
-
if (isPreviewBuildType(requestedBuildType)) return requestedBuildType;
|
|
2204
|
-
throw usageError(`Unsupported build type "${requestedBuildType}"`, `Only ${APP_BUILD_TYPE_LABELS} are supported in the current preview.`, "Pass a supported --build-type value.", getBuildTypeExamples("build"), "app");
|
|
2205
|
-
}
|
|
2206
|
-
function isPreviewBuildType(value) {
|
|
2207
|
-
return APP_BUILD_TYPES.includes(value);
|
|
2208
|
-
}
|
|
2209
|
-
function getBuildTypeExamples(commandName) {
|
|
2210
|
-
return RESOLVED_APP_BUILD_TYPES.map((buildType) => {
|
|
2211
|
-
return `prisma-cli app ${commandName} --build-type ${buildType}${buildType === "bun" ? " --entry server.ts" : ""}`;
|
|
2212
|
-
});
|
|
2213
|
-
}
|
|
2214
|
-
function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
|
|
2215
|
-
if (buildType !== "auto" && !ENTRYPOINT_BUILD_TYPES.includes(buildType) && entrypoint) {
|
|
2216
|
-
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");
|
|
2217
|
-
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");
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
/**
|
|
2221
|
-
* Resolves the framework for `app run` with the same detection as deploy, so
|
|
2222
|
-
* a repo that deploys without flags also runs without flags. Local dev server
|
|
2223
|
-
* support is intentionally narrower than deploy build support: only Next.js
|
|
2224
|
-
* and Bun/Hono have dev servers in the current preview.
|
|
2225
|
-
*/
|
|
2226
|
-
async function resolveLocalRunFramework(context, options) {
|
|
2227
|
-
if (options.requestedBuildType === "auto" && options.entrypoint) return frameworkFromUserFacingValue("bun", "set by --entry");
|
|
2228
|
-
if (LOCAL_DEV_BUILD_TYPES.includes(options.requestedBuildType)) {
|
|
2229
|
-
if (options.configFramework && computeFrameworkToBuildType(options.configFramework) === options.requestedBuildType) return frameworkFromUserFacingValue(options.configFramework, `set by ${COMPUTE_CONFIG_FILENAME}`);
|
|
2230
|
-
return frameworkFromUserFacingValue(options.requestedBuildType, "set by --build-type");
|
|
2231
|
-
}
|
|
2232
|
-
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
2233
|
-
if (detected && LOCAL_DEV_BUILD_TYPES.includes(detected.buildType)) return detected;
|
|
2234
|
-
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");
|
|
2235
|
-
}
|
|
2236
|
-
function parseLocalPort(requestedPort) {
|
|
2237
|
-
if (!requestedPort) return DEFAULT_LOCAL_DEV_PORT;
|
|
2238
|
-
const port = Number.parseInt(requestedPort, 10);
|
|
2239
|
-
if (!Number.isInteger(port) || port <= 0 || port > 65535) throw usageError(`Invalid port "${requestedPort}"`, "Port must be an integer between 1 and 65535.", "Pass --port <number> with a valid local port value.", ["prisma-cli app run --port 3000"], "app");
|
|
2240
|
-
return port;
|
|
2241
|
-
}
|
|
2242
|
-
function parseDeployPortMapping(requestedPort) {
|
|
2243
|
-
if (!requestedPort) return;
|
|
2244
|
-
return { http: parseDeployHttpPort(requestedPort) };
|
|
2245
|
-
}
|
|
2246
|
-
function parseDeployHttpPort(requestedPort) {
|
|
2247
|
-
const port = Number.parseInt(requestedPort, 10);
|
|
2248
|
-
if (!Number.isInteger(port) || port <= 0 || port > 65535) throw usageError(`Invalid HTTP port "${requestedPort}"`, "HTTP port must be an integer between 1 and 65535.", "Pass --http-port <number> with a valid port value.", ["prisma-cli app deploy --http-port 3000"], "app");
|
|
2249
|
-
return port;
|
|
2250
|
-
}
|
|
2251
|
-
function parseDeployRegion(requestedRegion, source) {
|
|
2252
|
-
const region = requestedRegion.trim();
|
|
2253
|
-
if (region.length === 0) throw usageError("Invalid app region", `The app region ${source} must be a non-empty region id.`, "Pass a Prisma Compute region id.", ["prisma-cli app deploy --region eu-central-1"], "app");
|
|
2254
|
-
if (!COMPUTE_REGION_IDS.has(region)) throw usageError("Invalid app region", `The app region ${source} must be one of: ${COMPUTE_REGIONS.join(", ")}.`, "Pass a supported Prisma Compute region id.", ["prisma-cli app deploy --region eu-central-1"], "app");
|
|
2255
|
-
return region;
|
|
2256
|
-
}
|
|
2257
|
-
function normalizeDeployRegionInput(region) {
|
|
2258
|
-
if (!region) return;
|
|
2259
|
-
return {
|
|
2260
|
-
...region,
|
|
2261
|
-
value: parseDeployRegion(region.value, region.annotation)
|
|
2262
|
-
};
|
|
2263
|
-
}
|
|
2264
|
-
function deployFailedError(summary, error, nextSteps) {
|
|
2265
|
-
return new CliError({
|
|
2266
|
-
code: "DEPLOY_FAILED",
|
|
2267
|
-
domain: "app",
|
|
2268
|
-
summary,
|
|
2269
|
-
why: error instanceof Error ? error.message : String(error),
|
|
2270
|
-
fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
|
|
2271
|
-
debug: formatDebugDetails(error),
|
|
2272
|
-
exitCode: 1,
|
|
2273
|
-
nextSteps
|
|
2274
|
-
});
|
|
2275
|
-
}
|
|
2276
|
-
function describeStalledDeployPhase(progress) {
|
|
2277
|
-
if (progress.uploadCompleted) return "The artifact uploaded, but the deployment did not start.";
|
|
2278
|
-
if (progress.archiveReady) return "The app built locally, but the artifact did not finish uploading.";
|
|
2279
|
-
return "The app built locally, but the deployment did not start.";
|
|
2280
|
-
}
|
|
2281
|
-
function localBuildFailedError(why, debug) {
|
|
2282
|
-
const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
|
|
2283
|
-
const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
|
|
2284
|
-
const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
|
|
2285
|
-
const nextActions = standaloneOutputFailure ? [{
|
|
2286
|
-
kind: "edit-file",
|
|
2287
|
-
journey: "deploy-app",
|
|
2288
|
-
label: "Add Next.js standalone output",
|
|
2289
|
-
reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
|
|
2290
|
-
}, {
|
|
2291
|
-
kind: "run-command",
|
|
2292
|
-
journey: "deploy-app",
|
|
2293
|
-
label: "Rerun deploy",
|
|
2294
|
-
command: "prisma-cli app deploy"
|
|
2295
|
-
}] : [];
|
|
2296
|
-
return new CliError({
|
|
2297
|
-
code: "BUILD_FAILED",
|
|
2298
|
-
domain: "app",
|
|
2299
|
-
summary: "Build failed locally.",
|
|
2300
|
-
why,
|
|
2301
|
-
fix,
|
|
2302
|
-
debug,
|
|
2303
|
-
meta: { phase: "build" },
|
|
2304
|
-
humanLines: [
|
|
2305
|
-
"Build failed locally.",
|
|
2306
|
-
"",
|
|
2307
|
-
`✗ Built ${why}`,
|
|
2308
|
-
"",
|
|
2309
|
-
`Fix: ${fix}`
|
|
2310
|
-
],
|
|
2311
|
-
exitCode: 1,
|
|
2312
|
-
nextSteps,
|
|
2313
|
-
nextActions
|
|
2314
|
-
});
|
|
2315
|
-
}
|
|
2316
|
-
function appDeployFailedError(error, progress) {
|
|
2317
|
-
const why = error instanceof Error ? error.message : String(error);
|
|
2318
|
-
const debug = formatDebugDetails(error);
|
|
2319
|
-
if (progress.buildStarted && !progress.buildCompleted) return localBuildFailedError(why, debug);
|
|
2320
|
-
if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
|
|
2321
|
-
const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
|
|
2322
|
-
const recoveryLines = progress.deploymentId ? ["See what happened", `prisma-cli app logs --deployment ${progress.deploymentId}`] : ["Fix", "Retry the command, or rerun with --trace for more detailed diagnostics."];
|
|
2323
|
-
const urlLines = progress.deploymentUrl ? [
|
|
2324
|
-
"",
|
|
2325
|
-
"URL",
|
|
2326
|
-
progress.deploymentUrl
|
|
2327
|
-
] : [];
|
|
2328
|
-
const humanLines = progress.containerLive ? [
|
|
2329
|
-
phaseHeadline,
|
|
2330
|
-
"",
|
|
2331
|
-
"This is usually a missing env var, a failed DB connection,",
|
|
2332
|
-
"or a crash on startup.",
|
|
2333
|
-
"",
|
|
2334
|
-
...recoveryLines,
|
|
2335
|
-
...urlLines
|
|
2336
|
-
] : [
|
|
2337
|
-
phaseHeadline,
|
|
2338
|
-
"",
|
|
2339
|
-
describeStalledDeployPhase(progress),
|
|
2340
|
-
"",
|
|
2341
|
-
...recoveryLines
|
|
2342
|
-
];
|
|
2343
|
-
return new CliError({
|
|
2344
|
-
code: "DEPLOY_FAILED",
|
|
2345
|
-
domain: "app",
|
|
2346
|
-
summary: phaseHeadline,
|
|
2347
|
-
why,
|
|
2348
|
-
fix: progress.deploymentId ? `Inspect logs with prisma-cli app logs --deployment ${progress.deploymentId}.` : "Retry the command, or rerun with --trace for more detailed diagnostics.",
|
|
2349
|
-
debug,
|
|
2350
|
-
meta: {
|
|
2351
|
-
phase: progress.containerLive ? "runtime_ready" : "deploy",
|
|
2352
|
-
deploymentId: progress.deploymentId,
|
|
2353
|
-
deploymentUrl: progress.deploymentUrl
|
|
2354
|
-
},
|
|
2355
|
-
humanLines,
|
|
2356
|
-
exitCode: 1,
|
|
2357
|
-
nextSteps: []
|
|
2358
|
-
});
|
|
2359
|
-
}
|
|
2360
|
-
function localResolutionPinStaleError() {
|
|
2361
|
-
return new CliError({
|
|
2362
|
-
code: "LOCAL_STATE_STALE",
|
|
2363
|
-
domain: "project",
|
|
2364
|
-
summary: "Local project binding is stale",
|
|
2365
|
-
why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
|
|
2366
|
-
fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
|
|
2367
|
-
meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
|
|
2368
|
-
exitCode: 1,
|
|
2369
|
-
nextSteps: [
|
|
2370
|
-
"prisma-cli project list",
|
|
2371
|
-
"prisma-cli project link <id-or-name>",
|
|
2372
|
-
"prisma-cli app deploy --project <id-or-name>"
|
|
2373
|
-
]
|
|
2374
|
-
});
|
|
2375
|
-
}
|
|
2376
|
-
function localPinReadErrorToDeployError(error) {
|
|
2377
|
-
return matchError(error, {
|
|
2378
|
-
LocalResolutionPinInvalidJsonError: () => localResolutionPinStaleError(),
|
|
2379
|
-
LocalResolutionPinInvalidShapeError: () => localResolutionPinStaleError(),
|
|
2380
|
-
LocalResolutionPinReadAbortedError: (error) => {
|
|
2381
|
-
throw error;
|
|
2382
|
-
},
|
|
2383
|
-
UnhandledException: (error) => {
|
|
2384
|
-
throw error;
|
|
2385
|
-
}
|
|
2386
|
-
});
|
|
2387
|
-
}
|
|
2388
|
-
function readDeployEnvOverride(context, name) {
|
|
2389
|
-
const value = context.runtime.env[name]?.trim();
|
|
2390
|
-
return value ? value : void 0;
|
|
2391
|
-
}
|
|
2392
|
-
function projectSetupRequiredError(projects, suggestedName) {
|
|
2393
|
-
const createCommand = `prisma-cli app deploy --create-project ${formatCommandArgument(suggestedName.name)}`;
|
|
2394
|
-
return new CliError({
|
|
2395
|
-
code: "PROJECT_SETUP_REQUIRED",
|
|
2396
|
-
domain: "project",
|
|
2397
|
-
summary: "Choose a Project before deploying this directory",
|
|
2398
|
-
why: "This directory is not linked to a Prisma Project, and deploy will not choose or create one implicitly.",
|
|
2399
|
-
fix: "Choose an existing Project with --project, create one with --create-project, or rerun interactively to pick from the setup list.",
|
|
2400
|
-
meta: {
|
|
2401
|
-
candidates: sortProjects(projects).map((project) => ({
|
|
2402
|
-
id: project.id,
|
|
2403
|
-
name: project.name
|
|
2404
|
-
})),
|
|
2405
|
-
suggestedProjectName: suggestedName.name,
|
|
2406
|
-
suggestedProjectNameSource: suggestedName.source,
|
|
2407
|
-
recoveryCommands: ["prisma-cli app deploy --project <id-or-name>", createCommand]
|
|
2408
|
-
},
|
|
2409
|
-
exitCode: 1,
|
|
2410
|
-
nextSteps: [
|
|
2411
|
-
"prisma-cli project list",
|
|
2412
|
-
"prisma-cli app deploy --project <id-or-name>",
|
|
2413
|
-
createCommand
|
|
2414
|
-
],
|
|
2415
|
-
nextActions: buildProjectSetupNextActions({
|
|
2416
|
-
commandName: "app deploy",
|
|
2417
|
-
createCommand,
|
|
2418
|
-
reason: "This directory is not linked to a Prisma Project. Ask the user which Project to use before deploying; package and directory names are setup suggestions only."
|
|
2419
|
-
})
|
|
2420
|
-
});
|
|
2421
|
-
}
|
|
2422
|
-
function isNextStandaloneOutputFailure(message) {
|
|
2423
|
-
return NEXTJS_MENTION.test(message) && STANDALONE_OUTPUT_MENTION.test(message);
|
|
2424
|
-
}
|
|
2425
|
-
function noDeploymentsError(summary, why) {
|
|
2426
|
-
return new CliError({
|
|
2427
|
-
code: "NO_DEPLOYMENTS",
|
|
2428
|
-
domain: "app",
|
|
2429
|
-
summary,
|
|
2430
|
-
why,
|
|
2431
|
-
fix: "Run prisma-cli app deploy first, or use prisma-cli app show to inspect the current app state.",
|
|
2432
|
-
exitCode: 1,
|
|
2433
|
-
nextSteps: ["prisma-cli app deploy", "prisma-cli app show"]
|
|
2434
|
-
});
|
|
2435
|
-
}
|
|
2436
|
-
function buildFailedError(summary, error) {
|
|
2437
|
-
return new CliError({
|
|
2438
|
-
code: "BUILD_FAILED",
|
|
2439
|
-
domain: "app",
|
|
2440
|
-
summary,
|
|
2441
|
-
why: error instanceof Error ? error.message : String(error),
|
|
2442
|
-
fix: "Inspect the framework output, fix the build issue, and rerun prisma-cli app build.",
|
|
2443
|
-
debug: formatDebugDetails(error),
|
|
2444
|
-
exitCode: 1,
|
|
2445
|
-
nextSteps: ["prisma-cli app build", "prisma-cli app deploy"]
|
|
2446
|
-
});
|
|
2447
|
-
}
|
|
2448
|
-
function runFailedError(summary, error, exitCode = 1) {
|
|
2449
|
-
return new CliError({
|
|
2450
|
-
code: "RUN_FAILED",
|
|
2451
|
-
domain: "app",
|
|
2452
|
-
summary,
|
|
2453
|
-
why: error instanceof Error ? error.message : String(error),
|
|
2454
|
-
fix: "Inspect the framework output above, fix the issue, and rerun prisma-cli app run.",
|
|
2455
|
-
exitCode,
|
|
2456
|
-
nextSteps: ["prisma-cli app run"]
|
|
2457
|
-
});
|
|
2458
|
-
}
|
|
2459
|
-
function formatFrameworkName(framework) {
|
|
2460
|
-
return framework === "nextjs" ? "Next.js" : "Bun";
|
|
2461
|
-
}
|
|
2462
|
-
function isAutoBuildDetectionError(error) {
|
|
2463
|
-
return error instanceof Error && error.message.startsWith("Entrypoint is required.");
|
|
2464
|
-
}
|
|
2465
|
-
function formatBuildTypeName(buildType) {
|
|
2466
|
-
if (buildType === "auto") return "Auto";
|
|
2467
|
-
for (let index = FRAMEWORKS.length - 1; index >= 0; index -= 1) {
|
|
2468
|
-
const framework = FRAMEWORKS[index];
|
|
2469
|
-
if (framework?.buildType === buildType) return framework.displayName;
|
|
2470
|
-
}
|
|
2471
|
-
return buildType;
|
|
2472
|
-
}
|
|
2473
|
-
function removeFailedError(summary, error, nextSteps) {
|
|
2474
|
-
return new CliError({
|
|
2475
|
-
code: "REMOVE_FAILED",
|
|
2476
|
-
domain: "app",
|
|
2477
|
-
summary,
|
|
2478
|
-
why: error instanceof Error ? error.message : String(error),
|
|
2479
|
-
fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
|
|
2480
|
-
debug: formatDebugDetails(error),
|
|
2481
|
-
exitCode: 1,
|
|
2482
|
-
nextSteps
|
|
2483
|
-
});
|
|
2484
|
-
}
|
|
2485
|
-
function localStateCleanupWarning(target, error) {
|
|
2486
|
-
return `The app was removed remotely, but the local ${target} state could not be cleared: ${error instanceof Error ? error.message : String(error)}`;
|
|
2487
|
-
}
|
|
2488
|
-
function formatDebugDetails(error) {
|
|
2489
|
-
if (error instanceof Error) return error.stack ?? error.message;
|
|
2490
|
-
return typeof error === "string" ? error : null;
|
|
2491
|
-
}
|
|
2492
|
-
function isMissingProjectError(error) {
|
|
2493
|
-
return error instanceof Error && error.message === "Resource Not Found";
|
|
2494
|
-
}
|
|
2495
|
-
function findAppByName(apps, name) {
|
|
2496
|
-
return apps.find((app) => app.name === name);
|
|
2497
|
-
}
|
|
2498
|
-
function findAppsByName(apps, name) {
|
|
2499
|
-
return apps.filter((app) => app.name === name);
|
|
2500
|
-
}
|
|
2501
|
-
function sortApps(apps) {
|
|
2502
|
-
return apps.slice().sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
|
|
2503
|
-
}
|
|
2504
|
-
function toOptionalEnvVars(envVars) {
|
|
2505
|
-
return Object.keys(envVars).length > 0 ? envVars : void 0;
|
|
2506
|
-
}
|
|
2507
|
-
//#endregion
|
|
2508
|
-
export { detectDeployFramework, runAppBuild, runAppDeploy, runAppDomainAdd, runAppDomainRemove, runAppDomainRetry, runAppDomainShow, runAppDomainWait, runAppListDeploys, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy };
|